我有一个应用程序,读取一个CSV文件与成堆的数据行。我根据数据类型向用户提供了行数的摘要,但我想确保不会读入太多行数据而导致OutOfMemoryErrors。每一行转换成一个对象。有没有一种简单的方法以编程方式找出该对象的大小?是否有一个引用定义了一个VM的基本类型和对象引用有多大?
现在,我有代码说读取多达32,000行,但我还希望有代码说读取尽可能多的行,直到我使用了32MB的内存。也许这是另一个问题,但我还是想知道。
我有一个应用程序,读取一个CSV文件与成堆的数据行。我根据数据类型向用户提供了行数的摘要,但我想确保不会读入太多行数据而导致OutOfMemoryErrors。每一行转换成一个对象。有没有一种简单的方法以编程方式找出该对象的大小?是否有一个引用定义了一个VM的基本类型和对象引用有多大?
现在,我有代码说读取多达32,000行,但我还希望有代码说读取尽可能多的行,直到我使用了32MB的内存。也许这是另一个问题,但我还是想知道。
当前回答
如果你只是想知道在你的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();
edit:我认为这可能会有帮助,因为问题作者还表示,他希望有处理“读取尽可能多的行,直到我使用了32MB内存”的逻辑。
其他回答
我怀疑您是否希望以编程方式完成它,除非您只是想执行一次并将其存储起来以供将来使用。这是一件代价高昂的事情。在Java中没有sizeof()操作符,即使有,它也只会计算引用其他对象的代价和原语的大小。
你可以这样做的一种方法是将它序列化到File中,然后查看文件的大小,就像这样:
Serializable myObject;
ObjectOutputStream oos = new ObjectOutputStream (new FileOutputStream ("obj.ser"));
oos.write (myObject);
oos.close ();
当然,这假设每个对象都是不同的,并且不包含对其他任何对象的非瞬时引用。
另一种策略是获取每个对象并通过反射检查其成员,并将大小相加(boolean & byte = 1字节,short & char = 2字节,等等),沿着成员层次结构向下工作。但这既乏味又昂贵,而且最终与序列化策略所做的事情相同。
首先,“对象的大小”在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大小初始化为一个合理的值,代码将会更有效。
还有内存测量器工具(以前在谷歌Code,现在在GitHub上),它很简单,在商业友好的Apache 2.0许可下发布,就像在类似的问题中讨论的那样。
如果您想测量内存字节消耗,它也需要一个java解释器的命令行参数,但在其他方面似乎工作得很好,至少在我使用它的场景中是这样。
没有方法调用,如果这是你想要的。只要稍加研究,我想你就可以自己写了。一个特定的实例具有一个固定的大小,该大小来自引用和原语值的数量以及实例簿记数据。您可以简单地遍历对象图。行类型变化越少,越容易。
如果这太慢或者麻烦太多,总有好的老式的行计数经验法则。
这个答案与对象大小无关,而是当你使用数组来容纳对象时;它将为对象分配多少内存大小。
所以数组,列表,或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字节)