如我们所见,垃圾收集器的性能不是由死对象的数量决定的,而是由活动对象的数量决定的。死亡的对象越多,垃圾收集速度就越快。如果要对堆中的每个对象进行垃圾回收,则GC周期几乎是瞬时的。此外,垃圾收集器必须暂停应用程序的执行,以确保对象树的完整性。发现的活动物体越多,悬架时间越长,这直接影响响应时间和吞吐量。
分析垃圾收集对应用程序性能的影响垃圾收集的基本原理及其对应用程序执行的影响称为垃圾收集暂停或GC暂停时间。在具有多个线程的应用程序中,这会迅速导致可伸缩性问题。
图2.4:来自Oracle GC Tuning文章
图2.4说明了GC挂起对多线程应用程序吞吐量的影响。将其执行时间的1%用于垃圾回收的应用程序在32处理器系统上将失去20%以上的吞吐量。如果我们将GC时间增加到2%,则总体吞吐量将再下降20%。这就是同时挂起32个正在执行的线程的影响!
有两种常用的减少GC暂停时间的方法:
通过调整标记扫频算法来减少暂停时间。
限制需要标记的对象数。
但是,在研究提高垃圾收集性能的方法之前,您应该了解内存碎片,这会影响暂停时间和应用程序性能。
关于零散的内存并找到足够大的洞
每当我们用Java创建新对象时,JVM都会自动分配足够大的内存块以适合堆上的新对象。重复分配和回收会导致内存碎片,这类似于磁盘碎片。内存碎片导致两个问题:
降低的分配速度:JVM跟踪按块大小组织的列表中的可用内存。为了创建一个新对象,Java搜索列表以选择并分配一个最佳大小的块。碎片会减慢分配过程,从而有效地减慢应用程序的执行速度。
分配错误:当碎片变得太大而导致JVM无法为新对象分配足够大的内存块时,就会发生分配错误。
Java不依靠操作系统来解决这些问题,必须自己处理这些问题。Java通过在成功的GC周期结束时执行压缩(图2.5)来避免内存碎片。该过程与硬盘碎片整理非常相似。
图2.5
当堆由于重复的分配和垃圾回收而变得碎片化时,JVM将执行压缩步骤,该步骤将所有对象整齐地对齐并关闭所有漏洞。
压缩只是将所有活动对象移动到堆的一端,从而有效地关闭了所有漏洞。可以全速分配对象(不再需要空闲列表),并且避免了创建大对象的问题。
不利的一面是更长的GC周期,并且由于大多数JVM在压缩期间中止了应用程序的执行,因此对性能的影响可能很大。
- 关注Java架构师Carl -
领取专属 10元无门槛券
私享最新 技术干货