国产成人精品亚洲777人妖,欧美日韩精品一区视频,最新亚洲国产,国产乱码精品一区二区亚洲

您的位置:首頁(yè)技術(shù)文章
文章詳情頁(yè)

一個(gè)Java對(duì)象到底占多大內(nèi)存?

瀏覽:80日期:2022-09-06 09:21:43

最近在讀《深入理解Java虛擬機(jī)》,對(duì)Java對(duì)象的內(nèi)存布局有了進(jìn)一步的認(rèn)識(shí),于是腦子里自然而然就有一個(gè)很普通的問(wèn)題,就是一個(gè)Java對(duì)象到底占用多大內(nèi)存?

在網(wǎng)上搜到了一篇博客講的非常好:http://yueyemaitian.iteye.com/blog/2033046,里面提供的這個(gè)類也非常實(shí)用:

import java.lang.instrument.Instrumentation; import java.lang.reflect.Array; import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.util.ArrayDeque; import java.util.Deque; import java.util.HashSet; import java.util.Set; /** * 對(duì)象占用字節(jié)大小工具類 * * @author tianmai.fh * @date 2014-03-18 11:29 */ public class SizeOfObject { static Instrumentation inst;public static void premain(String args, Instrumentation instP) { inst = instP; }/** * 直接計(jì)算當(dāng)前對(duì)象占用空間大小,包括當(dāng)前類及超類的基本類型實(shí)例字段大小、<br></br> * 引用類型實(shí)例字段引用大小、實(shí)例基本類型數(shù)組總占用空間、實(shí)例引用類型數(shù)組引用本身占用空間大小;<br></br> * 但是不包括超類繼承下來(lái)的和當(dāng)前類聲明的實(shí)例引用字段的對(duì)象本身的大小、實(shí)例引用數(shù)組引用的對(duì)象本身的大小 <br></br> * * @param obj * @return */ public static long sizeOf(Object obj) { return inst.getObjectSize(obj); }/** * 遞歸計(jì)算當(dāng)前對(duì)象占用空間總大小,包括當(dāng)前類和超類的實(shí)例字段大小以及實(shí)例字段引用對(duì)象大小 * * @param objP * @return * @throws IllegalAccessException */ public static long fullSizeOf(Object objP) throws IllegalAccessException { Set<Object> visited = new HashSet<Object>(); Deque<Object> toBeQueue = new ArrayDeque<Object>(); toBeQueue.add(objP); long size = 0L; while (toBeQueue.size() > 0) { Object obj = toBeQueue.poll(); //sizeOf的時(shí)候已經(jīng)計(jì)基本類型和引用的長(zhǎng)度,包括數(shù)組 size += skipObject(visited, obj) ? 0L : sizeOf(obj); Class<?> tmpObjClass = obj.getClass(); if (tmpObjClass.isArray()) { //[I , [F 基本類型名字長(zhǎng)度是2 if (tmpObjClass.getName().length() > 2) { for (int i = 0, len = Array.getLength(obj); i < len; i++) { Object tmp = Array.get(obj, i); if (tmp != null) { //非基本類型需要深度遍歷其對(duì)象 toBeQueue.add(Array.get(obj, i)); } } } } else { while (tmpObjClass != null) { Field[] fields = tmpObjClass.getDeclaredFields(); for (Field field : fields) { if (Modifier.isStatic(field.getModifiers()) //靜態(tài)不計(jì) || field.getType().isPrimitive()) { //基本類型不重復(fù)計(jì) continue; } field.setAccessible(true); Object fieldValue = field.get(obj); if (fieldValue == null) { continue; } toBeQueue.add(fieldValue); } tmpObjClass = tmpObjClass.getSuperclass(); } } } return size; }/** * String.intern的對(duì)象不計(jì);計(jì)算過(guò)的不計(jì),也避免死循環(huán) * * @param visited * @param obj * @return */ static boolean skipObject(Set<Object> visited, Object obj) { if (obj instanceof String && obj == ((String) obj).intern()) { return true; } return visited.contains(obj); } }

大家可以用這個(gè)代碼邊看邊驗(yàn)證,注意的是,運(yùn)行這個(gè)程序需要通過(guò)javaagent注入Instrumentation,具體可以看原博客。我今天主要是總結(jié)下手動(dòng)計(jì)算Java對(duì)象占用字節(jié)數(shù)的基本規(guī)則,做為基本的技能必須get√,希望能幫到和我一樣的Java菜鳥。

在介紹之前,簡(jiǎn)單回顧下,Java對(duì)象的內(nèi)存布局:對(duì)象頭(Header),實(shí)例數(shù)據(jù)(Instance Data)和對(duì)齊填充(Padding),詳細(xì)的可以看我的讀書筆記。另外:不同的環(huán)境結(jié)果可能有差異,我所在的環(huán)境是HotSpot虛擬機(jī),64位Windwos。

下面進(jìn)入正文:

對(duì)象頭

對(duì)象頭在32位系統(tǒng)上占用8bytes,64位系統(tǒng)上占用16bytes。

一個(gè)Java對(duì)象到底占多大內(nèi)存?

一個(gè)Java對(duì)象到底占多大內(nèi)存?

實(shí)例數(shù)據(jù)

原生類型(primitive type)的內(nèi)存占用如下:

Primitive TypeMemory Required(bytes)boolean1byte1short2char2int4float4long8double8

reference類型在32位系統(tǒng)上每個(gè)占用4bytes, 在64位系統(tǒng)上每個(gè)占用8bytes。

對(duì)齊填充

HotSpot的對(duì)齊方式為8字節(jié)對(duì)齊:

(對(duì)象頭 + 實(shí)例數(shù)據(jù) + padding) % 8等于0且0 <= padding < 8

指針壓縮

對(duì)象占用的內(nèi)存大小收到VM參數(shù)UseCompressedOops的影響。

1)對(duì)對(duì)象頭的影響

開啟(-XX:+UseCompressedOops)對(duì)象頭大小為12bytes(64位機(jī)器)。

static class A {int a; }

A對(duì)象占用內(nèi)存情況:

關(guān)閉指針壓縮: 16+4=20不是8的倍數(shù),所以+padding/4=24

一個(gè)Java對(duì)象到底占多大內(nèi)存?

開啟指針壓縮: 12+4=16已經(jīng)是8的倍數(shù)了,不需要再padding。

一個(gè)Java對(duì)象到底占多大內(nèi)存?

2) 對(duì)reference類型的影響

64位機(jī)器上reference類型占用8個(gè)字節(jié),開啟指針壓縮后占用4個(gè)字節(jié)。

static class B2 {int b2a;Integer b2b;}

B2對(duì)象占用內(nèi)存情況:

關(guān)閉指針壓縮: 16+4+8=28不是8的倍數(shù),所以+padding/4=32

一個(gè)Java對(duì)象到底占多大內(nèi)存?

開啟指針壓縮: 12+4+4=20不是8的倍數(shù),所以+padding/4=24

一個(gè)Java對(duì)象到底占多大內(nèi)存?

數(shù)組對(duì)象

64位機(jī)器上,數(shù)組對(duì)象的對(duì)象頭占用24個(gè)字節(jié),啟用壓縮之后占用16個(gè)字節(jié)。之所以比普通對(duì)象占用內(nèi)存多是因?yàn)樾枰~外的空間存儲(chǔ)數(shù)組的長(zhǎng)度。

