我有一个应用程序,读取一个CSV文件与成堆的数据行。我根据数据类型向用户提供了行数的摘要,但我想确保不会读入太多行数据而导致OutOfMemoryErrors。每一行转换成一个对象。有没有一种简单的方法以编程方式找出该对象的大小?是否有一个引用定义了一个VM的基本类型和对象引用有多大?
现在,我有代码说读取多达32,000行,但我还希望有代码说读取尽可能多的行,直到我使用了32MB的内存。也许这是另一个问题,但我还是想知道。
我有一个应用程序,读取一个CSV文件与成堆的数据行。我根据数据类型向用户提供了行数的摘要,但我想确保不会读入太多行数据而导致OutOfMemoryErrors。每一行转换成一个对象。有没有一种简单的方法以编程方式找出该对象的大小?是否有一个引用定义了一个VM的基本类型和对象引用有多大?
现在,我有代码说读取多达32,000行,但我还希望有代码说读取尽可能多的行,直到我使用了32MB的内存。也许这是另一个问题,但我还是想知道。
当前回答
首先,“对象的大小”在Java中并不是一个定义明确的概念。你可以指对象本身,包括它的成员、对象和它引用的所有对象(引用图)。您可以指内存中的大小或磁盘上的大小。JVM可以优化字符串之类的东西。
所以唯一正确的方法是用一个好的分析器(我使用YourKit)询问JVM,这可能不是你想要的。
然而,从上面的描述来看,似乎每一行都是自包含的,没有很大的依赖树,因此序列化方法在大多数jvm上可能是一个很好的近似方法。最简单的方法如下:
Serializable ser;
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(ser);
oos.close();
return baos.size();
请记住,如果对象具有公共引用,这将不会给出正确的结果,并且序列化的大小并不总是与内存中的大小匹配,但这是一个很好的近似值。如果您将ByteArrayOutputStream大小初始化为一个合理的值,代码将会更有效。
其他回答
如果您的应用程序有Apache公共lang库作为依赖项,或者正在使用Spring框架,那么您还可以使用SerializationUtils类快速查找任何给定对象的大约字节大小。
byte[] data = SerializationUtils.serialize(user);
System.out.println("Approximate object size in bytes " + data.length);
这个答案与对象大小无关,而是当你使用数组来容纳对象时;它将为对象分配多少内存大小。
所以数组,列表,或map所有这些集合不会真正存储对象(只有在使用原语时,需要实际对象的内存大小),它只存储这些对象的引用。
现在在集合中使用的堆内存= sizeOfObj + sizeOfRef(* 4字节)
(4/8字节)取决于(32/64位)操作系统
原语
int [] intArray = new int [1]; will require 4 bytes.
long [] longArray = new long [1]; will require 8 bytes.
对象
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.
我的意思是说,所有对象REFERENCE只需要4个字节的内存。它可能是字符串引用或双对象引用,但根据对象创建所需的内存会有所不同。
例)如果我为下面的类ReferenceMemoryTest创建对象,那么将创建4 + 4 + 4 = 12字节的内存。当您尝试初始化引用时,内存可能会有所不同。
class ReferenceMemoryTest {
public String refStr;
public Object refObj;
public Double refDoub;
}
因此,当创建对象/引用数组时,它的所有内容都将被NULL引用占用。我们知道每个引用需要4个字节。
最后,下面代码的内存分配为20字节。
参考内存测试ref1 = new参考内存测试();(4(ref1) + 12 = 16字节) 参考内存测试ref2 = ref1;(4(ref2) + 16 = 20字节)
首先,“对象的大小”在Java中并不是一个定义明确的概念。你可以指对象本身,包括它的成员、对象和它引用的所有对象(引用图)。您可以指内存中的大小或磁盘上的大小。JVM可以优化字符串之类的东西。
所以唯一正确的方法是用一个好的分析器(我使用YourKit)询问JVM,这可能不是你想要的。
然而,从上面的描述来看,似乎每一行都是自包含的,没有很大的依赖树,因此序列化方法在大多数jvm上可能是一个很好的近似方法。最简单的方法如下:
Serializable ser;
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(ser);
oos.close();
return baos.size();
请记住,如果对象具有公共引用,这将不会给出正确的结果,并且序列化的大小并不总是与内存中的大小匹配,但这是一个很好的近似值。如果您将ByteArrayOutputStream大小初始化为一个合理的值,代码将会更有效。
使用java visual VM即可。
它具有分析和调试内存问题所需的一切。
它还有一个OQL(对象查询语言)控制台,可以让你做很多有用的事情,其中之一是sizeof(o)
可以使用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);
}
}
将以下内容添加到您的清单中。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