我有一个应用程序,读取一个CSV文件与成堆的数据行。我根据数据类型向用户提供了行数的摘要,但我想确保不会读入太多行数据而导致OutOfMemoryErrors。每一行转换成一个对象。有没有一种简单的方法以编程方式找出该对象的大小?是否有一个引用定义了一个VM的基本类型和对象引用有多大?
现在,我有代码说读取多达32,000行,但我还希望有代码说读取尽可能多的行,直到我使用了32MB的内存。也许这是另一个问题,但我还是想知道。
我有一个应用程序,读取一个CSV文件与成堆的数据行。我根据数据类型向用户提供了行数的摘要,但我想确保不会读入太多行数据而导致OutOfMemoryErrors。每一行转换成一个对象。有没有一种简单的方法以编程方式找出该对象的大小?是否有一个引用定义了一个VM的基本类型和对象引用有多大?
现在,我有代码说读取多达32,000行,但我还希望有代码说读取尽可能多的行,直到我使用了32MB的内存。也许这是另一个问题,但我还是想知道。
当前回答
instrumentation类提供了一种获取Java对象大小的好方法,但它要求您定义一个premain并使用Java代理运行程序。当您不需要任何代理,而又必须为应用程序提供一个虚拟Jar代理时,这是非常无聊的。
所以我使用sun.misc中的Unsafe类获得了一个替代解决方案。因此,根据处理器架构考虑对象堆对齐并计算最大字段偏移量,就可以测量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() { }
}
其他回答
几年前,Javaworld有一篇关于确定组合和潜在嵌套Java对象大小的文章,他们基本上介绍了如何在Java中创建sizeof()实现。这种方法基本上建立在其他工作的基础上,在这些工作中,人们通过实验确定了原语和典型Java对象的大小,然后将该知识应用于递归地遍历对象图以计算总大小的方法。
它总是比原生C实现更不准确,这仅仅是因为类背后发生的事情,但它应该是一个很好的指示器。
另外一个SourceForge项目被适当地称为sizeof,它提供了一个带有sizeof()实现的Java5库。
附注:不要使用序列化方法,序列化对象的大小和它在运行时所消耗的内存量之间没有相关性。
你必须利用反射在物体上行走。做的时候要小心:
仅仅分配一个对象在JVM中就有一些开销。这个量因JVM而异,所以可以将此值作为参数。至少让它成为一个常量(8字节?),并应用于分配的任何东西。 仅仅因为一个字节理论上是1个字节,并不意味着内存中只需要一个字节。 在对象引用中会有循环,因此您需要使用object-equals作为比较器来保留HashMap或类似的东西,以消除无限循环。
@jodonnell:我喜欢你的解决方案的简单性,但许多对象是不可序列化的(所以这将抛出一个异常),字段可以是暂时的,对象可以覆盖标准方法。
如果您的应用程序有Apache公共lang库作为依赖项,或者正在使用Spring框架,那么您还可以使用SerializationUtils类快速查找任何给定对象的大约字节大小。
byte[] data = SerializationUtils.serialize(user);
System.out.println("Approximate object size in bytes " + data.length);
当使用JetBrains IntelliJ时,首先在|文件设置|构建,执行,部署|调试器中启用“附加内存代理”。
调试时,右键单击感兴趣的变量,选择“计算保留大小”:
许多其他答案提供了浅的大小——例如,没有任何键或值的HashMap的大小,这可能不是您想要的。
jamm项目使用上面的java.lang.instrumentation包,但是遍历树,因此可以为您提供深层内存使用。
new MemoryMeter().measureDeep(myHashMap);
https://github.com/jbellis/jamm
要使用MemoryMeter,请使用“-javaagent:/jam .jar”启动JVM