4

我正在尝试使用 visualvm 进行一些内存分析。我已经编写了一个基本代码,它运行一个无限循环来将对象添加到列表中。

package home.always.learning.java;  

import java.util.ArrayList;  
import java.util.List;  

public class Heaper {  

    private static List<PersonDetails> listObj = new ArrayList<PersonDetails>();  

    private static final String nameConst = "Tarun Trehan";  

    public static void main(String[] args)throws Exception{  
        personListCreation();  
    }  

    public static void personListCreation() throws Exception  
    {  
        int i = 0;  
        while(true)  
        {  
            System.out.println("Looping to create person list...");  
            i++;  
            listObj.add(new PersonDetails(nameConst+i));  
            //Thread.sleep(1000L);  
        }  
    }  
}  

正如预期的那样,内存迅速增加并在附加的visualvm 1st 快照中可见,即Memory_Shoots.JPG

但是,如果我从代码中删除以下注释并让程序休眠 1 秒;结果不同:

Thread.sleep(1000L);

内存增加但随后稳定并继续。请参考附上的第二张快照,即Memory_Stabilizes.JPG

我无法理解这种行为?您能否提供您的意见。

Memory_Shoots

Memory_Stabilizes

4

2 回答 2

4

查看稍微修改过的代码版本(固定的迭代次数,在启动后添加了 5 秒的暂停以允许我的 IDE 连接到 visualvm,删除 system.out.print 以提高速度等),您的罪魁祸首似乎是垃圾收集:

100L睡眠:(跑了5:18) 带睡眠的垃圾收集 带睡眠的垃圾收集

10L 睡眠:(跑了 5:01) 带睡眠的垃圾收集 带睡眠的垃圾收集

2L睡眠:(跑了4:57) 带睡眠的垃圾收集 带睡眠的垃圾收集

1L睡眠:(跑了6:36) 带睡眠的垃圾收集 带睡眠的垃圾收集

0L 睡眠:(跑了 0:23) 带睡眠的垃圾收集 带睡眠的垃圾收集

所以基本上我们发现使用的堆会缓慢上升,直到 Eden 空间被填满(导致分配失败),将 eden 的旧成员(几乎所有,查看空间使用情况)移动到幸存者 0/1,还有一些是老一代的。sleep 和 no sleep 之间的差异很可能是次要收集和主要收集的相对频率之间的差异。

于 2013-06-18T05:43:49.743 回答
2

有几件事值得思考:

  • 这些 PersonDetail 对象有多大?
  • 创建不属于该对象的 PersonDetail 对象需要多少内存?

这第二个值可以是很多东西。数组列表会经常产生一些垃圾。创建字符串“nameConst+i”的过程会产生一些垃圾对象。

假设答案是 PersonDetail 很小,但它需要中等大小的内存来制作。然后 JVM 将为您创建的每个 PersonDetail 扔掉一堆垃圾(垃圾收集)。当您创建 PersonDetails 时,垃圾会堆积起来,最终 JVM 会收集它。垃圾收集步骤会发现大量空闲内存,因为大多数分配都是短期对象。这可能会产生类似于第二张图片中的图形,锯齿,其中使用内存然后收集大量垃圾。

现在想象一下,您快速创建了一堆对象(没有 sleep 语句)。现在,使用的总内存将通过垃圾和您在列表中持有的对象迅速增加。当您达到最大内存时,垃圾收集将不得不更频繁地发生。有点像上图。

我不确定这是怎么回事,但你可以做的一件事是查看 VisualVM 中的采样,看看类创建了多少对象,更重要的是它们占用了多少内存。将 PersonDetails 使用的数量与其他需要清理的垃圾进行比较。

于 2013-06-18T05:43:04.543 回答