Як ви розбиваєте JVM?


144

Я читав книгу про навички програмування, в якій автор запитував респондента: "Як ви розбиваєте JVM?" Я думав, що ви можете це зробити, написавши нескінченний цикл for, який врешті-решт використає всю пам'ять.

Хтось має ідею?



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 шляхом ініціювання її послідовності відключення ... запускаються всі зареєстровані гачки відключення, якщо такі є ... всі запущені фіналізатори запущені" - це не правильне очищення?
користувач85421

51
Це не руйнує JVM, це цілеспрямовано і явно починає впорядковане припинення виконання.
БенМ

9
Ближче до збоїв jvm - Runtime.getRuntime (). Halt (статус). Згідно з документами, "цей метод не спричиняє запуск гачків відключення і не запускає незакликані фіналізатори, якщо фіналізація при виході була включена". Все ще не збій, але ближче, ніж System.exit.
Генрі

48
Це справді погана відповідь.
Антоніо

174

Я б не називав викид OutOfMemoryError або StackOverflowError аварією. Це лише звичайні винятки. Для справжнього виходу з ладу VM є 3 способи:

  1. Використовуйте JNI та зазнайте краху в нашому коді.
  2. Якщо не встановлено жодного менеджера безпеки, ви можете використовувати відображення для збою віртуального комп'ютера. Це специфічно для VM, але зазвичай VM зберігає купу вказівників на власні ресурси в приватних полях (наприклад, вказівник на власний об'єкт потоку зберігається у довгому полі в java.lang.Thread ). Просто змініть їх за допомогою відображення, і VM рано чи пізно вийде з ладу.
  3. У всіх віртуальних машинах є помилки, тож їх просто потрібно запустити.

Для останнього методу я маю короткий приклад, який невдало зірве VM Hotspot VM:

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

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

Це призводить до переповнення стека в GC, тому ви не отримаєте StackOverflowError, а справжній збій, включаючи файл hs_err *.


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 ГБ оперативної пам’яті / JDK1.7 64bit, але 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є, за визначенням, "небезпечним". Це трохи обман.
Гарячі лизання

1
Підтверджено роботу за допомогою getDeclaredFieldтрюку в JDK 8u131 на Linux x64, включаючи виробництво hs_err_pid*.logз a SIGSEGV.
Джессі Глік

Використання Unsafe- це не обман. ОП не шукає «чистого» рішення проблеми програмування. Йому потрібен jvm, щоб збитися якомога потворнішим способом. Робити неприємні рідні речі - це те, що може зробити це, і саме це і Unsafeробиться.
jvdneste

34

Я прийшов сюди, тому що я також зіткнувся з цим питанням у Страсному програмісті Чада Фаулера. Для тих, хто не має доступу до копії, питання формулюється як своєрідний фільтр / тест для кандидатів, які беруть інтерв'ю на посаду, яка вимагає "дійсно хороших програмістів Java".

Зокрема, він запитує:

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

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

  1. Після 15+ років на виробничому рівні JRE, це рідко.
  2. Будь-які подібні помилки, ймовірно, будуть виправлені в наступному випуску, тож наскільки ви, як програміст, натрапите на та запам’ятаєте подробиці поточного набору пробок JRE?

Як вже згадували інші, якийсь нативний код через 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 "навіть не змогли придумати неправильну відповідь", тобто не намагалися думати, коли цілий клас потенційних проблем. Підсумок: це запитання вирізняється питанням "Як запобігти збоям JVM?" що, очевидно, ще краще знати.
Шонзілла

1
Я б шукав людей, які просто відповідають (як і більшість тут) "переповнюють стек", system.exit () або інше "нормальне" відключення, оскільки вони насправді не розуміють JVM або слово "Збій". Визнати (як ви робили), що це дуже ненормально - це досить хороший спосіб визначити більш просунутого програміста. Я погоджуюся з вашим першим твердженням, я вважаю це прекрасним і цілком справедливим питанням, яке потрібно ставити або запитувати - ті, без конкретних відповідей, завжди найкращі.
Білл К

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
Це кидає InternalErrorв JDK 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, і включає повний код.
Гарячі лизання

Я погоджуюся з тим, що голосова акція є невиправданою - це справжня аварія, але як би ви відповіли на питання інтерв'ю. Якщо ви раніше не досліджували та запам'ятовували це, ви не могли б дати це як відповідь в інтерв'ю, і якщо ви могли б, я б вважав, що все одно буде нейтральним до поганим, - це не показує, як би ви підійшли до проблеми. Я очікую, що ви зробили Google для використання jvm-помилок і реалізували те, що є відмінною відповіддю.
Білл К

@BillK - Ні, все вище - це моя власна робота, хоча я придумав це кілька років тому, експериментуючи з різними речами. В інтерв'ю я, швидше за все, сказав: "Я це зробив, використовуючи спробу / ловіння та рекурсію, але я не можу прямо зараз відточити точний код".
Гарячі лизання

1
Які деталі (версія JDK, будь-які аргументи VM)? Не впевнений, що таке версія "11.2-b0". Я працюю цим, але він споживає лише багато процесора.
Стефан Рейх

@Hot Licks: Що таке концепція на прикладі краху JVM? Чому JVM пробивається через код. Усі
теми

15

Ідеальна реалізація JVM ніколи не вийде з ладу.

