我知道java中的char是2bytes。但如果在列表中加载一些字符,它们将花费87B来保存字符。测试如下:
有一个包含995328行的文件“source”。每一行都只是一个字符:'a'。 (所以在java中花费将近2MB来保存所有字符)。
在我的源代码中调用了两个sleep方法,我使用top命令随时检查内存使用情况。
运行第一个睡眠(10000)方法时的RSIZE值为25M,运行第二个睡眠方法时为108M。所以每个字符串(这只是一个“a”)成本:(108MB-25MB)/ 995328 = 87B。我不知道为什么一个字符串“a”花费了这么多内存!任何人都可以告诉我为什么吗?
public static void main(String[] args) throws Exception{
File file = new File("source");
BufferedReader br = new BufferedReader(new FileReader(file));
String line = null;
List<String> list = new ArrayList<String>();
Thread.sleep(10000);
while((line = br.readLine())!=null){
list.add(line);
}
Thread.sleep(10000);
}
答案 0 :(得分:3)
@Amir是正确的说,有比top更好的方法(例如,JDK中包含hprof)来衡量内存使用情况,但是有一些更深层次的问题会混淆你的内存数量。
file
或br
。这是一个很大的问题。这些对象中的每一个都是一堆本机代码的包装器,用于与操作系统的文件I / O库进行交互。这些资源包括文件句柄和缓存缓冲区,因此您从文件中读取的某些数据在内存使用中计算两次 - 一次在附加到br
的缓存中,一次在list
中。 / LI>
list
变量也有开销。有一个支持数组,每个插槽都是一个指针(8个字节),并且有很多空插槽。随着后备阵列增长以容纳线,ArrayList
类留下一些额外的空间,因为数组调整大小(即,创建一个新数组并复制旧数组中的所有元素)是昂贵的,并且每个空插槽在64位系统上是8个字节。ArrayList
的后备存储大小所剩下的所有额外数组很可能仍在内存中,并且计入最顶层的数字。由于这些数组很大(最可能有一个至少500K的插槽,每个都是一个8字节的指针),这会增加程序的总内存使用量。N.B。我在上面讨论了8字节指针,假设是一个64位系统。在一个32位系统上,我说的一切都有,除了指针只有4个字节。
答案 1 :(得分:2)
我不会依靠top来计算这些数字。你为什么不使用像VisualVM这样的东西 - 它会告诉你数据结构占用了多少内存?
RSIZE
我认为反映了总驻留内存,其中包括JVM本身使用的内存!除了这个问题,您的基准测试不会考虑JVM尚未收集的无法访问的对象。使用探查器的堆快照会触发GC,这会考虑到这一点。
答案 2 :(得分:1)
您不是只保存arraylist中的字符,而是在文件中每行存储一个String
个实例。
我自己没有完成这些计算,但根据Neil Coffeys tutorial on string memory utilization,每个字符串都会占用:
最小字符串内存使用量(字节)= 8 *(int)((((无字符)* 2)+ 45)/ 8)
如果您的文件每行包含一个char,则每个字符串将至少花费8*((2+45) / 8)
= 47个字节。
再加上arraylist的费用。
答案 3 :(得分:0)
您完全忽略了您正在创建的字符串以及列表,特别是其增长策略的成本。检查Javadoc。我发现内部的ArrayList实现在溢出时会使列表增加50%。
答案 4 :(得分:0)
Java是一种垃圾收集语言,因此您不能通过执行某些代码来查看外部测量的虚拟内存占用量变化来估计数据结构的大小。您正在考虑堆中的增量,这可能是由于垃圾的累积以及由于垃圾的累积造成的。如果给自己提供比表示实时对象集所需的空间更多的空间,那么垃圾收集也会更好,因此收集不是那么频繁。一般来说,如果空间很小,垃圾收集会变慢。如果虚拟机的内存占用量接近表示所有对象所需的最小值,那么它的性能会非常差。