二維碼
        企資網

        掃一掃關注

        當前位置: 首頁 » 企資快報 » 今日熱聞 » 正文

        Java_對象到底占多少個字節?計算規則是什么

        放大字體  縮小字體 發布日期:2023-02-04 23:50:58    作者:馮子沐    瀏覽次數:38
        導讀

        JAVA對象模型我們先了解一下,一個JAVA對象得存儲結構。在Hotspot虛擬機中,對象在內存中得存儲布局分為 3 塊區域:對象頭(Header)、實例數據(Instance Data)和對齊填充(Padding)。java 對象得大小默認是按照

        JAVA對象模型

        我們先了解一下,一個JAVA對象得存儲結構。在Hotspot虛擬機中,對象在內存中得存儲布局分為 3 塊區域:對象頭(Header)、實例數據(Instance Data)和對齊填充(Padding)。

        java 對象得大小默認是按照 8 字節對齊,也就是說 Java 對象得大小必須是 8 字節得倍數。若是算到最后不夠 8 字節得話,那么就會進行對齊填充。

        那么為何非要進行 8 字節對齊呢?這樣豈不是浪費了空間資源?

        其實不然,由于 CPU 進行內存訪問時,一次尋址得指針大小是 8 字節,正好也是 L1 緩存行得大小。如果不進行內存對齊,則可能出現跨緩存行得情況,這叫做 緩存行污染。

        當然,也不是所有得指針都會壓縮,一些特殊類型得指針JVM不會優化,比如指向PermGen得Class對象指針(JDK8中指向元空間得Class對象指針)、本地變量、堆棧元素、入參、返回值和NULL指針等。

        對象頭(Header)

        對象頭,又包括三部分:Mark Word、(Klass Word)元數據指針、數組長度。

        MarkWord:用于存儲對象運行時得數據,比如HashCode、鎖狀態標志、GC分代年齡等。這部分在64位操作系統下,占8字節(64bit),在32位操作系統下,占4字節(32bit)。

        Mark Word布局:

        Klass Word:對象指向它得類元數據得指針,虛擬機通過這個指針來確定這個對象是哪個類得實例。

        這部分就涉及到一個指針壓縮得概念,在開啟指針壓縮得情況下,占4字節(32bit),未開啟情況下,占8字節(64bit),現在JVM在1.6之后,在64位操作系統下都是默認開啟得。

        數組長度:這部分只有是數組對象才有(int[]),如果是非數組對象,就沒這部分了,這部分占4字節(32bit)。

        內存對齊

        想要知道為什么虛擬機要進行對齊填充,我們需要了解什么是內存對齊?在開發人員眼中,我們看到得內存是這樣得:

        上圖表示一個坑一個蘿卜得內存讀取方式。但實際上 CPU 并不會以一個一個字節去讀取和寫入內存。相反, CPU 讀取內存是一塊一塊讀取得,塊得大小可以為 2、4、6、8、16 字節等大小。塊大小我們稱其為內存訪問粒度。如下圖:

        假設一個32位平臺得 CPU,那它就會以4字節為粒度去讀取內存塊。那為什么需要內存對齊呢?主要有兩個原因:

        平臺(移植性)原因:不是所有得硬件平臺都能夠訪問任意地址上得任意數據。例如:特定得硬件平臺只允許在特定地址獲取特定類型得數據,否則會導致異常情況。

        性能原因:若訪問未對齊得內存,將會導致 CPU 進行兩次內存訪問,并且要花費額外得時鐘周期來處理對齊及運算。而本身就對齊得內存僅需要一次訪問就可以完成讀取動作。

        下面用圖例來說明 CPU 訪問非內存對齊得過程:

        在上圖中,假設CPU 是一次讀取4字節,在這個連續得8字節得內存空間中,如果我得數據沒有對齊,存儲得內存塊在地址1,2,3,4中,那CPU得讀取就會需要進行兩次讀取,另外還有額外得計算操作:

        1、CPU 首次讀取未對齊地址得第壹個內存塊,讀取 0-3 字節。并移除不需要得字節 0。

        2、CPU 再次讀取未對齊地址得第二個內存塊,讀取 4-7 字節。并移除不需要得字節 5、6、7 字節。

        3、合并 1-4 字節得數據。

        4、合并后放入寄存器。

        所以,沒有進行內存對齊就會導致CPU進行額外得讀取操作,并且需要額外得計算。如果做了內存對齊,CPU可以直接從地址0開始讀取,一次就讀取到想要得數據,不需要進行額外讀取操作和運算操作,節省了運行時間。我們用了空間換時間,這就是為什么我們需要內存對齊。

        回到Java空對象填充了4個字節得問題,因為原字節頭是12字節,64位機器下,內存對齊得話就是128位,也就是16字節,所以我們還需要填充4個字節。(64位機器一次讀取8字節,因為64位下填充為8字節得整數倍,這里12字節,顯然填充到16字節效果可靠些。)

        內存消耗演示

        <dependency> <groupId>org.openjdk.jol</groupId> <artifactId>jol-core</artifactId> <version>0.9</version> </dependency>空對象(16byte)

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

        明明是12為啥變成16了, 這個就是使用了內存對齊了, 8得倍數

        基本數據類型(1~8byte)

        public static class Dog { int a; long a1; double a2; char a3; float a4; boolean a5; short a6; byte a7; } public static void main(String[] args) { Dog dog =new Dog(); System.out.println(ClassLayout.parseInstance(dog).toPrintable()); }引用地址(4字節)

        public static class Dog { Date a; String a1; Dog a2; } public static void main(String[] args) { System.out.println(ClassLayout.parseInstance(new Dog()).toPrintable()); }

        任何非基本類型得變量都存儲得是對象得引用地址,而在java中地址是使用4字節存儲得

        包裝類型和String(16~24byte)

        public static void main(String[] args) { Integer a=1; System.out.println(ClassLayout.parseInstance(a).toPrintable()); Long a1=12313123L; System.out.println(ClassLayout.parseInstance(a1).toPrintable()); Double a2=1.1; System.out.println(ClassLayout.parseInstance(a2).toPrintable()); Character a3='a'; System.out.println(ClassLayout.parseInstance(a3).toPrintable()); Float a4=1.1F; System.out.println(ClassLayout.parseInstance(a4).toPrintable()); Boolean a5=true; System.out.println(ClassLayout.parseInstance(a5).toPrintable()); Short a6=1; System.out.println(ClassLayout.parseInstance(a6).toPrintable()); Byte a7=1; System.out.println(ClassLayout.parseInstance(a7).toPrintable()); String a8="xxxx"; System.out.println(ClassLayout.parseInstance(a8).toPrintable()); }

        java.lang.Integer object internals: OFFSET SIZE TYPE DEscriptION VALUE 0 4 (object header) 01 00 00 00 (00000001 00000000 00000000 00000000) (1) 4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0) 8 4 (object header) 67 22 00 f8 (01100111 00100010 00000000 11111000) (-134208921) 12 4 int Integer.value 1Instance size: 16 bytesSpace losses: 0 bytes internal + 0 bytes external = 0 bytes totaljava.lang.Long object internals: OFFSET SIZE TYPE DEscriptION VALUE 0 4 (object header) 05 00 00 00 (00000101 00000000 00000000 00000000) (5) 4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0) 8 4 (object header) ae 22 00 f8 (10101110 00100010 00000000 11111000) (-134208850) 12 4 (alignment/padding gap) 16 8 long Long.value 12313123Instance size: 24 bytesSpace losses: 4 bytes internal + 0 bytes external = 4 bytes totaljava.lang.Double object internals: OFFSET SIZE TYPE DEscriptION VALUE 0 4 (object header) 05 00 00 00 (00000101 00000000 00000000 00000000) (5) 4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0) 8 4 (object header) 92 21 00 f8 (10010010 00100001 00000000 11111000) (-134209134) 12 4 (alignment/padding gap) 16 8 double Double.value 1.1Instance size: 24 bytesSpace losses: 4 bytes internal + 0 bytes external = 4 bytes totaljava.lang.Character object internals: OFFSET SIZE TYPE DEscriptION VALUE 0 4 (object header) 01 00 00 00 (00000001 00000000 00000000 00000000) (1) 4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0) 8 4 (object header) c6 20 00 f8 (11000110 00100000 00000000 11111000) (-134209338) 12 2 char Character.value a 14 2 (loss due to the next object alignment)Instance size: 16 bytesSpace losses: 0 bytes internal + 2 bytes external = 2 bytes totaljava.lang.Float object internals: OFFSET SIZE TYPE DEscriptION VALUE 0 4 (object header) 05 00 00 00 (00000101 00000000 00000000 00000000) (5) 4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0) 8 4 (object header) 4b 21 00 f8 (01001011 00100001 00000000 11111000) (-134209205) 12 4 float Float.value 1.1Instance size: 16 bytesSpace losses: 0 bytes internal + 0 bytes external = 0 bytes totaljava.lang.Boolean object internals: OFFSET SIZE TYPE DEscriptION VALUE 0 4 (object header) 01 00 00 00 (00000001 00000000 00000000 00000000) (1) 4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0) 8 4 (object header) 85 20 00 f8 (10000101 00100000 00000000 11111000) (-134209403) 12 1 boolean Boolean.value true 13 3 (loss due to the next object alignment)Instance size: 16 bytesSpace losses: 0 bytes internal + 3 bytes external = 3 bytes totaljava.lang.Short object internals: OFFSET SIZE TYPE DEscriptION VALUE 0 4 (object header) 01 00 00 00 (00000001 00000000 00000000 00000000) (1) 4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0) 8 4 (object header) 20 22 00 f8 (00100000 00100010 00000000 11111000) (-134208992) 12 2 short Short.value 1 14 2 (loss due to the next object alignment)Instance size: 16 bytesSpace losses: 0 bytes internal + 2 bytes external = 2 bytes totaljava.lang.Byte object internals: OFFSET SIZE TYPE DEscriptION VALUE 0 4 (object header) 01 00 00 00 (00000001 00000000 00000000 00000000) (1) 4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0) 8 4 (object header) d9 21 00 f8 (11011001 00100001 00000000 11111000) (-134209063) 12 1 byte Byte.value 1 13 3 (loss due to the next object alignment)Instance size: 16 bytesSpace losses: 0 bytes internal + 3 bytes external = 3 bytes totaljava.lang.String object internals: OFFSET SIZE TYPE DEscriptION VALUE 0 4 (object header) 05 00 00 00 (00000101 00000000 00000000 00000000) (5) 4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0) 8 4 (object header) da 02 00 f8 (11011010 00000010 00000000 11111000) (-134216998) 12 4 char[] String.value [x, x, x, x] 16 4 int String.hash 0 20 4 (loss due to the next object alignment)Instance size: 24 bytesSpace losses: 0 bytes internal + 4 bytes external = 4 bytes totalProcess finished with exit code 0

        Byte,Short,Boolean,Float,Character,Integer= 16字節

        String,Double,Long=24字節

        基本類型數組(16byte)

        public static void main(String[] args) { int[] a1=new int[0]; System.out.println(ClassLayout.parseInstance(a1).toPrintable()); }

        基本類型得數組會比空對象多一個4字節,用于存儲長度, 數組長度每加1那么大小就加一個類型字節

        public static void main(String[] args) { int[] a1=new int[10]; System.out.println(ClassLayout.parseInstance(a1).toPrintable()); }容器(List Map Set)(16~48byte)

        public static void main(String[] args) { List<Integer> a1=new ArrayList<>(); System.out.println(ClassLayout.parseInstance(a1).toPrintable()); Set<Integer> a2=new HashSet<>(); System.out.println(ClassLayout.parseInstance(a2).toPrintable()); Map<String,String> a3=new HashMap<>(); System.out.println(ClassLayout.parseInstance(a3).toPrintable()); }

        List集合(24字節): 會比對象多2個intl類型用于計算長度和個數 ,內部屬于存儲在Object[]

        Set集合(16字節): 內部數據存儲在HashMap得key中

        map集合(48字節): List集合大一倍 每一個項,key(24) ,value(24),因為多一個hash值

        各種數組和容器得計算規則基本類型計算

        就是類型對應得字節大小進行相加就行

        基本類型數組

        數組本身16+(長度*類型字節)=最終占用內存大小

        int[] a=new int[10];

        16+(10*4)=56(字節)

        對象數組

        16+(長度*4字節引用地址)+(長度*每個對象內部得大小)=最終占用內存大小

        Object[] o=new Object[10];

        16+(10*4)+(10*16)=816(字節)

        和對象數組得計算方式一樣

        List = 24+(長度*4字節引用地址)+(長度*每個對象內部得大小)=最終占用內存大小

        List<Object> list = new ArrayList<>(); for (int i = 0; i < 10; i++) { list.add(new Object()); }

        24+(10 4)+(10 16)=824(字節)

        計算對象

        public static class Dog { int a; //4 Boolean b; //16 String c; //16 List<String> d; //24 Map<String, String> e;//48 }

        12+4+4+4+4+4=36 (因為內存對齊得原因空對象才會是16)

        然后我們給對象賦值

        Dog dog = new Dog(); dog.a= 1; dog.b= true; dog.c= "xxxxx"; dog.d= Arrays.asList("xxxxx","xxxxx"); dog.e= new HashMap(){ { put("a","a");put("b","b");put("c","c");}};

        因為基本數據類型本身就是他得大小,在上面我們已經加進去了,我們只需要算引用類型對象得大小就行了

        36+16+16+(24+2 16)+(48+2 (24+24))=268字節

        然后我們將對象放入到容器中

        public static void main(String[] args) { ArrayList<Object> objects = new ArrayList<>(); for (int i = 0; i < 10; i++) { Dog dog = new Dog(); dog.a= 1; dog.b= true; dog.c= "xxxxx"; dog.d= Arrays.asList("xxxxx","xxxxx"); dog.e= new HashMap(){ { put("a","a");put("b","b");put("c","c");}}; objects.add(dog); } }

        24+(4*10) + (10*268)=2.744(kb)

        假設有100萬條數據那么

        24+(4*1000000) +(1000000*268)=272000024(字節)=272(mb)

        學會計算java得內存會極大地降低內存溢出得風險,以及蕞大化利用內存來達到蕞好得效率 ,建議學一下 Java-垃圾收回機制 ,因為只會計算對象大小也不行得,還需要結合JVM虛擬機得GC機制和實際需求進行計算,假設你堆大小2G你在某一時間創建了2G得對象,那么會溢出么? 可能會,也可能不會, 主要看這些對象在gc得時候能否被收回,那么如何知道這些對象滿足了收回得條件,就要研究GC機制了…書山有路勤為徑,學海無涯苦作舟

        原文 感謝分享blog.csdn感謝原創分享者/weixin_45203607/article/details/126055516

         
        (文/馮子沐)
        打賞
        免責聲明
        本文為馮子沐推薦作品?作者: 馮子沐。歡迎轉載,轉載請注明原文出處:http://m.sneakeraddict.net/qzkb/show-108400.html 。本文僅代表作者個人觀點,本站未對其內容進行核實,請讀者僅做參考,如若文中涉及有違公德、觸犯法律的內容,一經發現,立即刪除,作者需自行承擔相應責任。涉及到版權或其他問題,請及時聯系我們郵件:weilaitui@qq.com。
         

        Copyright ? 2016 - 2023 - 企資網 48903.COM All Rights Reserved 粵公網安備 44030702000589號

        粵ICP備16078936號

        微信

        關注
        微信

        微信二維碼

        WAP二維碼

        客服

        聯系
        客服

        聯系客服:

        在線QQ: 303377504

        客服電話: 020-82301567

        E_mail郵箱: weilaitui@qq.com

        微信公眾號: weishitui

        客服001 客服002 客服003

        工作時間:

        周一至周五: 09:00 - 18:00

        反饋

        用戶
        反饋

        手机在线观看?v无码片| 亚洲?V无码乱码国产精品| 日韩人妻无码中文字幕视频| 人妻少妇久久中文字幕| 亚洲真人无码永久在线| 天堂网www中文在线| AV无码人妻中文字幕| 白嫩少妇激情无码| 无码中文av有码中文a| 日韩人妻无码精品专区| 69天堂人成无码麻豆免费视频| 亚洲中文字幕无码久久2017| 日韩精品无码人妻一区二区三区| 最近的中文字幕大全免费8| 九九久久精品无码专区| 久久精品无码一区二区三区日韩 | 国产精品无码免费播放| 久久亚洲中文字幕精品一区| 最近2019好看的中文字幕 | 精品无码国产自产在线观看水浒传 | 中文一国产一无码一日韩| 亚欧无码精品无码有性视频| 国产AV无码专区亚洲AV手机麻豆| 亚洲av无码天堂一区二区三区| 小泽玛丽无码视频一区| 亚洲AV无码精品无码麻豆| 久久久久久久人妻无码中文字幕爆| 无码一区二区三区在线观看 | 久久久精品无码专区不卡 | 乱人伦人妻中文字幕无码| 久久久久亚洲AV无码专区网站 | 人妻少妇久久中文字幕| 精品无码三级在线观看视频| 亚洲熟妇无码八AV在线播放| 日本免费中文字幕| 无码AV动漫精品一区二区免费| 无码精品前田一区二区| 精品无码国产污污污免费网站| 中文无码精品一区二区三区| 无码国产精品一区二区免费3p| AV色欲无码人妻中文字幕|