搜尋
首頁Javajava教程一個java物件到底佔多大記憶體?

一個java物件到底佔多大記憶體?

Nov 26, 2016 am 11:45 AM
javajava對象記憶體

最近在讀《深入理解Java虛擬機》,對Java物件的記憶體佈局有了進一步的認識,於是腦中自然而然就有一個很普通的問題,就是一個Java物件到底佔用多大記憶體?

  在網上搜到了一篇博客講的非常好: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; 
   
/**
 * 对象占用字节大小工具类
 *
 * @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; 
    } 
   
    /**
     * 直接计算当前对象占用空间大小,包括当前类及超类的基本类型实例字段大小、<br></br>
     * 引用类型实例字段引用大小、实例基本类型数组总占用空间、实例引用类型数组引用本身占用空间大小;<br></br>
     * 但是不包括超类继承下来的和当前类声明的实例引用字段的对象本身的大小、实例引用数组引用的对象本身的大小 <br></br>
     *
     * @param obj
     * @return
     */ 
    public static long sizeOf(Object obj) { 
        return inst.getObjectSize(obj); 
    } 
   
    /**
     * 递归计算当前对象占用空间总大小,包括当前类和超类的实例字段大小以及实例字段引用对象大小
     *
     * @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的时候已经计基本类型和引用的长度,包括数组 
            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())   //静态不计 
                                || field.getType().isPrimitive()) {    //基本类型不重复计 
                            continue; 
                        } 
   
                        field.setAccessible(true); 
                        Object fieldValue = field.get(obj); 
                        if (fieldValue == null) { 
                            continue; 
                        } 
                        toBeQueue.add(fieldValue); 
                    } 
                    tmpObjClass = tmpObjClass.getSuperclass(); 
                } 
            } 
        } 
        return size; 
    } 
   
    /**
     * String.intern的对象不计;计算过的不计,也避免死循环
     *
     * @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); 
    } 
}

    可以用這個代碼邊看邊驗證,注意的是,執行這個程式需要透過javaagent注入Instrumentation,具體可以看​​原始部落格。我今天主要是總結下手動計算Java物件佔用位元組數的基本規則,做為基本的技能必須get√,希望能幫助到跟我一樣的Java菜鳥。

  在介紹之前,簡單回顧下,Java物件的記憶體佈局:物件頭(Header),實例資料(Instance Data)和對齊填充(Padding),詳細的可以看我的閱讀筆記。另外:不同的環境結果可能有差異,我所在的環境是HotSpot虛擬機,64位Windwos。

  下面進入正文:

  物件頭

  物件頭在32位元系統上佔用8bytes,64位元系統上佔用16bytes。

一個java物件到底佔多大記憶體?

 實例資料

  原生類型(primitive type)的記憶體佔用如下:

itive 類型    Memory Required(bytes)     1    

short    2    

char    2    

int    4    

float    4    

long    8    

double    8    

tes,  referenceence在32位元系統上佔用每個位元系統所佔用每個位元系統類型。

  對齊填充

  HotSpot的對齊方式為8位元組對齊:

(物件頭+ 實例資料+ padding) % 8等於0且0 『到VM參數UseCompressedOops的影響。

  1)對物件頭的影響

  開啟(-XX:+UseCompressedOops)物件頭大小為12bytes(64位元機器)。

static class A {
        int a;
    }

    

A物件佔用記憶體狀況:

  關閉指標壓縮: 16+4=20不是8的倍數,所以+padding/4=24

1

11 開啟指標已經是8的倍數了,不需要再padding。

  2) 對reference類型的影響一個java物件到底佔多大記憶體?

  64位元機器上reference類型佔用8個位元組,開啟4個位元組壓縮後佔用4個位元組。

static class B2 {
        int b2a;
        Integer b2b;
}

    

B2物件佔用記憶體狀況:

一個java物件到底佔多大記憶體?  關閉指標壓縮: 16+4+8=28不是8的倍數,所以+padding/4=32 開啟指標

1 =20不是8的倍數,所以+padding/4=24

數組物件

  64位元機器上,陣列物件的物件頭佔用24個位元組,啟用壓縮之後佔用16個位元組。之所以比一般物件佔用記憶體多是因為需要額外的空間儲存數組的長度。

  先考慮下new Integer[0]佔用的記憶體大小,長度為0,也就是物件頭的大小:

  未開啟壓縮:24bytes

一個java物件到底佔多大記憶體?

1

一個java物件到底佔多大記憶體?  接著計算new Integer[1],new Integer[2],new Integer[3]和new Integer[4]就很容易了:

未開啟壓縮:

一個java物件到底佔多大記憶體?

  开启压缩:

一個java物件到底佔多大記憶體?

  拿new Integer[3]来具体解释下:

  未开启压缩:24(对象头)+8*3=48,不需要padding;

  开启压缩:16(对象头)+3*4=28,+padding/4=32,其他依次类推。

  自定义类的数组也是一样的,比如:

static class B3 {
        int a;
        Integer b;
    }

    new B3[3]占用的内存大小:

  未开启压缩:48

  开启压缩后:32

  复合对象

  计算复合对象占用内存的大小其实就是运用上面几条规则,只是麻烦点。

  1)对象本身的大小

  直接计算当前对象占用空间大小,包括当前类及超类的基本类型实例字段大小、引用类型实例字段引用大小、实例基本类型数组总占用空间、实例引用类型数组引用本身占用空间大小; 但是不包括超类继承下来的和当前类声明的实例引用字段的对象本身的大小、实例引用数组引用的对象本身的大小。

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)当前对象占用的空间总大小

  递归计算当前对象占用空间总大小,包括当前类和超类的实例字段大小以及实例字段引用对象大小。

  递归计算复合对象占用的内存的时候需要注意的是:对齐填充是以每个对象为单位进行的,看下面这个图就很容易明白。

一個java物件到底佔多大記憶體?

现在我们来手动计算下C对象占用的全部内存是多少,主要是三部分构成:C对象本身的大小+数组对象的大小+B对象的大小。

  未开启压缩:

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

  开启压缩:

  (12 + 4 + 4 +4(padding)) + (16 + 4*3 +4(数组对象padding)) + (12+8+4(B对象padding))*3= 128bytes

  大家有兴趣的可以试试。

  实际工作中真正需要手动计算对象大小的场景应该很少,但是个人觉得做为基础知识每个Java开发人员都应该了解,另外:对自己写的代码大概占用多少内存,内存中是怎么布局的应该有一个直觉性的认识。


陳述
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
如何將Maven或Gradle用於高級Java項目管理,構建自動化和依賴性解決方案?如何將Maven或Gradle用於高級Java項目管理,構建自動化和依賴性解決方案?Mar 17, 2025 pm 05:46 PM

本文討論了使用Maven和Gradle進行Java項目管理,構建自動化和依賴性解決方案,以比較其方法和優化策略。

如何使用適當的版本控制和依賴項管理創建和使用自定義Java庫(JAR文件)?如何使用適當的版本控制和依賴項管理創建和使用自定義Java庫(JAR文件)?Mar 17, 2025 pm 05:45 PM

本文使用Maven和Gradle之類的工具討論了具有適當的版本控制和依賴關係管理的自定義Java庫(JAR文件)的創建和使用。

如何使用咖啡因或Guava Cache等庫在Java應用程序中實現多層緩存?如何使用咖啡因或Guava Cache等庫在Java應用程序中實現多層緩存?Mar 17, 2025 pm 05:44 PM

本文討論了使用咖啡因和Guava緩存在Java中實施多層緩存以提高應用程序性能。它涵蓋設置,集成和績效優勢,以及配置和驅逐政策管理最佳PRA

如何將JPA(Java持久性API)用於具有高級功能(例如緩存和懶惰加載)的對象相關映射?如何將JPA(Java持久性API)用於具有高級功能(例如緩存和懶惰加載)的對象相關映射?Mar 17, 2025 pm 05:43 PM

本文討論了使用JPA進行對象相關映射,並具有高級功能,例如緩存和懶惰加載。它涵蓋了設置,實體映射和優化性能的最佳實踐,同時突出潛在的陷阱。[159個字符]

Java的類負載機制如何起作用,包括不同的類載荷及其委託模型?Java的類負載機制如何起作用,包括不同的類載荷及其委託模型?Mar 17, 2025 pm 05:35 PM

Java的類上載涉及使用帶有引導,擴展程序和應用程序類負載器的分層系統加載,鏈接和初始化類。父代授權模型確保首先加載核心類別,從而影響自定義類LOA

See all articles

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

AI Hentai Generator

AI Hentai Generator

免費產生 AI 無盡。

熱門文章

R.E.P.O.能量晶體解釋及其做什麼(黃色晶體)
3 週前By尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.最佳圖形設置
3 週前By尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.如果您聽不到任何人,如何修復音頻
3 週前By尊渡假赌尊渡假赌尊渡假赌
WWE 2K25:如何解鎖Myrise中的所有內容
3 週前By尊渡假赌尊渡假赌尊渡假赌

熱工具

EditPlus 中文破解版

EditPlus 中文破解版

體積小,語法高亮,不支援程式碼提示功能

記事本++7.3.1

記事本++7.3.1

好用且免費的程式碼編輯器

SecLists

SecLists

SecLists是最終安全測試人員的伙伴。它是一個包含各種類型清單的集合,這些清單在安全評估過程中經常使用,而且都在一個地方。 SecLists透過方便地提供安全測試人員可能需要的所有列表,幫助提高安全測試的效率和生產力。清單類型包括使用者名稱、密碼、URL、模糊測試有效載荷、敏感資料模式、Web shell等等。測試人員只需將此儲存庫拉到新的測試機上,他就可以存取所需的每種類型的清單。

MinGW - Minimalist GNU for Windows

MinGW - Minimalist GNU for Windows

這個專案正在遷移到osdn.net/projects/mingw的過程中,你可以繼續在那裡關注我們。 MinGW:GNU編譯器集合(GCC)的本機Windows移植版本,可自由分發的導入函式庫和用於建置本機Windows應用程式的頭檔;包括對MSVC執行時間的擴展,以支援C99功能。 MinGW的所有軟體都可以在64位元Windows平台上運作。

ZendStudio 13.5.1 Mac

ZendStudio 13.5.1 Mac

強大的PHP整合開發環境