先考慮下new Integer[0]占用的內(nèi)存大小,長(zhǎng)度為0,即是對(duì)象頭的大小:

未開啟壓縮:24bytes

一個(gè)Java對(duì)象到底占多大內(nèi)存?

開啟壓縮后:16bytes

一個(gè)Java對(duì)象到底占多大內(nèi)存?

接著計(jì)算new Integer[1],new Integer[2],new Integer[3]和new Integer[4]就很容易了:

未開啟壓縮:

一個(gè)Java對(duì)象到底占多大內(nèi)存?

開啟壓縮:

一個(gè)Java對(duì)象到底占多大內(nèi)存?

拿new Integer[3]來(lái)具體解釋下:

未開啟壓縮:24(對(duì)象頭)+8*3=48,不需要padding;

開啟壓縮:16(對(duì)象頭)+3*4=28,+padding/4=32,其他依次類推。

自定義類的數(shù)組也是一樣的,比如:

static class B3 {int a;Integer b; }

new B3[3]占用的內(nèi)存大小:

未開啟壓縮:48

開啟壓縮后:32

復(fù)合對(duì)象

計(jì)算復(fù)合對(duì)象占用內(nèi)存的大小其實(shí)就是運(yùn)用上面幾條規(guī)則,只是麻煩點(diǎn)。

