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

Adw*_*mar 6 java garbage-collection g1gc java-11

喜欢读一些答案后,这个JEP-346,我已经意识到,G1确实释放内存返回给操作系统。

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

假设我有一个 Java 11 VM在RAM上运行XmsXmx设置为,但是我只消耗大约. G1 会向操作系统释放足够的内存吗?5GB8GB1GB

我没有在任何地方找到任何文件说 G1 仅限于释放Xms记住阈值。

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

另外,向操作系统释放内存到底是什么意思,它是调用 free/unmap 吗?

Eug*_*ene 14

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

简短的回答

JVM 11 version(目前),当使堆变小时不会低于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);
Run Code Online (Sandbox Code Playgroud)

不幸的是,这就是它变得棘手的地方,因为要真正了解这些人体工程学是如何设置的,需要遵循和理解大量代码;特别是因为它们依赖于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.
Run Code Online (Sandbox Code Playgroud)

请注意,它们甚至指的-Xmsminimum;尽管文档说它是初始的. 您还可以注意到它进一步取决于另外两个属性

 reasonable_minimum , InitialHeapSize
Run Code Online (Sandbox Code Playgroud)

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


假设你有这个非常简单的代码:

public class HeapShrinkExpand {

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

        for (int i = 0; i < 10; i++) {
            Thread.sleep(500);
            System.gc();
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

我运行它:

-Xmx22g 
-XX:InitialHeapSize=1g
"-Xlog:heap*=debug" 
"-Xlog:gc*=debug" 
"-Xlog:ergo*=debug" 
Run Code Online (Sandbox Code Playgroud)

在日志中,我会看到:

[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
Run Code Online (Sandbox Code Playgroud)

这会告诉你一些关于需要多少收缩的统计数据,当前容量是多少等等。下一行将显示堆减少了多少,实际上:

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

堆确实缩小了,缩小到大约29MB

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

事实上,如果我自己运行JMV(启用一些日志记录),这两个值:capacity_after_gcmaximum_desired_capacity始终具有相同的值;意味着if statement永远不会进入,堆永远不会低于-Xms


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

 -Xmx22g -Xms5g -XX:InitialHeapSize=1g
Run Code Online (Sandbox Code Playgroud)

将正确出错:

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