当我使用NetBeans运行下面提到的代码时,分配的堆大小图类似于锯齿形状。我附加了JVisualVM的屏幕截图,它显示了锯齿形状的堆分配图。该程序是一个简单的无限循环打印“Hello,World!”进入控制台。
public class HelloWorld {
public static void main(String a[]){
while(true) {
System.out.println("Hello, World!");
}
}
}
任何人都可以解释使用堆图形状背后的原因吗?
PS :即使我在不使用NetBeans的情况下运行它,也会发生这种情况,因此很可能与NetBeans无关......
答案 0 :(得分:23)
堆使用中的锯齿模式可以通过在调用System.out.println
调用期间创建几个局部变量的事实来解释。最值得注意的是在Oracle / Sun JRE中,在年轻代中创建了几个HeapCharBuffer
实例,如使用VisualVM的内存分析器获得的以下快照中所述:
有趣的一点是堆上存在的活动对象的数量。锯齿形图案是由伊甸园空间填满时发生的年轻垃圾收集周期产生的;由于在程序中没有执行繁重的计算活动,JVM能够执行循环的多次迭代,从而导致伊甸园空间(大小为4MB)填满。随后的年轻人收集周期然后清除大部分垃圾;它几乎总是整个伊甸园空间,除非对象仍在使用中,如从VisualVM获得的以下gc轨迹所示:
锯齿模式的行为因此可以通过快速连续的一系列对象分配来解释,这些对象分配填满了伊甸园空间,触发了年轻的垃圾收集循环;由于底层JVM进程没有被另一个进程抢占,因此这个进程不间断地重复循环,并且JVM中负责对象分配的主线程也不会被另一个线程抢占。
答案 1 :(得分:6)
任何以常规速率分配对象的进程都会导致堆内存消耗量稳步增加,然后当垃圾收集器收集不再使用的对象时会立即下降,从而产生锯齿形状。
如果您想知道为什么您的java进程在写入System.out
时保留分配内存,请记住其他线程(例如将当前内存统计信息提供给JVisualVM的线程)可能是分配内存的线程。
答案 2 :(得分:1)
它可能来自很多地方,而且可能依赖于实施。至少以下是可能的(但都只是推测)
在System.out.println下面的流堆栈中的某处有一个字节数组分配(假设输出流的一个基本方法是write(bytes [] b,int off,int len) )
您正在使用的监控软件使用的开销(我没有使用过它)
它在netbeans VM中的开销最终会显示输出
答案 3 :(得分:1)
实际上jVisualVM会导致额外的对象分配。 jVisualVM和jconsole正在使用Java Management Extensions。附加到正在运行的应用程序并请求JVM指标,从而导致正在创建其他对象。 您可以通过添加到程序调用
来验证这一点Runtime.getRuntime().freeMemory()
报告JVM堆中的可用内存。它将通过运行代码显示[几乎]没有更改内存,但只要将jVisualVM连接到程序,您就会看到内存使用量增加。