1)對(duì)象本身的大小

直接計(jì)算當(dāng)前對(duì)象占用空間大小,包括當(dāng)前類及超類的基本類型實(shí)例字段大小、引用類型實(shí)例字段引用大小、實(shí)例基本類型數(shù)組總占用空間、實(shí)例引用類型數(shù)組引用本身占用空間大小; 但是不包括超類繼承下來(lái)的和當(dāng)前類聲明的實(shí)例引用字段的對(duì)象本身的大小、實(shí)例引用數(shù)組引用的對(duì)象本身的大小。

static class B {int a;int b; }static class C {int ba;B[] as = new B[3];C() { for (int i = 0; i < as.length; i++) {as[i] = new B(); }} }

未開啟壓縮:16(對(duì)象頭)+4(ba)+8(as引用的大小)+padding/4=32

開啟壓縮:12+4+4+padding/4=24

2)當(dāng)前對(duì)象占用的空間總大小

遞歸計(jì)算當(dāng)前對(duì)象占用空間總大小,包括當(dāng)前類和超類的實(shí)例字段大小以及實(shí)例字段引用對(duì)象大小。

遞歸計(jì)算復(fù)合對(duì)象占用的內(nèi)存的時(shí)候需要注意的是:對(duì)齊填充是以每個(gè)對(duì)象為單位進(jìn)行的,看下面這個(gè)圖就很容易明白。

一個(gè)Java對(duì)象到底占多大內(nèi)存?

現(xiàn)在我們來(lái)手動(dòng)計(jì)算下C對(duì)象占用的全部?jī)?nèi)存是多少,主要是三部分構(gòu)成:C對(duì)象本身的大小+數(shù)組對(duì)象的大小+B對(duì)象的大小。

未開啟壓縮:

(16 + 4 + 8+4(padding)) + (24+ 8*3) +(16+8)*3 = 152bytes

開啟壓縮:

(12 + 4 + 4 +4(padding)) + (16 + 4*3 +4(數(shù)組對(duì)象padding)) + (12+8+4(B對(duì)象padding))*3= 128bytes

大家有興趣的可以試試。

實(shí)際工作中真正需要手動(dòng)計(jì)算對(duì)象大小的場(chǎng)景應(yīng)該很少,但是個(gè)人覺(jué)得做為基礎(chǔ)知識(shí)每個(gè)Java開發(fā)人員都應(yīng)該了解,另外:對(duì)自己寫的代碼大概占用多少內(nèi)存,內(nèi)存中是怎么布局的應(yīng)該有一個(gè)直覺(jué)性的認(rèn)識(shí)。

標(biāo)簽: Java
相關(guān)文章:
主站蜘蛛池模板: 铁力市| 成都市| 孟村| 叶城县| 信丰县| 芜湖市| 谢通门县| 扶绥县| 会同县| 晋江市| 海安县| 类乌齐县| 罗甸县| 建始县| 佳木斯市| 信宜市| 宁波市| 依兰县| 施甸县| 资中县| 东安县| 海门市| 兴山县| 缙云县| 安化县| 封开县| 崇明县| 南丹县| 都匀市| 英山县| 东乡县| 霞浦县| 河西区| 呼图壁县| 巫山县| 屯门区| 吉首市| 家居| 金湖县| 临湘市| 苗栗市|