【问题标题】:Does GC release back memory to OS?GC 是否会将内存释放回操作系统?
【发布时间】:2015-08-08 02:33:22
【问题描述】:

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

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

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

【问题讨论】:

  • 即使使用 C 或 C++ 中的标准堆分配方法也不必释放回操作系统,并且操作系统本身可以保持新分配的页面映射到进程(因此它不会似乎是免费的)。
  • Java’s Runtime is written in C/C++ so I guess the same thing applies? Java 的 GC 也是用 C++ 编写的……它不是“低于”C++
  • 到目前为止,没有堆内存释放给操作系统。如果仍未达到最大堆大小,则仅占用额外的内存。这与 C 中的相同,而 C 有一个额外的问题,即内存重用受到内存碎片的阻碍(尽管 C 可以自己管理分配的内存)。
  • JVM 以最小堆大小开始,我们可以将最大和最小堆大小设置为相同的值。如果最大和最小堆大小相同,它将预先分配 JAVA 堆,实际上可能只有少数 Java 对象使用该堆,在这种情况下,GC 不会将内存释放回操作系统。 GC 实际上声称使用的内存是未引用的对象,因此它们可以用于其他对象。它不会直接转化为将内存释放回操作系统。它可以通过从 C 库中调用 free 来释放内存。但是malloc在内部也可能不会立即将内存释放给OS。
  • @JoopEggen:所以进程永远不会缩小以帮助操作系统?

标签: java memory-management garbage-collection


【解决方案1】:

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

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

JDK 8 及更早版本

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

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

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

如果您使用具有默认暂停时间目标的收集器(例如 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 在ShenandoahOpenJ9 VM 中也提到了类似的功能。

JDK 13

添加类似的行为for ZGC,在这种情况下默认启用。

【讨论】:

    【解决方案2】:

    在某些情况下,JVM 确实会释放回内存,但是(出于性能原因)当某些内存被垃圾回收时不会发生这种情况。它还取决于 JVM、操作系统、垃圾收集器等。您可以使用 JConsole、VisualVM 或其他分析器查看应用程序的内存消耗。

    另见related bug report

    【讨论】:

    • 我认为您从这些 java 内存工具中看到的是在 Java 进程空间内释放的内存。但这并不意味着操作系统可以取回内存并将其提供给另一个应用程序
    • 您还可以查看总堆大小,但如果您不信任 Java 工具,也可以使用本机的操作系统级工具。
    • 如果我错了,请纠正我,但我真的不记得例如 java 进程的情况。任务管理器不断缩小。但我在监控方面并没有太多经验
    • 我刚刚验证确定,我已经看到它恢复了一些记忆!在 Windows 上,我喜欢使用 vmmap 应用程序(它可以从 Microsoft 免费下载),它可以让您看到很多关于进程内存使用情况的有趣信息。
    【解决方案3】:

    如果您使用 G1 收集器并偶尔调用 System.gc()(我每分钟调用一次),Java 将可靠地缩小堆并将内存归还给操作系统。

    从 Java 12 开始,如果应用程序空闲,G1 会自动执行此操作。

    我建议将这些选项与上述建议结合使用,以获得非常紧凑的常驻进程大小:

    -XX:+UseG1GC -XX:MaxHeapFreeRatio=30 -XX:MinHeapFreeRatio=10
    

    几个月来,每天都在使用这些选项来处理具有动态加载和卸载类的大型应用程序(整个基于 Java 的客户操作系统),而 Java 进程几乎总是保持在 400 到 800 MB 之间。

    【讨论】:

    • 如果XmsXmx 设置为相同的值会怎样。 G1 还会将内存还给操作系统吗? (JDK 12 之前)
    • @StefanReich 我不认为这是正确的,例如,initial heapmax heap 可以设置为相同的值,而应用程序可以消耗更少。
    • @StefanReich 1) 你必须通过@ 标记我,否则我不知道你回复了 2) Xms 不是不是 最小内存,而是首字母,这样你就错了。
    • @Eugene 很棒的分析!所以看来问题更多出在-Xms 的文档中,现在越来越多的人(包括那些开发中 GC)说它 最小堆值?
    • @Matthieu 特别是因为已经有InitialHeapSize,这让这更加令人困惑。
    【解决方案4】:

    这篇文章here 解释了 GC 在 Java 7 中是如何工作的。简而言之,有许多不同的垃圾收集器可用。通常内存是为 Java 进程保留的,只有一些 GC 将其释放给系统(我认为是根据请求)。但是,Java 进程使用的内存不会无限增长,因为 Xmx 选项定义了一个上限(通常为 256m,但我认为它取决于操作系统/机器)。

    【讨论】:

    • 如果堆有上限但如果堆的未使用部分最终被操作系统回收以供其他进程使用,则OP不会更新
    • 我认为 Francesco Foresti 的意思是,一些垃圾收集设置会导致堆缩小并将内存返回给操作系统,我在实践中也看到过,例如:@ 987654322@
    【解决方案5】:

    ZGC在java 13中发布,可以将未使用的堆内存返回给操作系统 请看the link

    【讨论】:

    • 不鼓励仅链接的答案,因为链接可能会随着时间的推移而中断。请从帖子中提取相关信息并将其添加到您的答案中,无论是引用还是您自己的话。
    • @Genhis OTOH 此特定答案中提供的链接应该“相当”稳定,因为它们指向 java.net 上的 JEP。
    • ZGC 在 JDK 11 中是实验性的。它仍然是实验性的。
    猜你喜欢
    • 2019-11-02
    • 1970-01-01
    • 2011-01-26
    • 2020-04-09
    • 1970-01-01
    相关资源
    最近更新 更多