0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

Object o = new Object() 占用多少字節(jié)?

jf_ro2CN3Fa ? 來源:芋道源碼 ? 2023-11-01 15:47 ? 次閱讀


前言

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

基于 Spring Boot + MyBatis Plus + Vue & Element 實(shí)現(xiàn)的后臺(tái)管理系統(tǒng) + 用戶小程序,支持 RBAC 動(dòng)態(tài)權(quán)限、多租戶、數(shù)據(jù)權(quán)限、工作流、三方登錄、支付、短信、商城等功能

  • 項(xiàng)目地址:https://github.com/YunaiV/ruoyi-vue-pro
  • 視頻教程:https://doc.iocoder.cn/video/

對(duì)象的指向

先來看一段代碼:

packagecom.zwx.jvm;

publicclassHeapMemory{
privateObjectobj1=newObject();

publicstaticvoidmain(String[]args){
Objectobj2=newObject();
}
}

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

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

4bdfef78-7881-11ee-939d-92fbcf53809c.png

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

4beaddd4-7881-11ee-939d-92fbcf53809c.png

這里我們?cè)賮硭伎家幌?,我們一個(gè)變量指向了堆,而堆內(nèi)只是存儲(chǔ)了一個(gè)實(shí)例對(duì)象,那么堆內(nèi)的示例對(duì)象是如何知道自己屬于哪個(gè)Class,也就是說這個(gè)實(shí)例是如何知道自己所對(duì)應(yīng)的類元信息的呢?這就涉及到了一個(gè)Java對(duì)象在內(nèi)存中是如何布局的。

基于 Spring Cloud Alibaba + Gateway + Nacos + RocketMQ + Vue & Element 實(shí)現(xiàn)的后臺(tái)管理系統(tǒng) + 用戶小程序,支持 RBAC 動(dòng)態(tài)權(quán)限、多租戶、數(shù)據(jù)權(quán)限、工作流、三方登錄、支付、短信、商城等功能

  • 項(xiàng)目地址:https://github.com/YunaiV/yudao-cloud
  • 視頻教程:https://doc.iocoder.cn/video/

Java內(nèi)存模型

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

4c000240-7881-11ee-939d-92fbcf53809c.png

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

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

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

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

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

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

<dependency>
<groupId>org.openjdk.jolgroupId>
<artifactId>jol-coreartifactId>
<version>0.10version>
dependency>

然后新建一個(gè)簡單的demo:

packagecom.zwx.jvm;

importorg.openjdk.jol.info.ClassLayout;

publicclassHeapMemory{
publicstaticvoidmain(String[]args){
Objectobj=newObject();
System.out.println(ClassLayout.parseInstance(obj).toPrintable());
}
}

輸出結(jié)果如下:4c14f07e-7881-11ee-939d-92fbcf53809c.png最后的結(jié)果是16字節(jié),沒有問題,這是因?yàn)槟J(rèn)開啟了指針壓縮,那我們現(xiàn)在把指針壓縮關(guān)閉之后再去試試。

-XX:+UseCompressedOops開啟指針壓縮
-XX:-UseCompressedOops關(guān)閉指針壓縮
4c20b5f8-7881-11ee-939d-92fbcf53809c.png

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

4c3aa47c-7881-11ee-939d-92fbcf53809c.png

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

下面我們?cè)賮硌菔疽幌氯绻粋€(gè)對(duì)象中帶有屬性之后的大小。

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

packagecom.zwx.jvm;

publicclassMyItem{
bytei=0;
}

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

packagecom.zwx.jvm;

importorg.openjdk.jol.info.ClassLayout;

publicclassHeapMemory{
publicstaticvoidmain(String[]args){
MyItemmyItem=newMyItem();
System.out.println(ClassLayout.parseInstance(myItem).toPrintable());
}
}

開啟指針壓縮,占用16字節(jié):4c5338fc-7881-11ee-939d-92fbcf53809c.png關(guān)閉指針壓縮,占用24字節(jié):

4c5e2f8c-7881-11ee-939d-92fbcf53809c.png

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

