最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

測量Java對象所占內存大小方式

 更新時間:2023年09月20日 14:38:35   作者:tiandee  
這篇文章主要介紹了測量Java對象所占內存大小方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教

背景:

相信大部分人都不會關注這個問題吧,只有一些偏執(zhí)狂才會抓著這些不放,我們平時寫代碼時經(jīng)常會new ArrayList<>(),new String()之類的,那么這些剛new出來的對象在內存中占用多大空間呢?

方法一

設置-Xms和-Xmx的大小,然后在程序中循環(huán)new對象,直到發(fā)生OOM異常,記錄下此時new了多少個對象,大家覺得這種方法可靠不?

下面放上設置參數(shù)以及測試代碼。

/**
 * 研究new出的對象大小
 * -Xms1m -Xmx1m -XX:+PrintGCDetails
 */
public class TestObjectSize {
    int i = 0;
    @Test
    public void testObjectSize() {
        List<Object> list = new ArrayList<>();
        try {
            while (true) {
                list.add(new Object());
                i++;
            }
        } catch (Exception e) {
            System.out.println(i);
            e.printStackTrace();
        }finally {
            System.out.println(i);
        }
    }
}
運行結果:
14053
java.lang.OutOfMemoryError: Java heap space

根據(jù)上面的結果可以算出每個Object對象大小1024*1024/14053=74.61Byte,這個結果好像挺意外,個人覺得有點大了,下面來分析一下,由于設置了-XX:+PrintGCDetails參數(shù),控制臺實時輸出GC情況,

如下圖:

可以發(fā)現(xiàn),在new對象的過程中執(zhí)行了六次GC(Minor GC),也就是說JVM在青年代區(qū)回收了6次垃圾,執(zhí)行了N次FullGC,F(xiàn)ullGC會回收PSYoungGen、ParOldGen、Metaspace,這么看來這個結果是不可可信的,因為回收的過程中,銷毀了很多對象,但是計數(shù)器一直是增加的,所以74.61Byte這個結果毫無疑問是偏大的。

【錯誤糾正】

經(jīng)過同事的糾正,回收過程中Object對象不會被銷毀,因為被放到了list中,list并不會銷毀,因此在執(zhí)行GC過程中,只是在不斷地搬家,從一個survivor到另一個survivor.

方法二

利用java.lang.instrument.Instrumentation這個interface的特有屬性,可以在JVM運行過程中實時測量對象大小。簡單介紹下這個類,利用 Java 代碼,即 java.lang.instrument 做動態(tài) Instrumentation 是 Java SE 5 的新特性,它把 Java 的 instrument 功能從本地代碼中解放出來,使之可以用 Java 代碼的方式解決問題。使用 Instrumentation,開發(fā)者可以構建一個獨立于應用程序的代理程序(Agent),用來監(jiān)測和協(xié)助運行在 JVM 上的程序,甚至能夠替換和修改某些類的定義。有了這樣的功能,開發(fā)者就可以實現(xiàn)更為靈活的運行時虛擬機監(jiān)控和 Java 類操作了,這樣的特性實際上提供了一種虛擬機級別支持的 AOP 實現(xiàn)方式,使得開發(fā)者無需對 JDK 做任何升級和改動,就可以實現(xiàn)某些 AOP 的功能了。

在 Java SE 6 里面,instrumentation 包被賦予了更強大的功能:啟動后的 instrument、本地代碼(native code)instrument,以及動態(tài)改變 classpath 等等。這些改變,意味著 Java 具有了更強的動態(tài)控制、解釋能力,它使得 Java 語言變得更加靈活多變。

在 Java SE6 里面,最大的改變使運行時的 Instrumentation 成為可能。在 Java SE 5 中,Instrument 要求在運行前利用命令行參數(shù)或者系統(tǒng)參數(shù)來設置代理類,在實際的運行之中,虛擬機在初始化之時(在絕大多數(shù)的 Java 類庫被載入之前),instrumentation 的設置已經(jīng)啟動,并在虛擬機中設置了回調函數(shù),檢測特定類的加載情況,并完成實際工作。但是在實際的很多的情況下,我們沒有辦法在虛擬機啟動之時就為其設定代理,這樣實際上限制了 instrument 的應用。而 Java SE 6 的新特性改變了這種情況,通過 Java Tool API 中的 attach 方式,我們可以很方便地在運行過程中動態(tài)地設置加載代理類,以達到 instrumentation 的目的。

