我有一个应用程序,读取一个CSV文件与成堆的数据行。我根据数据类型向用户提供了行数的摘要,但我想确保不会读入太多行数据而导致OutOfMemoryErrors。每一行转换成一个对象。有没有一种简单的方法以编程方式找出该对象的大小?是否有一个引用定义了一个VM的基本类型和对象引用有多大?
现在,我有代码说读取多达32,000行,但我还希望有代码说读取尽可能多的行,直到我使用了32MB的内存。也许这是另一个问题,但我还是想知道。
我有一个应用程序,读取一个CSV文件与成堆的数据行。我根据数据类型向用户提供了行数的摘要,但我想确保不会读入太多行数据而导致OutOfMemoryErrors。每一行转换成一个对象。有没有一种简单的方法以编程方式找出该对象的大小?是否有一个引用定义了一个VM的基本类型和对象引用有多大?
现在,我有代码说读取多达32,000行,但我还希望有代码说读取尽可能多的行,直到我使用了32MB的内存。也许这是另一个问题,但我还是想知道。
当前回答
我正在寻找一个对象大小的运行时计算,满足以下要求:
在运行时可用,不需要包括插装。 使用Java 9+,无需访问Unsafe。 仅基于类。不是考虑字符串长度,数组长度等的深度sizeOf。
以下内容基于java专家的原始文章(https://www.javaspecialists.eu/archive/Issue078.html)的核心代码,以及不安全版本中对这个问题的另一个回答中的一些内容。
我希望有人觉得它有用。
public class JavaSize {
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 HEADER_SIZE = 8;
public static int sizeOf(Class<?> clazz) {
int result = 0;
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()) {
Class<?> primitiveClass = fields[i].getType();
if (primitiveClass == boolean.class || primitiveClass == byte.class) {
result += 1;
} else if (primitiveClass == short.class) {
result += 2;
} else if (primitiveClass == int.class || primitiveClass == float.class) {
result += 4;
} else if (primitiveClass == double.class || primitiveClass == long.class) {
result += 8;
}
} else {
// assume compressed references.
result += 4;
}
}
}
clazz = clazz.getSuperclass();
// round up to the nearest WORD length.
if ((result % WORD) != 0) {
result += WORD - (result % WORD);
}
}
result += HEADER_SIZE;
return result;
}
}
其他回答
许多其他答案提供了浅的大小——例如,没有任何键或值的HashMap的大小,这可能不是您想要的。
jamm项目使用上面的java.lang.instrumentation包,但是遍历树,因此可以为您提供深层内存使用。
new MemoryMeter().measureDeep(myHashMap);
https://github.com/jbellis/jamm
要使用MemoryMeter,请使用“-javaagent:/jam .jar”启动JVM
您必须使用工具来测量它,或者手工估计它,这取决于您正在使用的JVM。
每个对象都有一些固定的开销。它是jvm特有的,但我通常估计有40个字节。然后你要看看这个班级的成员。对象引用在32位(64位)JVM中是4(8)个字节。基本类型是:
布尔值和字节:1字节 Char和short: 2字节 Int和float: 4字节 Long和double: 8字节
数组也遵循同样的规则;也就是说,它是一个对象引用,因此在对象中占用4(或8)个字节,然后它的长度乘以其元素的大小。
试图通过调用Runtime.freeMemory()以编程方式来实现这一点并不能提供很高的准确性,因为对垃圾收集器的异步调用等等。使用-Xrunhprof或其他工具对堆进行分析将为您提供最准确的结果。
long heapSizeBefore = Runtime.getRuntime().totalMemory();
// Code for object construction
...
long heapSizeAfter = Runtime.getRuntime().totalMemory();
long size = heapSizeAfter - heapSizeBefore;
大小提供了由于创建对象而增加的JVM内存使用,通常是对象的大小。
我的答案是基于Nick提供的代码。该代码测量被序列化对象占用的字节总数。因此,这实际上衡量的是序列化的东西+普通对象的内存占用(只要序列化,例如int,你会看到序列化的字节总数不是4)。所以,如果你想获得对象使用的原始字节数,你需要修改一下代码。像这样:
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;
}
}
我已经用基本类型String和一些普通类测试了这个解决方案。可能也有不包括在内的情况。
更新:示例修改为支持数组对象的内存占用计算。
当我在Twitter工作时,我写了一个计算深度对象大小的实用程序。它考虑了不同的内存模型(32位,压缩oops, 64位),填充,子类填充,在循环数据结构和数组上正确工作。你可以编译这个。java文件;它没有外部依赖:
https://github.com/twitter/commons/blob/master/src/java/com/twitter/common/objectsize/ObjectSizeCalculator.java