對(duì)象的訪問

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

  • 句柄訪問 使用句柄訪問的話,Java虛擬機(jī)會(huì)在堆內(nèi)劃分出一塊內(nèi)存來存儲(chǔ)句柄池,那么對(duì)象當(dāng)中存儲(chǔ)的就是句柄地址,然后句柄池中才會(huì)存儲(chǔ)對(duì)象實(shí)例數(shù)據(jù)和對(duì)象類型數(shù)據(jù)地址。4c694bc4-7881-11ee-939d-92fbcf53809c.png
  • 直接指針訪問(Hot Spot虛擬機(jī)采用的方式) 直接指針訪問的話對(duì)象中就會(huì)直接存儲(chǔ)對(duì)象類型數(shù)據(jù)。4c752e12-7881-11ee-939d-92fbcf53809c.png

句柄訪問和直接指針訪問對(duì)比

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

堆內(nèi)存

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

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

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

之所以會(huì)這么劃分是因?yàn)槿绻麄€(gè)堆只有一個(gè)區(qū)的話,那么垃圾回收的時(shí)候每次都需要把堆內(nèi)所有對(duì)象都掃描一遍,浪費(fèi)性能。而其實(shí)大部分Java對(duì)象的生命周期都是很短的,一旦一個(gè)對(duì)象回收很多次都回收不掉,可以認(rèn)為下一次垃圾回收的時(shí)候可能也回收不掉,所以Young區(qū)和Old區(qū)的垃圾回收可以分開進(jìn)行,只有當(dāng)Young區(qū)在進(jìn)行垃圾回收之后還是沒有騰出空間,那么再去觸發(fā)Old區(qū)的垃圾回收。4c879a70-7881-11ee-939d-92fbcf53809c.png

Young區(qū)

現(xiàn)在拆分成了Young區(qū),那我們看下面一個(gè)場景,下面的Young是經(jīng)過垃圾回收之后的一個(gè)概圖:4c9cf3ac-7881-11ee-939d-92fbcf53809c.png假如說現(xiàn)在來了一個(gè)對(duì)象,要占用2個(gè)對(duì)象的大小,會(huì)發(fā)現(xiàn)放不下去了,這時(shí)候就會(huì)觸發(fā)GC(垃圾回收),但是一旦觸發(fā)了GC(垃圾回收),對(duì)用戶線程是有影響的,因?yàn)镚C過程中為了確保對(duì)象引用不會(huì)不斷變化,需要停止所有用戶線程,Sun把這個(gè)事件稱之為:Stop the World(STW)。這些在下一篇講解垃圾回收的時(shí)候會(huì)詳細(xì)介紹,這里先不深入。

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

解決的思路就是把Young區(qū)的對(duì)象按順序放好,所以就產(chǎn)生了一個(gè)方法,把Young區(qū)再次劃分一下,分為2個(gè)區(qū):Eden區(qū)Survivor區(qū) 。4ca8eb3a-7881-11ee-939d-92fbcf53809c.png具體操作是:一個(gè)對(duì)象來了之后,先分配到Eden區(qū),Eden區(qū)滿了之后,觸發(fā)GC,經(jīng)過GC之后,為了防止空間不連續(xù),把幸存下來的對(duì)象復(fù)制到Survivor區(qū),然后Eden區(qū)就可以完整清理掉了,當(dāng)然這么做是有一個(gè)前提的,就是大部分對(duì)象都是生命周期極短的,基本一次垃圾回收就可以把Eden區(qū)大部分對(duì)象回收掉 (這個(gè)前提是經(jīng)過測(cè)試總結(jié)得到的)。

觸發(fā)GC的時(shí)候Survivor區(qū)也會(huì)一起回收,并不是說單獨(dú)只觸發(fā)Eden區(qū),但是這樣問題又來了,Eden區(qū)是保證空間基本連續(xù)了,但是Survivor區(qū)又可能產(chǎn)生空間碎片,導(dǎo)致不連續(xù)了,所以就又把Survivor區(qū)給一分為二了:4cc3b302-7881-11ee-939d-92fbcf53809c.png這個(gè)時(shí)候工作流程又變成這樣了:首先還是在Eden區(qū)分配空間,Eden區(qū)滿了之后觸發(fā)GC,GC之后把幸存對(duì)象 復(fù)制到S0區(qū)(S1區(qū)是空的),然后繼續(xù)在Eden區(qū)分配對(duì)象,再次觸發(fā)GC之后如果發(fā)現(xiàn)S0區(qū)放不下了(產(chǎn)生空間碎片,實(shí)際還有空間),那么就把S0區(qū)對(duì)象復(fù)制到S1區(qū),并把幸存對(duì)象也復(fù)制到S1區(qū),這時(shí)候S0區(qū)是空的了,并依次反復(fù)操作,假如說S0區(qū)或者S1區(qū)空間對(duì)象復(fù)制移動(dòng)了之后還是放不下,那就說明這時(shí)候是真的滿了,那就去老年區(qū)借點(diǎn)空間過來(這就是擔(dān)保機(jī)制 ,老年代需要提供這種空間分配擔(dān)保),假如說老年區(qū)空間也不夠了,那就會(huì)觸發(fā)Full GC,如果還是不夠,那就會(huì)拋出OutOfMemeoyError異常了。

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

Old區(qū)

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

名詞掃盲

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

  • 垃圾回收:簡稱GC。
  • Minor GC:針對(duì)新生代的GC
  • Major GC:針對(duì)老年代的GC,一般老年代觸發(fā)GC的同時(shí)也會(huì)觸發(fā)Minor GC,也就等于觸發(fā)了Full GC。
  • Full GC:新生代+老年代同時(shí)發(fā)生GC。
  • Young區(qū):新生代
  • Old區(qū):老年代
  • Eden區(qū):暫時(shí)沒發(fā)現(xiàn)有什么中文翻譯(伊甸園?)
  • Surcivor區(qū):幸存區(qū)
  • S0和S1:也稱之為from區(qū)和to區(qū),注意from和to兩個(gè)區(qū)是不斷互換身份的,且S0和S1一定要相等,并且保證一塊區(qū)域是空的

一個(gè)對(duì)象的人生軌跡圖

從上面的介紹大家應(yīng)該有一個(gè)大致的印象,一個(gè)對(duì)象會(huì)在Eden區(qū),S0區(qū),S1區(qū),Old區(qū)不斷流轉(zhuǎn)(當(dāng)然,一開始就會(huì)被回收的短命對(duì)象除外),我們可以得到下面的一個(gè)流程圖:4cd80a64-7881-11ee-939d-92fbcf53809c.png

總結(jié)

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



聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • 數(shù)據(jù)
    +關(guān)注

    關(guān)注

    8

    文章

    7048

    瀏覽量

    89078
  • 函數(shù)
    +關(guān)注

    關(guān)注

    3

    文章

    4332

    瀏覽量

    62666
  • 收集器
    +關(guān)注

    關(guān)注

    0

    文章

    30

    瀏覽量

    3147

原文標(biāo)題:Object o = new Object() 占用多少字節(jié)?

文章出處:【微信號(hào):芋道源碼,微信公眾號(hào):芋道源碼】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。

