Java中的垃圾回收是高级主题之一。Java GC知识有助于我们微调应用程序运行时性能。
Java中的垃圾收集
- 在Java中,程序员不需要负责销毁不再使用的对象。垃圾收集器负责处理它。
- 垃圾收集器是一个一直在后台运行的守护进程thread。基本上,它通过销毁无法到达的对象来释放堆memory](/community/tutorials/java-heap-space-vs-stack-memory)。
- 无法到达的对象是那些不再被程序的任何部分引用的对象。
- 我们可以通过JVM选项为我们的Java程序选择垃圾收集器,我们将在本教程的后面部分讨论这些问题。
垃圾自动收集的原理是什么?
自动垃圾回收是一个查看堆内存、识别(也称为)无法访问的对象并通过压缩销毁它们的过程。这种方法的一个问题是,随着对象数量的增加,垃圾收集时间不断增加,因为它需要遍历整个对象列表,寻找无法到达的对象。然而,对应用的实证分析表明,大多数对象都是短暂的。这种行为被用来提高JVM的性能,所采用的方法通常称为世代垃圾收集。在这种方法中,堆空间被划分为几代,如年轻一代、老年或终身一代和永久一代。青年一代堆空间是创建所有新对象的新空间。一旦它被填满,就会发生次要垃圾收集(也称为次要GC)。这意味着,这一代的所有死亡物体都被销毁了,这个过程很快,因为正如我们从图表中看到的那样,他们中的大多数都是死亡的。年轻一代幸存的物体会老化,最终会转移到老一辈人手中。老一代是用来存储长期存活的对象的。通常,为年轻一代对象设置阈值,当达到该年龄时,该对象被转移到老一代。最终,老一代需要被收集起来。此事件称为重大GC(重大垃圾收集)。通常,它的速度要慢得多,因为它涉及所有活的对象。此外,还有完整的GC,这意味着清理整个Heap-包括年轻一代和老一辈人。最后,在Java 7之前,有一个永久生成(或Perm Gen),它包含JVM描述应用程序中使用的类和方法所需的元数据。它在Java 8中被删除了。
Java垃圾收集器
JVM实际上提供了四种不同的垃圾收集器,它们都是分代的。每一个都有自己的优势和劣势。选择使用哪种垃圾收集器取决于我们,在吞吐量和应用程序暂停方面可能会有很大差异。所有这些都使用一个古老的假设,即堆中的大多数对象都是短暂的,应该快速回收,从而将托管堆拆分成不同的段。因此,四种类型的垃圾收集器是:
串口GC
这是最简单的垃圾收集器,专为单线程系统和小堆大小而设计。它会在工作时冻结所有应用程序。可以使用-XX:+UseSerialGC
JVM选项打开。
并行/吞吐量GC
这是JVM在JDK 8中的默认收集器。顾名思义,它使用多个线程扫描堆空间并执行压缩。该收集器的一个缺点是,它在执行次要或完整GC时暂停应用程序线程。它最适合能够处理此类暂停的应用程序,并尝试优化由收集器造成的CPU开销。
CMS收集器
CMS收集器()算法使用多个线程()在堆()中扫描可回收()的未使用对象。此收集器在两种情况下进入停止世界(STW)模式:-同时初始化根的初始标记,即。可从线程入口点或静态变量访问的旧一代对象-当应用程序在算法并发运行时更改了堆的状态,并强制它返回并进行一些最后的修改,以确保它标记了正确的对象。此收集器可能面临升级失败。如果将年轻一代的一些对象移到老一代,而收集器没有足够的时间在老一代空间中腾出空间,则会发生升级失败。为了防止这种情况,我们可能会将更多的堆大小提供给老一代,或者向收集器提供更多的后台线程。
G1采集器
最后但并非最不重要的是垃圾优先收集器,它专为大于4 GB的堆大小而设计。它根据堆大小将堆大小划分为跨度从1MB到32MB的区域。有一个并发的全局标记阶段来确定整个堆中对象的活跃性。在标记阶段完成后,G1知道哪些区域大部分是空的。它首先从这些区域收集不可访问的对象,这通常会产生大量的空闲空间。因此,G1首先收集这些区域(包含垃圾),因此得名为垃圾优先。G1还使用暂停预测模型以满足用户定义的暂停时间目标。它根据指定的暂停时间目标选择要收集的区域数量。G1垃圾收集周期包括如图所示的阶段:
1.纯年轻阶段:该阶段只包括年轻一代的对象,并将其提升到老一代。当老一辈人被占用到一定的门槛时,即年轻一代阶段和空间开垦阶段之间的过渡开始。正在启动的堆占用率阈值。此时,G1计划最初仅限Mark Young收藏,而不是常规的仅限Young收藏。 2.初始评分:这种类型的收藏除了开始常规的仅限年轻人的收藏外,还会开始评分过程。并发标记确定要为下一个空间回收阶段保留的老一代区域中的所有当前活动对象。虽然标记还没有完全完成,但可能会发生定期的仅限年轻人的收集。标记结束时有两个特殊的停止世界的停顿:备注和清理。 3.备注:此暂停完成标记本身,并执行全局引用处理和类卸载。在备注和清理之间,G1同时计算活跃度信息的摘要,该摘要将被最终确定并用于清理暂停以更新内部数据结构。 4.清理:这一暂停也会处理完全空白的区域,并确定是否会真正进入空间回收阶段。如果空间回收阶段紧随其后,则仅限年轻人的阶段以单一的仅限年轻人的集合结束。 5.空间复垦阶段:这一阶段包括多个混合收藏 --除了年轻一代区域外,还疏散了老一代区域的活物体。当G1确定疏散更多的老一代地区将不会产生足够的空闲空间时,空间回收阶段就结束了。
G1可以使用-XX:+G1 GC
标志启用。此策略减少了在后台线程完成扫描无法访问的对象之前耗尽堆的可能性。此外,它还在移动中压缩堆,CMS收集器只能在STW模式下执行此操作。在Java 8中,G1收集器提供了一个漂亮的优化,称为 string deduplication 。正如我们所知,表示字符串的字符数组占据了堆的大部分空间。我们做了一个新的优化,使G1收集器能够识别在堆中重复多次的字符串,并将它们修改为指向同一个内部char[]数组,以避免同一个字符串的多个副本不必要地驻留在堆中。我们可以使用JVM参数-XX:+String String
来启用这种优化。G1是JDK 9中的默认垃圾收集器。
Java 8 PermGen和Metspace
如前所述,永久生成空间从Java 8开始就被移除了。所以现在,JDK 8热点JVM使用本机内存来表示类元数据,这就是所谓的Metspace。类元数据的大部分分配都是在本机内存之外进行的。此外,还有一个新的标志MaxMetaspaceSize,用于限制用于类元数据的内存量。如果我们不为其指定值,Metspace将根据运行应用程序的需求在运行时调整大小。当类元数据使用量达到MaxMetaspaceSize限制时,将触发Metspace垃圾回收。过度的Metspace垃圾收集可能是类、类加载器内存泄漏或应用程序大小不足的症状。这就是Java中的垃圾收集。我希望您了解我们在Java中使用的不同垃圾收集器。参考资料:ORACLE Documentation,G1 GC.