Java垃圾收集快速入门:它是什么以及如何工作

在本教程中,我们将讨论不同的Java垃圾收集器如何工作以及可以从中获得什么。 这将为我们提供必要的背景,以开始调整您选择的垃圾收集算法。

Before going into Java Garbage Collection tuning we need to understand two things. First of all, how garbage collection works in theory and how it works in the system we are going to tune. Our system’s garbage collector work is described by garbage collector logs and metrics from observability tools like Sematext Cloud for JVM. We talked about how to read and understand Java Garbage Collection logs in a previous blog post.

What is Garbage Collection in Java: A Definition

Java垃圾回收是一个自动过程,在此过程中,Java虚拟机将检查堆上的对象,检查是否仍在引用它们,并释放那些不再需要的对象使用的内存。

Object Eligibility: When Does Java Perform Garbage Collection

让我们快速查看何时可以通过垃圾回收收集对象,以及如何实际请求Java虚拟机开始垃圾回收。

How to Make an Object Eligible for GC?

简而言之,您无需明确地进行任何操作即可使对象有资格进行垃圾收集。 当在应用程序代码中不再使用对象时,可以回收该对象使用的堆空间。 查看以下Java代码:

public Integer run() {
  Integer variableOne = 10;
  Integer variableTwo = 20;
  return variableOne + variableTwo;
}

在里面跑()方法我们显式创建两个变量。 首先将它们放在年轻一代的堆中。 一旦方法执行完毕,就不再需要它们,并且开始有资格进行垃圾回收。 当发生新一代垃圾回收时,这些变量使用的内存可能会被回收。 如果发生这种情况,先前占用的内存将显示为空闲。

How to Request the JVM to Run GC?

关于Java垃圾收集的最好的事情是它是自动的。 在时间到来之前,并且您想要并且需要控制和调整它,您无需执行任何操作。 当Java虚拟机决定开始回收堆上的空间并丢弃未使用的对象时,它将开始垃圾回收过程。

If you want to force garbage collection you can use the System object from the java.lang package and its gc() method or the Runtime.getRuntime().gc() call. As the documentation states – Java Virtual Machine will do its best efforts to reclaim the space. This means that the garbage collection may actually not happen, this depends on the JVM. If the garbage collection happens it will be a Major collection which means that we can expect a stop-the-world event to happen. In general, using the System.gc() is considered a bad practice and we should tune the work of the garbage collector instead of calling it explicitly.

How Does Java Garbage Collection Work?

无论我们使用哪种垃圾回收器实现来清理内存,都需要短暂的停顿。 这些暂停也称为世界停止事件或简称STW。 您可以通过以下方式设想基于JVM的应用程序的工作周期:

Alt Text

当您的应用程序线程启动并且您的业务代码正在运行时,周期的第一步开始。 这是您的应用程序代码运行的地方。 在某个时间点,会发生一个触发垃圾回收的事件。 要清除内存,必须停止应用程序线程。 这是您的应用程序停止工作并开始下一步的地方。 垃圾收集器标记不再使用的对象并回收内存。 最后,可能的话,可能会发生堆大小调整的可选步骤。 然后圈再次开始,应用程序线程被启动。 垃圾收集的整个周期称为时代。

运行JVM应用程序并调整垃圾回收器的关键是使应用程序线程保持尽可能长的运行时间。 这意味着由垃圾收集器引起的停顿应该最小化。

我们需要谈论的第二件事是几代人。 Java垃圾收集器是代代相传的,这意味着它们按照某些原则工作:

  • 年轻数据将无法长期生存旧数据将继续保留在内存中

因此,JVM堆内存分为几代:

  • 年轻一代分为两个部分伊甸园空间和幸存空间老一辈,或终身空间。

Alt Text

下面的示例可以说明空间和代之间对象的简化提升。 创建对象后,首先将其放入年轻一代空间进入伊甸园空间。 一旦发生了年轻的垃圾收集,对象就会被提升到幸存者空间0然后进入幸存者空间1。 如果此时仍使用该对象,则下一个垃圾回收周期会将其移至终身空间这意味着它已移至老一辈。 您可以想象如下:

Alt Text

所以伊甸园该空间包含新创建的对象,并且在开始时为空时代. During the 时代, the 伊甸园空间将填满,最终触发次要GC填满时发生的事件。 的幸存者空格包含至少一次使用的对象时代。 通过许多幸存的物体时代s最终将晋升为终身制代。

在Java 8之前,有一个额外的内存空间称为彼尔姆。彼尔姆否则永久世代 was a special space on the heap separated from its other parts – the young and the tenured generation。It was used to store metadata such as classes and methods.

从Java 8开始,元空间 is the memory space that replaces the removed PermGen space. The implementation differs from the PermGen and this space of the heap is now automatically resized limiting the problems of going into out of memory in this region of the heap. The 元空间 memory can be garbage collected and the classes that are no longer used can be cleaned when the 元空间 reaches its maximum size.

