我有一个应用程序,读取一个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大小初始化为一个合理的值,代码将会更有效。
其他回答
这个答案与对象大小无关,而是当你使用数组来容纳对象时;它将为对象分配多少内存大小。
所以数组,列表,或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字节)
当使用JetBrains IntelliJ时,首先在|文件设置|构建,执行,部署|调试器中启用“附加内存代理”。
调试时,右键单击感兴趣的变量,选择“计算保留大小”:
可以使用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
一个可能的答案是2022年。
https://github.com/ehcache/sizeof
https://mvnrepository.com/artifact/org.ehcache/sizeof
https://mvnrepository.com/artifact/org.ehcache/sizeof/0.4.0
版本0.4.0只有一个(编译)依赖
https://mvnrepository.com/artifact/org.slf4j/slf4j-api
这是一件好事。
示例代码:
//import org.ehcache.sizeof.SizeOf;
SizeOf sizeOf = SizeOf.newInstance(); // (1)
long shallowSize = sizeOf.sizeOf(someObject); // (2)
long deepSize = sizeOf.deepSizeOf(someObject); // (3)
首先,“对象的大小”在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大小初始化为一个合理的值,代码将会更有效。