Java内存行为:与Thread.sleep不同

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

package home.always.learning.java; import java.util.ArrayList; import java.util.List; public class Heaper { private static List listObj = new ArrayList(); 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 snapshot中显示出来,即Memory_Shoots.JPG

但是,如果我从代码中删除以下注释并允许程序hibernate1秒; 结果不同:

 Thread.sleep(1000L); 

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

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

Memory_Shoots

Memory_Stabilizes

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

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

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

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

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

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

所以基本上我们发现使用的堆将缓慢上升,直到Eden空间被填满(导致分配失败),将伊甸园的旧成员(几乎全部,看着空间使用)移动到幸存者0/1,还有一些旧的。 睡眠和不睡眠之间的区别很可能是次要集合和主要集合的相对频率之间的差异。

有几件事值得考虑:

  • 这些PersonDetail对象有多大?
  • 创建PersonDetail对象需要多少内存,而不是对象的一部分?

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

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

现在想象一下你快速创建一堆对象(没有sleep语句)。 现在,使用的总内存将通过垃圾和您在列表中保存的对象快速上升。 当你到达最大内存时,垃圾收集必须更频繁地发生。 有点像顶部的图表。

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