最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

面試官:Java中new Object()到底占用幾個字節(jié)

 更新時間:2021年02月25日 14:31:03   投稿:mrr  
這篇文章主要介紹了面試官:Java中new Object()到底占用幾個字節(jié),本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下

前言

我們來分析一下堆內(nèi)布局以及Java對象在內(nèi)存中的布局吧。

對象的指向

先來看一段代碼:

package com.zwx.jvm;

public class HeapMemory {
  private Object obj1 = new Object();

  public static void main(String[] args) {
    Object obj2 = new Object();
  }
}

上面的代碼中,obj1 和obj2在內(nèi)存中有什么區(qū)別?

我們先來回憶一下JVM系列1的文章中有提到,方法區(qū)存儲每個類的結(jié)構(gòu),比如:運行時常量池、屬性和方法數(shù)據(jù),以及方法和構(gòu)造函數(shù)等數(shù)據(jù)。所以我們這個obj1是存在方法區(qū)的,而new會創(chuàng)建一個對象實例,對象實例是存儲在內(nèi)的,于是就有了下面這幅圖(方法區(qū)指向堆):

而obj2 是屬于方法內(nèi)的局部變量,存儲在Java虛擬機棧內(nèi)的棧幀中的局部變量表內(nèi),這就是經(jīng)典的棧指向堆:

這里我們再來思考一下,我們一個變量指向了堆,而堆內(nèi)只是存儲了一個實例對象,那么堆內(nèi)的示例對象是如何知道自己屬于哪個Class,也就是說這個實例是如何知道自己所對應(yīng)的類元信息的呢?這就涉及到了一個Java對象在內(nèi)存中是如何布局的。

Java內(nèi)存模型

對象內(nèi)存中可以分為三塊區(qū)域:對象頭(Header),實例數(shù)據(jù)(Instance Data)和對齊填充(Padding),以64位操作系統(tǒng)為例(未開啟指針壓縮的情況)Java對象布局如下圖所示:

其中對象頭中的Mark Word中的詳細(xì)信息在文章synchronized鎖升級原理中有詳細(xì)介紹。上圖中的對齊填充不是一定有的,如果對象頭和實例數(shù)據(jù)加起來剛好是8字節(jié)的倍數(shù),那么就不需要對齊填充。

知道了Java內(nèi)存布局,那么我們來看一個面試問題

Object obj=new Object()占用字節(jié)

這是網(wǎng)上很多人都會提到的一個問題,那么結(jié)合上面的Java內(nèi)存布局,我們來分析下,以64位操作系統(tǒng)為例,new Object()占用大小分為兩種情況:

  • 未開啟指針壓縮        占用大小為:8(Mark Word)+8(Class Pointer)=16字節(jié)
  • 開啟了指針壓縮(默認(rèn)是開啟的)        開啟指針壓縮后,Class Pointer會被壓縮為4字節(jié),最終大小為:8(Mark Word)+4(Class Pointer)+4(對齊填充)=16字節(jié)

結(jié)果到底是不是這個呢?我們來驗證一下。首先引入一個pom依賴:

<dependency>
      <groupId>org.openjdk.jol</groupId>
      <artifactId>jol-core</artifactId>
      <version>0.10</version>
    </dependency>

然后新建一個簡單的demo:

package com.zwx.jvm;

import org.openjdk.jol.info.ClassLayout;

public class HeapMemory {
  public static void main(String[] args) {
    Object obj = new Object();
    System.out.println(ClassLayout.parseInstance(obj).toPrintable());
  }
}

輸出結(jié)果如下:

最后的結(jié)果是16字節(jié),沒有問題,這是因為默認(rèn)開啟了指針壓縮,那我們現(xiàn)在把指針壓縮關(guān)閉之后再去試試。

-XX:+UseCompressedOops 開啟指針壓縮
-XX:-UseCompressedOops 關(guān)閉指針壓縮

再次運行,得到如下結(jié)果:

可以看到,這時候已經(jīng)沒有了對齊填充部分了,但是占用大小還是16位。

下面我們再來演示一下如果一個對象中帶有屬性之后的大小。

新建一個類,內(nèi)部只有一個byte屬性:

package com.zwx.jvm;

public class MyItem {
  byte i = 0;
}

然后分別在開啟指針壓縮和關(guān)閉指針壓縮的場景下分別輸出這個類的大小。

package com.zwx.jvm;

import org.openjdk.jol.info.ClassLayout;

public class HeapMemory {
  public static void main(String[] args) {
    MyItem myItem = new MyItem();
    System.out.println(ClassLayout.parseInstance(myItem).toPrintable());
  }
}

開啟指針壓縮,占用16字節(jié):

關(guān)閉指針壓縮,占用24字節(jié):

這個時候就能看出來開啟了指針壓縮的優(yōu)勢了,如果不斷創(chuàng)建大量對象,指針壓縮對性能還是有一定優(yōu)化的。

對象的訪問

創(chuàng)建好一個對象之后,當(dāng)然需要去訪問它,那么當(dāng)我們需要訪問一個對象的時候,是如何定位到對象的呢?目前最主流的訪問對象方式有兩種:句柄訪問和直接指針訪問。

句柄訪問        使用句柄訪問的話,Java虛擬機會在堆內(nèi)劃分出一塊內(nèi)存來存儲句柄池,那么對象當(dāng)中存儲的就是句柄地址,然后句柄池中才會存儲對象實例數(shù)據(jù)和對象類型數(shù)據(jù)地址。

直接指針訪問(Hot Spot虛擬機采用的方式)        直接指針訪問的話對象中就會直接存儲對象類型數(shù)據(jù)。

句柄訪問和直接指針訪問對比

上面圖形中我們很容易對比,就是如果使用句柄訪問的時候,會多了一次指針定位,但是他也有一個好處就是,假如一個對象被移動(地址改變了),那么只需要改變句柄池的指向就可以了,不需要修改reference對象內(nèi)的指向,而如果使用直接指針訪問,就還需要到局部變量表內(nèi)修改reference指向。

堆內(nèi)存

上面我們提到,在Java對象頭當(dāng)中的Mark Word存儲了對象的分代年齡,那么什么是分代年齡呢?

一個對象的分代年齡可以理解為垃圾回收次數(shù),當(dāng)一個對象經(jīng)過一次垃圾回收之后還存在,那么分代年齡就會加1,在64位的虛擬機中,分代年齡占了4位,最大值為15。分代年齡默認(rèn)為0000,隨著垃圾回收次數(shù),會逐漸遞增。

Java堆內(nèi)存中按照分代年齡來劃分,分為Young區(qū)和Old區(qū),對象分配首先會到Y(jié)oung區(qū),達(dá)到一定分代年齡(-XX:MaxTenuringThreshold可以設(shè)置大小,默認(rèn)為15)就會進入Old區(qū)(注意:如果一個對象太大,那么就會直接進入Old區(qū))。

之所以會這么劃分是因為如果整個堆只有一個區(qū)的話,那么垃圾回收的時候每次都需要把堆內(nèi)所有對象都掃描一遍,浪費性能。而其實大部分Java對象的生命周期都是很短的,一旦一個對象回收很多次都回收不掉,可以認(rèn)為下一次垃圾回收的時候可能也回收不掉,所以Young區(qū)和Old區(qū)的垃圾回收可以分開進行,只有當(dāng)Young區(qū)在進行垃圾回收之后還是沒有騰出空間,那么再去觸發(fā)Old區(qū)的垃圾回收。

Young區(qū)

現(xiàn)在拆分成了Young區(qū),那我們看下面一個場景,下面的Young是經(jīng)過垃圾回收之后的一個概圖:

假如說現(xiàn)在來了一個對象,要占用2個對象的大小,會發(fā)現(xiàn)放不下去了,這時候就會觸發(fā)GC(垃圾回收),但是一旦觸發(fā)了GC(垃圾回收),對用戶線程是有影響的,因為GC過程中為了確保對象引用不會不斷變化,需要停止所有用戶線程,Sun把這個事件稱之為:Stop the World(STW)。這些在下一篇講解垃圾回收的時候會詳細(xì)介紹,這里先不深入。

所以說一般是越少GC越好,而實際上上圖中可以看到至少還可以放入3個對象,只要按照對象都按照順序放好,那么是可以放得下的,所以這就產(chǎn)生了問題了,明明有空間,但是因為空間不連續(xù),導(dǎo)致對象申請內(nèi)存失敗,導(dǎo)致觸發(fā)GC了,那么如何解決這種問題呢?

