Я читал книгу о навыках программирования, в которой автор спрашивает интервьюируемого: «Как ты разбил JVM?» Я думал, что вы можете сделать это, написав бесконечный цикл for, который в конечном итоге израсходует всю память.
У кого-нибудь есть идеи?
Я читал книгу о навыках программирования, в которой автор спрашивает интервьюируемого: «Как ты разбил JVM?» Я думал, что вы можете сделать это, написав бесконечный цикл for, который в конечном итоге израсходует всю память.
У кого-нибудь есть идеи?
Ответы:
Наиболее близким к единственному «ответу» является то, System.exit()
что JVM немедленно завершает работу без надлежащей очистки. Но кроме этого, нативный код и исчерпание ресурсов являются наиболее вероятными ответами. В качестве альтернативы вы можете посмотреть на систему отслеживания ошибок Sun на предмет ошибок в вашей версии JVM, некоторые из которых допускают повторяющиеся сценарии сбоев. Мы привыкли получать полурегулярные сбои при приближении к пределу памяти 4 Гб в 32-битных версиях (сейчас мы обычно используем 64-битные).
Я бы не назвал сброс OutOfMemoryError или StackOverflowError аварийным завершением. Это просто нормальные исключения. Чтобы действительно разбить ВМ, есть 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 *.
java.lang.OutOfMemoryError: GC overhead limit exceeded
JNI . Фактически, с JNI сбой является режимом работы по умолчанию. Вы должны работать очень усердно, чтобы не потерпеть крах.
Использовать это:
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 :. авария
Field f = Unsafe.class.getDeclaredField( "theUnsafe" ); f.setAccessible( true ); unsafe = (Unsafe) f.get( null );
отлично сработало для меня.
Unsafe
по определению "небезопасно". Это немного обмануть.
getDeclaredField
трюка в JDK 8u131 на Linux x64, включая производство hs_err_pid*.log
из SIGSEGV
.
Unsafe
это не чит. OP не ищет «чистого» решения проблемы программирования. Ему нужен JVM, чтобы разбиться самым уродливым способом. Делать неприятные нативные вещи - вот что может сделать это, и это именно то, что Unsafe
делает.
Я пришел сюда, потому что я также столкнулся с этим вопросом в Страстный программист » Чеда Фаулера. Для тех, у кого нет доступа к копии, вопрос оформлен как своего рода фильтр / тест для кандидатов, проходящих собеседование на должность, требующую «действительно хороших Java-программистов».
В частности, он спрашивает:
Как бы вы написали программу на чистом Java, которая вызывала бы сбой виртуальной машины Java?
Я программировал на Java более 15 лет, и я нашел этот вопрос одновременно озадачивающим и несправедливым. Как уже отмечали другие, Java, как управляемый язык, специально разработан, чтобы не падать . Конечно, всегда есть ошибки JVM, но:
Как уже упоминали другие, некоторый нативный код через 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: в остальном это отличная книга. Я взял это для моральной поддержки, изучая Руби.
Этот код приведет к аварийному завершению JVM
import sun.dc.pr.PathDasher;
public class Crash
{
public static void main(String[] args)
{
PathDasher dasher = new PathDasher(null) ;
}
}
InternalError
JDK 1.8. JVM больше не выходит из строя.
В прошлый раз, когда я пытался это сделать это:
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
Идеальная реализация JVM никогда не рухнет.
Для сбоя JVM, кроме JNI, вам нужно найти ошибку в самой виртуальной машине. Бесконечный цикл просто потребляет процессор. Бесконечное распределение памяти должно просто вызывать OutOfMemoryError в хорошо построенной JVM. Это может вызвать проблемы для других потоков, но хорошая JVM по-прежнему не должна аварийно завершать работу.
Если вы можете найти ошибку в исходном коде виртуальной машины и, например, вызвать ошибку сегментации при использовании памяти реализацией виртуальной машины, то вы можете ее фактически аварийно завершить.
Если вы хотите завершить работу JVM - используйте следующее в Sun JDK 1.6_23 или ниже:
Double.parseDouble("2.2250738585072012e-308");
Это связано с ошибкой в Sun JDK, также обнаруженной в OpenJDK. Это исправлено в Oracle JDK 1.6_24 и далее.
Зависит от того, что вы подразумеваете под крахом.
Вы можете выполнить бесконечную рекурсию, чтобы она исчерпала пространство стека, но это завершится «изящно». Вы получите исключение, но сама JVM будет обрабатывать все.
Вы также можете использовать JNI для вызова нативного кода. Если вы не сделаете это просто правильно, то вы можете заставить его сильно разбиться. Отладка этих сбоев - это «весело» (поверьте мне, мне пришлось написать большую C ++ DLL, которую мы вызываем из подписанного Java-апплета). :)
В книге « Виртуальная машина Java» Джона Мейера приведен пример серии инструкций байт-кода, которые привели к дампу ядра JVM. Я не могу найти свою копию этой книги. Если у кого-то есть такой, пожалуйста, посмотрите его и отправьте ответ.
на winxpsp2 с wmp10 jre6.0_7
Desktop.open (uriToAviOrMpgFile)
Это приводит к тому, что порожденная нить выбрасывает неперехваченный Throwable и вылетает из точки доступа
YMMV
Сломанное оборудование может привести к сбою любой программы. Однажды я воспроизводил аварийное завершение работы приложения на определенной машине, а на других машинах с точно такой же настройкой он работал нормально. Оказывается, у машины был неисправный ОЗУ.
Не сбой, но ближе к падению, чем принятый ответ использования System.exit
Вы можете остановить JVM, позвонив
Runtime.getRuntime().halt( status )
Согласно документам:
msgstr "этот метод не приводит к запуску перехватчиков завершения работы и не запускает непроверенные финализаторы, если включена финализация при выходе".
Вот подробное объяснение причин, по которым JVM вызывает дамп ядра (т.е. сбой): http://kb.adobe.com/selfservice/viewContent.do?externalId=tn_17534
Если вы хотите притвориться, что у вас недостаточно памяти, вы можете сделать
public static void main(String[] args) {
throw new OutOfmemoryError();
}
Я знаю несколько способов заставить JVM создавать дамп файла ошибок, вызывая нативные методы (встроенные), но лучше всего вы не знаете, как это сделать. ;)
Если вы определяете сбой как прерывание процесса из-за необработанной ситуации (то есть отсутствия исключений или ошибок Java), то это невозможно сделать из Java (если у вас нет разрешения на использование класса sun.misc.Unsafe). В этом весь смысл управляемого кода.
Типичные сбои в нативном коде происходят путем разыменования указателей на неправильные области памяти (нулевой адрес или неправильное выравнивание). Другим источником могут быть недопустимые машинные инструкции (коды операций) или необработанные сигналы от вызовов библиотеки или ядра. Оба могут быть запущены, если в JVM или в системных библиотеках есть ошибки.
Например, JIT-код (сгенерированный), нативные методы или системные вызовы (графический драйвер) могут иметь проблемы, приводящие к реальным сбоям (было довольно часто случаться сбой, когда вы использовали функции ZIP, и у них не хватало памяти). В этих случаях обработчик сбоя JVM запускает и выводит состояние. Он также может генерировать файл ядра ОС (Dr. Watson в Windows и дамп ядра в * nix).
В Linux / Unix вы можете легко вызвать сбой JVM, отправив ему сигнал запущенному процессу. Примечание: вы не должны использовать SIGSEGV
для этого, так как Hotspot ловит этот сигнал и повторно генерирует его как NullPointerException в большинстве мест. Так что лучше отправить, SIGBUS
например.
Если вы создадите процесс потока, который бесконечно порождает больше потоков (которые порождают больше потоков, что ...), вы в конечном итоге вызовете ошибку переполнения стека в самой 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:
#
Если под «аварийным завершением» вы имеете в виду внезапное прерывание работы 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 работала как служба, например, с некоторыми серверами приложений. Опять же, это меня поразило, поэтому я хотела поделиться им.)
Если вы замените этот бесконечный цикл for на рекурсивный вызов той же функции, вы получите исключение переполнения стека:
public static void main(String[] args) {
causeStackOverflow();
}
public void causeStackOverflow() {
causeStackOverflow();
}
Я делаю это сейчас, но не совсем уверен, как ... :-) JVM (и мое приложение) иногда просто полностью исчезают. Нет ошибок, ничего не регистрируется. Переходит от работы к тому, чтобы вообще не бегать без предупреждения.
Кратчайший? Используйте класс роботов, чтобы вызвать CTRL + BREAK. Я заметил это, когда пытался закрыть свою программу, не закрывая консоль (у нее не было функции выхода).
Это считается?
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
с сообщением, что уничтожение текущего процесса не разрешено .
Эта проблема возникает при попытке воспроизвести сбой JVM.
Jni работает, но его нужно настроить для разных платформ. В конце концов, я использую эту комбинацию, чтобы вызвать сбой JVM
-XX:+CrashOnOutOfMemoryError
long[] l = new long[Integer.MAX_VALUE];
для запуска OOMЗатем JVM аварийно завершит работу и сгенерирует журнал сбоев.
Если «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 ?!
ArithmeticException
это