代码如下:
System.out.println("Runtime max: " + mb(Runtime.getRuntime().maxMemory()));
MemoryMXBean m = ManagementFactory.getMemoryMXBean();
System.out.println("Non-heap: " + mb(m.getNonHeapMemoryUsage().getMax()));
System.out.println("Heap: " + mb(m.getHeapMemoryUsage().getMax()));
for (MemoryPoolMXBean mp : ManagementFactory.getMemoryPoolMXBeans()) {
System.out.println("Pool: " + mp.getName() +
" (type " + mp.getType() + ")" +
" = " + mb(mp.getUsage().getMax()));
}
在JDK8上运行代码是:
[root@docker-runner-2486794196-0fzm0 docker-runner]# java -version java version "1.8.0_181" Java(TM) SE Runtime Environment (build 1.8.0_181-b13) Java HotSpot(TM) 64-Bit Server VM (build 25.181-b13, mixed mode) [root@docker-runner-2486794196-0fzm0 docker-runner]# java -jar -Xmx1024M -XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap test.jar Runtime max: 954728448 (910.50 M) Non-heap: -1 (-0.00 M) Heap: 954728448 (910.50 M) Pool: Code Cache (type Non-heap memory) = 251658240 (240.00 M) Pool: Metaspace (type Non-heap memory) = -1 (-0.00 M) Pool: Compressed Class Space (type Non-heap memory) = 1073741824 (1024.00 M) Pool: PS Eden Space (type Heap memory) = 355467264 (339.00 M) Pool: PS Survivor Space (type Heap memory) = 1048576 (1.00 M) Pool: PS Old Gen (type Heap memory) = 716177408 (683.00 M)
*运行时最大值:954728448(910.50 M)*
Runtime.maxMemory为910.50M,我想知道它是如何工作的
On JDK7, "Runtime.getRuntime().maxMemory()" = "-Xmx" - "Survivor" ,但是在JDK8上不起作用。
答案 0 :(得分:2)
在JDK 8中,公式Runtime.maxMemory() = Xmx - Survivor
仍然很合理,但诀窍在于如何估算幸存者。
您尚未设置初始堆大小(-Xms
),并且默认情况下“自适应大小策略”处于启用状态。这意味着堆可以调整大小,堆生成边界可以在运行时移动。 Runtime.maxMemory()
保守地估计内存量,从“新一代”的大小中减去最大可能的个幸存者大小。
Runtime.maxMemory() = OldGen + NewGen - MaxSurvivor
where MaxSurvivor = NewGen / MinSurvivorRatio
在您的示例中,默认情况下,OldGen = 683 MB,NewGen = 341 MB,MinSurvivorRatio = 3。也就是说,
Runtime.maxMemory() = 683 + 341 - (341/3) = 910.333 MB
如果您禁用-XX:-UseAdaptiveSizePolicy
或将初始堆大小-Xms
设置为与-Xmx
相同的值,则会再次看到Runtime.maxMemory() = OldGen + Eden + Survivor
。
答案 1 :(得分:1)
报告的最大堆与实际最大堆之间的差异源自幸存者空间的假设是基于经验数据的,但尚未被证明是有意特征。
我对该程序进行了扩展(最后是代码)。使用JDK 6
在-Xmx1G -XX:-UseParallelGC
上运行此扩展程序给了我
Runtime max: 1037959168 (989 MiB)
Heap: 1037959168 (989 MiB)
Pool: Eden Space = 286326784 (273 MiB)
Pool: Survivor Space = 35782656 (34 MiB)
Pool: Tenured Gen = 715849728 (682 MiB)
Pool: Heap memory total = 1037959168 (989 MiB)
Eden + 2*Survivor + Tenured = 1073741824 (1024 MiB)
(Non-heap: omitted)
此处,值匹配。报告的最大大小等于堆空间的总和,因此报告的最大大小和一个幸存者空间的大小之和等于公式Eden + 2*Survivor + Tenured
(精确的堆大小)的结果。
我之所以指定-XX:-UseParallelGC
的原因是,链接答案中的“ Tenured”一词为我提供了有关此假设来自何处的提示。因此,当我在Java 6上运行程序而没有在计算机上使用-XX:-UseParallelGC
时,我得到了
Runtime max: 954466304 (910 MiB)
Heap: 954466304 (910 MiB)
Pool: PS Eden Space = 335609856 (320 MiB)
Pool: PS Survivor Space = 11141120 (10 MiB)
Pool: PS Old Gen = 715849728 (682 MiB)
Pool: Heap memory total = 1062600704 (1013 MiB)
Eden + 2*Survivor + Tenured = 1073741824 (1024 MiB)
(Non-heap: omitted)
在此,报告的最大大小不等于堆内存池的总和,因此“报告的最大大小加生存程序”公式产生了不同的结果。这些是相同的值,我使用默认选项通过Java 8获得,因此您的问题与Java 8不相关,因为即使在Java 6上,当垃圾收集器与链接的Q&A中使用的垃圾收集器不同时,这些值也不匹配。
请注意,从Java 9开始,-XX:+UseG1GC
成为默认值,我得到了
Runtime max: 1073741824 (1024 MiB)
Heap: 1073741824 (1024 MiB)
Pool: G1 Eden Space = unspecified/unlimited
Pool: G1 Survivor Space = unspecified/unlimited
Pool: G1 Old Gen = 1073741824 (1024 MiB)
Pool: Heap memory total = 1073741824 (1024 MiB)
Eden + 2*Survivor + Tenured = N/A
(Non-heap: omitted)
最重要的是,假设差异等于“幸存者空间”的大小仅适用于一个特定的(过时的)垃圾收集器。但是,如果适用,公式Eden + 2*Survivor + Tenured
会给出确切的堆大小。对于不适用于该公式的“垃圾优先”收集器,报告的最大大小已经是正确的值。
所以最好的策略是获取Eden
,Survivor
和Tenured
(又称Old
)的最大值,然后检查这些值中的任意一个是否为{ {1}}。如果是这样,只需使用-1
,否则,请计算Runtime.getRuntime().maxMemory()
。
程序代码:
Eden + 2*Survivor + Tenured