另外,對 native 的 Instrumentation 也是 Java SE 6 的一個嶄新的功能,這使以前無法完成的功能 —— 對 native 接口的 instrumentation 可以在 Java SE 6 中,通過一個或者一系列的 prefix 添加而得以完成。

最后,Java SE 6 里的 Instrumentation 也增加了動態(tài)添加 class path 的功能。所有這些新的功能,都使得 instrument 包的功能更加豐富,從而使 Java 語言本身更加強大。

上面這段摘自IBM Developers,讓我自己解釋,有點不好解釋,大家看權威解答就好。

下面這段是來自Java8 API,英文好的可以讀一下。

/**
 * This class provides services needed to instrument Java
 * programming language code.
 * Instrumentation is the addition of byte-codes to methods for the
 * purpose of gathering data to be utilized by tools.
 * Since the changes are purely additive, these tools do not modify
 * application state or behavior.
 * Examples of such benign tools include monitoring agents, profilers,
 * coverage analyzers, and event loggers.
 *
 * <P>
 * There are two ways to obtain an instance of the
 * <code>Instrumentation</code> interface:
 *
 * <ol>
 *   <li><p> When a JVM is launched in a way that indicates an agent
 *     class. In that case an <code>Instrumentation</code> instance
 *     is passed to the <code>premain</code> method of the agent class.
 *     </p></li>
 *   <li><p> When a JVM provides a mechanism to start agents sometime
 *     after the JVM is launched. In that case an <code>Instrumentation</code>
 *     instance is passed to the <code>agentmain</code> method of the
 *     agent code. </p> </li>
 * </ol>
 * <p>
 * These mechanisms are described in the
 * {@linkplain java.lang.instrument package specification}.
 * <p>
 * Once an agent acquires an <code>Instrumentation</code> instance,
 * the agent may call methods on the instance at any time.
 *
 * @since   1.5
 */

知道了這個類可以在JVM運行過程中測量對象大小,下面談談如何使用,這個Instrumentation接口不太友好,筆者跟蹤了下這個接口,發(fā)現(xiàn)它的實現(xiàn)類是在rt.jar包里的,簡單介紹下rt.jar,大家都知道是極為重要的一個文件,rt是runtime的縮寫,即運行時的意思。

是java程序在運行時必不可少的文件。里面包含了java程序員常用的包,如java.lang,java.util,java.io,java.net, java.applet等。

也就是說,我們想得到Instrumentation的實例,必須得在JVM運行過程中才能取得,翻開源碼構造方法是private類型,沒有任何getInstance的方法,寫這個類干嘛?

看來這個只能被JVM自己給初始化了,那么怎么將它自己初始化的東西取出來用呢,唯一能想到的就是javaagent代理,我說這是代理技術,不知道準不準確,若不正確請大家指正。

Step1:先創(chuàng)建一個用于測試對象大小的處理類(代理類)

import java.lang.instrument.Instrumentation;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.IdentityHashMap;
import java.util.Map;
import java.util.Stack;
public class MySizeOf {
    static Instrumentation inst;
    public static void premain(String agentArgs, Instrumentation instP) {
        inst = instP;
    }
    public static long sizeOf(Object o) {
        if(inst == null) {
            throw new IllegalStateException("Can not access instrumentation environment.\n" +
                    "Please check if jar file containing SizeOfAgent class is \n" +
                    "specified in the java's \"-javaagent\" command line argument.");
        }
        return inst.getObjectSize(o);
    }
}

這就是agent的代碼,此時我們要將上面這個類編譯后打包為一個jar文件,并且在其包內部的META-INF/MANIFEST.MF文件中增加一行:Premain-Class: MySizeOf代表執(zhí)行代理的全名,這里的類名稱是沒有package的,如果你有package,那么就寫全名,假設打包完的jar包名稱為agent.jar,打包的過程簡單說一下,jar命令參考文章最后的Extra部分。

  • jar cf agent.jar MySizeOf.java //將MySizeOf.java打成jar包
  • 修改jar包中的META-INF/MANIFEST.MF文件,增加一行:Premain-Class: MySizeOf,這里的類名稱是沒有package的,如果你有package,那么就寫全名。

Step2:編寫測試類TestSize

這里我打算測一下Interger、String、ArrayList、Object以及Long類型的對象大小。

代碼如下:

public class TestSize {
    public static void main(String []args) {
        System.out.println("一個Interger對象大小為:"+MySizeOf.sizeOf(new Integer(1)));
        System.out.println("一個String對象大小為:"+MySizeOf.sizeOf(new String("a")));
        System.out.println("一個String對象大小(關閉指針壓縮)為:"+MySizeOf.fullSizeOf(new String("a")));
        System.out.println("一個char對象大小為:"+MySizeOf.sizeOf(new char[1]));
        System.out.println("一個ArrayList對象大小為:"+MySizeOf.sizeOf(new ArrayList<>()));
        System.out.println("一個Object對象大小為:"+MySizeOf.sizeOf(new Object()));
        System.out.println("一個Long對象大小為:"+MySizeOf.sizeOf(new Long(10000000000L)));
    }
}

Step3:執(zhí)行TestSize,進行測試

這里需要注意一點,這個Test方法不能在IDE環(huán)境中執(zhí)行,因為你無法使用Instrumentation的實例,必須采用古老的javac,java命令先編譯再執(zhí)行,請按照以下步驟執(zhí)行,

// step1,編譯TestSize.java,并將agent.jar包中的Instrumentation的實例引入ClassPath,這樣執(zhí)行TestSize.java時才能引用Instrumentation實例。
javac -classpath agent.jar TestSize.java
//step2,執(zhí)行TestSize,使用-javaagent:agent.jar,意思是使用agent.jar作為代理,用到agent技術
java -javaagent:agent.jar TestSize  

運行結果:

一個Interger對象大小為:16
一個String對象大小為:24
一個String對象大?。P閉指針壓縮)為:48
一個char對象大小為:24
一個ArrayList對象大小為:24
一個Object對象大小為:16
一個Long對象大小為:24

在網(wǎng)上看到一個更全面的Agent測試類,里面提供了不少測量方法,提供給大家,

public class MySizeOf {
    static Instrumentation inst;
    public static void premain(String agentArgs, Instrumentation instP) {
        inst = instP;
    }
    public static long sizeOf(Object o) {
        if(inst == null) {
            throw new IllegalStateException("Can not access instrumentation environment.\n" +
                    "Please check if jar file containing SizeOfAgent class is \n" +
                    "specified in the java's \"-javaagent\" command line argument.");
        }
        return inst.getObjectSize(o);
    }
    /**
     * 遞歸計算當前對象占用空間總大小,包括當前類和超類的實例字段大小以及實例字段引用對象大小
     */
    public static long fullSizeOf(Object obj) {//深入檢索對象,并計算大小
        Map<Object, Object> visited = new IdentityHashMap<Object, Object>();
        Stack<Object> stack = new Stack<Object>();
        long result = internalSizeOf(obj, stack, visited);
        while (!stack.isEmpty()) {//通過棧進行遍歷
            result += internalSizeOf(stack.pop(), stack, visited);
        }
        visited.clear();
        return result;
    }
    //判定哪些是需要跳過的
    private static boolean skipObject(Object obj, Map<Object, Object> visited) {
        if (obj instanceof String) {
            if (obj == ((String) obj).intern()) {
                return true;
            }
        }
        return (obj == null) || visited.containsKey(obj);
    }
    private static long internalSizeOf(Object obj, Stack<Object> stack, Map<Object, Object> visited) {
        if (skipObject(obj, visited)) {//跳過常量池對象、跳過已經(jīng)訪問過的對象
            return 0;
        }
        visited.put(obj, null);//將當前對象放入棧中
        long result = 0;
        result += sizeOf(obj);
        Class <?>clazz = obj.getClass();
        if (clazz.isArray()) {//如果數(shù)組
            if(clazz.getName().length() != 2) {// skip primitive type array
                int length =  Array.getLength(obj);
                for (int i = 0; i < length; i++) {
                    stack.add(Array.get(obj, i));
                }
            }
            return result;
        }
        return getNodeSize(clazz , result , obj , stack);
    }
    //這個方法獲取非數(shù)組對象自身的大小,并且可以向父類進行向上搜索
    private static long getNodeSize(Class <?>clazz , long result , Object obj , Stack<Object> stack) {
        while (clazz != null) {
            Field[] fields = clazz.getDeclaredFields();
            for (Field field : fields) {
                if (!Modifier.isStatic(field.getModifiers())) {//這里拋開靜態(tài)屬性
                    if (field.getType().isPrimitive()) {//這里拋開基本關鍵字(因為基本關鍵字在調用java默認提供的方法就已經(jīng)計算過了)
                        continue;
                    }else {
                        field.setAccessible(true);
                        try {
                            Object objectToAdd = field.get(obj);
                            if (objectToAdd != null) {
                                stack.add(objectToAdd);//將對象放入棧中,一遍彈出后繼續(xù)檢索
                            }
                        } catch (IllegalAccessException ex) {
                            assert false;
                        }
                    }
                }
            }
            clazz = clazz.getSuperclass();//找父類class,直到?jīng)]有父類
        }
        return result;
    }
}

到這里就結束了,有幾個疑問,大家有興趣可以關注下:

1.修改MANIFEST文件應該還有其他方法,好像是jar cmf manifest-addition jar-file input-file(s),大家有興趣可以關注下

2.如何在IDE中實現(xiàn)以上這些步驟,javac和java畢竟是遠古時代的東西了。

3.Instrumentation 的最大作用再說一說,就是類定義動態(tài)改變和操作。在 Java SE 5 及其后續(xù)版本當中,開發(fā)者可以在一個普通 Java 程序(帶有 main 函數(shù)的 Java 類)運行時,通過 – javaagent參數(shù)指定一個特定的 jar 文件(包含 Instrumentation 代理)來啟動 Instrumentation 的代理程序,然后利用Instrumentation的實現(xiàn)類做一列測量,這個有點類似AOP

4.先想這么多,想到更多問題再補充。

Extra:

jar操作指令

操作命令Cool
創(chuàng)建一個JAR文件jar cf jar-file input-file(s)
查看JAR文件的內容jar tf jar-file
導出JAR文件jar xf jar-file
導出JAR文件中制定的文件包jar xf jar-file archived-file(s)
運行JAR文件中的應用jre -cp app.jar MainClass
運行用JAR格式打包的應用java -jar app.jar
調用一個打包成JAR的applet

方法三

使用JOL 工具來查看一個對象的大小和分布

這個和前兩個方法不太一樣,是用來測一個JVM中對象的內部分布情況的,而且測得是引用的大小,Object obj中,這個引用obj也是占大小的。

這個方法作為拓展即可。

JOL (Java Object Layout) is the tiny toolbox to analyze object layout schemes in JVMs.

These tools are using Unsafe, JVMTI, and Serviceability Agent (SA) heavily to decoder the actual object layout, footprint, and references.

This makes JOL much more accurate than other tools relying on heap dumps, specification assumptions, etc.

該工具官網(wǎng):

http://openjdk.java.NET/projects/code-tools/jol/

下載jar包后保持如下的相對路徑 和你要測試的類在一起.

編寫測試對象的類:VolatileLong

public final class VolatileLong {
        /**
         * 大小計算:
         *  long 8 字節(jié)
         * [1]java對象頭: 32位 :8 byte 64位:12 byte
         * 所以總共: 6 個填充 * 8 byte + 8 (value) + 8 (對象頭) = 64 byte
         *  還有采用如下方式的
         *  long p1, p2, p3, p4, p5, p6, p7; // cache line padding  -> 7 *8 = 56 字節(jié)
         *  long value;  ->  8 字節(jié)
         *  long p8, p9, p10, p11, p12, p13, p14; // cache line padding -> 7*8 = 56 字節(jié)
         *
         *  java.util.concurrent.Exchanger.Slot<V>
         *          // Improve likelihood of isolation on <= 64 byte cache lines
         *  long q0, q1, q2, q3, q4, q5, q6, q7, q8, q9, qa, qb, qc, qd, qe;  15 * 8
         *  不知道為啥是這樣實現(xiàn):因為父類還有一個Long值 所以總的來說已經(jīng)超過128了
         *
         */
        public volatile long value = 0L;
        public long p1, p2, p3, p4, p5, p6; // comment out
        //objectsize = 6*8 + 8 + 4 =
}

執(zhí)行下面指令:

java -jar jol-cli-0.9-full.jar internals -cp . VolatileLong

//jar包必須帶上全路徑,先把class文件編譯出來,然后再執(zhí)行,因為此命令只接受class方式,若在IDE中測試,去掉報名,不然會報找不到類的錯誤,如下

java.lang.NoClassDefFoundError: ObjectLoc (wrong name: com/pingan/jvm/objectsize/ObjectLoc)
        at java.lang.ClassLoader.defineClass1(Native Method)
        at java.lang.ClassLoader.defineClass(ClassLoader.java:763)
        at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142)
        at java.net.URLClassLoader.defineClass(URLClassLoader.java:467)
        at java.net.URLClassLoader.access$100(URLClassLoader.java:73)
        at java.net.URLClassLoader$1.run(URLClassLoader.java:368)
        at java.net.URLClassLoader$1.run(URLClassLoader.java:362)
        at java.security.AccessController.doPrivileged(Native Method)
        at java.net.URLClassLoader.findClass(URLClassLoader.java:361)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
        at java.lang.Class.forName0(Native Method)
        at java.lang.Class.forName(Class.java:348)
        at org.openjdk.jol.util.ClassUtils.loadClass(ClassUtils.java:70)
        at org.openjdk.jol.operations.ClasspathedOperation.run(ClasspathedOperation.java:76)
        at org.openjdk.jol.Main.main(Main.java:60)