Щоб розбити JVM, окрім JNI, вам потрібно знайти помилку в самому ВМ. Нескінченний цикл просто споживає процесор. Нескінченно розподілення пам'яті повинно просто викликати OutOfMemoryError у добре побудованому JVM. Це, ймовірно, спричинить проблеми для інших потоків, але хороший JVM все одно не повинен виходити з ладу.

Якщо ви можете знайти помилку у вихідному коді VM і, наприклад, викликати помилку сегментації у використанні пам'яті реалізації VM, то ви можете насправді її зламати.


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). :)


6

У книзі " Віртуальна машина Java " Джона Мейєра є приклад серії інструкцій по байт-кодам, які спричинили JVM до дампу ядра. Я не можу знайти свою копію цієї книги. Якщо хтось там є, будь ласка, подивіться його та опублікуйте відповідь.


5

на winxpsp2 w / wmp10 jre6.0_7

Desktop.open (uriToAviOrMpgFile)

Це призводить до того, що породжена нитка викине невловимий метальний і вибиває точку доступу

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
Quazi Irfan

5

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

Ви можете зупинити JVM, зателефонувавши

Runtime.getRuntime().halt( status )

За даними:

"цей метод не спричиняє запуск гачок відключення та не запускає незвані фіналізатори, якщо фіналізація при виході була включена".



4

Якщо ви хочете зробити вигляд, що у вас закінчилося пам'ять, ви можете це зробити

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

Я знаю пару способів викликати скидання файлу помилок JVM шляхом виклику власних методів (вбудованих), але, мабуть, найкраще ви не знаєте, як це зробити. ;)


4

Якщо ви визначите збій як переривання процесу через нездійснену ситуацію (тобто відсутність виключення Java або помилка), це неможливо зробити зсередини Java (якщо ви не маєте дозволу на використання класу sun.misc.Unsafe). У цьому вся суть керованого коду.

Типові збої в нативному коді трапляються шляхом відсилання вказівників на неправильні області пам’яті (нульова адреса або неправильне налаштування). Іншим джерелом можуть бути незаконні інструкції на машині (опкоди) або необроблені сигнали з викликів бібліотеки або ядра. І те й інше може бути спрацьовано, якщо JVM або системні бібліотеки мають помилки.

Наприклад, код JITed (згенерований), нативні методи або системні виклики (графічний драйвер) можуть мати проблеми, що призводять до реальних збоїв (досить часто трапляються збої, коли ви використовуєте 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:
# 

1

Якщо під «крахом» ви маєте на увазі різкий перерив JVM, такий як, що призведе до того, що JVM запише його hs_err_pid% p.log , ви можете зробити це таким чином.

Встановіть аргумент-Xmx на крихітне значення та скажіть JVM, щоб примусити збій на outfememory:

 -Xmx10m -XX:+CrashOnOutOfMemoryError

Щоб було зрозуміло, без другого аргументу вище, це просто призведе до того, що jvm закінчиться OutOfMemoryError, але він не "збоїться" або різко скасує jvm.

Ця методика виявилася корисною, коли я намагався протестувати аргумент JVM -XX: ErrorFile, який контролює, де такий запис hs_err_pid повинен бути записаний. Я знайшов цю посаду тут, намагаючись знайти способи змусити такий збій. Коли згодом я виявив, що вищезазначене працювало як найлегше для моєї потреби, я хотів додати його до списку тут.

Нарешті, FWIW, якщо хтось може перевірити це, коли у них вже встановлено значення -Xms у ваших аргументах (на якесь більше значення, ніж вище), ви також захочете видалити або змінити це, інакше ви отримаєте не збій, а просто невдача запуску jvm, повідомляючи про "Початковий розмір купи, встановлений на велике значення, ніж максимальний розмір купи". (Це не було б очевидно, якби запустити JVM як службу, наприклад, із деякими серверами додатків. Знову ж таки, це мене покусало, тому я хотів ним поділитися.)


0

Якщо ви зміните цей нескінченний цикл на рекурсивний виклик тієї самої функції, ви отримаєте виняток переповнення стека:

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

public void causeStackOverflow() {
    causeStackOverflow();
}

0

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


Почніть перевіряти обладнання, особливо пам'ять!
Thorbjørn Ravn Andersen

На жаль, це на кількох машинах, які в іншому випадку працюють просто чудово. Це робить лише це одне конкретне додаток (і це не пам'ять чи процесор).
Брайан Кноблауш

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з повідомленням знищення поточного процесу не дозволено .


0

Натрапили на цю проблему при спробі повторити аварію JVM.

Jni працює, але його потрібно налаштувати для різних платформ. Врешті-решт, я використовую цю комбінацію, щоб зробити аварію JVM

  1. Запустіть програму за допомогою цих параметрів JVM -XX:+CrashOnOutOfMemoryError
  2. Використовуйте a, long[] l = new long[Integer.MAX_VALUE];щоб запустити OOM

Тоді JVM вийде з ладу і генерує журнал аварій.


-2

Якщо "Збій" - це щось, що перериває jvm / програму від нормального завершення, то це може зробити виняток Un-handled.

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
  }

Отже, це залежить від того, який тип КРАНУ ?!


3
Якщо викинути виняток, це не збій.
Куазі Ірфан

Винятки з безрегламентованого виконання - це збої, але ArithmeticExceptionце
середина
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.