G1GC是否会在Xms=Xmx的情况下将内存释放回操作系统?

9
阅读了像这篇回答JEP-346这样的内容后,我意识到G1确实会将内存释放回操作系统。
然而,它是否会将内存释放回操作系统,甚至降低当前内存使用量到初始堆内存以下(即在此JEP之前,在我的情况下是JDK11)?
假设我有一个Java 11 VM运行,其中XmsXmx设置为5GB,在8GB RAM上运行,但我只消耗了约1GB。 G1 是否会释放足够的内存回到操作系统?
我没有在任何地方找到任何文档表明G1仅考虑保留Xms阈值来释放内存。
我正在生产环境中观察到这一点,MemAvailable不断减少直到某个点,然后在GC后,它跳回到接近8GB盒子的30-35%。因此,我认为它正在释放内存,这就是为什么MemAvailable会再次跳回去的原因。
此外,释放内存到操作系统精确意味着什么?是调用free/unmap吗?
1个回答

19
注意:我已删除之前的答案并研究了来源(还构建了自己的JVM,以找出这个特定时刻),以下是答案。
简短回答:
目前的JVM 11版本在缩小堆时,不会低于Xms
长回答:
真相在源代码中。这里是决定是否收缩堆的地方。在下面几行,您可以看到如果我们进入了那个if,就会有一个日志声明:
尝试缩小堆(Full GC后容量高于最大期望容量)。
因此,本质上,如果我们能理解两个参数:capacity_after_gcmaximum_desired_capacity - 我们就可以解决这个谜团。通常,capacity_after_gc不是很容易理解;主要是因为它取决于有多少垃圾以及当前GC可以回收多少。为简单起见,我将编写一些不生成任何垃圾的代码,以使该值保持不变。
在这种情况下,我们只需要理解maximum_desired_capacity

在几行之前, 你可以看到这是计算为:

maximum_desired_capacity =  MAX2(maximum_desired_capacity, min_heap_size);

不幸的是,这就是问题所在,因为需要跟踪和理解大量代码才能真正看到这些人体工程学是如何设置的;特别是因为它们依赖于启动了各种参数的JVM

例如,min_heap_size被设置为

// 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_after_gcmaximum_desired_capacity将始终具有相同的值;这意味着if语句永远不会被执行,堆永远不会低于-Xms

我已经使用JDK-13运行了相同的代码,尽管在给出-Xms参数时存在缩小日志,但基础堆仍停留在-Xms。更有趣的是,在java-13下尝试运行:

 -Xmx22g -Xms5g -XX:InitialHeapSize=1g

将会正确报错,错误信息为:

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


JEP 对此非常明确,但我的问题是 JDK11 中是否仍然保持这种现状?我编辑了我的问题以使其更加明确。 - Adwait Kumar
@AdwaitKumar 我认为是的,但具体何时发生这种情况 - 你需要研究G1源代码。人们说它会,我相信他们。 - Eugene
我在上面的回答中发表了评论,但没有得到回复。 - Adwait Kumar
@AdwaitKumar,抱歉回复晚了,我已经编辑了答案。 - Eugene
@Eugene讲解得非常好,值得等待。谢谢。 - Adwait Kumar
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接