Java GC 垃圾回收与内存管理

垃圾收集器 (GC)负责 Java 中的内存管理。因此,程序员不需要显式地处理内存分配和释放。

在 Java 中,JVM 一开始就预留了一定的内存。有时,实际使用的内存明显少于保留量。在这种情况下,我们更愿意将多余的内存返回给操作系统。

这整个过程依赖于用于垃圾收集的算法。因此,我们可以根据需要的行为选择 GC 和 JVM 的类型。

JVM 内存结构

JVM初始化时,会在其内部创建不同类型的内存区域,如Heap area、Stack area、 Method Area、PC Registers和Native Method Stack。

GC 主要处理堆区 Heap,这里我们重点关注与堆相关的内存交互。

我们可以分别使用标志 -Xms 和 -Xmx 指定初始和最大堆大小。如果 -Xms 低于 -Xmx,则意味着 JVM 一开始并没有将所有保留的内存提交给堆。简而言之,堆大小从 -Xms 开始,可以扩展到 -Xmx。这允许开发人员配置所需堆内存的大小。

当应用程序运行时,不同的对象会在堆内分配内存。在垃圾回收时,GC 清理未引用的对象并释放内存。此释放的内存当前是堆本身的一部分,因为它是一个 CPU 密集型过程,在每次释放后与 OS 交互。

对象以分散的方式驻留在堆内,GC 需要压缩内存并创建一个空闲区域以返回给 OS。它在返回内存时涉及额外的进程执行。此外,Java 应用程序可能在稍后阶段需要额外的内存。为此,我们需要再次与操作系统通信以请求更多内存。但是,我们无法确保在请求的时间操作系统中内存的可用性。因此,使用内部堆而不是频繁调用操作系统来获取内存是一种更安全的方法。

如果我们的应用程序不需要整个堆内存,我们就会浪费这些内存资源,其实这些资源可以被操作系统用于其他应用程序。考虑到这一点,JVM 引入了高效和自动化的内存释放技术。

GC 与 OS 内存

随着 Java 版本的升级,GC 也在一同演进。堆和操作系统之间的内存交互依赖于 JVM 和 GC 的实现。如今,很多GC 都支持堆收缩,如 G1、Serial、ZGC等,堆收缩是将多余的内存从堆释放回操作系统以优化资源使用的过程。

并行 GC 不会轻易将未使用的内存释放回操作系统。而一些支持堆收缩的 GC 分析内存消耗并相应地决定从堆中释放一些空闲内存给 OS。

G1 实现思路

G1 支持没有长时间停顿的压缩过程。它使用内部自适应优化算法,根据应用程序使用情况分析所需的 RAM,并在需要时取消提交内存。

一开始,G1 实现支持在Full GC 之后或在并发循环事件期间进行堆收缩。但是,在理想情况下,我们希望及时将未使用的内存返回给操作系统,尤其是在我们的应用程序空闲时。我们希望 GC 在运行时动态适应应用程序的内存使用情况。

G1 可以基于G1PeriodicGCInvokesConcurrent选项启动并发循环或Full GC。循环执行后,G1 需要调整堆大小并将释放的内存返回给 OS。

JVM 参数控制

我们可以使用不同的参数来配置 GC 的默认堆行为:

  • -XX:GCTimeRatio:指定应用程序执行和 GC 执行之间所需的时间间隔。我们可以使用它来让 GC 运行更长时间
  • -XX:MinHeapFreeRatio:指定垃圾回收后堆中可用空间的最小预期比例
  • -XX:MaxHeapFreeRatio:指定垃圾回收后堆中空闲空间的最大预期比例

如果堆中的可用空闲空间高于使用-XX:MaxHeapFreeRatio选项指定的比率,则 GC 可以将未使用的内存返回给 OS。我们可以配置上述参数的值来限制堆中未使用的内存量。

并发垃圾收集进程提供了类似的参数:

  • -XX:InitiatingHeapOccupancyPercent:指定启动并发垃圾回收所需的堆占用百分比。
  • -XX:-ShrinkHeapInSteps:立即将堆大小减小到-XX:MaxHeapFreeRatio值。默认实现需要此进程的多个垃圾回收周期。
转载请注明出处:码谱记录 » Java GC 垃圾回收与内存管理
标签: