GC 是否向操作系统释放回内存?

当垃圾回收器运行并释放内存时,此内存是返回到操作系统,还是作为进程的一部分保留。我的印象是,内存实际上从未释放回操作系统,而是作为内存区域/池的一部分保留下来,以便由同一进程重用。

因此,进程的实际内存永远不会减少。有一篇文章提醒我,Java的运行时是用C / C++写的,所以我想同样的事情也适用吗?

更新
我的问题是关于Java的。我之所以提到C/C++,是因为我假设Java的分配/解除分配是由JRE使用某种形式的malloc/delete完成的。


答案 1

HotSpot JVM确实将内存释放回操作系统,但这样做是不情愿的,因为调整堆的大小很昂贵,并且假设如果你需要该堆一次,你会再次需要它。

一般来说,收缩能力和行为取决于所选的垃圾回收器,JVM版本,因为收缩功能通常在GC本身添加很久之后的版本中引入。某些收集器可能还需要传递其他选项以选择收缩。有些人很可能永远不会支持它,例如EpsilonGC。因此,如果需要堆收缩,则应针对特定的 JVM 版本和 GC 配置对其进行测试。

JDK 8 及更早版本

在这些版本中没有用于提示内存回收的显式选项,但您可以通过设置使GC更具侵略性,这将允许它花费更多的CPU时间来收集和限制GC周期后分配但未使用的堆内存量。-XX:GCTimeRatio=19 -XX:MinHeapFreeRatio=20 -XX:MaxHeapFreeRatio=30

如果使用并发收集器,还可以将 N 设置为某个较低的值,以使 GC 几乎连续运行并发收集,这将消耗更多的 CPU 周期,但会更快地缩小堆。这通常不是一个好主意,但在某些类型的计算机上,有很多备用CPU内核但内存不足,这可能是有意义的。-XX:InitiatingHeapOccupancyPercent=N

如果您使用的是G1GC,请注意,它只获得了使用jdk8u20在堆中间返回未使用块的能力,早期版本只能在堆的末尾返回块,这大大限制了可以回收的数量。

如果您使用的是具有默认暂停时间目标(例如 CMS 或 G1)的收集器,则还可以放宽该目标以对收集器施加较少的约束,或者您可以切换并行收集器以优先使用占用空间而不是暂停时间。

为了验证是否发生收缩或诊断为什么GC决定不收缩,您可以使用GC日志记录也可以提供洞察力,例如,当JVM尝试为年轻一代使用更多内存来实现某些目标时。-XX:+PrintAdaptiveSizePolicy

JDK 9

添加了可用于更积极地应用由上一节中提到的选项引起的收缩的选项的选项。相关的 OpenJDK 错误-XX:-ShrinkHeapInSteps

对于已替换为-XX:+PrintAdaptiveSizePolicy-Xlog:gc+ergo

JDK 12

引入了通过 (JEP 346) 为 G1GC 启用提示内存释放的选项,这同样是以牺牲一些额外的 CPU 为代价的。JEP还提到了ShenandoahOpenJ9 VM中的类似功能。G1PeriodicGCInterval

JDK 13

为 ZGC 添加类似的行为,在本例中,默认情况下启用该行为。此外,对于某些工作负荷,将平均堆大小保持在某个阈值以下,同时仍允许暂时性峰值,也会有所帮助。XXSoftMaxHeapSize


答案 2

JVM 在某些情况下确实会释放回内存,但是(出于性能原因)每当对某些内存进行垃圾回收时,都不会发生这种情况。它还取决于JVM,OS,垃圾回收器等。您可以使用 JConsole、VisualVM 或其他探查器监视应用的内存消耗。

另请参阅此相关错误报告