如何查看java對象所佔的記憶體大小

來源:互聯網
上載者:User

參考:http://www.jroller.com/maxim/entry/again_about_determining_size_of

參考:http://blog.csdn.net/xieyuooo/article/details/7068216

我們為什麼想要知道對象所佔對記憶體的大小呢?

(1)做一些cache的時候,我們不可能把資料庫的所有的資料都緩衝到記憶體裡面,我們要估計緩衝的大小。
(2)記憶體泄露的時候,我們可以查看某些對象的大小來定位問題,當然還有其他的更有效方式,比如使用MAT分析dump檔案
(3)根據jvm的堆記憶體設定,我們可以知道最多可以建立多少個對象。
從jdk5開始,提供了Instrumentation API,它有一個叫做getObjectSize()的方法,但是,這個方法存在兩個問題:
(1)不可以直接使用。必須要實現一個Instrumentation Agent,還得放到jar包裡面。
(2)它只能返回單個對象的大小,不能返回內部包含的子物件的大小。
關於第一個問題,很好解決,在任何一個類裡面聲明一個"premain"方法,就可以把這個類做成是一個agent:
public class SizeOfAgent {
         static Instrumentation inst;
         /** initializes agent */
         public static void premain(String agentArgs, Instrumentation instP) {
                 inst = instP;           
         }
 }
jvm在啟動的時候會調用premain()方法,同時會傳遞Instrumentation這個對象執行個體,要告訴jvm Instrumentation agent所在的類,需要把這個類打到jar包裡面,
然後在manifest.mf這個檔案設定一些屬性:
Premain-Class: sizeof.agent.SizeOfAgent
Boot-Class-Path:
Can-Redefine-Classes: false
java應用在啟動的時候,指定-javaagent參數:
java -javaagent:sizeofag.jar <Your main class>
拿到Instrumentation這個執行個體以後,就可以調用sizeOf()方法了:
public class SizeOfAgent {
         static Instrumentation inst;
         // ...
         public static long sizeOf(Object o) {
                 return inst.getObjectSize(o);
         }
 }
然後可以使用反射來擷取子物件的大小。
完整的代碼如下:
package com.bj58.test;
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 SizeOfAgent {
    static Instrumentation inst;
    /** initializes agent */
    public static void premain(String agentArgs, Instrumentation instP) {
        inst = instP;
    }

    /**
     * Returns object size without member sub-objects.
     *
     * @param o
     *            object to get size of
     * @return object size
     */
    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);
    }

    /**
     * Calculates full size of object iterating over its hierarchy graph.
     *
     * @param obj
     *            object to calculate size of
     * @return object size
     */
    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) {
            // skip interned string
            if (obj == ((String) obj).intern()) {
                return true;
            }
        }
        return (obj == null) // skip visited object
                || visited.containsKey(obj);
    }

    private static long internalSizeOf(Object obj, Stack<Object> stack,
            Map<Object, Object> visited) {
        if (skipObject(obj, visited)) {
            return 0;
        }
        visited.put(obj, null);
        long result = 0;
        // get size of object + primitive variables + member pointers
        result += SizeOfAgent.sizeOf(obj);
        // process all array elements
        Class clazz = obj.getClass();
        if (clazz.isArray()) {
            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;
        }
        // process all fields of the object
        while (clazz != null) {
            Field[] fields = clazz.getDeclaredFields();
            for (int i = 0; i < fields.length; i++) {
                if (!Modifier.isStatic(fields[i].getModifiers())) {
                    if (fields[i].getType().isPrimitive()) {
                        continue; // skip primitive fields
                    } else {
                        fields[i].setAccessible(true);
                        try {
                            // objects to be estimated are put to stack
                            Object objectToAdd = fields[i].get(obj);
                            if (objectToAdd != null) {
                                stack.add(objectToAdd);
                            }
                        } catch (IllegalAccessException ex) {
                            assert false;
                        }
                    }
                }
            }
            clazz = clazz.getSuperclass();
        }
        return result;
    }
}
然後我們可以做一個測試:
public class Test {
    static class Person{
        private int id;
        private String name;
        private String address;
        public Person(int id, String name, String address) {
            this.id = id;
            this.name = name;
            this.address = address;
        }
    }
    public static void main(String[] args) throws Exception {
        Person p = new Person(12, "xujsh","bj");
        long size = SizeOfAgent.fullSizeOf(p);
        System.out.println(size);
    }
}
切換到命令列:
D:\workspace\objsize\src>java -version
java version "1.6.0_22"
Java(TM) SE Runtime Environment (build 1.6.0_22-b04)
Java HotSpot(TM) Client VM (build 17.1-b03, mixed mode, sharing)

D:\workspace\objsize\src>javac com/bj58/test/*.java

D:\workspace\objsize\src>jar -cvfm size.jar MANIFEST.MF com/bj58/test/*
標明清單(manifest)
增加:com/bj58/test/SizeOfAgent.class(讀入= 3119) (寫出= 1698)(壓縮了 45%)
增加:com/bj58/test/SizeOfAgent.java(讀入= 3147) (寫出= 1204)(壓縮了 61%)
增加:com/bj58/test/Test$Person.class(讀入= 442) (寫出= 305)(壓縮了 30%)
增加:com/bj58/test/Test.class(讀入= 692) (寫出= 441)(壓縮了 36%)
增加:com/bj58/test/Test.java(讀入= 509) (寫出= 290)(壓縮了 43%)

D:\workspace\objsize\src>java -javaagent:size.jar com.bj58.test.Test
24
MANIFEST.MF:
Manifest-Version: 1.0
Main-Class: com.bj58.test.Test
Premain-Class: com.bj58.test.SizeOfAgent
Boot-Class-Path:
Can-Redefine-Classes: false

【注意】MANIFEST.MF檔案的格式要求比較嚴格,每一行要滿足:key:空格value斷行符號

如何在web應用程式裡面使用呢?
以我的tomcat為例,
(1)把size.jar上傳tomcat的lib目錄下面
(2)修改catalina.sh:
添加一行:
JAVA_OPTS="$JAVA_OPTS -javaagent:$CATALINA_HOME/lib/size.jar"  //這一行是新添加的
if [ -z "$LOGGING_MANAGER" ]; then
  JAVA_OPTS="$JAVA_OPTS -Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager"
else
  JAVA_OPTS="$JAVA_OPTS $LOGGING_MANAGER"
fi
(3)在應用裡面添加一個controler:
@Path(value = "/api/size")
@GET
public ActionResult size() {
    Map<Long, List<Long>> map = ApiUtils.getHotindexBaidu();
    long size = SizeOfAgent.fullSizeOf(map);
    return new ApiActionResult("size:"+size);
}
然後就可以用瀏覽器來訪問了。

其他的還有:http://www.javaspecialists.co.za/archive/Issue078.html

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.