有一些标志可用于控制元空间存储空间的大小和行为:

  • -XX:MetaspaceSize–元空间存储区的初始大小,-XX:MaxMetaspaceSize – maximum size of the Metaspace memory & region,-XX:MinMetaspaceFreeRatio–垃圾收集后应释放的最小类元数据容量百分比,-XX:MaxMetaspaceFreeRatio–垃圾回收后应该释放的类元数据容量的最大百分比。

您现在可以想象为什么某些垃圾收集器可能需要大量时间来清理旧的空间。 只需一步即可完成。 终身代是堆的一大空间,要清除它,必须停止应用程序线程。

The Heap Structure of G1 Garbage Collector

我们上面写的内容适用于所有垃圾收集器,包括串行,并行和并发标记扫描。 我们稍后再讨论。 但是,G1垃圾收集器又走了一步,将堆划分为地区。 一种区域是一个小的独立堆,可以动态设置为Eden,Survivor或Tenured类型:

Alt Text

除了上面提到的三种类型,我们还有空闲内存,即图像上的白色单元格。

这种架构允许不同的操作。 首先,由于使用期限生成是划分的,因此可以按影响延迟的部分进行收集,从而使垃圾收集器更快地用于旧的生成空间。 此类堆可以轻松进行碎片整理并动态调整大小。 没有缺点,对不对? 嗯,那实际上不是真的。 与传统的堆体系结构相比,维护此类堆体系结构的成本更高。 它需要更多的CPU和内存。

使用G1GC时可以控制区域大小。 当堆大小设置为小于4GB时,区域大小将自动设置为1MB。 对于4到8GB之间的堆,区域大小将设置为2MB,依此类推,对于64GB或更大的堆,区域大小最大为32MB。 通常,区域大小必须为2的幂并在1到32MB之间。 默认情况下,JVM将尝试在应用程序启动期间设置最佳数目的两千个或更多区域。 我们可以使用-XX:G1HeapRegionSize = N JVM参数来控制它。

对于G1GC,清除堆是通过将活动数据从现有区域复制到空区域并完全丢弃旧区域来完成的。 之后,旧区域被认为是空闲的,可以为其分配对象。 同时释放多个区域可进行碎片整理和分配巨大的对象–大于堆区域50%的对象。

您现在可能想知道是什么触发了垃圾回收,这将是一个很大的问题。 常见的垃圾收集触发条件是:Eden空间已满,没有足够的可用空间来分配对象,外部资源如System.gc(),jmap之类的工具,或者没有足够的可用空间来创建对象。

What Triggers Java Garbage Collection

为了使事情变得更加复杂,有几种类型的垃圾回收事件。 您可以通过非常简化的方式对它们进行划分,如下所示:

  • 次要 event – happen when the Eden space is full and moved data to Survivor space. So a 次要 event happens within the young generation混合的 event – a 次要 event plus reclaim of the Tenured generation全GC活动–年轻人和老年人空间一起清理

即使查看事件的名称,您也可以看到,在大多数情况下,关键在于降低混合和完全GC事件的暂停时间。 现在让我们停止讨论垃圾收集事件。 还有更多,我们可以越来越深入。 但是现在,我们应该很好。

我想提及的下一件事情是巨大的 object. Remember? Those are the ones that are larger than a single region in our heap when dealing with the G1 garbage collector (G1GC). Actually, any object larger than 50% of the region size is considered 巨大的. Those objects are not allocated in the young generation space, but instead, they are put directly in the Tenured generation. Such objects can increase the pause time of the garbage collector and can increase the risk of triggering the Full GC because of running out of continued free space.

Java Garbage Collectors Types

现在,我们了解了基础知识,是时候了解我们可以使用哪种垃圾收集器,以及每种垃圾收集器在我们的应用程序中如何工作。 请记住,不同的Java版本将具有不同的可用垃圾收集器。 例如,Java 9将同时具有并发标记扫描和G1垃圾收集器,而Java 7的较早更新将没有可用的G1垃圾收集器。

也就是说,Java中有五种类型的垃圾收集器:

Serial Garbage Collector

的串行垃圾收集器是设计的用于单线程环境。 在进行垃圾收集之前,此垃圾收集器冻结一切应用程序线程. Because of that, it是not suited for multi-threaded environments, like server-side applications. However, it是perfectly suited for single-threaded applications that don’t require low pause time. For example batch jobs.

The documentation on Java garbage collectors also mentions that this garbage collector may be useful on multiprocessor machines for applications with a data set up to approximately 100MB.

Parallel Garbage Collector

的平行垃圾收集器也称为吞吐量收集器与串行垃圾收集器非常相似。 在进行垃圾回收时,还需要冻结应用程序线程。 但是,它旨在多处理器环境 and in multi-threaded applications with medium and large-sized data. 的idea is that using 多线程将加快垃圾收集使此类用例的速度更快。

如果您的应用程序的优先级是最高性能,并且线程暂停时间不超过一秒甚至更长,那么并行垃圾收集器可能是个好主意。 与串行垃圾收集器相比,它将不时地冻结应用程序线程并使用多个线程执行GC,从而加快了运行速度。