解決的思路就是把Young區(qū)的對象按順序放好,所以就產(chǎn)生了一個方法,把Young區(qū)再次劃分一下,分為2個區(qū):Eden區(qū)和Survivor區(qū)。

具體操作是:一個對象來了之后,先分配到Eden區(qū),Eden區(qū)滿了之后,觸發(fā)GC,經(jīng)過GC之后,為了防止空間不連續(xù),把幸存下來的對象復(fù)制到Survivor區(qū),然后Eden區(qū)就可以完整清理掉了,當(dāng)然這么做是有一個前提的,就是大部分對象都是生命周期極短的,基本一次垃圾回收就可以把Eden區(qū)大部分對象回收掉(這個前提是經(jīng)過測試總結(jié)得到的)。

觸發(fā)GC的時候Survivor區(qū)也會一起回收,并不是說單獨只觸發(fā)Eden區(qū),但是這樣問題又來了,Eden區(qū)是保證空間基本連續(xù)了,但是Survivor區(qū)又可能產(chǎn)生空間碎片,導(dǎo)致不連續(xù)了,所以就又把Survivor區(qū)給一分為二了:

這個時候工作流程又變成這樣了:首先還是在Eden區(qū)分配空間,Eden區(qū)滿了之后觸發(fā)GC,GC之后把幸存對象 復(fù)制到S0區(qū)(S1區(qū)是空的),然后繼續(xù)在Eden區(qū)分配對象,再次觸發(fā)GC之后如果發(fā)現(xiàn)S0區(qū)放不下了(產(chǎn)生空間碎片,實際還有空間),那么就把S0區(qū)對象復(fù)制到S1區(qū),并把幸存對象也復(fù)制到S1區(qū),這時候S0區(qū)是空的了,并依次反復(fù)操作,假如說S0區(qū)或者S1區(qū)空間對象復(fù)制移動了之后還是放不下,那就說明這時候是真的滿了,那就去老年區(qū)借點空間過來(這就是擔(dān)保機制,老年代需要提供這種空間分配擔(dān)保),假如說老年區(qū)空間也不夠了,那就會觸發(fā)Full GC,如果還是不夠,那就會拋出OutOfMemeoyError異常了。

注意:為了確保S0和S1兩個區(qū)域之間每次復(fù)制都能順利進行,S0和S1兩個區(qū)的大小必須要保持一致,而且同一時間有一個區(qū)域一定是空的。雖然說這種做法是會導(dǎo)致了一小部分空間的浪費,但是綜合其他性能的提升來說,是值得的。

Old區(qū)

當(dāng)Young區(qū)的對象達(dá)到設(shè)置的分代年齡之后,對象會進入Old區(qū),Old區(qū)滿了之后會觸發(fā)Full GC,如果還是清理不掉空間,那么就拋出OutOfMemeoyError異常。

名詞掃盲

上面提到了很多新的名詞,而實際上很多這種名詞還有其他叫法,這個還是覺得有必要了解一下。

垃圾回收:簡稱GC。

Minor GC:針對新生代的GC

Major GC:針對老年代的GC,一般老年代觸發(fā)GC的同時也會觸發(fā)Minor GC,也就等于觸發(fā)了Full GC。

Full GC:新生代+老年代同時發(fā)生GC。

Young區(qū):新生代

Old區(qū):老年代

Eden區(qū):暫時沒發(fā)現(xiàn)有什么中文翻譯(伊甸園?)

Surcivor區(qū):幸存區(qū)

S0和S1:也稱之為from區(qū)和to區(qū),注意from和to兩個區(qū)是不斷互換身份的,且S0和S1一定要相等,并且保證一塊區(qū)域是空的

一個對象的人生軌跡圖

從上面的介紹大家應(yīng)該有一個大致的印象,一個對象會在Eden區(qū),S0區(qū),S1區(qū),Old區(qū)不斷流轉(zhuǎn)(當(dāng)然,一開始就會被回收的短命對象除外),我們可以得到下面的一個流程圖:

總結(jié)

本文主要介紹了一個Java對象在堆內(nèi)是如何存儲的,并結(jié)合Java對象的內(nèi)存布局示范了一個普通對象占用大小問題,然后還分析了堆內(nèi)的空間劃分以及劃分原因,本文中涉及到了GC相關(guān)知識均沒有深入講解,關(guān)于GC及GC算法和GC收集器等相關(guān)知識將放在下一篇進行詳細(xì)分析。

