中文字幕在线观看,亚洲а∨天堂久久精品9966,亚洲成a人片在线观看你懂的,亚洲av成人片无码网站,亚洲国产精品无码久久久五月天

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

2018-07-20    來源:編程學(xué)習(xí)網(wǎng)

容器云強(qiáng)勢上線!快速搭建集群,上萬Linux鏡像隨意使用

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

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

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;  
  
/** 
 * 對象占用字節(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;  
    }  
  
    /** 
     * 直接計算當(dāng)前對象占用空間大小,包括當(dāng)前類及超類的基本類型實例字段大小、<br></br> 
     * 引用類型實例字段引用大小、實例基本類型數(shù)組總占用空間、實例引用類型數(shù)組引用本身占用空間大小;<br></br> 
     * 但是不包括超類繼承下來的和當(dāng)前類聲明的實例引用字段的對象本身的大小、實例引用數(shù)組引用的對象本身的大小 <br></br> 
     * 
     * @param obj 
     * @return 
     */  
    public static long sizeOf(Object obj) {  
        return inst.getObjectSize(obj);  
    }  
  
    /** 
     * 遞歸計算當(dāng)前對象占用空間總大小,包括當(dāng)前類和超類的實例字段大小以及實例字段引用對象大小 
     * 
     * @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的時候已經(jīng)計基本類型和引用的長度,包括數(shù)組  
            size += skipObject(visited, obj) ? 0L : sizeOf(obj);  
            Class<?> tmpObjClass = obj.getClass();  
            if (tmpObjClass.isArray()) {  
                //[I , [F 基本類型名字長度是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) {  
                            //非基本類型需要深度遍歷其對象  
                            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)不計  
                                || field.getType().isPrimitive()) {    //基本類型不重復(fù)計  
                            continue;  
                        }  
  
                        field.setAccessible(true);  
                        Object fieldValue = field.get(obj);  
                        if (fieldValue == null) {  
                            continue;  
                        }  
                        toBeQueue.add(fieldValue);  
                    }  
                    tmpObjClass = tmpObjClass.getSuperclass();  
                }  
            }  
        }  
        return size;  
    }  
  
    /** 
     * String.intern的對象不計;計算過的不計,也避免死循環(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);  
    }  
}

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

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

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

  對象頭

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

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

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

Primitive Type Memory Required(bytes)
boolean 1
byte 1
short 2
char 2
int 4
float 4
long 8
double 8

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

  對齊填充

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

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

  指針壓縮

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

  1)對對象頭的影響

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

static class A {
        int a;
    }

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

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

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

  2) 對reference類型的影響

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

static class B2 {
        int b2a;
        Integer b2b;
}

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

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

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

  數(shù)組對象

  64位機(jī)器上,數(shù)組對象的對象頭占用24個字節(jié),啟用壓縮之后占用16個字節(jié)。之所以比普通對象占用內(nèi)存多是因為需要額外的空間存儲數(shù)組的長度。

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

  未開啟壓縮:24bytes

  開啟壓縮后:16bytes

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

  未開啟壓縮:

  開啟壓縮:

  拿new Integer[3]來具體解釋下:

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

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

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

static class B3 {
        int a;
        Integer b;
    }

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

  未開啟壓縮:48

  開啟壓縮后:32

  復(fù)合對象

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

  1)對象本身的大小

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

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(對象頭)+4(ba)+8(as引用的大。+padding/4=32

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

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

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

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

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

  未開啟壓縮:

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

  開啟壓縮:

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

  大家有興趣的可以試試。

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

標(biāo)簽: b2b isp 代碼

版權(quán)申明:本站文章部分自網(wǎng)絡(luò),如有侵權(quán),請聯(lián)系:west999com@outlook.com
特別注意:本站所有轉(zhuǎn)載文章言論不代表本站觀點(diǎn)!
本站所提供的圖片等素材,版權(quán)歸原作者所有,如需使用,請與原作者聯(lián)系。

上一篇:Android Touch事件傳遞機(jī)制通俗講解

下一篇:Java Servlet完全教程