ChatGPT解决这个技术问题 Extra ChatGPT

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

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

结果,进程的实际内存永远不会减少。 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:所以一个进程永远不会缩小以帮助操作系统?

t
the8472

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

一般来说,收缩能力和行为取决于选择的垃圾收集器,JVM 版本,因为收缩能力通常是在添加 GC 本身很久之后的更高版本中引入的。一些收集器可能还需要传递其他选项来选择收缩。有些很可能永远不会支持它,例如 EpsilonGC。因此,如果需要堆收缩,则应针对特定的 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 Logging 也可以提供洞察力,例如,当 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,在这种情况下默认启用。


l
lbalazscs

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

另请参阅此related bug report


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

如果您使用 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 我认为这是不正确的,例如,初始堆和最大堆可以设置为相同的值,而应用程序的消耗可能会少很多。
@StefanReich 1) 你必须通过 @ 标记我,否则我不知道你回复了 2) Xms不是 最小内存,而是 initial,因此您错了。
@Eugene 很棒的分析!所以看起来问题更多在于 -Xms 的文档,现在越来越多的人(包括那些开发中 GC)说它最小堆值?
@Matthieu 特别是因为已经有 InitialHeapSize ,这使得这更加令人困惑。
f
francesco foresti

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


如果堆有上限,但如果堆的未使用部分最终被操作系统回收以供其他进程使用,则 OP 不会更新
我认为 Francesco Foresti 的意思是,一些垃圾收集设置会导致堆缩小并将内存返回给操作系统,我在实践中也看到过,例如:link
М
Макс Даниленко

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


不鼓励仅链接的答案,因为链接可能会随着时间的推移而中断。请从帖子中提取相关信息并将其添加到您的答案中,无论是引用还是您自己的话。
@Genhis OTOH 此特定答案中提供的链接应该“相当”稳定,因为它们指向 java.net 上的 JEP。
ZGC 在 JDK 11 中是实验性的。它仍然是实验性的。