Как вы терпите крах JVM?

144

Я читал книгу о навыках программирования, в которой автор спрашивает интервьюируемого: «Как ты разбил JVM?» Я думал, что вы можете сделать это, написав бесконечный цикл for, который в конечном итоге израсходует всю память.

У кого-нибудь есть идеи?

Шивасубраманян А
источник
1
Возможный расширенный набор: stackoverflow.com/questions/6470651/…
Сиро Сантилли 郝海东 冠状 病 六四 事件 法轮功
stackoverflow.com/questions/30072883/… "Если я использую JDK1.8_40 или новее (Oracle или OpenJDK делают то же самое), следующий код вместе с изменением размера диалога приведет к сбою приложения (пробовал Windows 7, x64, 64-битный JDK)" - Код состоит всего из 40 строк, и это вызывает правильный сбой JVM.
Президент Dreamspace

Ответы:

6

Наиболее близким к единственному «ответу» является то, System.exit()что JVM немедленно завершает работу без надлежащей очистки. Но кроме этого, нативный код и исчерпание ресурсов являются наиболее вероятными ответами. В качестве альтернативы вы можете посмотреть на систему отслеживания ошибок Sun на предмет ошибок в вашей версии JVM, некоторые из которых допускают повторяющиеся сценарии сбоев. Мы привыкли получать полурегулярные сбои при приближении к пределу памяти 4 Гб в 32-битных версиях (сейчас мы обычно используем 64-битные).

Ли Колдуэлл
источник
71
без надлежащей очистки? Ты уверен? Документация гласит: «Завершает работающую в настоящее время виртуальную машину Java, инициируя ее последовательность выключения ... все зарегистрированные обработчики завершения работы, если таковые имеются, запускаются ... все непроверенные финализаторы запускаются» - разве это не правильная очистка?
user85421
51
Это не сбой JVM, это целенаправленное и явное начало упорядоченного завершения работы.
BenM
9
Ближе к сбою jvm является Runtime.getRuntime (). Halt (status). В соответствии с документацией «этот метод не приводит к запуску перехватчиков завершения работы и не запускает непроверенные финализаторы, если включена финализация при выходе». Все еще не сбой, но ближе, чем System.exit.
Генри
48
Это действительно плохой ответ.
Антонио
174

Я бы не назвал сброс OutOfMemoryError или StackOverflowError аварийным завершением. Это просто нормальные исключения. Чтобы действительно разбить ВМ, есть 3 способа:

  1. Используйте JNI и вылетите в нативный код.
  2. Если менеджер безопасности не установлен, вы можете использовать отражение для сбоя виртуальной машины. Это зависит от ВМ, но обычно ВМ хранит несколько указателей на собственные ресурсы в частных полях (например, указатель на объект собственного потока хранится в длинном поле в java.lang.Thread ). Просто измените их с помощью отражения, и виртуальная машина рано или поздно рухнет.
  3. Все виртуальные машины имеют ошибки, поэтому вам просто нужно их вызвать.

Для последнего метода у меня есть короткий пример, который тихо завершит работу виртуальной машины Sun Hotspot:

public class Crash {
    public static void main(String[] args) {
        Object[] o = null;

        while (true) {
            o = new Object[] {o};
        }
    }
}

Это приводит к переполнению стека в GC, поэтому вы получите не StackOverflowError, а реальный сбой, включая файл hs_err *.

Ralfs
источник
9
Вот Это Да! Это приводит к сбою Sun Java 5, Sun Java 6 и OpenJDK 6 (в Ubuntu 9.04) без файла hs_err *, а только с «Ошибка сегментации!» ...
Иоахим Зауэр
1
System.exit () - это гораздо более простой способ сбоя JVM (если не установлен менеджер безопасности)
instantsetsuna
4
Не знаю, когда это было исправлено, но только что протестировано в 1.7.0_09, и это нормально. Только что получил ожидаемое: Исключение в потоке "main" java.lang.OutOfMemoryError: Пространство кучи Java в CrashJVM.main (CrashJVM.java:7)
Чад, NB,
2
Я попробовал код выше на Intel Core i7 2,4 ГГц / 8 ГБ RAM / JDK1.7 64 бит, но JVM все еще работает через 20 минут. (Забавно: мой поклонник ноутбука был громче, чем истребитель в небе). Исправлена ​​ли эта проблема в JDK 1.7+?
realPK
3
В JDK 1.8_u71 это вызываетjava.lang.OutOfMemoryError: GC overhead limit exceeded
Clashsoft
56

Использовать это:

import sun.misc.Unsafe;

public class Crash {
    private static final Unsafe unsafe = Unsafe.getUnsafe();
    public static void crash() {
        unsafe.putAddress(0, 0);
    }
    public static void main(String[] args) {
        crash();
    }
}

Этот класс должен находиться в пути загрузки класса, потому что он использует доверенный код, поэтому запустите его так:

java -Xbootclasspath / p :. авария

Дэйв Гриффитс
источник
14
Вместо использования -Xbootclasspath вы также можете просто сделать это: Field f = Unsafe.class.getDeclaredField( "theUnsafe" ); f.setAccessible( true ); unsafe = (Unsafe) f.get( null );отлично сработало для меня.
Напористый
Unsafeпо определению "небезопасно". Это немного обмануть.
Hot Licks
1
Подтвердил работу с помощью getDeclaredFieldтрюка в JDK 8u131 на Linux x64, включая производство hs_err_pid*.logиз SIGSEGV.
Джесси Глик
Использование Unsafeэто не чит. OP не ищет «чистого» решения проблемы программирования. Ему нужен JVM, чтобы разбиться самым уродливым способом. Делать неприятные нативные вещи - вот что может сделать это, и это именно то, что Unsafeделает.
17
34

Я пришел сюда, потому что я также столкнулся с этим вопросом в Страстный программист » Чеда Фаулера. Для тех, у кого нет доступа к копии, вопрос оформлен как своего рода фильтр / тест для кандидатов, проходящих собеседование на должность, требующую «действительно хороших Java-программистов».

В частности, он спрашивает:

Как бы вы написали программу на чистом Java, которая вызывала бы сбой виртуальной машины Java?

Я программировал на Java более 15 лет, и я нашел этот вопрос одновременно озадачивающим и несправедливым. Как уже отмечали другие, Java, как управляемый язык, специально разработан, чтобы не падать . Конечно, всегда есть ошибки JVM, но:

  1. После 15 с лишним лет JRE на уровне производства это случается редко.
  2. Любые подобные ошибки, вероятно, будут исправлены в следующем выпуске, так какова вероятность того, что вы, как программист, столкнетесь и вспомните детали текущего набора JRE show-stoppers?

Как уже упоминали другие, некоторый нативный код через JNI - верный способ сбить JRE. Но автор специально упомянул в чистой Java , так что это не так.

Другим вариантом может быть подача фиктивных байтовых кодов JRE; достаточно просто скопировать некоторые двоичные данные мусора в файл .class и попросить JRE запустить его:

$ echo 'crap crap crap' > crap.class
$ java crap
Exception in thread "main" java.lang.ClassFormatError: Incompatible magic value 1668440432 in class file crap

Это считается? Я имею в виду, что сама JRE не разбилась; он правильно обнаружил поддельный код, сообщил об этом и вышел.

Это оставляет нам наиболее очевидные виды решений, такие как очистка стека с помощью рекурсии, исчерпание памяти кучи с помощью выделения объектов или просто выброс RuntimeException. Но это просто приводит к выходу JRE с StackOverflowErrorаналогичным исключением, что опять-таки не является крахом .

Так что осталось? Мне бы очень хотелось услышать, что автор действительно имел в виду как правильное решение.

Обновление : Чед Фаулер ответил здесь .

PS: в остальном это отличная книга. Я взял это для моральной поддержки, изучая Руби.

Джордж Армгольд
источник
1
Такие вопросы для собеседования служат лакмусовой бумажкой для разработчиков Java, которые достаточно долго были для того, чтобы 1) засвидетельствовать (много) сбоев JVM и 2) сделать некоторые выводы или, что еще лучше, поскольку (самопровозглашенные) Java-эксперты пытались понять основные причины аварии. Чед Фаулер также утверждает в своей книге, что самопровозглашенные Java-программисты «не могли даже придумать неправильный ответ», то есть не пытались думать о том, когда возникнет целый класс потенциальных проблем. Суть в том, что это вопрос segway к "Как предотвратить сбои JVM?" что явно еще лучше знать.
Shonzilla
1
Я бы искал людей, которые просто отвечают (как большинство здесь сделали) «переполнение стека», system.exit () или другого «нормального» выключения, так как они на самом деле не понимают JVM или слово «Crash». Признать (как вы это сделали), что это очень ненормально, - очень хороший способ определить более продвинутого программиста. Я согласен с вашим первым утверждением, я считаю, что это отличный и абсолютно справедливый вопрос, который нужно задать или задать - те, у которых нет конкретных ответов, всегда являются лучшими.
Билл К
20

Этот код приведет к аварийному завершению JVM

import sun.dc.pr.PathDasher; 

public class Crash
{
     public static void main(String[] args)
     {    
        PathDasher dasher = new PathDasher(null) ;
     }
}
Роб Мэйхью
источник
1
Получение ошибки компиляции при использовании JDK 1.7 с этим кодом: Ограничение доступа: Тип PathDasher недоступен из-за ограничения на требуемую библиотеку C: \ Program Files \ Java \ jdk1.7.0_51 \ jre \ lib \ rt.jar
realPK
7
Это добавляет InternalErrorJDK 1.8. JVM больше не выходит из строя.
Сотириос Делиманолис
18

В прошлый раз, когда я пытался это сделать это:

public class Recur {
    public static void main(String[] argv) {
        try {
            recur();
        }
        catch (Error e) {
            System.out.println(e.toString());
        }
        System.out.println("Ended normally");
    }
    static void recur() {
        Object[] o = null;
        try {
            while(true) {
                Object[] newO = new Object[1];
                newO[0] = o;
                o = newO;
            }
        }
        finally {
            recur();
        }
    }
}

Первая часть сгенерированного файла журнала:

#
# An unexpected error has been detected by Java Runtime Environment:
#
#  EXCEPTION_STACK_OVERFLOW (0xc00000fd) at pc=0x000000006dad5c3d, pid=6752, tid=1996
#
# Java VM: Java HotSpot(TM) 64-Bit Server VM (11.2-b01 mixed mode windows-amd64)
# Problematic frame:
# V  [jvm.dll+0x2e5c3d]
#
# If you would like to submit a bug report, please visit:
#   http://java.sun.com/webapps/bugreport/crash.jsp
#

---------------  T H R E A D  ---------------

Current thread (0x00000000014c6000):  VMThread [stack: 0x0000000049810000,0x0000000049910000] [id=1996]

siginfo: ExceptionCode=0xc00000fd, ExceptionInformation=0x0000000000000001 0x0000000049813fe8 

Registers:
EAX=0x000000006dc83090, EBX=0x000000003680f400, ECX=0x0000000005d40ce8, EDX=0x000000003680f400
ESP=0x0000000049813ff0, EBP=0x00000000013f2df0, ESI=0x00000000013f0e40, EDI=0x000000003680f400
EIP=0x000000006dad5c3d, EFLAGS=0x0000000000010206
Горячие лижет
источник
Я немного удивлен понижением голосов, учитывая, что это один из двух ответов, показывающих, как сделать это исключительно с помощью кода Java, и включает полный код.
Hot Licks
Я согласен, что отрицательное голосование неоправданно - это настоящий крах, но как бы вы ответили на вопрос интервью. Если вы ранее не исследовали и не запомнили это, вы не могли бы дать это в качестве ответа в интервью, и если бы вы могли, я бы в любом случае рассмотрел ответ от нейтрального до плохого - это не показывает, как вы подходите к проблеме. Я ожидаю, что вы сделали Google для эксплойтов с ошибками в jvm и реализовали один, который является отличным ответом.
Билл К
@BillK - Нет, все вышесказанное - моя собственная работа, хотя я придумал ее несколько лет назад, экспериментируя с разными вещами. В интервью я, скорее всего, скажу: «Я сделал это, используя try / catch и recursion, но сейчас я не могу набросать точный код».
Hot Licks
1
Какие детали (версия JDK, любые аргументы VM)? Не уверен, что версия "11.2-b0". Я запускаю это, но он потребляет только много процессора.
Стефан Райх,
@Hot Licks: Какова концепция в примере сбоя JVM? Почему JVM вылетает из-за кода. Все
потоки
15

Идеальная реализация JVM никогда не рухнет.

Для сбоя JVM, кроме JNI, вам нужно найти ошибку в самой виртуальной машине. Бесконечный цикл просто потребляет процессор. Бесконечное распределение памяти должно просто вызывать OutOfMemoryError в хорошо построенной JVM. Это может вызвать проблемы для других потоков, но хорошая JVM по-прежнему не должна аварийно завершать работу.

Если вы можете найти ошибку в исходном коде виртуальной машины и, например, вызвать ошибку сегментации при использовании памяти реализацией виртуальной машины, то вы можете ее фактически аварийно завершить.

Дэйв Л.
источник
14

Если вы хотите завершить работу JVM - используйте следующее в Sun JDK 1.6_23 или ниже:

Double.parseDouble("2.2250738585072012e-308");

Это связано с ошибкой в Sun JDK, также обнаруженной в OpenJDK. Это исправлено в Oracle JDK 1.6_24 и далее.

Прабат Сиривардена
источник
10

Зависит от того, что вы подразумеваете под крахом.

Вы можете выполнить бесконечную рекурсию, чтобы она исчерпала пространство стека, но это завершится «изящно». Вы получите исключение, но сама JVM будет обрабатывать все.

Вы также можете использовать JNI для вызова нативного кода. Если вы не сделаете это просто правильно, то вы можете заставить его сильно разбиться. Отладка этих сбоев - это «весело» (поверьте мне, мне пришлось написать большую C ++ DLL, которую мы вызываем из подписанного Java-апплета). :)

Herms
источник
6

В книге « Виртуальная машина Java» Джона Мейера приведен пример серии инструкций байт-кода, которые привели к дампу ядра JVM. Я не могу найти свою копию этой книги. Если у кого-то есть такой, пожалуйста, посмотрите его и отправьте ответ.

reevesy
источник
5

на winxpsp2 с wmp10 jre6.0_7

Desktop.open (uriToAviOrMpgFile)

Это приводит к тому, что порожденная нить выбрасывает неперехваченный Throwable и вылетает из точки доступа

YMMV


источник
5

Сломанное оборудование может привести к сбою любой программы. Однажды я воспроизводил аварийное завершение работы приложения на определенной машине, а на других машинах с точно такой же настройкой он работал нормально. Оказывается, у машины был неисправный ОЗУ.

Майкл Боргвардт
источник
5

кратчайший путь :)

public class Crash
{
    public static void main(String[] args)
    {
        main(args);
    }
}
РРМ
источник
Не вылетает Это дает ошибку времени компиляции Exception in thread "main" java.lang.StackOverflowError at Test.main. Я использую jdk1.8.0_65
Irfan
5

Не сбой, но ближе к падению, чем принятый ответ использования System.exit

Вы можете остановить JVM, позвонив

Runtime.getRuntime().halt( status )

Согласно документам:

msgstr "этот метод не приводит к запуску перехватчиков завершения работы и не запускает непроверенные финализаторы, если включена финализация при выходе".

Генри
источник
4

Если вы хотите притвориться, что у вас недостаточно памяти, вы можете сделать

public static void main(String[] args) {
    throw new OutOfmemoryError();
}

Я знаю несколько способов заставить JVM создавать дамп файла ошибок, вызывая нативные методы (встроенные), но лучше всего вы не знаете, как это сделать. ;)

Питер Лори
источник
4

Если вы определяете сбой как прерывание процесса из-за необработанной ситуации (то есть отсутствия исключений или ошибок Java), то это невозможно сделать из Java (если у вас нет разрешения на использование класса sun.misc.Unsafe). В этом весь смысл управляемого кода.

Типичные сбои в нативном коде происходят путем разыменования указателей на неправильные области памяти (нулевой адрес или неправильное выравнивание). Другим источником могут быть недопустимые машинные инструкции (коды операций) или необработанные сигналы от вызовов библиотеки или ядра. Оба могут быть запущены, если в JVM или в системных библиотеках есть ошибки.

Например, JIT-код (сгенерированный), нативные методы или системные вызовы (графический драйвер) могут иметь проблемы, приводящие к реальным сбоям (было довольно часто случаться сбой, когда вы использовали функции ZIP, и у них не хватало памяти). В этих случаях обработчик сбоя JVM запускает и выводит состояние. Он также может генерировать файл ядра ОС (Dr. Watson в Windows и дамп ядра в * nix).

В Linux / Unix вы можете легко вызвать сбой JVM, отправив ему сигнал запущенному процессу. Примечание: вы не должны использовать SIGSEGVдля этого, так как Hotspot ловит этот сигнал и повторно генерирует его как NullPointerException в большинстве мест. Так что лучше отправить, SIGBUSнапример.

Экес
источник
3

JNI является крупным источником сбоев. Вы также можете аварийно завершить работу, используя интерфейс JVMTI, так как он также должен быть написан на C / C ++.

Джаред
источник
2

Если вы создадите процесс потока, который бесконечно порождает больше потоков (которые порождают больше потоков, что ...), вы в конечном итоге вызовете ошибку переполнения стека в самой JVM.

public class Crash {
    public static void main(String[] args) {

        Runnable[] arr = new Runnable[1];
        arr[0] = () -> {

            while (true) {
                new Thread(arr[0]).start();
            }
        };

        arr[0].run();
    }
}

Это дало мне выходной сигнал (через 5 минут следи за своим бараном)

An unrecoverable stack overflow has occurred.
#
# A fatal error has been detected by the Java Runtime Environment:
#
#  EXCEPTION_STACK_OVERFLOW (0xc00000fd) at pc=0x0000000070e53ed7, pid=12840, tid=0x0000000000101078
#
# JRE version: Java(TM) SE Runtime Environment (8.0_144-b01) (build 1.8.0_144-b01)
# Java VM: Java HotSpot(TM) 64-Bit Server VM (25.144-b01 mixed mode windows-amd64 compressed oops)
# Problematic frame:
# 
Lightfire228
источник
1

Если под «аварийным завершением» вы имеете в виду внезапное прерывание работы JVM, например, из-за которого JVM записывает в свой hs_err_pid% p.log , вы можете сделать это следующим образом.

Установите для аргумента -Xmx значение крошечное и скажите JVM вызвать аварийное завершение работы вне памяти:

 -Xmx10m -XX:+CrashOnOutOfMemoryError

Чтобы было ясно, без второго арг выше, было бы просто привести к Jvm заканчивающейся с OutOfMemoryError, но это не будет «крах» или внезапно прервать JVM.

Этот метод оказался полезным, когда я пытался протестировать аргумент JVM -XX: ErrorFile, который определяет, где должен быть записан такой журнал hs_err_pid. Я нашел этот пост здесь, пытаясь найти способы вызвать такой крах. Когда позже я обнаружил, что вышеупомянутое работает как самый простой для моих нужд, я хотел добавить его в список здесь.

Наконец, FWIW, если кто-то может проверить это, когда в ваших аргументах уже установлено значение -Xms (на большее значение, чем указано выше), вы также захотите удалить или изменить это, иначе вы получите не крах, а просто сбой запуска jvm с сообщением «Начальный размер кучи установлен на большее значение, чем максимальный размер кучи». (Это было бы неочевидно, если бы JVM работала как служба, например, с некоторыми серверами приложений. Опять же, это меня поразило, поэтому я хотела поделиться им.)

Чарли Арехарт
источник
0

Если вы замените этот бесконечный цикл for на рекурсивный вызов той же функции, вы получите исключение переполнения стека:

public static void main(String[] args) {
    causeStackOverflow();
}

public void causeStackOverflow() {
    causeStackOverflow();
}
Майк Стоун
источник
0

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

Брайан Кноблаух
источник
Начните проверять ваше оборудование, особенно вашу память!
Торбьерн Равн Андерсен
К сожалению, это на нескольких машинах, которые в противном случае работают просто отлично. Это только одно конкретное приложение, которое делает это (и это не интенсивно использует память или процессор).
Брайан Кноблаух
0

Кратчайший? Используйте класс роботов, чтобы вызвать CTRL + BREAK. Я заметил это, когда пытался закрыть свою программу, не закрывая консоль (у нее не было функции выхода).


источник
Старый вопрос - надеюсь, кто-то выиграет от вашего ответа в будущем.
dbmitch
0

Это считается?

long pid = ProcessHandle.current().pid();
try { Runtime.getRuntime().exec("kill -9 "+pid); } catch (Exception e) {}

Работает только для Linux и Java 9.

По какой-то причине я не получаю, ProcessHandle.current().destroyForcibly();не убиваю JVM и бросаю java.lang.IllegalStateExceptionс сообщением, что уничтожение текущего процесса не разрешено .

mszmurlo
источник
0

Эта проблема возникает при попытке воспроизвести сбой JVM.

Jni работает, но его нужно настроить для разных платформ. В конце концов, я использую эту комбинацию, чтобы вызвать сбой JVM

  1. Запустите приложение с этими опциями JVM -XX:+CrashOnOutOfMemoryError
  2. Используйте long[] l = new long[Integer.MAX_VALUE];для запуска OOM

Затем JVM аварийно завершит работу и сгенерирует журнал сбоев.

Фрэнк Тен
источник
-2

Если «Crash» - это что-то, что прерывает jvm / программу от обычного завершения, то необработанное исключение может сделать это.

public static void main(String args[]){
   int i = 1/0;
   System.out.print(i); // This part will not be executed due to above  unhandled exception
  }

Итак, это зависит от того, какой тип CRASH ?!

Санкарганеш Ишваран
источник
3
Бросок исключения не является крахом.
Quazi Irfan
Необработанные исключения во время выполнения являются сбои, хотя, что ArithmeticExceptionэто
середина