Статьи

Недостаточно памяти без OutOfMemoryError

На самом деле это реинкарнация поста, первоначально опубликованного в 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 до того, как меня понизили до руководства, у меня не было патронов для устранения ситуации. Таким образом, я взял образец хардкорным хакерам 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, я продолжил работу и восстановил пост из давно забытого блога, в который я в свое время вносил свой вклад.

Ссылка: Недостаточно памяти без OutOfMemoryError от нашего партнера по JCG Иво Мяги в блоге Plumbr Blog .