java - GC 是否将内存释放回操作系统?

标签 java memory-management garbage-collection

当垃圾收集器运行并释放内存时,这些内存是返回给操作系统还是作为进程的一部分保留。我的强烈印象是,内存实际上从未释放回操作系统,而是保留为内存区域/池的一部分,以供同一进程重用。

因此,进程的实际内存永远不会减少。 An article这让我想起了这一点,而且 Java 的运行时是用 C/C++ 编写的,所以我想同样的事情也适用?

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

最佳答案

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

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

JDK 8 及更早版本

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

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

如果您使用的是 G1GC,请注意它只能获得 yield back unused chunks in the middle of the heap 的能力使用 jdk8u20,早期版本只能在堆的末尾返回 block ,这对可以回收的数量施加了很大的限制。

如果您使用具有默认暂停时间目标的收集器(例如 CMS 或 G1),您还可以放宽该目标以减少对收集器的限制,或者您可以切换到并行收集器以优先考虑占用空间而不是暂停时间.

要验证收缩发生或诊断 GC 决定不收缩的原因,您可以使用带有 -XX:+PrintAdaptiveSizePolicy 的 GC 日志记录,也可以提供洞察力,例如当 JVM 尝试为年轻代使用更多内存以满足某些目标时。

JDK 9

添加了 -XX:-ShrinkHeapInSteps 选项,可用于更积极地应用由上一节中提到的选项引起的收缩。 Relevant OpenJDK bug .

用于记录 -XX:+PrintAdaptiveSizePolicy 已替换为 -Xlog:gc+ergo

JDK 12

引入了通过 G1PeriodicGCInterval ( JEP 346 ) 为 G1GC 启用即时内存释放的选项,同样以增加一些 CPU 为代价。 JEP 在 Shenandoah 中也提到了类似的功能。和 OpenJ9 VM .

JDK 13

添加类似行为 for ZGC ,在这种情况下,它默认启用。此外,XXSoftMaxHeapSize 有助于某些工作负载将平均堆大小保持在某个阈值以下,同时仍允许瞬时峰值。

关于java - GC 是否将内存释放回操作系统?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30458195/

相关文章:

java - 写入 CSV 文件 : opencsv 2. 3

java - JsonMappingException : Cannot find a deserialzer for non-concrete Map type

memory-management - 有什么方法可以在Redmine中快速(拖放)重新排序任务?

c - 套接字接受在没有释放的情况下消耗了我在 Windows 上的内存

java - 找不到响应正文的消息正文编写器

java - 如何将 Timestamp 转换为 LocalDateTime 保留时区?

C++ 堆碎片分配错误?

java - 分析生成最多垃圾的 jvm 及其类型,逃逸分析优化

java - 我的 JVM 内存泄漏在哪里?垃圾收集器工作正常吗?

Java 原语和原语包装器