1. 程式人生 > >Java中物件佔用記憶體計算方法

Java中物件佔用記憶體計算方法

普通物件的結構如下,按64位機器的長度計算

1. 物件頭(_mark), 8個位元組

2. Oop指標,如果是32G記憶體以下的,預設開啟物件指標壓縮,4個位元組

3. 資料區

4.Padding(記憶體對齊),按照8的倍數對齊

陣列物件結構是

1. 物件頭(_mark), 8個位元組

2. Oop指標,如果是32G記憶體以下的,預設開啟物件指標壓縮,4個位元組

3. 陣列長度,4個位元組

4. 資料區

5. Padding(記憶體對齊),按照8的倍數對齊


清楚了物件在記憶體的基本佈局後,咱們說兩種計算Java物件大小的方法

1. 通過java.lang.instrument.Instrumentation的getObjectSize(obj)直接獲取物件的大小

2. 通過sun.misc.Unsafe物件的objectFieldOffset(field)等方法結合反射來計算物件的大小

java.lang.instrument.Instrumentation.getObjectSize()的方式

先講講java.lang.instrument.Instrumentation.getObjectSize()的方式,這種方法得到的是Shallow Size,即遇到引用時,只計算引用的長度,不計算所引用的物件的實際大小。如果要計算所引用物件的實際大小,可以通過遞迴的方式去計算。

java.lang.instrument.Instrumentation的例項必須通過指定javaagent的方式才能獲得,具體的步驟如下:

1. 定義一個類,提供一個premain方法: public static void premain(String agentArgs, Instrumentation instP)

2. 建立META-INF/MANIFEST.MF檔案,內容是指定PreMain的類是哪個: Premain-Class: sizeof.ObjectShallowSize

3. 把這個類打成jar,然後用java -javaagent XXXX.jar XXX.main的方式執行

下面先定義一個類來獲得java.lang.instrument.Instrumentation的例項,並提供了一個static的sizeOf方法對外提供Instrumentation的能力

  1. package sizeof;  
  2. import java.lang.instrument.Instrumentation;  
  3. publicclass ObjectShallowSize {  
  4.     privatestatic Instrumentation inst;  
  5.     publicstaticvoid premain(String agentArgs, Instrumentation instP){  
  6.         inst = instP;  
  7.     }  
  8.     publicstaticlong sizeOf(Object obj){  
  9.         return inst.getObjectSize(obj);  
  10.     }  
  11. }  

定義META-INF/MANIFEST.MF檔案

  1. Premain-Class: sizeof.ObjectShallowSize  

打成jar包
  1. cd 編譯後的類和META-INF資料夾所在目錄  
  2. jar cvfm java-agent-sizeof.jar META-INF/MANIFEST.MF  .  

準備好了這個jar之後,我們可以寫測試類來測試Instrumentation的getObjectSize方法了。在這之前我們先來看物件在記憶體中是按照什麼順序排列的

有如下這個類,欄位的定義按如下順序

  1. privatestaticclass ObjectA {  
  2.         String str;  // 4
  3.         int i1; // 4
  4.         byte b1; // 1
  5.         byte b2; // 1
  6.         int i2;  // 4 
  7.         ObjectB obj; //4
  8.         byte b3;  // 1
  9.     }  

按照我們之前說的方法來計算一下這個物件所佔大小,注意按8對齊

8(_mark) + 4(oop指標) + 4(str) + 4(i1) + 1(b1) + 1(b2) + 2(padding) + 4(i2) + 4(obj) + 1(b3) + 7(padding) = 40 ?

但事實上是這樣的嗎? 我們來用Instrumentation的getObjectSize來計算一下先:

  1. package test;  
  2. import sizeof.ObjectShallowSize;  
  3. publicclass SizeofWithInstrumetation {  
  4.     privatestaticclass ObjectA {  
  5.         String str;  // 4
  6.         int i1; // 4
  7.         byte b1; // 1
  8.         byte b2; // 1
  9.         int i2;  // 4 
  10.         ObjectB obj; //4
  11.         byte b3;  // 1
  12.     }  
  13.     privatestaticclass ObjectB {  
  14.     }  
  15.     publicstaticvoid main(String[] args){  
  16.         System.out.println(ObjectShallowSize.sizeOf(new ObjectA()));  
  17.     }  
  18. }  


得到的結果是32!不是會按8對齊嗎,b3之前的資料加起來已經是32了,多了1個b3,為33,應該對齊到40才對啊。事實上,HotSpot建立的物件的欄位會先按照給定順序排列一下,預設的順序如下,從長到短排列,引用排最後:  long/double --> int/float -->  short/char --> byte/boolean --> Reference

這個順序可以使用JVM引數:  -XX:FieldsAllocationSylte=0(預設是1)來改變。

我們使用sun.misc.Unsafe物件的objectFieldOffset方法來驗證一下:

  1. Field[] fields = ObjectA.class.getDeclaredFields();  
  2.         for(Field f: fields){  
  3.             System.out.println(f.getName() + " offset: " +unsafe.objectFieldOffset(f));  
  4.         }  


可以看到確實是按照從長到短,引用排最後的方式在記憶體中排列的。按照這種方法我們來重新計算下ObjectA建立的物件的長度:

8(_mark) + 4(oop指標) + 4(i1) + + 4(i2) + 1(b1) + 1(b2) + 1(b3) + 1(padding) +  4(str) + 4(obj) = 32

得到的結果和java.lang.instrument.Instrumentation.getObjectSize()的結果是一樣的,證明我們的計算方式是正確的。

sun.misc.Unsafe的方式

下面說一下通過sun.misc.Unsafe物件的objectFieldOffset(field)等方法結合反射來計算物件的大小。基本的思路如下:

1. 通過反射獲得一個類的Field

2. 通過Unsafe的objectFieldOffset()獲得每個Field的offSet

3. 對Field按照offset排序,取得最大的offset,然後加上這個field的長度,再加上Padding對齊

上面三步就可以獲得一個物件的Shallow size。可以進一步通過遞迴去計算所引用物件的大小,從而可以計算出一個物件所佔用的實際大小。

Oop指標是4還是未壓縮的8也可以通過unsafe.arrayIndexScale(Object[].class)來獲得,這個方法返回一個引用所佔用的長度

  1. static {  
  2.         try {  
  3.             Field field = Unsafe.class.getDeclaredField("theUnsafe");  
  4.             field.setAccessible(true);  
  5.             unsafe = (Unsafe) field.get(null);  
  6.             objectRefSize = unsafe.arrayIndexScale(Object[].class);  
  7.         } catch (Exception e) {  
  8.             thrownew RuntimeException(e);  
  9.         }  
  10.     }  

下面的原始碼摘自 http://java-performance.info/memory-introspection-using-sun-misc-unsafe-and-reflection/, 原文中的程式碼在計算物件大小的時候有問題,我做了微調,並加上了記憶體對齊的方法,這樣計算出的結果和Instrumentation的getObjectSize方法是一樣的。
  1. package test;  
  2. import java.util.ArrayList;  
  3. import java.util.Collections;  
  4. import java.util.Comparator;  
  5. import java.util.List;  
  6. /** 
  7.  * This class contains object info generated by ClassIntrospector tool 
  8.  */
  9. publicclass ObjectInfo {  
  10.     /** Field name */
  11.     publicfinal String name;  
  12.     /** Field type name */
  13.     publicfinal String type;  
  14.     /** Field data formatted as string */
  15.     publicfinal String contents;  
  16.     /** Field offset from the start of parent object */
  17.     publicfinalint offset;  
  18.     /** Memory occupied by this field */
  19.     publicfinalint length;  
  20.     /** Offset of the first cell in the array */
  21.     publicfinalint arrayBase;  
  22.     /** Size of a cell in the array */
  23.     publicfinalint arrayElementSize;  
  24.     /** Memory occupied by underlying array (shallow), if this is array type */
  25.     publicfinalint arraySize;  
  26.     /** This object fields */
  27.     publicfinal List<ObjectInfo> children;  
  28.     public ObjectInfo(String name, String type, String contents, int offset, int length, int arraySize,  
  29.     int arrayBase, int arrayElementSize)  
  30. 相關推薦

    Java物件佔用記憶體計算方法

    普通物件的結構如下,按64位機器的長度計算1. 物件頭(_mark), 8個位元組2. Oop指標,如果是32G記憶體以下的,預設開啟物件指標壓縮,4個位元組3. 資料區4.Padding(記憶體對齊),按照8的倍數對齊陣列物件結構是1. 物件頭(_mark), 8個位元組2

    圖片佔用記憶體計算方法

    Android中有四種,分別是: ALPHA_8:每個畫素佔用1byte記憶體 ARGB_4444:每個畫素佔用2byte記憶體 ARGB_8888:每個畫素佔用4byte記憶體 RGB_565:每個畫素佔用2byte記憶體 Android預設的顏色模式為ARG

    java物件可以存在記憶體哪些地方

    注意:以下都是個人理解。如有不同之處,望提出(-_-)。 java中識別符號對應的值可以改變的叫做變數,不可以改變的叫做常量。如: //識別符號a的值可以改變,叫做變數 int a=3; a=4; //識別符號b的值不可以改變,叫做常量 final int b=3;

    javasynchronized 用在例項方法物件方法上面的區別

    https://bijian1013.iteye.com/blog/1836575    在Java中,synchronized 是用來表示同步的,我們可以synchronized 來修飾一個方法。也可以synchronized 來修飾方法裡面的一個語句塊。    

    java 物件的 一對一關係 (封裝和構造方法)

    java 中物件的 一對一關係 簡單介紹: … java中物件的對應關係有很多種,比如單向一對一,雙向一對一,一對多,多對一,多對多等,其實現原理相同,接下來,我們詳解一對一關係。 說明: … 所

    Android圖片佔用記憶體計算

    本人的網易部落格原文 在Android開發中,我現在發現很多人還不會對圖片佔用記憶體進行很好的計算。因此撰寫該博文來做介紹,期望達到拋磚引玉的作用。   Android中一張圖片(BitMap)佔用的記憶體主要和以下幾個因數有關:圖片長度,圖片寬度,單位畫素佔用的位元組

    Java物件的建立過程(記憶體分析)

    Java中物件建立的時候,用到了new關鍵字。那麼在記憶體中,究竟發生了什麼? 下面先看一段簡單的程式碼: 先是主函式類(Animal) package cn.sg.oop.second; public class Animals { public static

    java物件作為方法的入參

    在java中,物件作為方法的入參時,如果傳進去該物件已經new好了,那麼可以在方法中為屬性賦值。跳出方法後,賦值的屬性會生效。 但是,如果傳進去的物件是null,在方法中才new ,為屬性賦值等。這樣,跳出方法後,物件還會是null。 例子: public class Wh

    Javabyte轉int的方法

    article 強制 能夠 java 等等 content 應用場景 ffffff 計算 byte轉化為int有兩種情況: 1)要保持數值不變 應用場景:數值計算,等等。 方法:能夠直接採用強制類型轉換:int i = (int) aByte, 比如:若aByte=0x

    Map、List、Set在Java的各種遍歷方法

    try one out 循環 java light size i++ pre 一、Map的4種遍歷 Map<String, String> map = new HashMap<String, String>(); map.put("姓名", "

    淺談JAVA“增強”類的某個方法的幾個方法

    exc 目標 byte 相同 nbsp 優點 method value oca 一、繼承 使用場景:能夠控制這個類的構造的時候,才可以使用繼承。  優點:簡單容易使用, 缺點:耦合性大大的增強,不利於後期的維護,所以對於繼承這種方法,謹慎使用。 代碼實現:二、裝飾者模式 

    Java對域和靜態方法的訪問不具有多態性

    ext 轉型 highlight .get 判斷 fin color icm true 1.將方法調用同方法主體關聯起來被稱為 2.編譯期綁定(靜態)是在程序編譯階段就確定了引用對象的類型 3.運行期綁定(動態綁定)是指在執行期間判斷所引用對象的實際類型,根據其實際的類型調

    JavaMath類的常用方法

    order ref oat math dom ack ron 方法 args 8243 [java] view plain copy public class MathDemo { public static void main(

    JavaList集合排序的方法 比較器的使用 根據學生對象數序 語文 英語成績總和進行sort排序

    private system.in set swift ringbuf 直觀 turn @override encoding package com.swift; import java.util.ArrayList; import java.util.Collecti

    理解Java的hashCode和equals 方法

    err array size tex nat 什麽 map 交流群 培訓 在Java裏面所有的類都直接或者間接的繼承了java.lang.Object類,Object類裏面提供了11個方法,如下: Java代碼 ```` 1,clone() 2,equals(Obje

    PHP一種sign計算方法

    get func return fun urn style 輸出 class pos 一言不合上代碼......... 1 <?php 2 function getsign($data,$key){ 3 $key=MD5("KEY_".$key."_K"); 4 $

    javabyte的範圍計算

    符號位 二進制 計算 post 取值 我們 log div ava 概念:java中用補碼表示二進制數,補碼的最高位是符號位,最高位為“0”表示正數,最高位為“1”表示負數。正數補碼為其本身;負數補碼為其絕對值各位取反加1;例如:+21,其二進制表示形式是00010101,

    JAVA獲取鍵盤輸入的方法總結

    鍵盤輸入 throws 應該 padding left 接收 [] util float Java程序開發過程中,需要從鍵盤獲取輸入值是常有的事,但Java它偏偏就沒有像c語言給我們提供的scanf(),C++給我們提供的cin()獲取鍵盤輸入值的現成函數!下面介紹三種解決

    JAVA關於set()和get()方法的理解及使用

    當我 一般來說 怎麽 而是 知識 了解 構造 set 今後 https://www.cnblogs.com/fly-sky-han/p/6564439.html 我們先來看看set和get這兩個詞的表面意思,set是設置的意思,而get是獲取的意思,顧名思義,這兩個方法是對

    javaMap集合的常用方法

    try con equals img all sem java 常用 strong Map集合和Collection集合的區別 Map集合是有Key和Value的,Collection集合是只有Value。 Collection集合底層也是有Key和Value,只是隱藏起來