Проведя день с еще одним гейзенбагом, который, казалось, менял свою форму всякий раз, когда я приближался к делу, я подумал, что стоит извлечь уроки из этого случая.
Чтобы продемонстрировать это поведение, я написал упрощенный пример. В этом примере я инициализирую карту и начинаю добавлять пары ключ-значение на карту в неопределенном цикле:
class Wrapper { public static void main(String args[]) throws Exception { Map map = System.getProperties(); Random r = new Random(); while (true) { map.put(r.nextInt(), "value"); } } }
Как вы можете догадаться, компиляция и выполнение приведенного выше кода не могут хорошо закончиться. И действительно, когда запускается с:
java -Xmx100m -XX:+UseParallelGC Wrapper
Я сталкиваюсь с сообщением « java.lang.OutOfMemoryError: Превышен предел накладных расходов GC » в моей оболочке. Но при запуске с другим размером кучи или другим GC мой Mac OS X 10.9.2 с Oracle Hotspot JDK 1.7.0_45 решит умереть по-другому.
Например, при работе с меньшей кучей, такой как следующее:
java -Xmx10m -XX:+UseParallelGC Wrapper
приложение умрет с более знакомым сообщением « java.lang.OutOfMemoryError: Java heap space », выдаваемым при изменении размера Map.
А при работе с другими алгоритмами сборки мусора, кроме ParallelGC, такими как -XX: + UseConcMarkSweepGC или -XX: + UseG1GC , ошибка, с которой вы сталкиваетесь, будет перехвачена обработчиком исключений по умолчанию и не имеет трассировки стека, поскольку куча исчерпана до такой степени, что где трассировка стека даже не может быть заполнена при создании исключения:
My Precious:examples vladimir$ java -Xmx100m -XX:+UseConcMarkSweepGC Wrapper Exception: java.lang.OutOfMemoryError thrown from the UncaughtExceptionHandler in thread "main"
Мораль этой истории? Вы не можете выбрать способ, которым ваше приложение умрет, когда ресурсы заканчиваются, поэтому не основывайте свои ожидания на определенной последовательности действий. Как видно из приведенного выше примера, вы можете потерпеть неудачу тремя совершенно разными способами:
- Попадание к проверке безопасности, встроенной в GC: всякий раз, когда GC тратит более 98% времени в GC и мало что показывает (менее 2% очищенной кучи), JVM сдается с java.lang.OutOfMemoryError : Сообщение о превышении предельных значений GC превышено .
- В противном случае , чтобы выделить больше памяти для следующей операции: всякий раз , когда следующая команда пытается выделить больше памяти , когда в настоящее время доступны в куче, вы получите» java.lang.OutOfMemoryError: Java пространство кучи» сообщение
- Возможно, вы построили ситуацию, когда ваша память используется до такой степени, что JVM не может создать новый экземпляр OutOfMemoryError () , заполнить его трассировку стека и отправить выходные данные в поток печати. В этом случае ошибка будет обнаружена UncaughtExceptionHandler, а не обычными потоками управления. Этот обработчик, как следует из названия, вступит в игру, когда поток собирается завершиться из-за необработанного исключения. В этом случае виртуальная машина Java запросит у потока свой UncaughtExceptionHandler и вызовет метод обработчика uncaughtException .
Поэтому всякий раз, когда вы думаете, что это покрыто при обнаружении ошибок, указывающих на нехватку ресурсов, подумайте еще раз Система может быть в таком хрупком состоянии, что симптомы, на присутствие которых вы полагали, что можете положиться, исчезают или изменяются. И оставлю тебя с таким же ослепительным лицом, как и я, на последние 12 часов.
Если вы сделали это далеко с чтением, я могу только рекомендовать подписаться на нашу ленту Twitter . Мы продолжаем публиковать информацию о нашей настройке производительности на еженедельной основе. И держите маркетроидов подальше от канала. Честно говоря, эта умирающая вещь в заголовке связана с нашими маркетологами.