我听到一位同事说:
JVM垃圾收集时间随着JVM大小呈指数级增长。这是因为引用树是要分配的对象数量的函数 - 随着对象数量的增加,遍历树的指数级变得越来越难。
听起来不错。
我听到另一位同事说:
同一台机器上的JVM垃圾收集是线性的。鉴于同一台计算机上的两个4G JVM中的8GB JVM拆分(通过微服务)将具有相同的垃圾收集持续时间,因为相同的操作系统会降低相同数量的对象的速度。
这看起来并不正确 - 因为两个较小的JVM上的对象树应该更浅,更容易遍历。
我的问题是: JVM收集时间是否随JVM RAM大小呈指数级增长?
假设:使用Oracle JVM。
答案 0 :(得分:2)
虽然霍尔格斯的解释是正确的,但我想对它略微不同。
GC采用的时间与实时集中的活动对象数成正比。这很容易证明。假设我们有两个具有相同大小的堆的应用程序。在第一个堆中,我们分配10个对象,每个对象100 MB,每个对应1000个100字节。在下一个gc中,每个应用程序中的一半对象都无法访问(死机)并且可以收集。
不言而喻,用最多的物体追踪图表需要更长的时间。
(顺便说一句,我记得读过“浅而宽”和“深而窄”的测量结果,并且没有明显的区别,但我无法记住在哪里。 @Holger:如果你有一个来源,我很乐意阅读它。)
请注意,遵循已建立的Java编码实践实际上将确保实时集很小。 JVM希望你能够以这种方式进行编码,并且非常努力地帮助保持实时设置很小,escape analysis只是Hot Spots套管中的一个技巧。
简而言之:否
答案 1 :(得分:1)
没有这种简单的依赖。
首先,将“垃圾收集”视为对象引用上的函数显然只是指标记阶段,忽略了分配,解除分配或复制/移动对象的成本。标记成本取决于必须遍历的 live 引用的数量,死对象或未使用的内存都不会对其产生任何影响。因此,仅向同一应用程序提供更多RAM并不一定会改变垃圾收集性能。
有一种趋势是使用你为JVM提供的任何数量的RAM,因此提供更多RAM可能会使垃圾收集周期减少,但可能需要更多时间来标记所有活动对象。但由于垃圾收集之间有更多的时间会增加对象被闲置的可能性,因此标记成本通常不会与收集之间的时间相同。
很容易证明它实际上是另一种方式。只需使用一个任意Java应用程序,将可用内存减少到几乎没有遇到OutOfMemoryError
的情况。您将看到提供更少RAM的速度如何使速度变得更慢,越接近这一点就越慢。另一方面,实际上没有必要证明给应用程序提供如此多的RAM以至于它在其生命周期中从不需要垃圾收集具有最小的成本。
当我们仅考虑标记阶段时,不考虑它发生的频率,只考虑它如何与实时参考的数量一起扩展,仍然没有理由说它应该是指数的。对象引用可以形成很少是树的任意图形。此外,垃圾收集器不需要遍历每个对象引用。它只需要遍历对之前没有遇到的对象的引用(猜测为什么它被称为“标记”),这意味着需要遍历的引用数量与活动对象的数量相同。可能需要花费一些成本才能找到不需要遍历的引用,但这仍然是线性开销。
像HotSpot这样的JVM(它不再是Sun的属性)使用分代垃圾收集和卡片标记,只遍历新对象和内存部分(卡片)已更改的旧对象的引用,而不是所有活动对象。由于更改旧对象和创建新对象都需要CPU时间,因此不会直接使用可用RAM进行扩展。