Как вы терпите крах JVM? - PullRequest
140 голосов
/ 15 сентября 2008

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

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

Ответы [ 25 ]

171 голосов
/ 16 сентября 2008

Я бы не назвал сброс 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 *.

122 голосов
/ 15 сентября 2008

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

55 голосов
/ 04 сентября 2009

Используйте это:

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 :. Крах

31 голосов
/ 24 июля 2012

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

20 голосов
/ 17 мая 2010

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

import sun.dc.pr.PathDasher; 

public class Crash
{
     public static void main(String[] args)
     {    
        PathDasher dasher = new PathDasher(null) ;
     }
}
17 голосов
/ 21 февраля 2015

В последний раз я пытался это сделать:

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
14 голосов
/ 15 сентября 2008

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

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

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

13 голосов
/ 03 сентября 2011

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

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

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

10 голосов
/ 15 сентября 2008

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

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

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

6 голосов
/ 15 сентября 2008

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...