Concurrent Mark Sweep Garbage Collector

的并发标记扫描(CMS)垃圾收集器是被称为的实现之一大部分并发。 他们表演昂贵的运营使用多线程。 他们也分享的线程数用于垃圾收集 with的应用. 的overhead for this type of 垃圾收集 comes not only from的fact that they do的collection concurrently, but also that的concurrent collection must be enabled.

CMS GC专为需要短暂停顿的应用程序而设计。 基本上,与并行或串行垃圾收集器相比,它的执行速度较慢,但​​不必停止应用程序线程来执行垃圾收集。

如果您的应用程序希望暂停时间较短,并且有能力与垃圾收集器共享应用程序线程,则应选择此垃圾收集器。 请记住,尽管Concurrent Mark Sweep垃圾收集器已在Java 14中删除,但是如果您尚未使用G1垃圾收集器,则应查看它。

G1 Garbage Collector

的G1垃圾收集器是Java 7的第4版的第4次更新中引入的垃圾收集算法,此后进行了改进。 G1GC被设计为具有低延迟,但这要付出一定的代价-更加频繁的工作,这意味着更多的CPU周期花费在垃圾收集上。 它将堆划分为较小的区域,以便更轻松地进行垃圾收集和疏散方式记忆清除。 这意味着将对象移出清除区域并复制到另一个区域。 大部分垃圾收集工作都是在最年轻的一代中完成的。

As the documentation states, the G1GC was designed for server-style applications running in a multiprocessor environment with a large amount of memory available. It tries to meet garbage collector pause goals with high probability. While doing that it also tries to achieve high throughput. All of that without the needs of complicated configuration, at least in theory.

这样考虑一下–如果您提供的服务延迟敏感的G1垃圾收集器可能是很好的选择。 低延迟意味着这些服务将不会遭受长时间的世界停止事件的困扰。 当然在CPU使用率较高的成本。 此外,G1垃圾收集器设计为可处理更大的堆大小-如果堆大小大于32GB,G1通常是一个不错的选择。 G1垃圾收集器是CMS垃圾收集器的替代品,也是最新Java版本中的默认垃圾收集器。

Z Garbage Collector

的Z垃圾收集器是一个实验性垃圾回收实施方案,目前仍不适用于所有平台,例如Windows和macOS。 它被设计为非常可扩展的低延迟实现。 它可以并发执行昂贵的垃圾回收工作,而无需停止应用程序线程。

预计ZGC可以与需要10ms或更短暂停时间的应用程序以及使用非常大堆的应用程序一起很好地工作。

Java Garbage Collection Benefits

有多重利益Java中的垃圾回收。 但是,您可能首先没有想到的主要问题是简化代码。 我们不必担心适当的内存分配和释放周期。 在我们的代码中,我们只是停止使用一个对象,而它正在使用的内存将是自动回收在某一点。 从简单性的角度来看,这是另一个附加价值。 内存回收过程是自动的,并且是JVM内部内部算法的工作。 我们只要控制我们要使用的算法就可以了。 当然,如果永远保留对对象的引用,我们仍然会遇到内存泄漏的情况,但这是一双不同的鞋子。

我们必须记住,尽管这些好处是有代价的–性能。 根据情况和垃圾收集算法,我们可以为在垃圾收集上花费的CPU周期中的内存管理的简易性和自动化性付出代价。 在极端情况下,当我们遇到内存或垃圾回收问题时,我们甚至可能会遇到整个应用程序停止运行的情况,直到空间回收过程结束为止。

Java Garbage Collection Best Practices

在本系列的下一篇文章中,我们将介绍优化垃圾收集的过程,但是在此之前,我们想分享一些有关垃圾收集的好与坏做法。 首先–您应该避免调用System.gc()方法来请求显式垃圾回收。 正如我们所提到的,这是一种不良做法,应避免使用。

The second thing I wanted to mention is the right amount of heap memory. If you don’t have enough memory for your application to work you will experience slowdowns, long garbage collection, stop the world events and eventually out of memory errors. All of that can indicate that your heap is too small, but can also mean that you have a memory leak in your application. Look at the JVM monitoring of your choice to see if the heap usage grows indefinitely – if it is, it may mean you have a bug in your application code. We will talk more about the heap size in the next post in the series.

最后,如果您运行的是小型独立应用程序,则可能不需要任何类型的垃圾回收优化。 只需使用默认值,您应该会更好。

之后的下一步是选择正确的垃圾收集器实现。 符合我们业务需求的一种。 如何做到这一点,以及有什么方法可以调整不同的垃圾回收算法-这将在下一篇博客文章中介绍-称为Java垃圾回收调优分步指南。

Conclusion

At this point, we know how the Java garbage collection process looks like, how each garbage collector works and what behavior we can expect from each of them. In addition to that, in the previous blog post, we also discussed how to turn on and understand the logs produced by each garbage collector. This means that we are ready for the final part of the series – tuning our garbage collector.

from: https://dev.to//sematext/a-quick-start-on-java-garbage-collection-what-it-is-and-how-it-works-14d9

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值