在Java中,确定对象大小的最佳方法是什么?
例如,假设我有一个应用程序可以读取一堆数据行的CSV文件。 我根据数据types向用户提供行数摘要,但是我想确保不读取太多数据行并导致OutOfMemoryError
。 每一行都翻译成一个对象。 有没有一种简单的方法来以编程方式找出该对象的大小? 是否有一个引用来定义一个VM
有多大的基本types和对象引用?
现在,我的代码说可以读取32,000行 ,但是我也希望代码能够读取尽可能多的行,直到我使用了32MB的内存。 也许这是一个不同的问题,但我仍然想知道。
你可以使用java.lang.instrument包
编译并把这个类放在一个JAR中:
import java.lang.instrument.Instrumentation; public class ObjectSizeFetcher { private static Instrumentation instrumentation; public static void premain(String args, Instrumentation inst) { instrumentation = inst; } public static long getObjectSize(Object o) { return instrumentation.getObjectSize(o); } }
将以下内容添加到您的MANIFEST.MF
:
Premain-Class: ObjectSizeFetcher
使用getObjectSize:
public class C { private int x; private int y; public static void main(String [] args) { System.out.println(ObjectSizeFetcher.getObjectSize(new C())); } }
调用:
java -javaagent:ObjectSizeFetcherAgent.jar C
几年前,Javaworld就有一篇关于确定组合和可能嵌套的Java对象的大小的文章 ,他们基本上是通过在Java中创buildsizeof()实现来完成的。 这个方法基本上build立在其他人们通过实验确定原语和典型Java对象的大小的工作上,然后将这些知识应用到recursion地走过一个对象图来统计总大小的方法。
它总是会比本地C实现稍微不准确,仅仅是因为在类的背后发生的事情,但它应该是一个很好的指标。
另外一个适当地称为sizeof的SourceForge项目提供了一个sizeof()实现的Java5库。
PS不要使用序列化方法,序列化对象的大小与其在现场消耗的内存量之间没有关联。
首先,“对象的大小”在Java中并不是一个明确的概念。 你可以指对象本身,只是它的成员,对象和它引用的所有对象(参考图)。 您可能意味着内存中的大小或磁盘上的大小。 JVM允许优化像Strings这样的东西。
所以唯一正确的方法是询问JVM,一个好的分析器(我使用YourKit ),这可能不是你想要的。
但是,从上面的描述中可以看出,每行都是独立的,没有大的依赖关系树,所以序列化方法可能是大多数JVM的一个很好的近似。 最简单的方法如下:
Serializable ser; ByteArrayOutputStream baos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(baos); oos.writeObject(ser); oos.close(); return baos.size();
请记住,如果具有通用引用的对象, 则不会给出正确的结果,并且序列化的大小并不总是与内存中的大小匹配,但是这是一个很好的近似值。 如果将ByteArrayOutputStream大小初始化为合理的值,代码将会更有效一些。
你应该使用jol ,作为OpenJDK项目的一部分开发的工具。
JOL(Java对象布局)是分析JVM中的对象布局scheme的小工具箱。 这些工具大量使用不安全,JVMTI和可维护性代理(SA)来解码实际的对象布局,占用空间和引用。 这使得JOL比依靠堆转储,规范假设等的其他工具更加准确。
要获取基元,引用和数组元素的大小,请使用VMSupport.vmDetails()
。 在64位Windows上运行的Oracle JDK 1.8.0_40(用于所有以下示例)中,此方法返回
Running 64-bit HotSpot VM. Using compressed oop with 0-bit shift. Using compressed klass with 3-bit shift. Objects are 8 bytes aligned. Field sizes by type: 4, 1, 1, 2, 2, 4, 4, 8, 8 [bytes] Array element sizes: 4, 1, 1, 2, 2, 4, 4, 8, 8 [bytes]
您可以使用ClassLayout.parseClass(Foo.class).toPrintable()
(可选地将实例传递给toPrintable
)来获取对象实例的浅层大小。 这只是该类的单个实例所占用的空间; 它不包含该类引用的任何其他对象。 它确实包含了对象头的VM开销,字段alignment和填充。 对于java.util.regex.Pattern
:
java.util.regex.Pattern object internals: OFFSET SIZE TYPE DESCRIPTION VALUE 0 4 (object header) 01 00 00 00 (0000 0001 0000 0000 0000 0000 0000 0000) 4 4 (object header) 00 00 00 00 (0000 0000 0000 0000 0000 0000 0000 0000) 8 4 (object header) cb cf 00 20 (1100 1011 1100 1111 0000 0000 0010 0000) 12 4 int Pattern.flags 0 16 4 int Pattern.capturingGroupCount 1 20 4 int Pattern.localCount 0 24 4 int Pattern.cursor 48 28 4 int Pattern.patternLength 0 32 1 boolean Pattern.compiled true 33 1 boolean Pattern.hasSupplementary false 34 2 (alignment/padding gap) N/A 36 4 String Pattern.pattern (object) 40 4 String Pattern.normalizedPattern (object) 44 4 Node Pattern.root (object) 48 4 Node Pattern.matchRoot (object) 52 4 int[] Pattern.buffer null 56 4 Map Pattern.namedGroups null 60 4 GroupHead[] Pattern.groupNodes null 64 4 int[] Pattern.temp null 68 4 (loss due to the next object alignment) Instance size: 72 bytes (reported by Instrumentation API) Space losses: 2 bytes internal + 4 bytes external = 6 bytes total
您可以使用GraphLayout.parseInstance(obj).toFootprint()
对象实例深度大小的摘要视图。 当然,脚印中的某些对象可能是共享的(也可能是来自其他对象的引用),因此当对象被垃圾收集时,可能会回收的空间过于复杂。 对于Pattern.compile("^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\\.[a-zA-Z0-9-.]+$")
的结果Pattern.compile("^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\\.[a-zA-Z0-9-.]+$")
(取自这个答案 ),jol报告1840个字节的总占用空间,其中只有72个是Pattern实例本身。
java.util.regex.Pattern instance footprint: COUNT AVG SUM DESCRIPTION 1 112 112 [C 3 272 816 [Z 1 24 24 java.lang.String 1 72 72 java.util.regex.Pattern 9 24 216 java.util.regex.Pattern$1 13 24 312 java.util.regex.Pattern$5 1 16 16 java.util.regex.Pattern$Begin 3 24 72 java.util.regex.Pattern$BitClass 3 32 96 java.util.regex.Pattern$Curly 1 24 24 java.util.regex.Pattern$Dollar 1 16 16 java.util.regex.Pattern$LastNode 1 16 16 java.util.regex.Pattern$Node 2 24 48 java.util.regex.Pattern$Single 40 1840 (total)
如果你改用GraphLayout.parseInstance(obj).toPrintable()
, GraphLayout.parseInstance(obj).toPrintable()
会告诉你每个引用对象的地址,大小,types,值和字段解引用的path,尽pipe这通常是非常有用的细节。 对于正在进行的模式示例,您可能会看到以下内容。 (地址在运行之间可能会改变。)
java.util.regex.Pattern object externals: ADDRESS SIZE TYPE PATH VALUE d5e5f290 16 java.util.regex.Pattern$Node .root.next.atom.next (object) d5e5f2a0 120 (something else) (somewhere else) (something else) d5e5f318 16 java.util.regex.Pattern$LastNode .root.next.next.next.next.next.next.next (object) d5e5f328 21664 (something else) (somewhere else) (something else) d5e647c8 24 java.lang.String .pattern (object) d5e647e0 112 [C .pattern.value [^, [, a, -, z, A, -, Z, 0, -, 9, _, ., +, -, ], +, @, [, a, -, z, A, -, Z, 0, -, 9, -, ], +, \, ., [, a, -, z, A, -, Z, 0, -, 9, -, ., ], +, $] d5e64850 448 (something else) (somewhere else) (something else) d5e64a10 72 java.util.regex.Pattern (object) d5e64a58 416 (something else) (somewhere else) (something else) d5e64bf8 16 java.util.regex.Pattern$Begin .root (object) d5e64c08 24 java.util.regex.Pattern$BitClass .root.next.atom.val$rhs (object) d5e64c20 272 [Z .root.next.atom.val$rhs.bits [false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, true, false, true, true, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, true, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false] d5e64d30 24 java.util.regex.Pattern$1 .root.next.atom.val$lhs.val$lhs.val$lhs.val$lhs.val$lhs.val$lhs (object) d5e64d48 24 java.util.regex.Pattern$1 .root.next.atom.val$lhs.val$lhs.val$lhs.val$lhs.val$lhs.val$rhs (object) d5e64d60 24 java.util.regex.Pattern$5 .root.next.atom.val$lhs.val$lhs.val$lhs.val$lhs.val$lhs (object) d5e64d78 24 java.util.regex.Pattern$1 .root.next.atom.val$lhs.val$lhs.val$lhs.val$lhs.val$rhs (object) d5e64d90 24 java.util.regex.Pattern$5 .root.next.atom.val$lhs.val$lhs.val$lhs.val$lhs (object) d5e64da8 24 java.util.regex.Pattern$5 .root.next.atom.val$lhs.val$lhs.val$lhs (object) d5e64dc0 24 java.util.regex.Pattern$5 .root.next.atom.val$lhs.val$lhs (object) d5e64dd8 24 java.util.regex.Pattern$5 .root.next.atom.val$lhs (object) d5e64df0 24 java.util.regex.Pattern$5 .root.next.atom (object) d5e64e08 32 java.util.regex.Pattern$Curly .root.next (object) d5e64e28 24 java.util.regex.Pattern$Single .root.next.next (object) d5e64e40 24 java.util.regex.Pattern$BitClass .root.next.next.next.atom.val$rhs (object) d5e64e58 272 [Z .root.next.next.next.atom.val$rhs.bits [false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, true, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false] d5e64f68 24 java.util.regex.Pattern$1 .root.next.next.next.atom.val$lhs.val$lhs.val$lhs (object) d5e64f80 24 java.util.regex.Pattern$1 .root.next.next.next.atom.val$lhs.val$lhs.val$rhs (object) d5e64f98 24 java.util.regex.Pattern$5 .root.next.next.next.atom.val$lhs.val$lhs (object) d5e64fb0 24 java.util.regex.Pattern$1 .root.next.next.next.atom.val$lhs.val$rhs (object) d5e64fc8 24 java.util.regex.Pattern$5 .root.next.next.next.atom.val$lhs (object) d5e64fe0 24 java.util.regex.Pattern$5 .root.next.next.next.atom (object) d5e64ff8 32 java.util.regex.Pattern$Curly .root.next.next.next (object) d5e65018 24 java.util.regex.Pattern$Single .root.next.next.next.next (object) d5e65030 24 java.util.regex.Pattern$BitClass .root.next.next.next.next.next.atom.val$rhs (object) d5e65048 272 [Z .root.next.next.next.next.next.atom.val$rhs.bits [false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, true, true, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false] d5e65158 24 java.util.regex.Pattern$1 .root.next.next.next.next.next.atom.val$lhs.val$lhs.val$lhs.val$lhs (object) d5e65170 24 java.util.regex.Pattern$1 .root.next.next.next.next.next.atom.val$lhs.val$lhs.val$lhs.val$rhs (object) d5e65188 24 java.util.regex.Pattern$5 .root.next.next.next.next.next.atom.val$lhs.val$lhs.val$lhs (object) d5e651a0 24 java.util.regex.Pattern$1 .root.next.next.next.next.next.atom.val$lhs.val$lhs.val$rhs (object) d5e651b8 24 java.util.regex.Pattern$5 .root.next.next.next.next.next.atom.val$lhs.val$lhs (object) d5e651d0 24 java.util.regex.Pattern$5 .root.next.next.next.next.next.atom.val$lhs (object) d5e651e8 24 java.util.regex.Pattern$5 .root.next.next.next.next.next.atom (object) d5e65200 32 java.util.regex.Pattern$Curly .root.next.next.next.next.next (object) d5e65220 120 (something else) (somewhere else) (something else) d5e65298 24 java.util.regex.Pattern$Dollar .root.next.next.next.next.next.next (object)
“(其他)”条目描述堆中不属于该对象图的其他对象 。
最好的jol文档是jol仓库中的jol样本 。 示例演示了常见的jol操作,并展示了如何使用jol来分析虚拟机和垃圾收集器内部。
如果您只想知道您的JVM中使用了多less内存,并且有多less空闲,则可以尝试如下所示:
// Get current size of heap in bytes long heapSize = Runtime.getRuntime().totalMemory(); // Get maximum size of heap in bytes. The heap cannot grow beyond this size. // Any attempt will result in an OutOfMemoryException. long heapMaxSize = Runtime.getRuntime().maxMemory(); // Get amount of free memory within the heap in bytes. This size will increase // after garbage collection and decrease as new objects are created. long heapFreeSize = Runtime.getRuntime().freeMemory();
编辑:我认为这可能是有帮助的,因为问题作者还表示,他希望有逻辑处理“尽可能多的行读取,直到我已经使用32MB的内存”。
当我在Twitter工作时,我写了一个用于计算深度对象大小的实用程序。 它考虑了不同的内存模型(32位,压缩oops,64位),填充,子类填充,在循环数据结构和数组上正常工作。 你可以编译这个.java文件; 它没有外部依赖:
我不小心发现了一个java类“jdk.nashorn.internal.ir.debug.ObjectSizeCalculator”,已经在jdk中,它很容易使用,并且对于确定一个对象的大小似乎非常有用。
System.out.println(ObjectSizeCalculator.getObjectSize(new gnu.trove.map.hash.TObjectIntHashMap<String>(12000, 0.6f, -1))); System.out.println(ObjectSizeCalculator.getObjectSize(new HashMap<String, Integer>(100000))); System.out.println(ObjectSizeCalculator.getObjectSize(3)); System.out.println(ObjectSizeCalculator.getObjectSize(new int[]{1, 2, 3, 4, 5, 6, 7 })); System.out.println(ObjectSizeCalculator.getObjectSize(new int[100]));
结果:
164192 48 16 48 416
许多其他的答案提供了浅的大小 – 例如没有任何键或值的HashMap的大小,这可能不是你想要的。
jamm项目使用上面的java.lang.instrumentation包,但走过树,因此可以给你深层的内存使用。
new MemoryMeter().measureDeep(myHashMap);
你必须用reflection来走路。 要小心,因为你这样做:
- 只是分配一个对象在JVM中有一些开销。 数量因JVM而异,所以您可以将此值设置为参数。 至less使它成为一个常量(8字节?)并适用于分配的任何东西。
- 仅仅因为
byte
在理论上是1个字节并不意味着它在内存中只需要一个字节。 - 在对象引用中会有循环,因此您需要保留一个
HashMap
或者使用object-equals作为比较器来消除无限循环。
@jodonnell:我喜欢你的解决scheme的简单性,但许多对象不是Serializable(所以这会抛出一个exception),字段可以是瞬态的,对象可以覆盖标准方法。
java.lang.instrument.Instrumentation
类提供了一个获取Java对象大小的好方法,但它需要你定义一个premain
并用java代理运行你的程序。 当你不需要任何代理时,这是非常无聊的,然后你必须提供一个虚拟Jar代理到你的应用程序。
所以我从sun.misc
得到了一个使用Unsafe
类的替代解决scheme。 因此,根据处理器体系结构考虑堆alignment问题并计算最大字段偏移量,可以测量Java对象的大小。 在下面的示例中,我使用辅助类UtilUnsafe
来获取对sun.misc.Unsafe
对象的引用。
private static final int NR_BITS = Integer.valueOf(System.getProperty("sun.arch.data.model")); private static final int BYTE = 8; private static final int WORD = NR_BITS/BYTE; private static final int MIN_SIZE = 16; public static int sizeOf(Class src){ // // Get the instance fields of src class // List<Field> instanceFields = new LinkedList<Field>(); do{ if(src == Object.class) return MIN_SIZE; for (Field f : src.getDeclaredFields()) { if((f.getModifiers() & Modifier.STATIC) == 0){ instanceFields.add(f); } } src = src.getSuperclass(); }while(instanceFields.isEmpty()); // // Get the field with the maximum offset // long maxOffset = 0; for (Field f : instanceFields) { long offset = UtilUnsafe.UNSAFE.objectFieldOffset(f); if(offset > maxOffset) maxOffset = offset; } return (((int)maxOffset/WORD) + 1)*WORD; } class UtilUnsafe { public static final sun.misc.Unsafe UNSAFE; static { Object theUnsafe = null; Exception exception = null; try { Class<?> uc = Class.forName("sun.misc.Unsafe"); Field f = uc.getDeclaredField("theUnsafe"); f.setAccessible(true); theUnsafe = f.get(uc); } catch (Exception e) { exception = e; } UNSAFE = (sun.misc.Unsafe) theUnsafe; if (UNSAFE == null) throw new Error("Could not obtain access to sun.misc.Unsafe", exception); } private UtilUnsafe() { } }
你必须用一个工具来衡量它,或者手工估计它,这取决于你正在使用的JVM。
每个对象有一些固定的开销。 这是JVM特定的,但我通常估计40个字节。 那么你必须看看class上的成员。 对象引用是32位(64位)JVM中的4(8)字节。 原始types是:
- 布尔值和字节:1个字节
- 字符和短:2个字节
- int和float:4个字节
- 长和双:8字节
数组遵循相同的规则; 也就是说,它是一个对象引用,因此在对象中需要4(或8)个字节,然后将其长度乘以其元素的大小。
试图以编程方式调用Runtime.freeMemory()
只是不会给你太多的准确性,因为对垃圾回收器的asynchronous调用等。使用-Xrunhprof或其他工具对堆进行性能分析将为您提供最准确的结果。
There is also the Memory Measurer tool (formerly at Google Code , now on GitHub ), which is simple and published under the commercial-friendly Apache 2.0 license , as discussed in a similar question .
It, too, requires a command-line argument to the java interpreter if you want to measure memory byte consumption, but otherwise seems to work just fine, at least in the scenarios I have used it.
Without having to mess with instrumentation and so on, and if you don't need to know the byte-exact size of an object, you could go with the following approach:
System.gc(); Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory(); do your job here System.gc(); Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();
This way you read the used memory before and after, and calling the GC just before getting the used memory you lower the "noise" almost to 0.
For a more reliable result you can run your job n times, and then divide the used memory by n, obtaining how much memory one run takes. Even more, you can run the whole thing more times and make an average.
I wrote a quick test once to estimate on the fly:
public class Test1 { // non-static nested class Nested { } // static nested static class StaticNested { } static long getFreeMemory () { // waits for free memory measurement to stabilize long init = Runtime.getRuntime().freeMemory(), init2; int count = 0; do { System.out.println("waiting..." + init); System.gc(); try { Thread.sleep(250); } catch (Exception x) { } init2 = init; init = Runtime.getRuntime().freeMemory(); if (init == init2) ++ count; else count = 0; } while (count < 5); System.out.println("ok..." + init); return init; } Test1 () throws InterruptedException { Object[] s = new Object[10000]; Object[] n = new Object[10000]; Object[] t = new Object[10000]; long init = getFreeMemory(); //for (int j = 0; j < 10000; ++ j) // s[j] = new Separate(); long afters = getFreeMemory(); for (int j = 0; j < 10000; ++ j) n[j] = new Nested(); long aftersn = getFreeMemory(); for (int j = 0; j < 10000; ++ j) t[j] = new StaticNested(); long aftersnt = getFreeMemory(); System.out.println("separate: " + -(afters - init) + " each=" + -(afters - init) / 10000); System.out.println("nested: " + -(aftersn - afters) + " each=" + -(aftersn - afters) / 10000); System.out.println("static nested: " + -(aftersnt - aftersn) + " each=" + -(aftersnt - aftersn) / 10000); } public static void main (String[] args) throws InterruptedException { new Test1(); } }
General concept is allocate objects and measure change in free heap space. The key being getFreeMemory()
, which requests GC runs and waits for the reported free heap size to stabilize . The output of the above is:
nested: 160000 each=16 static nested: 160000 each=16
Which is what we expect, given alignment behavior and possible heap block header overhead.
The instrumentation method detailed in the accepted answer here the most accurate. The method I described is accurate but only under controlled conditions where no other threads are creating/discarding objects.
Here is a utility I made using some of the linked examples to handle 32-bit, 64-bit and 64-bit with compressed OOP. It uses sun.misc.Unsafe
.
It uses Unsafe.addressSize()
to get the size of a native pointer and Unsafe.arrayIndexScale( Object[].class )
for the size of a Java reference.
It uses the field offset of a known class to work out the base size of an object.
import java.lang.reflect.Array; import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.util.IdentityHashMap; import java.util.Stack; import sun.misc.Unsafe; /** Usage: * MemoryUtil.sizeOf( object ) * MemoryUtil.deepSizeOf( object ) * MemoryUtil.ADDRESS_MODE */ public class MemoryUtil { private MemoryUtil() { } public static enum AddressMode { /** Unknown address mode. Size calculations may be unreliable. */ UNKNOWN, /** 32-bit address mode using 32-bit references. */ MEM_32BIT, /** 64-bit address mode using 64-bit references. */ MEM_64BIT, /** 64-bit address mode using 32-bit compressed references. */ MEM_64BIT_COMPRESSED_OOPS } /** The detected runtime address mode. */ public static final AddressMode ADDRESS_MODE; private static final Unsafe UNSAFE; private static final long ADDRESS_SIZE; // The size in bytes of a native pointer: 4 for 32 bit, 8 for 64 bit private static final long REFERENCE_SIZE; // The size of a Java reference: 4 for 32 bit, 4 for 64 bit compressed oops, 8 for 64 bit private static final long OBJECT_BASE_SIZE; // The minimum size of an Object: 8 for 32 bit, 12 for 64 bit compressed oops, 16 for 64 bit private static final long OBJECT_ALIGNMENT = 8; /** Use the offset of a known field to determine the minimum size of an object. */ private static final Object HELPER_OBJECT = new Object() { byte b; }; static { try { // Use reflection to get a reference to the 'Unsafe' object. Field f = Unsafe.class.getDeclaredField( "theUnsafe" ); f.setAccessible( true ); UNSAFE = (Unsafe) f.get( null ); OBJECT_BASE_SIZE = UNSAFE.objectFieldOffset( HELPER_OBJECT.getClass().getDeclaredField( "b" ) ); ADDRESS_SIZE = UNSAFE.addressSize(); REFERENCE_SIZE = UNSAFE.arrayIndexScale( Object[].class ); if( ADDRESS_SIZE == 4 ) { ADDRESS_MODE = AddressMode.MEM_32BIT; } else if( ADDRESS_SIZE == 8 && REFERENCE_SIZE == 8 ) { ADDRESS_MODE = AddressMode.MEM_64BIT; } else if( ADDRESS_SIZE == 8 && REFERENCE_SIZE == 4 ) { ADDRESS_MODE = AddressMode.MEM_64BIT_COMPRESSED_OOPS; } else { ADDRESS_MODE = AddressMode.UNKNOWN; } } catch( Exception e ) { throw new Error( e ); } } /** Return the size of the object excluding any referenced objects. */ public static long shallowSizeOf( final Object object ) { Class<?> objectClass = object.getClass(); if( objectClass.isArray() ) { // Array size is base offset + length * element size long size = UNSAFE.arrayBaseOffset( objectClass ) + UNSAFE.arrayIndexScale( objectClass ) * Array.getLength( object ); return padSize( size ); } else { // Object size is the largest field offset padded out to 8 bytes long size = OBJECT_BASE_SIZE; do { for( Field field : objectClass.getDeclaredFields() ) { if( (field.getModifiers() & Modifier.STATIC) == 0 ) { long offset = UNSAFE.objectFieldOffset( field ); if( offset >= size ) { size = offset + 1; // Field size is between 1 and PAD_SIZE bytes. Padding will round up to padding size. } } } objectClass = objectClass.getSuperclass(); } while( objectClass != null ); return padSize( size ); } } private static final long padSize( final long size ) { return (size + (OBJECT_ALIGNMENT - 1)) & ~(OBJECT_ALIGNMENT - 1); } /** Return the size of the object including any referenced objects. */ public static long deepSizeOf( final Object object ) { IdentityHashMap<Object,Object> visited = new IdentityHashMap<Object,Object>(); Stack<Object> stack = new Stack<Object>(); if( object != null ) stack.push( object ); long size = 0; while( !stack.isEmpty() ) { size += internalSizeOf( stack.pop(), stack, visited ); } return size; } private static long internalSizeOf( final Object object, final Stack<Object> stack, final IdentityHashMap<Object,Object> visited ) { // Scan for object references and add to stack Class<?> c = object.getClass(); if( c.isArray() && !c.getComponentType().isPrimitive() ) { // Add unseen array elements to stack for( int i = Array.getLength( object ) - 1; i >= 0; i-- ) { Object val = Array.get( object, i ); if( val != null && visited.put( val, val ) == null ) { stack.add( val ); } } } else { // Add unseen object references to the stack for( ; c != null; c = c.getSuperclass() ) { for( Field field : c.getDeclaredFields() ) { if( (field.getModifiers() & Modifier.STATIC) == 0 && !field.getType().isPrimitive() ) { field.setAccessible( true ); try { Object val = field.get( object ); if( val != null && visited.put( val, val ) == null ) { stack.add( val ); } } catch( IllegalArgumentException e ) { throw new RuntimeException( e ); } catch( IllegalAccessException e ) { throw new RuntimeException( e ); } } } } } return shallowSizeOf( object ); } }
I recommend the java-sizeof library for carrotsearch. 这很简单。
You can get it in maven:
<dependency> <groupId>com.carrotsearch</groupId> <artifactId>java-sizeof</artifactId> <version>0.0.3</version> </dependency>
It is only one code line that return the bytes of an object:
RamUsageEstimator.sizeOf(new Object());
You can see the source code at https://github.com/dweiss/java-sizeof
And there is a presentation from the author of the library http://www.slideshare.net/DawidWeiss/sizeofobject-how-much-memory-objects-take-on-jvms-and-when-this-may-matter?ref=http://cheremin.blogspot.com/2012/05/how-much-memory-objects-take-on-jvm-and.html
There isn't a method call, if that's what you're asking for. With a little research, I suppose you could write your own. A particular instance has a fixed sized derived from the number of references and primitive values plus instance bookkeeping data. You would simply walk the object graph. The less varied the row types, the easier.
If that's too slow or just more trouble than it's worth, there's always good old-fashioned row counting rule-of-thumbs.
You could generate a heap dump (with jmap, for example) and then analyze the output to find object sizes. This is an offline solution, but you can examine shallow and deep sizes, etc.
long heapSizeBefore = Runtime.getRuntime().totalMemory(); // Code for object construction ... long heapSizeAfter = Runtime.getRuntime().totalMemory(); long size = heapSizeAfter - heapSizeBefore;
size gives you the increase in memory usage of the jvm due to object creation and that typically is the size of the object.
My answer is based on the code supplied by Nick. That code measures total amount of bytes which are occupied by the serialized object. So this actually measures serialization stuff + plain object memory footprint (just serialize for example int
and you will see that total amount of serialized bytes is not 4
). So if you want to get raw byte number used exactly for your object – you need to modify that code a bit. 像这样:
import java.io.ByteArrayOutputStream; import java.io.ObjectOutputStream; import java.io.Serializable; public class ObjectSizeCalculator { private Object getFirstObjectReference(Object o) { String objectType = o.getClass().getTypeName(); if (objectType.substring(objectType.length()-2).equals("[]")) { try { if (objectType.equals("java.lang.Object[]")) return ((Object[])o)[0]; else if (objectType.equals("int[]")) return ((int[])o)[0]; else throw new RuntimeException("Not Implemented !"); } catch (IndexOutOfBoundsException e) { return null; } } return o; } public int getObjectSizeInBytes(Object o) { final String STRING_JAVA_TYPE_NAME = "java.lang.String"; if (o == null) return 0; String objectType = o.getClass().getTypeName(); boolean isArray = objectType.substring(objectType.length()-2).equals("[]"); Object objRef = getFirstObjectReference(o); if (objRef != null && !(objRef instanceof Serializable)) throw new RuntimeException("Object must be serializable for measuring it's memory footprint using this method !"); try { ByteArrayOutputStream baos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(baos); oos.writeObject(o); oos.close(); byte[] bytes = baos.toByteArray(); for (int i = bytes.length - 1, j = 0; i != 0; i--, j++) { if (objectType != STRING_JAVA_TYPE_NAME) { if (bytes[i] == 112) if (isArray) return j - 4; else return j; } else { if (bytes[i] == 0) return j - 1; } } } catch (Exception e) { return -1; } return -1; } }
I've tested this solution with primitive types, String, and on some trivial classes. There may be not covered cases also.
UPDATE: Example modified to support memory footprint calculation of array objects.
This answer is not related to Object size, but when you are using array to accommodate the objects; how much memory size it will allocate for the object.
So arrays, list, or map all those collection won't be going to store objects really (only at the time of primitives, real object memory size is needed), it will store only references for those objects.
Now the Used heap memory = sizeOfObj + sizeOfRef (* 4 bytes) in collection
- (4/8 bytes) depends on (32/64 bit) OS
PRIMITIVES
int [] intArray = new int [1]; will require 4 bytes. long [] longArray = new long [1]; will require 8 bytes.
OBJECTS
Object[] objectArray = new Object[1]; will require 4 bytes. The object can be any user defined Object. Long [] longArray = new Long [1]; will require 4 bytes.
I mean to say all the object REFERENCE needs only 4 bytes of memory. It may be String reference OR Double object reference, But depends on object creation the memory needed will vary.
eg) If i create object for the below class ReferenceMemoryTest
then 4 + 4 + 4 = 12 bytes of memory will be created. The memory may differ when you are trying to initialize the references.
class ReferenceMemoryTest { public String refStr; public Object refObj; public Double refDoub; }
So when are creating object/reference array, all its contents will be occupied with NULL references. And we know each reference requires 4 bytes.
And finally, memory allocation for the below code is 20 bytes.
ReferenceMemoryTest ref1 = new ReferenceMemoryTest(); ( 4(ref1) + 12 = 16 bytes) ReferenceMemoryTest ref2 = ref1; ( 4(ref2) + 16 = 20 bytes)
I doubt you want to do it programmatically unless you just want to do it once and store it for future use. It's a costly thing to do. There's no sizeof() operator in Java, and even if there was, it would only count the cost of the references to other objects and the size of the primitives.
One way you could do it is to serialize the thing to a File and look at the size of the file, like this:
Serializable myObject; ObjectOutputStream oos = new ObjectOutputStream (new FileOutputStream ("obj.ser")); oos.write (myObject); oos.close ();
Of course, this assumes that each object is distinct and doesn't contain non-transient references to anything else.
Another strategy would be to take each object and examine its members by reflection and add up the sizes (boolean & byte = 1 byte, short & char = 2 bytes, etc.), working your way down the membership hierarchy. But that's tedious and expensive and ends up doing the same thing the serialization strategy would do.