当前位置: 首页 > 知识库问答 >
问题:

即使Xms=Xmx,G1GC是否也会将内存释放回操作系统?

伯博
2023-03-14

在阅读了一些像这样的答案和JEP-346之后,我意识到G1确实会将内存释放回操作系统。

然而,它是否将内存释放回操作系统,即使当前内存使用可能低于初始堆内存(即在此JEP之前,在我的案例中为JDK11)?

假设我有一个Java11 VM,运行XmsXmx设置为5GB,在8GBRAM上,但是我只消耗大约1GB。G1会释放足够的内存回操作系统吗?

我在任何地方都没有找到任何文档说明G1仅限于在发布时考虑Xms阈值。

我在生产过程中观察到了这一点,MemAvailable不断减少,直到达到一个点,然后在GC之后,在8GB的机箱上,它会跳到接近30-35%。所以我假设它正在释放内存,这就是MemAvailable正在跳回的原因。

另外,向操作系统释放内存到底意味着什么,是调用空闲/取消映射吗?

共有1个答案

湛宏旷
2023-03-14

注意:我已经删除了之前的答案,并研究了源代码(还构建了我自己的JVM,只是为了找出这一特定时刻),下面是答案。

简短的回答

JVM 11版本(目前),在使堆变小时不会低于Xms

冗长的答案

源代码是绝对正确的。下面是是否缩小堆的决定。在下面几行中,您可以看到,如果我们输入该if,将出现一条log语句:

尝试堆收缩(容量高于完全GC后的最大所需容量)。

所以从本质上讲,如果我们能理解两个参数:capacity_after_gcmaximum_desired_capacity-我们就可以解开这个谜团。总的来说,capacity_after_gc不是一个容易掌握的东西;主要是因为它取决于有多少垃圾以及当前GC可以回收多少垃圾。为了简单起见,我准备写一些不产生任何垃圾的代码,这样这个值就是恒定的。

在这种情况下,我们只需要了解maximum_desired_capacity

在上面的几行中,您可以看到这是计算为:

maximum_desired_capacity =  MAX2(maximum_desired_capacity, min_heap_size);

不幸的是,这就是它变得棘手的地方,因为要真正了解这些人体工程学是如何设置的,需要遵循和理解很多代码;尤其是因为它们依赖于JVM开始时使用的各种参数。

例如,最小堆大小设置为:

// If the minimum heap size has not been set (via -Xms),
// synchronize with InitialHeapSize to avoid errors with the default value.

请注意,它们甚至至少引用了Xms;虽然文件上说这是最初的。您还可以注意到,它还取决于另外两个属性:

 reasonable_minimum , InitialHeapSize

这将很难进一步解释;这就是为什么我不会。相反,我将向您展示一些简单的证明(我确实阅读了大部分代码…)

假设您有以下非常简单的代码:

public class HeapShrinkExpand {

    public static void main(String[] args) throws InterruptedException {

        for (int i = 0; i < 10; i++) {
            Thread.sleep(500);
            System.gc();
        }
    }
}

我运行它时使用:

-Xmx22g 
-XX:InitialHeapSize=1g
"-Xlog:heap*=debug" 
"-Xlog:gc*=debug" 
"-Xlog:ergo*=debug" 

在日志中,我将看到:

[0.718s][debug][gc,ergo,heap   ] GC(0) Attempt heap shrinking (capacity higher than max desired capacity after Full GC). Capacity: 1073741824B occupancy: 8388608B live: 1018816B maximum_desired_capacity: 27962026B (70 %)
[0.719s][debug][gc,ergo,heap   ] GC(0) Shrink the heap. requested shrinking amount: 1045779798B aligned shrinking amount: 1044381696B attempted shrinking amount: 1044381696B

这告诉您一些关于所需收缩量、当前容量等的统计数据。下一行将显示堆实际上下降了多少:

[0.736s][debug][gc,ihop] GC(0) Target occupancy update: old: 1073741824B, new: 29360128B

堆确实缩小到了29MB左右。

如果我添加一个JVM启动标志:-Xms10g,那些负责显示堆收缩到多少的GC日志;将不再存在。

事实上,如果我运行自己的JMV(启用一些日志记录),这两个值:capacity\u after\u gc和maximum\u desired\u capacity总是具有相同的值;这意味着永远不会输入if语句,堆也永远不会低于Xms。

我已经使用JDK-13运行了相同的代码,当收缩日志存在时(当-Xms作为参数给出时),底层堆仍然停留在-Xms。我发现更有趣的是,在java-13下,尝试使用:

 -Xmx22g -Xms5g -XX:InitialHeapSize=1g

将正确错误:

指定的最小和初始堆大小不兼容

 类似资料:
  • 问题内容: 喜欢读一些答案后,这个和JEP-346,我已经意识到,G1确实释放内存返回给操作系统。 但是,它是否将内存释放回操作系统,甚至达到当前内存使用可能降至初始堆内存以下的程度(即在此JEP之前,在我的情况下为JDK11)? 假设我有一个运行在RAM 上并设置为的Java 11 VM ,但是我只消耗了。G1是否将足够的内存释放回操作系统? 我在任何地方都找不到任何文件说G1被限制在发布时要牢

  • 当垃圾收集器运行并释放内存时,内存会返回操作系统还是作为进程的一部分保留下来。我的强烈印象是,内存实际上从未释放回操作系统,而是作为内存区域/池的一部分保留下来,供同一进程重复使用。 因此,进程的实际内存永远不会减少。提醒我的一篇文章是这样的,Java的运行时是用C/C写的,所以我想同样的事情也适用? 更新< br >我的问题是关于Java的。我提到C/C是因为我假设Java的分配/释放是由JRE

  • 我们有一个简单的微服务设置,基于Windows服务器上的Spring Boot和Java8。 许多服务的负载很低,因为它们是与各种外部合作伙伴的集成。所以它们很多时候都是空闲的。 问题在于,JVM 仅在触发垃圾回收时才会将内存释放回操作系统。因此,服务可能会开始使用32mb,然后为单个请求提供服务并分配2GB内存。如果该服务上没有其他活动,则不会受到 GC 和服务器上的其他服务的影响。 使用Sys

  • 问题内容: 当垃圾收集器运行并释放内存时,该内存将返回操作系统,还是作为进程的一部分保留。我印象深刻的是,该内存实际上从未释放回OS,而是保留为内存区域/池的一部分,以供同一进程重用。 结果,进程的实际内存将永远不会减少。一篇使我想起的文章是Java的Runtime是用C / C ++编写的,所以我想同样的事情适用吗? 更新 我的问题是关于Java的。我之所以提到C / C ++,是因为我假设Ja

  • 我编写的java应用程序遇到了一个问题,导致硬件性能问题。问题(我很确定)是,我运行应用程序的一些机器只有1GB的内存。当我启动java应用程序时,我将堆大小设置为-xms512m-xmx1024m。 我的第一个问题是,我的假设是否正确,因为我将机器的所有内存分配给java堆,这显然会导致性能问题?

  • 我有一个用params运行的java应用程序,有人能解释一下为什么VCZ是3800076,而RSS是241304(这是更多的java params) 在命令中: