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

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

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

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

  最近在讀《深入理解Java虛擬機(jī)》,對(duì)Java對(duì)象的內(nèi)存布局有了進(jìn)一步的認(rèn)識(shí),于是腦子里自然而然就有一個(gè)很普通的問題,就是一個(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> 
     * 但是不包括超類繼承下來的和當(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ì)基本類型和引用的長度,包括數(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) {  
                            //非基本類型需要深度遍歷其對(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ì)算過的不計(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è)程序需要通過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。

  實(shí)例數(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)上每個(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

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

  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

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

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

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

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

  未開啟壓縮:24bytes

  開啟壓縮后:16bytes

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

  未開啟壓縮:

  開啟壓縮:

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

  未開啟壓縮: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ù)組引用本身占用空間大小; 但是不包括超類繼承下來的和當(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è)圖就很容易明白。

  現(xiàn)在我們來手動(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è)人覺得做為基礎(chǔ)知識(shí)每個(gè)Java開發(fā)人員都應(yīng)該了解,另外:對(duì)自己寫的代碼大概占用多少內(nèi)存,內(nèi)存中是怎么布局的應(yīng)該有一個(gè)直覺性的認(rèn)識(shí)。

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

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

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

下一篇:Java Servlet完全教程