運行結果:

VolatileLong object internals:
 OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
      0     4        (object header)                           01 00 00 00 (00000001 00000000 00000000 00000000) (1)
      4     4        (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
      8     4        (object header)                           85 07 02 f8 (10000101 00000111 00000010 11111000) (-134084731)
     12     4        (alignment/padding gap)                  
     16     8   long VolatileLong.value                        0
     24     8   long VolatileLong.p1                           0
     32     8   long VolatileLong.p2                           0
     40     8   long VolatileLong.p3                           0
     48     8   long VolatileLong.p4                           0
     56     8   long VolatileLong.p5                           0
     64     8   long VolatileLong.p6                           0
Instance size: 72 bytes
Space losses: 4 bytes internal + 0 bytes external = 4 bytes total

總結

以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。

相關文章

  • java不解壓直接讀取壓縮包中文件的實現(xiàn)方法

    java不解壓直接讀取壓縮包中文件的實現(xiàn)方法

    這篇文章主要介紹了java不解壓直接讀取壓縮包中文件的實現(xiàn)方法,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2021-04-04
  • intellij idea旗艦版解決學生無法注冊問題詳解

    intellij idea旗艦版解決學生無法注冊問題詳解

    這篇文章主要介紹了intellij idea旗艦版解決學生無法注冊問題詳解,文中通過圖文示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2020-07-07
  • Java 的抽象類原理與用法分析

    Java 的抽象類原理與用法分析

    這篇文章主要介紹了Java 的抽象類原理與用法,結合實例形式分析了java抽象類與接口的相關概念、原理、使用方法及操作注意事項,需要的朋友可以參考下
    2019-12-12
  • Spring Boot使用模板freemarker的示例代碼

    Spring Boot使用模板freemarker的示例代碼

    本篇文章主要介紹了Spring Boot使用模板freemarker的示例代碼,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-10-10
  • 解讀@RabbitListener起作用的原理

    解讀@RabbitListener起作用的原理

    這篇文章主要介紹了解讀@RabbitListener起作用的原理,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-03-03
  • Java中的線程安全問題詳細解析

    Java中的線程安全問題詳細解析

    這篇文章主要介紹了Java中的線程安全問題詳細解析,線程安全是如果有多個線程在同時運行,而這些線程可能會同時運行這段代碼,程序每次運行結果和單線程運行的結果是一樣的,而且其他的變量的值也和預期的是一樣的,此時我們就稱之為是線程安全的,需要的朋友可以參考下
    2023-11-11
  • SpringMVC處理數(shù)據(jù)輸出的實例代碼

    SpringMVC處理數(shù)據(jù)輸出的實例代碼

    這篇文章主要給大家介紹了關于SpringMVC處理數(shù)據(jù)輸出的相關資料,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2021-05-05
  • springboot application無法使用$獲取pom變量的問題及解決

    springboot application無法使用$獲取pom變量的問題及解決

    這篇文章主要介紹了springboot application無法使用$獲取pom變量的問題及解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-02-02
  • Docker使用Dockerfile自定義鏡像方式

    Docker使用Dockerfile自定義鏡像方式

    這篇文章主要介紹了Docker使用Dockerfile自定義鏡像方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-08-08
  • Java限流實現(xiàn)的幾種方法詳解

    Java限流實現(xiàn)的幾種方法詳解

    這篇文章主要介紹了Java限流實現(xiàn)的幾種方法,通俗的說,限流就是 限制一段時間內,用戶訪問資源的次數(shù),減輕服務器壓力,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2022-12-12

最新評論

呼图壁县| 浦北县| 锡林浩特市| 桂林市| 浙江省| 龙泉市| 忻州市| 伽师县| 大关县| 平乡县| 固原市| 长泰县| 安多县| 桦南县| 怀化市| 广西| 商城县| 东乡族自治县| 周宁县| 江门市| 云林县| 秦皇岛市| 寿光市| 济南市| 祁东县| 当雄县| 新营市| 旬阳县| 楚雄市| 汉中市| 汶川县| 湖州市| 宁安市| 巴彦县| 延川县| 广宁县| 辽宁省| 且末县| 镇平县| 比如县| 甘南县|