java - 内存使用情况

时间:2012-09-19 19:11:45

标签: java memory jvm heap

我正在开发一个加载大量数据的应用程序(例如来自csv)。

我正在创建List<List<SimpleCell>>并将加载的单元格加载到其中。 SimpleCell类包含5 * String,每个String平均包含10个字符。

所以我想如果我读了1000行 - 每行包含160列 -​​ 给出了1000 * 160 = 160 000 SimpleCell个实例 - 它将是大约160 000 * {{1} } = ~160 000 * 10 * 5 = 8 000 000字节= ~7.63 MB。

但是当我正在查看jconsole时(点击sizeof(SimpleCell.class)后)内存使用量约为790MB。怎么会这样?

注意我没有存储对任何“临时”对象的任何引用。 以下是内存使用率上升时的代码:

Perform GC

for(int i = r.getFromIndex(); i <= r.getToIndex(); ++i) { System.out.println("Processing: 'ZZ " + i + "'"); List<SimpleCell> values = saxRead("ZT/ZZ " + i + ""); rows.add(values); } 只是创建inputStream用SAX解析它,关闭流,并返回单元格(由SAXHandler创建) - 所以只有局部变量(我认为它将在接近'未来'中被吞噬)。 / p>

我在阅读1000行时得到saxRead,但我必须阅读大约7k。

显然 - 我不了解jvm内存。 那么为什么在加载这些相对少量的数据时内存使用量如此之大?

4 个答案:

答案 0 :(得分:3)

一个字符串使用48个字节加上文本的大小* 2.(每个字符是2个字节)Simple Cell对象使用40个字节,它们的List使用1064个字节。

这意味着每行使用1064 + 160 * 40 + 5 * 180 *(48 + 20)字节或大约68K。如果你有1000行,你将使用大约70 MB,这比你看到的要少得多。

我建议您使用内存配置文件来确切了解内存使用了多少内存。例如VisualVM或YourKit。

根据您构建字符串的方式,您可以保留比此更多的内存。例如,您可能保留对原始XML的引用,因为当您使用substring时,您实际上正在保留原始XML的副本。


您可能会发现此课程很有用。如果字符串使用超过他们需要的内存,它将减少使用的内存量,并使用固定大小的缓存减少重复项。

static class StringCache {
    final WeakReference<String>[] strings;
    final int mask;

    @SuppressWarnings("unchecked")
    StringCache(int size) {
        int size2 = 128;
        while (size2 < size)
            size2 *= 2;
        strings = new WeakReference[size2];
        mask = size2 - 1;
    }

    public String intern(String text) {
        if (text.length() == 0) return "";

        int hash = text.hashCode() & mask;
        WeakReference<String> wrs = strings[hash];
        if (wrs != null) {
            String ret = wrs.get();
            if (text.equals(ret))
                return ret;
        }
        String ret = new String(text);
        strings[hash] = new WeakReference<String>(ret);
        return ret;
    }
}

答案 1 :(得分:2)

JVM内存管理引入了大量开销。 例如,在32位vm上,一个包含5个字符的字符串消耗58个字节的内存(不仅仅是5个!):

JVM开销:16b +簿记字段:12b +指向char []的指针:4b + char [] jvm开销:16b +数据:10b

答案 2 :(得分:2)

使用VisualVM来分析您的堆使用情况,并准备好感到惊讶。

答案 3 :(得分:1)

Java 非常内存饥渴。考虑这些估计:

32位VM:

您的一个字符串(大约)的大小

10个UTF-16字符= 20个字节

1个数组长度= 4个字节

1个数组对象标头= 8个字节

1个数组引用= 4个字节

1个偏移量,计数,哈希码(内部字段)= 12个字节

1个对象标头= 8个字节

1个典型的Java字符串= 20 + 4 + 8 + 4 + 12 + 8 = 56字节

简单单元格的大小(大约包括字符串)

5个字符串= 56 * 5 = 280个字节

5个字符串引用= 5 * 4个字节= 20个字节

1个对象标头= 8个字节

1 SimpleCell = 180 + 20 + 8 = 308字节

160000 SimpleCell = 308 * 160000 = 49280000字节

64位VM(没有压缩的oops)

您的一个字符串(大约)的大小

10个UTF-16字符= 20个字节

1个数组长度= 4个字节

1个数组对象标头= 8个字节

1个数组引用= 8个字节

1个偏移量,计数,哈希码(内部字段)= 12个字节

1个对象标头= 8个字节

1个典型的Java字符串= 20 + 4 + 8 + 8 + 12 + 8 = 60字节

简单单元格的大小(大约包括字符串)

5个字符串= 60 * 5 = 300个字节

5个字符串引用= 5 * 8个字节= 40个字节

1个对象标头= 8个字节

1 SimpleCell = 300 + 40 + 8 = 308字节

160000 SimpleCell = 348 * 160000 = 55680000字节

显然你的790 Mb非常远(看起来像是泄漏),但几乎比你估计的高出一个数量级。