我正在开发一个加载大量数据的应用程序(例如来自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内存。 那么为什么在加载这些相对少量的数据时内存使用量如此之大?
答案 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 非常内存饥渴。考虑这些估计:
您的一个字符串(大约)的大小
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字节
您的一个字符串(大约)的大小
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非常远(看起来像是泄漏),但几乎比你估计的高出一个数量级。