我有一个应用程序,读取一个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;
    }
 }

其他回答

没有方法调用,如果这是你想要的。只要稍加研究,我想你就可以自己写了。一个特定的实例具有一个固定的大小,该大小来自引用和原语值的数量以及实例簿记数据。您可以简单地遍历对象图。行类型变化越少,越容易。

如果这太慢或者麻烦太多,总有好的老式的行计数经验法则。

一个可能的答案是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)

不需要干扰插装等,如果你不需要知道一个对象的确切字节大小,你可以使用以下方法:

System.gc();
Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();

do your job here

System.gc();
Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();

通过这种方式,您可以读取之前和之后使用的内存,并在获得使用的内存之前调用GC,将“噪声”降低到几乎为0。

为了得到更可靠的结果,您可以运行作业n次,然后将使用的内存除以n,得到一次运行占用的内存。甚至,你可以把整个过程运行更多次,得到一个平均值。

当我在Twitter工作时,我写了一个计算深度对象大小的实用程序。它考虑了不同的内存模型(32位,压缩oops, 64位),填充,子类填充,在循环数据结构和数组上正确工作。你可以编译这个。java文件;它没有外部依赖:

https://github.com/twitter/commons/blob/master/src/java/com/twitter/common/objectsize/ObjectSizeCalculator.java

几年前,Javaworld有一篇关于确定组合和潜在嵌套Java对象大小的文章,他们基本上介绍了如何在Java中创建sizeof()实现。这种方法基本上建立在其他工作的基础上,在这些工作中,人们通过实验确定了原语和典型Java对象的大小,然后将该知识应用于递归地遍历对象图以计算总大小的方法。

它总是比原生C实现更不准确,这仅仅是因为类背后发生的事情,但它应该是一个很好的指示器。

另外一个SourceForge项目被适当地称为sizeof,它提供了一个带有sizeof()实现的Java5库。

附注:不要使用序列化方法,序列化对象的大小和它在运行时所消耗的内存量之间没有相关性。