注意:我已经删除了之前的答案并研究了来源(也构建了我自己的JVM
只是为了找出这个特定的时刻),这就是答案。
简短的回答
JVM 11 version
(目前),将not往下走Xms
当使堆变小时。
长答案
绝对的真理在源代码中。和here https://hg.openjdk.java.net/jdk/jdk11/file/1ddf9a99e4ad/src/hotspot/share/gc/g1/g1CollectedHeap.cpp#l1213是决定是否缩小堆。下面几行,您可以看到如果我们输入if
,会有一条日志语句:
尝试堆收缩(Full GC 后容量高于最大所需容量)。
所以本质上,如果我们能理解两个参数:capacity_after_gc
and maximum_desired_capacity
- 我们可以解开这个谜团。一般来说,capacity_after_gc
不是一件容易掌握的事;主要是看当时有多少垃圾,以及当前GC能回收多少。为了简单起见,我将编写一些不会生成任何垃圾的代码,以便该值是恒定的。
在这种情况下,我们只需要了解maximum_desired_capacity
.
上面几行 https://hg.openjdk.java.net/jdk/jdk11/file/1ddf9a99e4ad/src/hotspot/share/gc/g1/g1CollectedHeap.cpp#l1199,您可以看到计算如下:
maximum_desired_capacity = MAX2(maximum_desired_capacity, min_heap_size);
不幸的是,这就是它变得棘手的地方,因为需要遵循和理解大量代码才能真正了解这些人体工程学是如何设置的;特别是因为它们依赖于各种论点JVM
已经开始了。
例如min_heap_size
被设置为 https://hg.openjdk.java.net/jdk/jdk11/file/1ddf9a99e4ad/src/hotspot/share/runtime/arguments.cpp#l1827:
// If the minimum heap size has not been set (via -Xms),
// synchronize with InitialHeapSize to avoid errors with the default value.
请注意,他们甚至指的是-Xms
as minimum;虽然文档说它是initial。您还可以注意到它further依赖于取决于另外两个属性 :
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_gc
and maximum_desired_capacity
will always具有相同的价值观;意思是if statement
永远不会被输入,堆也永远不会低于-Xms
.
我已经使用 JDK-13 运行了相同的代码,并且虽然那里存在收缩日志(当-Xms
作为参数给出),底层堆保留在-Xms
, 仍然。我发现更有趣的是java-13
,尝试运行:
-Xmx22g -Xms5g -XX:InitialHeapSize=1g
将正确错误:
指定的最小和初始堆大小不兼容