原文地址:http://www.jroller.com/maxim/entry/again_about_determining_size_of
译文地址:http://www.yeeyan.com/articles/view/104091/62930
再议如何确定Java对象的大小
有时计算java对象在内存中的大小是必要的。这篇文章将介绍一种借助于Java Instrumentation API的方法。
适用场景
计算java对象大小适用于如下场景
- 缓存 缓存通常用来提高频繁访问数据的性能。由于受到Java进程可分配内存大小的限制,它们通常不能从数据库(或者其他存储)中加载所有数据。也就是说,为了确保其内存大小不超过预设,缓存必须计算已加载数据的大小并且丢弃旧数据。
- 检测内存泄漏 在某些情况下你能在存在泄漏的指令的前后量测堆内存的大小并发现内存泄漏。如果你怀疑某些对象存在泄漏,你需要精确量测他们的大小并和泄漏的内存进行比较。有许多专门的大工具用可供使用,但是他们通常都太重量级了,并影响性能。在某些情况下如果有一种简单的对象大小计算方法,你能够更快的解决问题。
- 其他内存估算 例如,你能估算JVM最大堆内存的设置,如果你知道有多少对象将在你的应用程序中创建。
- 纯属娱乐
常见方法概述
有几种不同的方法可用于确定java对象的大小。他们大多是在JDK 5.0之前已经出现。
- http://jroller.com/page/mipsJava?entry=sizeof_java_objects -使用System.gc(),Runtime.freeMemory(), Runtime.totalMemory()方法来计算java对象的大小。这个方法通常需要许多资源才能精确计算出对象的大小。它必须创建许多的需要估算对象的实例(最好是几千个),在创建的前后量测堆内存的大小。这个方法对于使用缓存机制的生产系统并不奏效。这个方法的优点是可以得到较为精确的结果,而不受Java实现版本和操作系统的影响。
- 另一个更好的方法:http://www.javaspecialists.co.za/archive/Issue078.html - 它更加的巧妙。他使用真实的原始类型大小的对照表来确定整个对象的大小。使用反射API遍历对象继承链上的成员变量并且计算所有原始类型变量的大小。这个方法不像上一方法那样需要很多的资源并能够用于缓存机制。弊端是原始类型大小的对照表会随着JVM实现版本的不同而不同,对于不同的实现版本需要重新计算。
下面是一些关于类似方法的文章:
http://www.javaworld.com/javaworld/javatips/jw-javatip130.html
http://www.javaworld.com/javaworld/javaqa/2003-12/02-qa-1226-sizeof.html
http://www.javapractices.com/Topic83.cjp
http://forum.java.sun.com/thread.jspa?threadID=565721&messageID=2790847
使用Instrumentation API确定java对象大小
从JDK 5.0开始,新引入的 Instrumentation API 终于提供了 getObjectSize 方法。但是使用这个方法有两个问题:
- 这个方法不能直接使用,必须实现一个instrumentation代理类并且打包进JAR文件。
- 它仅返回某个对象的大小而不包括其成员变量所引用的对象。
这些问题很容易被解决。在任何类中,可以通过声明premain方法实现Java代理类:
1: public class SizeOfAgent { 2: 3: static Instrumentation inst; 4: 5: /** initializes agent */ 6: public static void premain(String agentArgs, Instrumentation instP) { 7: inst = instP; 8: } 9: }
Boot-Class-Path:
Can-Redefine-Classes: false
1: public class SizeOfAgent { 2: 3: static Instrumentation inst; 4: 5: // ... 6: 7: public static long sizeOf(Object o) { 8: return inst.getObjectSize(o); 9: } 10: }
001: package sizeof.agent; 002: 003: import java.lang.instrument.Instrumentation; 004: import java.lang.reflect.Array; 005: import java.lang.reflect.Field; 006: import java.lang.reflect.Modifier; 007: import java.util.IdentityHashMap; 008: import java.util.Map; 009: import java.util.Stack; 010: 011: /** Instrumentation agent used */ 012: public class SizeOfAgent { 013: 014: static Instrumentation inst; 015: 016: /** initializes agent */ 017: public static void premain(String agentArgs, Instrumentation instP) { 018: inst = instP; 019: } 020: 021: /** 022: * Returns object size without member sub-objects. 023: * @param o object to get size of 024: * @return object size 025: */ 026: public static long sizeOf(Object o) { 027: if(inst == null) { 028: throw new IllegalStateException("Can not access instrumentation environment.\n" + 029: "Please check if jar file containing SizeOfAgent class is \n" + 030: "specified in the java's \"-javaagent\" command line argument."); 031: } 032: return inst.getObjectSize(o); 033: } 034: 035: /** 036: * Calculates full size of object iterating over 037: * its hierarchy graph. 038: * @param obj object to calculate size of 039: * @return object size 040: */ 041: public static long fullSizeOf(Object obj) { 042: Map<Object, Object> visited = new IdentityHashMap<Object, Object>(); 043: Stack<Object> stack = new Stack<Object>(); 044: 045: long result = internalSizeOf(obj, stack, visited); 046: while (!stack.isEmpty()) { 047: result += internalSizeOf(stack.pop(), stack, visited); 048: } 049: visited.clear(); 050: return result; 051: } 052: 053: private static boolean skipObject(Object obj, Map<Object, Object> visited) { 054: if (obj instanceof String) { 055: // skip interned string 056: if (obj == ((String) obj).intern()) { 057: return true; 058: } 059: } 060: return (obj == null) // skip visited object 061: || visited.containsKey(obj); 062: } 063: 064: private static long internalSizeOf(Object obj, Stack<Object> stack, Map<Object, Object> visited) { 065: if (skipObject(obj, visited)){ 066: return 0; 067: } 068: visited.put(obj, null); 069: 070: long result = 0; 071: // get size of object + primitive variables + member pointers 072: result += SizeOfAgent.sizeOf(obj); 073: 074: // process all array elements 075: Class clazz = obj.getClass(); 076: if (clazz.isArray()) { 077: if(clazz.getName().length() != 2) {// skip primitive type array 078: int length = Array.getLength(obj); 079: for (int i = 0; i < length; i++) { 080: stack.add(Array.get(obj, i)); 081: } 082: } 083: return result; 084: } 085: 086: // process all fields of the object 087: while (clazz != null) { 088: Field[] fields = clazz.getDeclaredFields(); 089: for (int i = 0; i < fields.length; i++) { 090: if (!Modifier.isStatic(fields[i].getModifiers())) { 091: if (fields[i].getType().isPrimitive()) { 092: continue; // skip primitive fields 093: } else { 094: fields[i].setAccessible(true); 095: try { 096: // objects to be estimated are put to stack 097: Object objectToAdd = fields[i].get(obj); 098: if (objectToAdd != null) { 099: stack.add(objectToAdd); 100: } 101: } catch (IllegalAccessException ex) { 102: assert false; 103: } 104: } 105: } 106: } 107: clazz = clazz.getSuperclass(); 108: } 109: return result; 110: } 111: }
Last 5 posts by vangie
- win7无法运行程序,弹出“从服务器返回了一个参照 ”对话框 - February 21st, 2010
- win7 jumplist 失效问题 - February 1st, 2010
- 解决ATI显卡在ubuntu下开特效有点卡的问题 - January 3rd, 2010
- win7下访问ext4分区 - December 10th, 2009
- 恢复gnome-panel默认设置 - December 6th, 2009
Responses to “[翻译]再议如何确定Java对象的大小”
发表回复