为啥是锯齿形图形?
Posted
技术标签:
【中文标题】为啥是锯齿形图形?【英文标题】:Why a sawtooth shaped graph?为什么是锯齿形图形? 【发布时间】:2011-11-05 09:07:01 【问题描述】:当我使用 NetBeans 运行下面提到的代码时,分配的堆大小图类似于锯齿形状。我附上了来自 JVisualVM 的屏幕截图,它以锯齿形显示了堆分配图。该程序是一个打印“Hello, World!”的简单无限循环。进入控制台。
public class HelloWorld
public static void main(String a[])
while(true)
System.out.println("Hello, World!");
任何人都可以解释使用堆图形状背后的原因吗?
PS:即使我在不使用 NetBeans 的情况下运行它也会发生这种情况,因此它很可能与 NetBeans 无关...
【问题讨论】:
而且似乎相差很大……呃……3兆字节?这真的是真的吗? 您可以通过尝试其他垃圾收集器算法对此有所了解。请参阅此处了解信息:tikalk.com/java/… @buch11:永远不要低估大量垃圾,即使是最微不足道的 Java 方法/API 也能生成。我已经使用 Java 完成了多线程科学计算,我可以告诉你有很多陷阱,这甚至都不好笑;)你必须意识到大多数 Java 程序员会很乐意使用像 Map堆使用中的锯齿模式可以解释为在调用System.out.println
调用期间创建了几个局部变量。最值得注意的是,在 Oracle/Sun JRE 中,在年轻代中创建了几个 HeapCharBuffer
实例,如下面使用 VisualVM 的内存分析器获得的快照所示:
有趣的是堆上存在的活动对象的数量。锯齿模式是由伊甸园空间填满时发生的年轻代垃圾收集周期产生的;由于程序中没有执行繁重的计算活动,JVM 能够执行循环的多次迭代,从而导致伊甸园空间(4MB 大小)被填满。随后的年轻代收集周期会清除大部分垃圾;它几乎总是整个伊甸园空间,除非对象仍在使用中,如以下从 VisualVM 获得的 gc 跟踪所示:
因此,锯齿模式的行为可以通过一系列快速连续的对象分配来解释,这些分配填满了伊甸园空间,触发了新生代垃圾回收周期;这个过程循环重复,没有延迟,因为底层 JVM 进程没有被另一个进程抢占,并且 JVM 中负责对象分配的主线程也没有被另一个线程抢占。
【讨论】:
不客气。顺便说一句,我没有得到完美的锯齿波,因为我一直在 VisualVM 和 Eclipse 之间进行 Alt-tabbing。另一方面,您的屏幕截图表明您已经运行 VisualVM 有一段时间了。 实际上我正在编写一些使用线程的东西,突然代码进入无限循环,我去 JVisualVM 检查哪些线程正在运行/休眠/等待。锯齿进来了在我面前,有一次我以为它与线程有关,但后来尝试运行上面的代码,所有的谜题都从那里开始......所以是的,我的 VisualVM 一直在运行。 局部变量无关紧要。垃圾收集是关于对象,而不是变量。此外,值得注意的是,这种模式并非特定于年轻代,而是会在每次进行多次分配但只有一次释放操作时发生。【参考方案2】:任何以固定速率分配对象的进程都会导致堆内存消耗的稳定增加,然后当垃圾收集器收集不再使用的对象时会立即下降,从而形成锯齿形状。
如果您想知道为什么您的 java 进程在写入 System.out
时保持分配内存,请记住其他线程(例如将当前内存统计信息提供给 JVisualVM 的线程)可能是分配内存的线程。
【讨论】:
不必要的对象GC收集将导致instantaneous drops
。当要分配的对象非常大时,比如说一个对象~ RAM 大小的 10%,那么我们可能会得到reversed sawtooth
图形。 IE。对象分配器会立即获取新内存,但 GC 收集器会以稳定的下降速率释放内存。所以实际的图形取决于内存中的平均对象大小
你有任何证据证明这个相当令人惊讶的说法吗?
是的,我有 - check here。测试代码很简单 - 不间断循环中的 php 代码正在生成大的新对象 ~ 包含 9000 万个元素的封装数组的对象
PHP?这个问题是关于 Java ... AFAIK,所有 Java 垃圾收集器都在压缩,因此几乎可以立即释放内存。
Quote 如果垃圾创建率超过年轻/幼稚代清除在对象被永久化之前清除对象的速度,那么这些垃圾会在永久区域中堆积 在我的情况下,垃圾创建速度很慢,因为分配了巨大的对象,所以可能永久区域没有被很多对象填充,因此没有急剧下降。【参考方案3】:
它可能来自很多地方,并且可能取决于实现。至少以下是可能的(但都只是猜测)
在 System.out.println 下的流堆栈中的某处有一个字节数组分配(假设输出流的基本方法之一是 write(bytes []b, int off, int len) )
这是你正在使用的监控软件使用的开销(我没用过)
这是 netbeans VM 中的开销,它最终会显示输出
【讨论】:
【参考方案4】:实际上 jVisualVM 导致了额外的对象分配。 jVisualVM 和 jconsole 正在使用 Java 管理扩展。附加到正在运行的应用程序并请求 JVM 指标导致正在创建其他对象。 您可以通过添加到您的程序调用来验证这一点
Runtime.getRuntime().freeMemory()
报告 JVM 堆中的空闲内存。通过运行您的代码,它将显示 [几乎] 没有内存更改,但是一旦您将 jVisualVM 连接到您的程序,您就会看到内存使用量增加。
【讨论】:
以上是关于为啥是锯齿形图形?的主要内容,如果未能解决你的问题,请参考以下文章