Ответ 1
Разница, по-видимому, объясняется размером пространства для выживания сборщика мусора.
Флаг -Xmx
, как описано в docs, управляет максимальным размером пула распределения памяти. Куча части пула распределения памяти разделена на пространства Eden, Survivor и Tenured. Как описано в этом ответе, есть два региона, оставшихся в живых, только один из которых доступен для хранения живых объектов в любой момент времени. Таким образом, общее видимое пространство, доступное для выделения объектов, как сообщается Runtime.maxMemory()
, должно вычитать размер одного из оставшихся в живых мест из пула памяти кучи.
Вы можете использовать классы MemoryMXBean
и MemoryPoolMXBean
, чтобы получить немного больше информации о распределении памяти. Вот простая программа, которую я написал:
import java.lang.management.ManagementFactory;
import java.lang.management.MemoryMXBean;
import java.lang.management.MemoryPoolMXBean;
public class MemTest {
static String mb (long s) {
return String.format("%d (%.2f M)", s, (double)s / (1024 * 1024));
}
public static void main(String[] args) {
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()));
}
}
}
Выход этого в OpenJDK 7 для java -Xmx1024m MemTest
:
Runtime max: 1037959168 (989.88 M)
Non-heap: 224395264 (214.00 M)
Heap: 1037959168 (989.88 M)
Pool: Code Cache (type Non-heap memory) = 50331648 (48.00 M)
Pool: Eden Space (type Heap memory) = 286326784 (273.06 M)
Pool: Survivor Space (type Heap memory) = 35782656 (34.13 M)
Pool: Tenured Gen (type Heap memory) = 715849728 (682.69 M)
Pool: Perm Gen (type Non-heap memory) = 174063616 (166.00 M)
Обратите внимание, что Eden + 2 * Survivor + Tenured = 1024M, что соответствует количеству кучи, запрошенному в командной строке. Большое спасибо @Absurd-Mind за это.
Различия, которые вы наблюдаете между разными JVM, вероятно, связаны с различной эвристикой для выбора относительных размеров по умолчанию для разных поколений. Как описано в в этой статье (применимо к Java 6, не удалось найти более недавнюю версию), вы можете использовать -XX:NewRatio
и -XX:SurvivorRatio
, чтобы явно управлять этими настройками. Итак, запустив команду:
java -Xmx1024m -XX:NewRatio=3 -XX:SurvivorRatio=6
Вы сообщаете JVM, что:
Young:Tenured = (Eden + 2*Survivor):Tenured = 1:3 = 256m:768m
Survivor:Eden = 1:6 = 32m:192m
Таким образом, с этими параметрами разница между запрошенным значением -Xmx
и доступной памятью, сообщенной Runtime.maxMemory()
, должна быть 32 м, что подтверждается с использованием вышеуказанной программы. И теперь вы должны иметь возможность точно предсказать доступную память, сообщенную Runtime
для заданного набора аргументов командной строки, чего вы действительно хотели, правильно?