到此這篇關(guān)于面試官:Java中new Object()到底占用幾個字節(jié)的文章就介紹到這了,更多相關(guān)Java new Object()字節(jié)內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 一文搞懂spring boot本地事務(wù)@Transactional參數(shù)

    一文搞懂spring boot本地事務(wù)@Transactional參數(shù)

    這篇文章主要介紹了spring boot本地事務(wù)@Transactional參數(shù)詳解,本文通過示例代碼圖文相結(jié)合給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2021-10-10
  • SpringBoot靜態(tài)資源的訪問方法詳細(xì)介紹

    SpringBoot靜態(tài)資源的訪問方法詳細(xì)介紹

    最近在做SpringBoot項目的時候遇到了“白頁”問題,通過查資料對SpringBoot訪問靜態(tài)資源做了總結(jié),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)吧
    2022-09-09
  • spring boot+ redis 接口訪問頻率限制的實現(xiàn)

    spring boot+ redis 接口訪問頻率限制的實現(xiàn)

    這篇文章主要介紹了spring boot+ redis 接口訪問頻率限制的實現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2021-01-01
  • 一次Spring無法啟動的問題排查實戰(zhàn)之字節(jié)碼篇

    一次Spring無法啟動的問題排查實戰(zhàn)之字節(jié)碼篇

    最近學(xué)習(xí)了spring相關(guān)知識,公司項目也用到了spring,下面這篇文章主要給大家介紹了一次Spring無法啟動的問題排查實戰(zhàn)之字節(jié)碼篇的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2022-04-04
  • JavaWeb實現(xiàn)RSA+AES混合加密

    JavaWeb實現(xiàn)RSA+AES混合加密

    RSA+AES的混合加密時,AES用于給傳輸?shù)臄?shù)據(jù)加密,然后通過RSA給AES的秘鑰加密,本文就來詳細(xì)的介紹一下如何實現(xiàn),具有一定的參考價值,感興趣的可以了解一下
    2023-10-10
  • 圖解排序算法之希爾排序Java實現(xiàn)

    圖解排序算法之希爾排序Java實現(xiàn)

    希爾排序是希爾(Donald Shell)于1959年提出的一種排序算法。希爾排序也是一種插入排序,它是簡單插入排序經(jīng)過改進之后的一個更高效的版本,也稱為縮小增量排序,同時該算法是沖破O(n2)的第一批算法之一。本文會以圖解的方式詳細(xì)介紹希爾排序的基本思想及其代碼實現(xiàn)
    2021-06-06
  • SpringBoot中Bean拷貝及工具類封裝的實現(xiàn)

    SpringBoot中Bean拷貝及工具類封裝的實現(xiàn)

    本文主要介紹了SpringBoot中Bean拷貝及工具類封裝的實現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-05-05
  • SpringBoot操作Jedis案例代碼

    SpringBoot操作Jedis案例代碼

    這篇文章主要介紹了SpringBoot操作Jedis案例代碼,代碼部分包括pom依賴、配置相關(guān)參數(shù)、JedisPool的設(shè)置,代碼簡單易懂對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2023-08-08
  • 使用idea插件進行java代碼生成的操作

    使用idea插件進行java代碼生成的操作

    這篇文章主要介紹了使用idea插件進行java代碼生成的操作,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-10-10
  • spring中使用Mockito解決Bean依賴樹問題方法

    spring中使用Mockito解決Bean依賴樹問題方法

    在本篇文章里小編給各位整理了關(guān)于spring中使用Mockito解決Bean依賴樹問題方法,有需要的朋友們可以參考下。
    2020-01-01

最新評論

稷山县| 镇原县| 南开区| 闽清县| 安康市| 大姚县| 肇庆市| 文成县| 盈江县| 遂溪县| 石狮市| 盐源县| 应城市| 磐石市| 金湖县| 东丰县| 牟定县| 黑龙江省| 岚皋县| 商城县| 临清市| 称多县| 广水市| 永春县| 三原县| 瑞昌市| 肃南| 平原县| 色达县| 通州市| 榆中县| 涡阳县| 宜宾县| 金坛市| 黄冈市| 恩施市| 温泉县| 宜兰县| 克什克腾旗| 永清县| 基隆市|