收藏 人收藏

    評(píng)論

    相關(guān)推薦

    在通用的 class 調(diào)用中定義多個(gè) new object

    在通用的 class 調(diào)用中定義多個(gè) new object  在INC中定義如下: if ($con == "") { $q = new
    發(fā)表于 01-11 09:19

    proteus的object作用

    proteus中object有什么作用,在實(shí)際電路中如何連接
    發(fā)表于 06-04 11:15

    Open the RTOS Object View (ROV)

    Go to menu Tools -> RTOS Object View (ROV) to open ROV In RTOS Object View expand lcdSmartRF06EB_CC2650F128.out這個(gè)在ccs中怎么打開?沒找到在哪?希望大神給些建
    發(fā)表于 06-21 17:31

    Vuforia 4.0 beta的Object Recognition功能使用

    ARVR技術(shù)交流群:129340649歡迎加入!Vuforia 4.0 beta終于出來了,其中有個(gè)比較新穎的功能,那就是Object Recognition。這個(gè)功能對(duì)于有些行業(yè)是很有幫助
    發(fā)表于 09-19 18:13

    在STM32上使用cJson或Jansson創(chuàng)建object

    在STM32上使用cJson或Jansson創(chuàng)建object,添加的子object太多時(shí)會(huì)創(chuàng)建失敗,比如生產(chǎn)的字符串總長度為770字節(jié),此時(shí)需要修改startup_stm32xxx.s文件中
    發(fā)表于 01-11 06:25

    Java Object Serialization Spec

    Java Object Serialization SpecificationObject serialization in the Java™system is the process
    發(fā)表于 10-14 17:39 ?7次下載

    Object-Oriented Programming in

    This chapter applies a different programming paradigm to G: Object-Oriented Programming(OOP). New
    發(fā)表于 03-02 14:18 ?28次下載

    什么是CORBA (Common Object Reques

    什么是CORBA (Common Object Request Broker Architecture)  英文縮寫: CORBA (Common Object Request Broker Architecture) 中文譯名: 通用對(duì)象請(qǐng)求
    發(fā)表于 02-22 11:48 ?962次閱讀

    Video-Object-Segmentation-from-MPEG4講解

    本文檔內(nèi)容介紹了基于Video-Object-Segmentation-from-MPEG4講解,供參考
    發(fā)表于 03-15 10:32 ?2次下載

    Object中有哪一些公共方法

    大家在學(xué)習(xí)java的時(shí)候,一定遇到過Object類,因?yàn)樵趈ava單一繼承體系中Object類是根類,所有的類都會(huì)繼承它,并擁有Object的公共方法,意味著在java的面向?qū)ο蟮氖澜缰校袑?duì)象都擁有這些方法,非常通用。
    發(fā)表于 03-02 09:57 ?774次閱讀

    C# 中的 Object 虛方法 怎樣重寫

    在 C# 中 Object 是所有類的基類,所有的結(jié)構(gòu)和類都直接或間接的派生自它。
    發(fā)表于 03-18 11:51 ?1044次閱讀

    圖形界面介紹:GUI上的按鍵是Find/Select Object

    今天我們要介紹的GUI上的按鍵是Find/Select Object Form,這是一個(gè)平時(shí)我們比較常用的一個(gè)按鍵,用于尋找符合要求的Object。在Innovus中,大家可能平時(shí)更喜歡用dbGet
    的頭像 發(fā)表于 05-19 16:43 ?2735次閱讀

    Qt“靈魂”之Meta-Object系統(tǒng)

    Meta-Object即是Qt的元對(duì)象系統(tǒng),下文都以元對(duì)象系統(tǒng)進(jìn)行描述。在Qt中,具有標(biāo)志性特征的則是信號(hào)和槽函數(shù)機(jī)制,該機(jī)制的背后實(shí)現(xiàn)本質(zhì)上則是元對(duì)象系統(tǒng)。編寫Qt代碼的時(shí)候,在定義類的時(shí)候,需要放置一個(gè)Q_OBJECT,為什么呢?后文會(huì)描述到,例如如下代碼。
    的頭像 發(fā)表于 02-10 13:50 ?1993次閱讀

    Object類中的所有方法

    Object 類屬于 java.lang 包,此包下的所有類在使用時(shí)無需手動(dòng)導(dǎo)入,系統(tǒng)會(huì)在程序編譯期間自動(dòng)導(dǎo)入。Object 類是所有類的基類,當(dāng)一個(gè)類沒有直接繼承某個(gè)類時(shí),默認(rèn)繼承Object
    的頭像 發(fā)表于 10-13 11:50 ?540次閱讀
    <b class='flag-5'>Object</b>類中的所有方法

    基于FPGA加速的bird-oid object算法實(shí)現(xiàn)

    Bird-oid object 簡稱Boids模型,是美國的一個(gè)圖形計(jì)算機(jī)科學(xué)家Craig Reynolds在 1986 年開發(fā)出來的。
    的頭像 發(fā)表于 04-09 11:05 ?648次閱讀
    基于FPGA加速的bird-oid <b class='flag-5'>object</b>算法實(shí)現(xiàn)