java 對象內存大小

java 一個對象內存有多大

爲何想知道這個,自覺得很重要,其實 just for fun =。=

測試相關說明

  • jdk: java 官網上下載的,HotSpot 虛擬機java

    java version "1.8.0_121"
    Java(TM) SE Runtime Environment (build 1.8.0_121-b13)
    Java HotSpot(TM) 64-Bit Server VM (build 25.121-b13, mixed mode)
  • 使用的方法: Instrumentation 參考的是 一個對象佔用多少字節?
  • 確認編譯時是 32 位仍是 64 位,對測試結果有影響。x86 爲 32 位, amd64 爲 64 位,其他我不知道git

    System.out.println(System.getProperty("os.arch"));

準備工做

  • SizeOfObject.java 直接從上面連接上拷貝
  • 編譯這個類,獲得 .class 文件
  • 在包名路徑下執行一下命令進行打包(注意修改相應的包名):github

    jar cvfm SizeOfObject.jar manifest.mf org/seal_de/SizeOfObject.class
    • 其中 manifest.mf 清單信息爲: PreMain-Class: org.seal_de.SizeOfObject
    • PS: 若是在打包好的 jar 包中,META-INF/MANIFEST.MF 沒有 PreMain-Class 屬性,添加上去便可
  • 在運行測試程序的時候,添加 vm 參數jvm

    -javaagent:{jar包路徑}\SizeOfObject.jar

測試用例

  • 測試 int, Object, 引用的大小。其他類型測試都相似
public class MemoryTest {
    /**
     * -javaagent:{jar包路徑}\SizeOfObject.jar -XX:+UseCompressedOops
     * 使用指針壓縮,在必定狀況下64位HotSpot jvm默認指針壓縮
     *
     *Output:
     *amd64
     *Object:                              16
     *    
     *include one int:                     16
     *include two int:                     24
     *include three int:                   24
     *
     *include one object:                  16
     *include one object:                  24
     */
    static void test1() {
        System.out.println(System.getProperty("os.arch"));
        System.out.printf("%-30s%9d\n", "Object:", SizeOfObject.sizeOf(new Object()));
        System.out.println();
        System.out.printf("%-30s%9d\n", "include one int:", SizeOfObject.sizeOf(new IntegerTestOne()));
        System.out.printf("%-30s%9d\n", "include two int:", SizeOfObject.sizeOf(new IntegerTestTwo()));
        System.out.printf("%-30s%9d\n", "include three int:", SizeOfObject.sizeOf(new IntegerTestThree()));
        System.out.println();
        System.out.printf("%-30s%9d\n", "include one object:", SizeOfObject.sizeOf(new ReferenceTestOne()));
        System.out.printf("%-30s%9d\n", "include one object:", SizeOfObject.sizeOf(new ReferenceTestTwo()));
    }

    /**
     * -javaagent:{jar包路徑}\SizeOfObject.jar -XX:-UseCompressedOops
     * 不使用指針壓縮
     *
     *Output:
     *amd64
     *Object:                              16
     *
     *include one int:                     24
     *include two int:                     24
     *include three int:                   32
     *
     *include one object:                  24
     *include one object:                  32
     */
    static void test2() {
        System.out.println(System.getProperty("os.arch"));
        System.out.printf("%-30s%9d\n", "Object:", SizeOfObject.sizeOf(new Object()));
        System.out.println();
        System.out.printf("%-30s%9d\n", "include one int:", SizeOfObject.sizeOf(new IntegerTestOne()));
        System.out.printf("%-30s%9d\n", "include two int:", SizeOfObject.sizeOf(new IntegerTestTwo()));
        System.out.printf("%-30s%9d\n", "include three int:", SizeOfObject.sizeOf(new IntegerTestThree()));
        System.out.println();
        System.out.printf("%-30s%9d\n", "include one object:", SizeOfObject.sizeOf(new ReferenceTestOne()));
        System.out.printf("%-30s%9d\n", "include one object:", SizeOfObject.sizeOf(new ReferenceTestTwo()));
    }

    public static void main(String[] args) {
        test2();
    }

    static class IntegerTestOne {
        private int i1 = 1;
    }

    static class IntegerTestTwo {
        private int i1 = 1;
        private int i2 = 1;
    }

    static class IntegerTestThree {
        private int i1 = 1;
        private int i2 = 1;
        private int i3 = 1;
    }

    static class ReferenceTestOne {
        private Object o1 = new Object();
    }

    static class ReferenceTestTwo {
        private Object o1 = new Object();
        private Object o2 = new Object();
    }
}

一些概念

  • 對象內存 = 對象頭 + 類型指針 + 對齊填充
  • 對象頭不參與指針壓縮,而且 32 位時爲 4 個字節,64 位時爲 8 個字節
  • 類型指針參與指針壓縮,而且 32 位時爲 4 個字節,64 位時爲 8 個字節;指針壓縮時 64 位爲 4 個字節
  • 對齊填充,因爲 jvm 設計內存要爲 8 字節的整數倍,因此不足的須要填充。如 對象頭和類型指針一共 12 字節,填充後爲 16 字節,填充了 4 個字節

測試結果驗證上面的假設

  • 其中 (8 + 8) 爲對象頭和類型指針的字節數
64 位 -XX:-UseCompressedOops -XX:+UseCompressedOops
Object 8 + 8 = 16 (對象頭+類型指針) 8 + 4 + 4 = 16(對象頭+壓縮的類型指針+對齊填充)
包含一個int (8 + 8) + 4 + 4 = 24 (8 + 4) + 4 = 16
包含兩個int (8 + 8) + 4*2 = 24 (8 + 4) + 4*2 + 4 = 24
包含三個int (8 + 8) + 4*3 + 4 = 32 (8 + 4) + 4*3 = 24
不壓縮引用佔 8 個字節,壓縮佔 4 個字節
包含一個引用 (8 + 8) + 8 = 24 (8 + 4) + 4 = 16
包含兩個引用 (8 + 8) + 8*2 = 32 (8 + 4) + 4*2 + 4 = 24

靜態數據不在對象內存裏面

public class MemoryStaticTest {
    /**
     * -javaagent:{jar包路徑}\SizeOfObject.jar
     * 使用指針壓縮,在必定狀況下64位HotSpot jvm默認指針壓縮
     *
     * Output:
     * amd64    +UseCompressedOops
     * StaticTest:                          16
     * Integer:                             16
     * StaticReferenceTest:                 16
     *
     * @param args
     */
    public static void main(String[] args) {
        System.out.println(System.getProperty("os.arch") + "    +UseCompressedOops");
        System.out.printf("%-30s%9d\n", "StaticTest:", SizeOfObject.sizeOf(new StaticTest()));
        System.out.printf("%-30s%9d\n", "Integer:", SizeOfObject.sizeOf(new Integer(1)));
        System.out.printf("%-30s%9d\n", "StaticReferenceTest:", SizeOfObject.sizeOf(new StaticReferenceTest()));
    }

    static class StaticTest {
        private static int i1 = 1;
        private static int i2 = 1;
        private static int i3 = 1;
        private static int i4 = 1;
        private static int i5 = 1;
        private static int i6 = 1;
    }

    static class StaticReferenceTest {
        private static Object o1 = new Object();
        private static Object o2 = new Object();
        private static Object o3 = new Object();
        private static Object o4 = new Object();
        private static Object o5 = new Object();

    }
}

爲何是這個樣子呢

  • 對象頭在 64 位的時候,爲何是 8 個字節;32 位的時候,爲何是 4 個字節
  • 一個引用在 64 位的時候,爲何是 8 個字節;32 位的時候,爲何是 4 個字節測試

    • 這個跟 C++ 指針有關了,跟寄存器有關了
  • 指針壓縮又是怎麼個回事

這一切均可以在 jvm 的實現中找到答案,也就是看 C++ 代碼。只是我還沒到這個階段=。=ui

參考連接

相關文章
相關標籤/搜索