即使 Xms = Xmx,G1GC 是否将内存释放回 OS?

Does G1GC release back memory to the OS even if Xms = Xmx?

在阅读了 this and JEP-346 等一些答案后,我意识到 G1 确实将内存释放回 OS。

然而,它是否将内存释放回 OS,甚至达到当前内存使用量可能低于初始堆内存的程度(即在此 JEP 之前,在我的情况下为 JDK11)?

假设我在 8GB RAM 上有一个 Java 11 VM 运行 XmsXmx 设置为 5GB,但是我只消耗了 1GB 左右。 G1 会释放足够的内存给 OS 吗?

我没有在任何地方找到任何文档说明 G1 仅限于发布,同时牢记 Xms 阈值。

我在生产中观察到这一点,MemAvailable 一直下降到一个点,然后在 GC 之后,它在 8GB 盒子上跳升到接近 30-35%。所以我假设它正在释放内存,这就是 MemAvailable 跳回的原因。

另外释放内存到OS到底是什么意思,是在调用free/unmap吗?

注意:我已经删除了我以前的答案并研究了来源(也建立了我自己的 JVM 只是为了找出这个特定时刻),这里是答案。

简答

JVM 11 version(目前),不会在减小堆时低于Xms

长答案

绝对真理在源代码中。 here 是缩小堆或不缩小堆的决定。下面几行,可以看到如果我们输入那个if,就会有一条log语句:

Attempt heap shrinking (capacity higher than max desired capacity after Full GC).

所以本质上,如果我们能理解两个参数:capacity_after_gcmaximum_desired_capacity - 我们就可以解开这个谜团。一般来说,capacity_after_gc 不是一个容易掌握的东西;主要是看垃圾有多少,当前GC能回收多少。为简单起见,我将编写一些不产生任何垃圾的代码,以便该值保持不变。

这样的话,我们只需要了解一下maximum_desired_capacity

A few lines above,你可以看到这是计算为:

maximum_desired_capacity =  MAX2(maximum_desired_capacity, min_heap_size);

不幸的是,这就是它变得棘手的地方,因为需要遵循和理解大量代码才能真正了解这些人体工程学是如何设置的;特别是因为它们依赖于 JVM 开始的各种参数。

例如min_heap_size is set as:

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

注意,他们甚至将 -Xms 称为 minimum;虽然文档说它是 initial。您还可以注意到它 进一步 取决于 另外两个属性 :

 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 statement 永远不会进入,堆永远不会低于 -Xms


我有 运行 与 JDK-13 相同的代码,并且当那里存在收缩日志时(当 -Xms 作为参数给出时),底层堆保持不变在 -Xms,仍然。我发现更有趣的是,在 java-13 下,尝试 运行 与:

 -Xmx22g -Xms5g -XX:InitialHeapSize=1g

将正确地出错:

Incompatible minimum and initial heap sizes specified