Другой ответ действительно правильный, я отредактировал свой. Как небольшое дополнение, G1GC
не будет демонстрировать это поведение, в отличие от ParallelGC
; который используется по умолчанию в java-8
.
Как вы думаете, что произойдет, если я слегка изменю вашу программу на (запустить под jdk-8
с -Xmx20m
)
public static void main(String[] args) throws InterruptedException {
WeakHashMap<String, int[]> hm = new WeakHashMap<>();
int i = 0;
while (true) {
Thread.sleep(200);
i++;
String key = "" + i;
System.out.println(String.format("add new element %d", i));
hm.put(key, new int[512 * 1024 * 1]); // <--- allocate 1/2 MB
}
}
Это будет работать просто отлично. Это почему? Потому что это дает вашей программе достаточно места для новых распределений, прежде чем WeakHashMap
очистит свои записи. А другой ответ уже объясняет, как это происходит.
Теперь, в G1GC
, все будет немного иначе. Когда выделяется такой большой объект (более 1/2 МБ , обычно ), это называется humongous allocation
. Когда это произойдет, будет запущен одновременный G C. В рамках этого цикла: будет запущена коллекция young и будет инициирована Cleanup phase
, которая позаботится о публикации события в ReferenceQueue
, так что WeakHashMap
удалит его записи.
Итак, для этого кода:
public static void main(String[] args) throws InterruptedException {
Map<String, int[]> hm = new WeakHashMap<>();
int i = 0;
while (true) {
Thread.sleep(1000);
i++;
String key = "" + i;
System.out.println(String.format("add new element %d", i));
hm.put(key, new int[1024 * 1024 * 1]); // <--- 1 MB allocation
}
}
, который я запускаю с jdk-13 (где G1GC
по умолчанию)
java -Xmx20m "-Xlog:gc*=debug" gc.WeakHashMapTest
Вот часть logs:
[2.082s][debug][gc,ergo] Request concurrent cycle initiation (requested by GC cause). GC cause: G1 Humongous Allocation
Это уже делает что-то другое. Он запускает concurrent cycle
(выполнено , пока ваше приложение работает), потому что было G1 Humongous Allocation
. В рамках этого параллельного цикла выполняется цикл G C (который останавливает ваше приложение во время работы)
[2.082s][info ][gc,start] GC(0) Pause Young (Concurrent Start) (G1 Humongous Allocation)
Как часть этого young G C, он также очищает огромные области , здесь есть дефект .
Теперь вы можете видеть, что jdk-13
не ожидает накопления мусора в старом регионе, когда выделяются действительно большие объекты, но вызывает одновременное G C цикл, который спас день; в отличие от JDK-8.
Возможно, вы захотите прочитать, что означают DisableExplicitGC
и / или ExplicitGCInvokesConcurrent
в сочетании с System.gc
, и понять, почему на самом деле помогает вызов System.gc
.