На самом деле это реинкарнация поста, первоначально опубликованного в 2010 году. Воспоминание произошло, когда наши инженеры проклинали вчерашнюю неприятную ошибку, поднимающую голову. Когда проклятие прекратилось, я прошел, чтобы проверить свои сомнения. И вот, я был прав — перепад настроения был вызван тем, что приложение исчерпало пространство кучи, но умерло без обычного признака OutOfMemoryError .
Итак, позвольте мне рассмотреть случай отсутствия OutOfMemoryError с тем же примером кода, с которым я впервые столкнулся три года назад. Тогда я использовал Windows XP с установленным JDK 6 в середине 2010 года.
Я поиграл с ранним выпуском Plumbr, который должен был обнаружить утечки памяти из приложения (править: тогда ему не удавалось ничего сделать, кроме сбоя JDK). Чтобы проверить это, я написал небольшой фрагмент, который, как мне показалось, будет хорошим тестовым примером для обнаружения утечек (правка: на самом деле это не так). Я смог создать и запустить следующее:
1
2
3
4
5
6
7
8
9
|
class Leak { static List list = new ArrayList(); public static void main(String[] args) { for ( int i = 0 ; i >= 0 ;i++) { list.add(i); } System.out.println( "I will either reach here or die trying" ); } } |
Довольно хорошо для рынка, а? Но что вы думаете, запустив код, отображаемый в моей командной строке:
Вариант А:
1
2
3
4
5
6
|
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space at java.util.Arrays.copyOf(Arrays.java: 2760 ) at java.util.Arrays.copyOf(Arrays.java: 2734 ) at java.util.ArrayList.ensureCapacity(ArrayList.java: 167 ) at java.util.ArrayList.add(ArrayList.java: 351 ) at Leak.main(Leak.java: 6 ) |
Вариант Б:
1
|
I will either reach here or die trying |
Ну, как я выяснил, он ничего не распечатывает, поэтому я почти застрял, чтобы посмотреть пустую командную строку.
Так как прошло два года с моего последнего реального опыта разработки Java до того, как меня понизили до руководства, у меня не было патронов для устранения ситуации. Таким образом, я взял образец хардкорным хакерам Java, которые позже стали известны как основатели Zeroturnaround . За 10 добрых минут мне удалось ослепить их, пока они не ударили — память будет распределена таким образом, что не будет места для new OutOfMemoryError()
.
Если выполнить вышеизложенное с кучей 64 МБ (по умолчанию) в Windows XP с использованием сборок JDK середины 2010 года, вы увидите тихую ошибку:
1
2
|
C:\work\snippets\leak java -Xmx64m Leak C:\work\snippets\leak |
Но если вы немного увеличите (на самом деле измените) размер кучи, вы столкнетесь с более знакомой ситуацией:
1
2
3
4
5
6
7
8
|
C:\work\snippets\leak java -Xmx65m Leak Exception in thread "main" java.lang.OutOfMemoryError: Java heap space at java.util.Arrays.copyOf(Arrays.java: 2760 ) at java.util.Arrays.copyOf(Arrays.java: 2734 ) at java.util.ArrayList.ensureCapacity(ArrayList.java: 167 ) at java.util.ArrayList.add(ArrayList.java: 351 ) at Leak.main(Leak.java: 6 ) C:\work\snippets\leak |
Мораль истории? Я могу только порекомендовать перейти на более современный выпуск — независимо от того, какую конфигурацию я пробовал, мне не удалось воссоздать ситуацию с использованием сборок JDK 7, которые у меня были на моем Mac сегодня. Но, глядя на статистику популярных конфигураций среды выполнения Java , вы видите ошеломляющее количество развертываний JDK, начиная с выпусков до 2010 года, что означает, что проблема все еще существует, заставляя разработчиков сводить с ума всех, кто пытается выяснить источник проблема без каких-либо подсказок от трассировки стека для их поддержки.
В любом случае, поддерживая команду инженеров, обладающую обширными знаниями о внутреннем оборудовании JDK 6, я продолжил работу и восстановил пост из давно забытого блога, в который я в свое время вносил свой вклад.