例如,假设我有一个应用程序可以读取包含大量数据行的CSV文件.我根据数据类型向用户提供行数的摘要,但我想确保我不读取太多数据行并导致OutOfMemoryError
s.每行都转换为一个对象.有没有一种简单的方法可以通过编程方式找出该对象的大小?是否有一个引用定义了大型基元类型和对象引用的大小VM
?
现在,我的代码可以读取多达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
你应该使用jol,一个作为OpenJDK项目的一部分开发的工具.
JOL(Java Object Layout)是分析JVM中对象布局方案的微型工具箱.这些工具大量使用Unsafe,JVMTI和Serviceability Agent(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开销.用于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-.]+$")
(从这个答案中得到)的结果,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()
,jol会告诉您每个引用对象的字段解引用的地址,大小,类型,值和路径,尽管这通常太详细而无用.对于正在进行的模式示例,您可能会得到以下结果.(地址可能会在运行之间发生变化.)
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来分析VM和垃圾收集器内部.
几年前,Javaworld有一篇关于确定复合和可能嵌套的Java对象大小的文章,他们基本上是在Java中创建一个sizeof()实现.该方法基本上建立在其他工作上,其中人们通过实验确定基元和典型Java对象的大小,然后将该知识应用于递归遍历对象图以计算总大小的方法.
它总是比原生C实现更准确,仅仅是因为类的幕后发生的事情,但它应该是一个很好的指标.
或者,一个名为sizeof的SourceForge项目提供了一个带有sizeof()实现的Java5库.
PS不要使用序列化方法,序列化对象的大小与其实时消耗的内存量之间没有相关性.
我不小心发现了一个java类"jdk.nashorn.internal.ir.debug.ObjectSizeCalculator",它已经在jdk中,它易于使用,对于确定对象的大小似乎非常有用.
System.out.println(ObjectSizeCalculator.getObjectSize(new gnu.trove.map.hash.TObjectIntHashMap(12000, 0.6f, -1))); System.out.println(ObjectSizeCalculator.getObjectSize(new HashMap (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
首先,"对象的大小"在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大小初始化为合理值,则代码将更有效.
如果您只想知道JVM中使用了多少内存,以及多少是免费的,您可以尝试这样的方法:
// 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文件; 它没有外部依赖:
https://github.com/twitter/commons/blob/master/src/java/com/twitter/common/objectsize/ObjectSizeCalculator.java
许多其他答案提供了浅层大小 - 例如没有任何键或值的HashMap的大小,这可能不是您想要的.
jamm项目使用上面的java.lang.instrumentation包,但是遍历树,因此可以为您提供深度内存使用.
new MemoryMeter().measureDeep(myHashMap);
https://github.com/jbellis/jamm
你必须使用反射来对象.你要小心:
只是分配一个对象在JVM中有一些开销.数量因JVM而异,因此您可以将此值设为参数.至少使它成为常量(8个字节?)并应用于任何已分配的内容.
仅仅因为byte
理论上1个字节并不意味着它只需要一个内存.
在对象引用中会有循环,所以你需要使用object-equals作为比较器保留一个HashMap
或多个来消除无限循环.
@jodonnell:我喜欢你的解决方案的简单性,但是许多对象不是Serializable(所以这会抛出异常),字段可以是瞬态的,对象可以覆盖标准方法.
您必须使用工具进行测量,或者手动估算,这取决于您使用的JVM.
每个对象有一些固定的开销.它是特定于JVM的,但我通常估计40个字节.然后你必须看看班上的成员.对象引用在32位(64位)JVM中是4(8)个字节.原始类型是:
布尔和字节:1个字节
char和short:2个字节
int和float:4个字节
long和double:8个字节
数组遵循相同的规则; 也就是说,它是一个对象引用,因此在对象中占用4(或8)个字节,然后将其长度乘以其元素的大小.
尝试以编程方式执行调用Runtime.freeMemory()
只是不会给你很多准确性,因为异步调用垃圾收集器等.使用-Xrunhprof或其他工具对堆进行分析将为您提供最准确的结果.
本java.lang.instrument.Instrumentation
类提供了一个很好的方式来获得一个Java对象的大小,但它需要你定义一个premain
与一个Java代理运行程序.当您不需要任何代理时,这非常无聊,然后您必须为您的应用程序提供虚拟Jar代理.
所以我得到了另一种使用Unsafe
该类的替代解决方案sun.misc
.因此,根据处理器体系结构考虑对象堆对齐并计算最大字段偏移量,您可以测量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 // ListinstanceFields = new LinkedList (); 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() { } }
也有记忆测量仪工具(以前在谷歌代码,现在GitHub上),这是简单而下的商业友好发布Apache 2.0许可,如在讨论类似的问题.
它也需要一个命令行参数传递给Java解释器,如果你想测量内存字节消耗,但除此之外,似乎工作得很好,至少我已经用它的场景.