Предположение о том, что несоответствие между сообщаемой максимальной кучей и фактической максимальной кучей связано с пространством выживших, было основано на эмпирических данных, но не было доказано как преднамеренная особенность.
Я расширил программунемного (код в конце).Запуск этой расширенной программы на 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
, заключалась в том, что термин «штатный» в связанном ответе дал мне подсказку о том, откуда пришло это предположение.Поскольку, когда я запускаю программу на 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 значения не совпадают, когда сборщик мусора отличается от того, который используется в связанных вопросах и ответах.
Обратите внимание, что начиная с 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
.Если это так, просто используйте Runtime.getRuntime().maxMemory()
, в противном случае рассчитайте Eden + 2*Survivor + Tenured
.
Код программы:
public static void main(String[] args) {
System.out.println("Runtime max: " + mb(Runtime.getRuntime().maxMemory()));
MemoryMXBean m = ManagementFactory.getMemoryMXBean();
System.out.println("Heap: " + mb(m.getHeapMemoryUsage().getMax()));
scanPools(MemoryType.HEAP);
checkFormula();
System.out.println();
System.out.println("Non-heap: " + mb(m.getNonHeapMemoryUsage().getMax()));
scanPools(MemoryType.NON_HEAP);
System.out.println();
}
private static void checkFormula() {
long total = 0;
boolean eden = false, old = false, survivor = false, na = false;
for(MemoryPoolMXBean mp: ManagementFactory.getMemoryPoolMXBeans()) {
final long max = mp.getUsage().getMax();
if(mp.getName().contains("Eden")) { na = eden; eden = true; }
else if(mp.getName().matches(".*(Old|Tenured).*")) { na = old; old = true; }
else if(mp.getName().contains("Survivor")) {
na = survivor;
survivor = true;
total += max;
}
else continue;
if(max == -1) na = true;
if(na) break;
total += max;
}
System.out.println("Eden + 2*Survivor + Tenured = "
+(!na && eden && old && survivor? mb(total): "N/A"));
}
private static void scanPools(final MemoryType type) {
long total = 0;
for(MemoryPoolMXBean mp: ManagementFactory.getMemoryPoolMXBeans()) {
if(mp.getType()!=type) continue;
long max = mp.getUsage().getMax();
System.out.println("Pool: "+mp.getName()+" = "+mb(max));
if(max != -1) total += max;
}
System.out.println("Pool: "+type+" total = "+mb(total));
}
private static String mb(long mem) {
return mem == -1? "unspecified/unlimited":
String.format("%d (%d MiB)", mem, mem>>>20);
}