it-swarm-ru.tech

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

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

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

140
Shivasubramanian A

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

5
Leigh Caldwell

Я бы не назвал сброс OutOfMemoryError или StackOverflowError аварийным завершением. Это просто нормальные исключения. Для реального сбоя VM есть 3 способа:

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

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

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

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

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

171
ralfs

JNI . Фактически, с JNI сбой является режимом работы по умолчанию. Вы должны работать очень усердно, чтобы не потерпеть крах.

122
Dan Dyer

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

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 :. авария

55
Dave Griffiths

Я пришел сюда, потому что также столкнулся с этим вопросом в Страстный программист Чеда Фаулера. Для тех, у кого нет доступа к копии, вопрос оформлен как своего рода фильтр/тест для кандидатов, проходящих собеседование на должность, требующую "действительно хороших 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: это отличная книга. Я взял это для моральной поддержки, изучая Руби.

31
George Armhold

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

import Sun.dc.pr.PathDasher; 

public class Crash
{
     public static void main(String[] args)
     {    
        PathDasher dasher = new PathDasher(null) ;
     }
}
20
Rob Mayhew

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

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
17
Hot Licks

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

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

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

14
Dave L.

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

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

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

13
Prabath Siriwardena

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

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

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

10
Herms

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

6
Soulfly

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

5
Michael Borgwardt

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

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

Runtime.getRuntime().halt( status )

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

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

5
henry

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

public class Crash
{
    public static void main(String[] args)
    {
        main(args);
    }
}
5
RRM

на winxpsp2 с wmp10 jre6.0_7

Desktop.open (uriToAviOrMpgFile)

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

YMMV

5
kitsuneymg

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

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

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

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

4
eckes

вот подробное объяснение причин, по которым JVM вызывает дамп ядра (например, сбой): http://kb.Adobe.com/selfservice/viewContent.do?externalId=tn_17534

4
COTOHA

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

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

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

3
Peter Lawrey

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

3
Jared

Если вы создадите процесс потока, который бесконечно порождает больше потоков (которые порождают больше потоков, что ...), вы в конечном итоге вызовете ошибку переполнения стека в самой 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:
# 
2
Lightfire228

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

1
user6022288

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

0
Brian Knoblauch

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

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 с сообщением уничтожение текущего процесса не разрешено.

0
mszmurlo