Czytałem książkę o umiejętnościach programistycznych, w której autor pyta rozmówcę: „Jak załamać JVM?” Pomyślałem, że możesz to zrobić, pisząc nieskończoną pętlę for, która ostatecznie zużyje całą pamięć.
Czy ktoś ma jakiś pomysł?
Czytałem książkę o umiejętnościach programistycznych, w której autor pyta rozmówcę: „Jak załamać JVM?” Pomyślałem, że możesz to zrobić, pisząc nieskończoną pętlę for, która ostatecznie zużyje całą pamięć.
Czy ktoś ma jakiś pomysł?
Odpowiedzi:
Najbliższą pojedynczą „odpowiedzią” jest System.exit()
natychmiastowe zamknięcie maszyny JVM bez odpowiedniego czyszczenia. Ale poza tym najbardziej prawdopodobnymi odpowiedziami są kod natywny i wyczerpanie zasobów. Alternatywnie możesz poszukać w module śledzenia błędów firmy Sun błędów w Twojej wersji maszyny JVM, z których niektóre pozwalają na powtarzalne scenariusze awarii. Kiedyś otrzymywaliśmy pół-regularne awarie, gdy zbliżaliśmy się do limitu pamięci 4 Gb w wersjach 32-bitowych (zwykle używamy teraz 64-bitów).
Nie nazwałbym wyrzucania OutOfMemoryError lub StackOverflowError awarią. To są zwykłe wyjątki. Aby naprawdę zawiesić maszynę wirtualną, istnieją 3 sposoby:
W przypadku ostatniej metody mam krótki przykład, który spowoduje ładną awarię maszyny wirtualnej Sun Hotspot:
public class Crash {
public static void main(String[] args) {
Object[] o = null;
while (true) {
o = new Object[] {o};
}
}
}
Prowadzi to do przepełnienia stosu w GC, więc nie otrzymasz StackOverflowError, ale prawdziwy błąd, w tym plik hs_err *.
java.lang.OutOfMemoryError: GC overhead limit exceeded
JNI . W rzeczywistości w przypadku JNI zawieszanie się jest domyślnym trybem działania. Musisz bardzo ciężko pracować, aby się nie zawiesił.
Użyj tego:
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();
}
}
Ta klasa musi znajdować się w ścieżce startowej klasy, ponieważ używa zaufanego kodu, więc uruchom w ten sposób:
java -Xbootclasspath / p :. Wypadek
Field f = Unsafe.class.getDeclaredField( "theUnsafe" ); f.setAccessible( true ); unsafe = (Unsafe) f.get( null );
dla mnie zadziałało świetnie.
Unsafe
jest z definicji „niebezpieczny”. To trochę oszustwo.
getDeclaredField
sztuczki w JDK 8u131 na Linux x64, w tym produkcję hs_err_pid*.log
z pliku SIGSEGV
.
Unsafe
nie jest oszustwem. OP nie szuka „czystego” rozwiązania problemu programistycznego. Potrzebuje jvm do awarii w najbrzydszy możliwy sposób. Robienie paskudnych, rodzimych rzeczy jest tym, co może do tego doprowadzić, i właśnie tak Unsafe
jest.
Przyszedłem tutaj, ponieważ natknąłem się na to pytanie w The Passionate Programmer autorstwa Chada Fowlera. Dla tych, którzy nie mają dostępu do kopii, pytanie to jest traktowane jako rodzaj filtru / testu dla kandydatów przeprowadzających rozmowy kwalifikacyjne na stanowisko wymagające „naprawdę dobrych programistów Java”.
W szczególności pyta:
Jak napisałeś program w czystej Javie, który powodowałby awarię wirtualnej maszyny Java?
Programuję w Javie od ponad 15 lat i uznałem to pytanie za zagadkowe i niesprawiedliwe. Jak zauważyli inni, Java, jako język zarządzany, została specjalnie zaprojektowana, aby nie ulegać awariom . Oczywiście zawsze są błędy JVM, ale:
Jak wspominali inni, część kodu natywnego za pośrednictwem JNI to pewny sposób na awarię środowiska JRE. Ale autor wyraźnie wspomniał w czystej Javie , więc to się skończyło.
Inną opcją byłoby podanie fałszywych kodów bajtowych środowiska JRE; dość łatwo jest zrzucić trochę śmieciowych danych binarnych do pliku .class i poprosić środowisko JRE o jego uruchomienie:
$ echo 'crap crap crap' > crap.class
$ java crap
Exception in thread "main" java.lang.ClassFormatError: Incompatible magic value 1668440432 in class file crap
To się liczy? Mam na myśli to, że samo środowisko JRE nie uległo awarii; poprawnie wykrył fałszywy kod, zgłosił go i zakończył pracę.
To pozostawia nam najbardziej oczywiste rodzaje rozwiązań, takie jak przepuszczanie stosu przez rekurencję, wyczerpywanie się pamięci sterty przez alokacje obiektów lub po prostu rzucanie RuntimeException
. Ale to po prostu powoduje zamknięcie środowiska JRE z StackOverflowError
lub podobnym wyjątkiem, co ponownie nie jest tak naprawdę awarią .
Więc co zostało? Bardzo chciałbym usłyszeć, co autor miał na myśli jako właściwe rozwiązanie.
Aktualizacja : odpowiedział tutaj Chad Fowler .
PS: to poza tym świetna książka. Podniosłem go dla moralnego wsparcia podczas nauki Rubiego.
Ten kod spowoduje awarię maszyny JVM w nieprzyjemny sposób
import sun.dc.pr.PathDasher;
public class Crash
{
public static void main(String[] args)
{
PathDasher dasher = new PathDasher(null) ;
}
}
InternalError
JDK 1.8. JVM już nie zawodzi.
Ostatni raz, gdy próbowałem, zrobił to:
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();
}
}
}
Pierwsza część wygenerowanego pliku dziennika:
#
# 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
Idealna implementacja JVM nigdy nie ulegnie awarii.
Aby zawiesić maszynę JVM, oprócz JNI, musisz znaleźć błąd w samej maszynie wirtualnej. Nieskończona pętla po prostu zużywa procesor. Nieskończone przydzielanie pamięci powinno po prostu powodować błędy OutOfMemoryError w dobrze zbudowanej JVM. Prawdopodobnie spowodowałoby to problemy dla innych wątków, ale dobra JVM nadal nie powinna ulec awarii.
Jeśli możesz znaleźć błąd w kodzie źródłowym maszyny wirtualnej i na przykład spowodować błąd segmentacji w wykorzystaniu pamięci przez implementację maszyny wirtualnej, możesz ją faktycznie zawiesić.
Jeśli chcesz zawiesić maszynę JVM - użyj w Sun JDK 1.6_23 lub niższej:
Double.parseDouble("2.2250738585072012e-308");
Wynika to z błędu w Sun JDK - znalezionego również w OpenJDK. Jest to naprawione od Oracle JDK 1.6_24 i nowszych.
Zależy, co masz na myśli mówiąc o wypadku.
Możesz wykonać nieskończoną rekurencję, aby zabrakło miejsca na stosie, ale to się zawiesi "wdzięcznie". Dostaniesz wyjątek, ale sama maszyna JVM będzie obsługiwać wszystko.
Możesz również użyć JNI do wywołania kodu natywnego. Jeśli nie zrobisz tego dobrze, możesz sprawić, że się zawiesi. Debugowanie tych awarii jest „zabawne” (zaufaj mi, musiałem napisać dużą bibliotekę DLL C ++, którą wywołujemy z podpisanego apletu java). :)
Książka Java Virtual Machine autorstwa Jona Meyera zawiera przykład serii instrukcji kodu bajtowego, które spowodowały zrzut pamięci JVM. Nie mogę znaleźć mojej kopii tej książki. Jeśli ktoś tam ma, poszukaj go i opublikuj odpowiedź.
na winxpsp2 w / wmp10 jre6.0_7
Desktop.open (uriToAviOrMpgFile)
Powoduje to, że pojawiający się wątek rzuca niewyłapany przedmiot do rzucania i powoduje awarię hotspotu
YMMV
Zepsuty sprzęt może spowodować awarię dowolnego programu. Kiedyś miałem powtarzalną awarię aplikacji na konkretnym komputerze, podczas gdy działała dobrze na innych komputerach z dokładnie taką samą konfiguracją. Okazuje się, że maszyna miała wadliwą pamięć RAM.
najkrótsza możliwa droga :)
public class Crash
{
public static void main(String[] args)
{
main(args);
}
}
Exception in thread "main" java.lang.StackOverflowError at Test.main
. Używam jdk1.8.0_65
Nie awaria, ale bliżej awarii niż akceptowana odpowiedź użycia System.exit
Możesz zatrzymać JVM, dzwoniąc
Runtime.getRuntime().halt( status )
Według dokumentów: -
„ta metoda nie powoduje uruchomienia przechwytywania zamykania systemu i nie uruchamia nie wywołanych finalizatorów, jeśli włączono finalizację przy wyjściu”.
tutaj jest szczegółowe wyjaśnienie, co powoduje zrzut pamięci JVM (tj. awarię): http://kb.adobe.com/selfservice/viewContent.do?externalId=tn_17534
Jeśli chcesz udawać, że zabrakło Ci pamięci, możesz to zrobić
public static void main(String[] args) {
throw new OutOfmemoryError();
}
Znam kilka sposobów spowodowania zrzucenia przez maszynę JVM pliku błędu przez wywołanie metod natywnych (takich, które są wbudowane), ale prawdopodobnie najlepiej nie wiesz, jak to zrobić. ;)
Jeśli zdefiniujesz awarię jako przerwanie procesu z powodu nieobsługiwanej sytuacji (tj. Braku wyjątku Java lub błędu), nie można tego zrobić z poziomu Javy (chyba że masz uprawnienia do używania klasy sun.misc.Unsafe). To cały punkt zarządzanego kodu.
Typowe awarie w kodzie natywnym zdarzają się przez usuwanie odniesień do niewłaściwych obszarów pamięci (adres zerowy lub niewłaściwie wyrównany). Innym źródłem mogą być nielegalne instrukcje maszynowe (kody operacyjne) lub nieobsłużone sygnały z wywołań biblioteki lub jądra. Oba mogą zostać wyzwolone, jeśli JVM lub biblioteki systemowe zawierają błędy.
Na przykład kod JITed (wygenerowany), metody natywne lub wywołania systemowe (sterownik grafiki) mogą mieć problemy prowadzące do rzeczywistych awarii (dość często dochodziło do awarii, gdy korzystałeś z funkcji ZIP i zabrakło im pamięci). W takich przypadkach program obsługi awarii maszyny JVM włącza się i zrzuca stan. Może również wygenerować plik rdzenia systemu operacyjnego (Dr. Watson w systemie Windows i zrzut rdzenia na * nix).
W systemie Linux / Unix możesz łatwo spowodować awarię JVM, wysyłając sygnał do uruchomionego procesu. Uwaga: nie należy tego używać SIGSEGV
, ponieważ Hotspot przechwytuje ten sygnał i w większości miejsc ponownie wyrzuca go jako wyjątek NullPointerException. Dlatego lepiej wysłać SIGBUS
np.
Jeśli utworzysz proces wątku, który nieskończenie odradza więcej wątków (które odradzają więcej wątków, które ...) ostatecznie spowoduje błąd przepełnienia stosu w samej 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();
}
}
To dało mi wyjście (po 5 minutach uważaj na swojego barana)
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:
#
Jeśli przez „awarię” masz na myśli nagłe przerwanie pracy maszyny JVM, na przykład w wyniku czego JVM zapisuje do swojego hs_err_pid% p.log , możesz to zrobić w ten sposób.
Ustaw argument-Xmx na niewielką wartość i powiedz JVM, aby wymusił awarię przy braku pamięci:
-Xmx10m -XX:+CrashOnOutOfMemoryError
Dla jasności, bez drugiego argumentu powyżej, spowodowałoby to po prostu zakończenie działania jvm przez OutOfMemoryError, ale nie spowodowałoby "awarii" ani nagłego przerwania działania jvm.
Technika ta okazała się pomocna, gdy próbowałem przetestować argument JVM -XX: ErrorFile, który kontroluje, gdzie taki dziennik hs_err_pid powinien zostać zapisany. Znalazłem ten post tutaj, próbując znaleźć sposoby na wymuszenie takiej awarii. Kiedy później stwierdziłem, że powyższe działa jako najłatwiejsze dla moich potrzeb, chciałem dodać je do listy tutaj.
Wreszcie, FWIW, jeśli ktokolwiek może to przetestować, gdy ma już ustawioną wartość -Xms w twoich argumentach (do jakiejś większej wartości niż powyżej), będziesz chciał to również usunąć lub zmienić, albo nie dostaniesz awarii, ale po prostu błąd uruchomienia jvm powodujący zgłoszenie „Początkowa wielkość sterty ustawiona na wartość większą niż maksymalna wielkość sterty”. (Nie byłoby to oczywiste, gdyby uruchomiono maszynę JVM jako usługę, na przykład w przypadku niektórych serwerów aplikacji. Ponownie, ugryzło mnie to, więc chciałem się tym podzielić.)
Jeśli zmienisz tę nieskończoną pętlę for na rekurencyjne wywołanie tej samej funkcji, otrzymasz wyjątek przepełnienia stosu:
public static void main(String[] args) {
causeStackOverflow();
}
public void causeStackOverflow() {
causeStackOverflow();
}
Robię to teraz, ale nie do końca wiem, jak ... :-) JVM (i moja aplikacja) czasami po prostu znikają. Nie wyrzucono żadnych błędów, nic nie zostało zarejestrowane. Przechodzi od pracy do całkowitego braku działania natychmiast bez ostrzeżenia.
Najkrótsza? Użyj klasy Robot, aby wywołać CTRL + BREAK. Zauważyłem to, gdy próbowałem zamknąć program bez zamykania konsoli (nie miał funkcji „wyjścia”).
Czy to się liczy?
long pid = ProcessHandle.current().pid();
try { Runtime.getRuntime().exec("kill -9 "+pid); } catch (Exception e) {}
Działa tylko dla Linuksa i Java 9.
Z jakiegoś powodu nie dostaję, ProcessHandle.current().destroyForcibly();
nie zabija JVM i wyrzuca java.lang.IllegalStateException
z komunikatem zniszczenie bieżącego procesu niedozwolone .
Napotykam ten problem podczas próby replikacji awarii maszyny JVM.
Jni działa, ale należy go dostosować do różnych platform. Ostatecznie używam tej kombinacji, aby spowodować awarię JVM
-XX:+CrashOnOutOfMemoryError
long[] l = new long[Integer.MAX_VALUE];
aby wywołać OOMNastępnie JVM ulegnie awarii i wygeneruje dziennik awarii.
Jeśli 'Crash' jest czymś, co przerywa działanie jvm / programu od normalnego zakończenia, to może to zrobić wyjątek nieobsługiwany.
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
}
Więc to zależy od typu CRASH ?!
ArithmeticException
jest