1. Introduction
1.绪论
The Garbage Collector (GC) handles the memory management in Java. As a result, programmers don’t need to explicitly take care of memory allocation and deallocation.
垃圾收集器(GC)处理Java的内存管理。因此,程序员不需要明确地处理内存分配和去分配的问题。
In Java, JVM reserves a certain amount of memory at the beginning. Sometimes, the actual memory used is significantly less than the reserved amount. In such scenarios, we prefer to return the excess memory to OS.
在Java中,JVM在开始时保留了一定量的内存。有时,实际使用的内存远远小于保留的数量。在这种情况下,我们倾向于将多余的内存返回给操作系统。
This entire process is dependent on the algorithms used for garbage collection. Consequently, we can choose the type of GC and JVM as per the required behavior.
这整个过程取决于用于垃圾收集的算法。因此,我们可以根据需要的行为选择GC和JVM的类型。
In this tutorial, we’ll explore memory management by GC and its interaction with OS.
在本教程中,我们将探讨GC的内存管理以及它与操作系统的交互。
2. JVM Memory Organization
2.JVM的内存组织
When JVM is initialized, different types of memory areas are created inside it, such as Heap area, Stack area, Method Area, PC Registers, and Native Method Stack.
当JVM被初始化时,其内部会创建不同类型的内存区域,如堆区、栈区、方法区、PC寄存器和本地方法栈。
GC deals with heap storage. Hence, we’ll focus on memory interaction related to the heap in this article.
GC处理的是堆存储。因此,我们将在本文中重点讨论与堆有关的内存交互。
We can specify the initial and maximum heap sizes using the flags -Xms and -Xmx, respectively. If -Xms is lower than -Xmx, it implies that JVM hasn’t committed all the reserved memory to the heap in the beginning. In short, heap size starts from -Xms and can expand up to -Xmx. This allows a developer to configure the size of the required heap memory.
我们可以使用标志-Xms和-Xmx分别指定初始和最大的堆大小。如果-Xms低于-Xmx,这意味着JVM在一开始就没有将所有保留的内存投入到堆中。简而言之,堆大小从-Xms开始,可以扩展到-Xmx。这允许开发者配置所需的堆内存的大小。
Now, when the application runs, different objects are allocated memory inside the heap. At the time of garbage collection, GC deallocates the unreferenced objects and frees the memory. This deallocated memory is currently a part of the heap itself, as it is a CPU intensive procedure to interact with OS after each deallocation.
现在,当应用程序运行时,不同的对象在堆内被分配内存。在垃圾收集的时候,GC会将未引用的对象去掉并释放内存。这个去分配的内存目前是堆本身的一部分,因为每次去分配后与操作系统交互是一个CPU密集型的程序。
Objects reside in a scattered manner inside the heap. GC needs to compact the memory and create a free block to return to OS. It involves an additional process execution while returning the memory. Also, Java applications might need additional memory at a later stage. For this, we need to communicate with the OS again to request more memory. Moreover, we can’t ensure the availability of memory in the OS at the requested time. Hence, it is a safer approach to use the internal heap instead of frequently calling the OS to fetch memory.
对象以分散的方式存在于堆中。GC需要压缩内存并创建一个空闲块来返回给操作系统。这涉及到在返回内存时的额外的进程执行。此外,Java应用程序可能在后期需要额外的内存。为此,我们需要再次与操作系统沟通以请求更多的内存。此外,我们无法确保在请求的时间内操作系统中的内存是否可用。因此,使用内部堆而不是频繁地调用操作系统来获取内存是一个更安全的方法。
However, if our applications don’t require entire heap memory, we’re just blocking the available resources, which could have been used by the OS for other applications. Considering this, the JVM has introduced efficient and automated techniques for memory release.
然而,如果我们的应用程序不需要整个堆内存,我们只是阻塞了可用的资源,而这些资源本来可以被操作系统用于其他应用程序。考虑到这一点,JVM引入了高效和自动化的内存释放技术。
3. Garbage Collectors
3.垃圾收集器
Progressing over different release versions, Java has introduced different types of GCs. Memory interaction between heap and OS is dependent on the JVM and GC implementations. Some GC implementations actively support heap shrinking. Heap shrinking is the process of releasing back the excess memory from heap to OS for optimal resource usage.
随着不同版本的进展,Java已经引入了不同类型的GC。堆和操作系统之间的内存交互取决于JVM和GC的实现。一些GC实现积极支持堆缩减。堆缩减是将多余的内存从堆中释放回操作系统以实现最佳资源使用的过程。
For example, Parallel GC doesn’t release unused memory back to the OS readily. On the other hand, some GCs analyze the memory consumption and determine accordingly to release some free memory from the heap. G1, Serial, Shenandoah, and Z GCs support heap shrinking.
例如,Parallel GC不会轻易地将未使用的内存释放回给操作系统。另一方面,一些GC会分析内存消耗,并相应地决定从堆中释放一些空闲内存。G1、Serial、Shenandoah和Z GCs支持堆收缩。
Let’s explore these processes now.
现在让我们来探讨这些过程。
3.1. Garbage First (G1) GC
3.1.垃圾优先(G1)GC
G1 has been the default GC since Java 9. It supports compaction processes without lengthy pauses. Using internal adaptive optimization algorithms, it analyzes the RAM required as per application usage and uncommits the memory if required.
自Java 9以来,G1一直是默认的GC。 它支持压实过程,没有冗长的停顿。它使用内部自适应优化算法,根据应用程序的使用情况分析所需的RAM,并在需要时解除对内存的承诺。
Initial implementations support heap shrinking either after full GC or during concurrent cycle events. However, for an ideal situation, we want to promptly return the unused memory to the OS, especially for the periods when our application is idle. We want the GC to dynamically adapt to the memory usage by our applications at runtime.
最初的实现支持在完整的GC之后或者在并发的周期事件中进行堆缩减。然而,在理想的情况下,我们希望及时将未使用的内存返回给操作系统,尤其是在应用程序闲置的时候。我们希望GC能够动态地适应我们的应用程序在运行时的内存使用情况。
Java has included such capabilities in different GCs. For G1, JEP 346 introduces these changes. From Java 12 and higher, heap shrinking is also possible in the concurrent remark phase. G1 tries to analyze the heap usage when the application is idle and triggers the periodic garbage collection as needed. G1 can either start a concurrent cycle or a full GC based on the G1PeriodicGCInvokesConcurrent option. After the cycle executes, G1 needs to resize the heap and return freed memory back to OS.
Java已经在不同的GC中包含了这样的功能。对于G1,JEP 346介绍了这些变化。从Java 12及以上版本开始,在并发的remark阶段中也可以进行堆缩减。当应用程序处于空闲状态时,G1会尝试分析堆的使用情况,并根据需要触发定期的垃圾收集。G1可以根据G1PeriodicGCInvokesConcurrent选项启动一个并发的循环或一个完整的GC。在周期执行后,G1需要调整堆的大小,并将释放的内存返回给操作系统。
3.2. Serial GC
3.2.串行GC
Serial GC also supports heap shrinking behavior. In comparison to G1, it requires additional four full GC cycles to uncommit freed memory.
串行GC也支持堆缩减行为。与G1相比,它需要额外的4个完整的GC周期来解除对已释放内存的承诺。
3.3. ZGC
3.3 ZGC
ZGC was introduced with Java 11. It was also enhanced with the functionality to return unused memory to OS in JEP 351.
ZGC是在Java 11中引入的。在JEP 351中,它还增强了将未使用的内存返回给操作系统的功能。
3.4. Shenandoah GC
3.4.谢南多亚俱乐部(Shenandoah GC)
Shenandoah is a concurrent GC. It performs the garbage collection asynchronously. Eliminating the need for full GC greatly helps in the performance optimization of the application.
Shenandoah是一个并发的GC。它异步地执行垃圾收集。消除对完全GC的需求,大大有助于应用程序的性能优化。
4. Using JVM Flags
4.使用JVM标志
We’ve previously seen that we can specify heap sizes using JVM command-line options. Similarly, we can use different flags to configure the default heap shrinking behavior of a GC :
我们之前已经看到,我们可以使用JVM命令行选项来指定堆的大小。同样地,我们可以使用不同的标志来配置GC的默认堆缩减行为。
- -XX:GCTimeRatio: To specify the desired time split between application execution and GC execution. We can use it to make the GC run longer
- -XX:MinHeapFreeRatio: To specify the minimum expected proportion of free space in the heap after garbage collection
- -XX:MaxHeapFreeRatio: To specify the maximum expected proportion of free space in the heap after garbage collection
If the available free space in the heap is higher than the ratio specified using -XX:MaxHeapFreeRatio option, then GC can return the unused memory to the OS. We can configure the value of the above flags to constrain the amount of unused memory in the heap. We’ve similar options available for concurrent garbage collection processes:
如果堆中可用的自由空间高于使用-XX:MaxHeapFreeRatio选项指定的比例,那么GC可以将未使用的内存返回给操作系统。我们可以配置上述标志的值来约束堆中未使用的内存量。我们对并发的垃圾收集过程也有类似的选项。
- -XX:InitiatingHeapOccupancyPercent: To specify the heap occupancy percentage required to start a concurrent garbage collection.
- -XX:-ShrinkHeapInSteps: To reduce the heap size to the -XX:MaxHeapFreeRatio value immediately. The default implementation requires multiple garbage collection cycles for this process.
5. Conclusion
5.总结
In this article, we’ve seen that Java provides different types of GCs catering to different requirements. GC can reclaim and return the free memory to the host OS. We can choose the type of GC as per our requirements.
在这篇文章中,我们已经看到Java提供了不同类型的GC,以满足不同的要求。GC可以回收并将空闲内存返回给主机操作系统。我们可以根据自己的要求来选择GC的类型。
We’ve also explored the use of JVM parameters to tweak the GC behavior to reach desired performance levels. Additionally, we can opt for dynamic scaling of memory utilization by JVM. We should consider the trade-offs related to each chosen option for our application and the resources involved.
我们还探索了使用JVM参数来调整GC行为以达到理想的性能水平。此外,我们可以选择通过JVM来动态调整内存利用率。我们应该考虑与我们的应用程序和所涉及的资源有关的每个选择的权衡。