Как стать автором
Обновить

Комментарии 15

Не раскрыта очень важная тема, что память может сжирать один тред, а падать с OOMом — другой. Особенно забавно, когда с OOMом падает не готовый к этому сервисный тред, например, который разбирает очередь RPC.

Тут рассмотрены примеры, когда тред потребляющий память известен. Да и вообще основная идея в том, что пытаться ловить такие ошибки весьма плохая идея.
Не java программист, но пришел в голову такой подход. Заранее создать статическую переменную небольшого объема, и когда произошел out of memory, эту переменную очищать перед логгированием
char _dummy[16384]; // make program look bigger
Заранее создать статическую переменную небольшого объема, и когда произошел out of memory, эту переменную очищать перед логгированием

В Java нельзя очистить переменную, можно только убрать ссылку на нее, а очиститься она только при очередной сборки мусора, и нет никакой гарантии, что между очисткой и логированием успеет пройти сборка и мусора. Так же после сборки мусора память кто-то может занять в другом треде.
Можно, если воспользоваться SoftReference. И для неё спецификация как раз гарантирует, что ссылка будет очищена до того, как выбросится OutOfMemoryError.

Зря комментарий svr_91 заминусовали. Вполне годное решение, которое встречал в реальных проектах.
Нет, в том-то и дело, что сначала будет очищена SoftReference, а только потом (если места для следующего выделения памяти не хватит) выбросится OutOfMemoryError. Никто не гарантирует, что все что было занято SoftReference не окажется занятым к моменту исчерпания памяти. По сути SoftReference это просто незанятая память, когда вся остальная память занята.

То есть SoftReference никак не гарантирует, что вы сможете залогировать при OutOfMemoryError.
1. В качестве резерва памяти для обработки OOME создаём байтовый массив.
2. Держим на него strong reference и SoftReference.
3. При возникновении OOME обнуляем strong reference; массив станет softly reachable, а, значит, резерв заведомо освободится до возникновения следующего OOME.

нет никакой гарантии, что между очисткой и логированием успеет пройти сборка и мусора.
Либо сборка мусора пройдёт, либо логирование успешно выполнится безо всякой сборки. Аспект многопоточности оставлю в стороне — можно манипулировать размером резерва.
А зачем тогда SoftReference, если можно просто strong reference тогда уж обнулить?
Да, на практике можно и одним strong reference обойтись. SoftReference лишь для того, чтобы ткнуть в конкретное место в спецификации :)
Ну тут таже проблема, что и с переменной на стеке, которая якобы должна очиститься после пропадания со стека. Но мое решение хотябы не заставляет всех пользователей держать все переменные на стеке.

Для очистки gc можно использовать System.gc() тоже без гарантии правда.

Вообще, с gc в яве конечно сложно. Есть старое поколение, есть новое, и не факт, что они очищаются по одинаковым правилам. Плюс переменная может попасть в какую-нибудь constant memory, если такое в яве есть. Но мне все равно кажется, что мое решение имеет право на жизнь
Работать, скорее всего, будет, т.к. перед тем как выкинуть ООМ еще раз, JVM сделает full GC. Только это будет очень плохо поддерживаемый код. Да и вообще писать код, который использует специфику JVM — не самая лучшая идея. Есть шанс получить разное поведение на разных ОС и между разными версиями Java. Поведение, которое выходит за рамки JLS — уже черная магия. Я как раз пытался предостеречь от нее.
Ну да, это понятно. Просто раз уж приведено несколько разных методов, почему бы не привести еще один.

А насчет неподдерживаемого кода, то можно просто вывести этот код в отдельный метод :)
А у нас часто было так что линукс кернел убивал процесс, и никаких дампов и т.п. небыло. Kernel OOM Killer вот
А это вообще не ловится в рамках данной задачи — если прибили всю Java VM, несмотря на её -Xmx-предел, значит, проблема где-то ещё, а не в джава-приложении.
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Истории