JVM'yi nasıl çökertiyorsunuz?


144

Programlama becerileri üzerine bir kitap okuyordum, burada yazar röportaj yapılan kişiye "JVM'yi nasıl çökertiyorsunuz?" Sonunda tüm belleği tüketen sonsuz bir for-loop yazarak bunu yapabileceğinizi düşündüm.

Herhangi bir fikri olan var mı?



stackoverflow.com/questions/30072883/… "JDK1.8_40 veya daha yenisini kullanırsam (Oracle veya OpenJDK aynı şeyi yaparsa), bir iletişim kutusu yeniden boyutlandırmasıyla birlikte aşağıdaki kod uygulamayı kilitler (Windows 7, x64, 64bit JDK'yı denedi)" - Kod sadece 40 satırdır ve JVM'nin düzgün çökmesine neden olur .
Düşler Uzay Başkanı

Yanıtlar:


6

Tek bir "cevap" a en yakın şey, System.exit()uygun temizlik olmadan JVM'yi hemen sonlandıran şeydir . Ancak bunun dışında, yerel kod ve kaynak tükenmesi en olası cevaplardır. Alternatif olarak, JVM sürümünüzdeki hatalar için Sun'ın hata izleyicisine bakabilirsiniz, bazıları tekrarlanabilir kilitlenme senaryolarına izin verir. 32 bit sürümlerde 4 Gb bellek sınırına yaklaşırken yarı düzenli çökmeler alırdık (genellikle 64 bit kullanıyoruz).


71
düzgün temizlik olmadan? Emin misiniz? Belgelerde "Şu anda çalışan Java sanal makinesini kapatma sırasını başlatarak sonlandırılıyor ... varsa tüm kayıtlı kapatma kancaları başlatılır ... tüm uyarılmamış sonlandırıcılar çalıştırılır" - doğru temizleme değil mi?
user85421

51
Bu JVM'yi çökertmiyor, amaca ve açıkça yürütmenin düzenli bir şekilde kapatılmasına başlıyor.
BenM

9
Jvm çökmesine yakın Runtime.getRuntime (). Durt (durum). Dokümanlara göre "bu yöntem kapatma kancalarının başlatılmasına neden olmaz ve çıkışta sonlandırma etkinleştirilmişse davetsiz sonlandırıcıları çalıştırmaz". Hala bir kilitlenme değil, System.exit'ten daha yakın.
henry

48
Bu gerçekten kötü bir cevap.
Antonio

174

Ben bir kilitlenme OutOfMemoryError veya StackOverflowError atma çağırmak olmaz. Bunlar sadece normal istisnalardır. Bir VM'yi gerçekten çökertmenin 3 yolu vardır:

  1. JNI kullanın ve yerel kodda kilitlenme.
  2. Yüklü bir güvenlik yöneticisi yoksa VM'yi çökertmek için yansıma kullanabilirsiniz. Bu, VM'ye özeldir, ancak normal olarak bir VM, özel alanlarda yerel kaynaklara bir grup işaretçi depolar (örneğin, yerel iş parçacığı nesnesine yönelik bir işaretçi java.lang.Thread içindeki uzun bir alanda saklanır ). Sadece yansıma yoluyla değiştirin ve VM er ya da geç çökecektir.
  3. Tüm VM'lerde hatalar var, bu yüzden sadece bir tane tetiklemelisiniz.

Son yöntem için güzel bir Sun Hotspot VM sessiz güzel çökecek kısa bir örnek var:

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

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

Bu, GC'de bir yığın taşmasına neden olur, böylece StackOverflowError değil, bir hs_err * dosyası içeren gerçek bir kilitlenme elde edersiniz.


9
Vaov! Bu, Sun Java 5, Sun Java 6 ve OpenJDK 6'yı (Ubuntu 9.04'te) hs_err * dosyası olmadan, yalnızca "Segmentasyon hatası!" ...
Joachim Sauer

1
System.exit () (bir güvenlik yöneticisinin yüklü olduğu sürece) bir JVM çökmesine çok daha kolay bir yoludur
instantsetsuna

4
Ne zaman sabitlendiğini bilmiyorum, ama sadece 1.7.0_09'da test edildi ve gayet iyi. Sadece beklenen: "ana" iş parçacığında istisna java.lang.OutOfMemoryError: CrashJVM.main'de Java yığın alanı (CrashJVM.java:7)
Chad NB

2
Yukarıdaki kodu Intel Core i7 2.4 GHz / 8 GB RAM / JDK1.7 64bit üzerinde denedim, ancak JVM 20 dakika sonra hala yükseliyor. (Komik: Dizüstü bilgisayarımın hayranı gökyüzündeki bir savaş uçağından daha yüksekti). Bu sorun JDK 1.7 ve sonraki sürümlerinde düzeltildi mi?
realPK

3
JDK 1.8_u71'de bu birjava.lang.OutOfMemoryError: GC overhead limit exceeded
Clashsoft


56

Bunu kullan:

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();
    }
}

Bu sınıf, güvenilir kod kullandığından önyükleme sınıfyolunda olmalıdır, bu nedenle şu şekilde çalıştırın:

java -Xbootclasspath / p :. kaza


14
-Xbootclasspath kullanmak yerine bunu da yapabilirsiniz: Field f = Unsafe.class.getDeclaredField( "theUnsafe" ); f.setAccessible( true ); unsafe = (Unsafe) f.get( null );Benim için harika çalıştı.
saldırgan

Unsafetanımı gereği "güvensiz" dir. Bu biraz hile.
Hot Licks

1
Kullanarak çalışan Onaylandı getDeclaredFieldüretimi de dahil olmak üzere, Linux x64 üzerinde JDK 8u131 yılında hile hs_err_pid*.logbir mesafede SIGSEGV.
Jesse Glick

Kullanmak Unsafebir hile değildir. OP bir programlama problemine 'temiz' bir çözüm aramıyor. Jvm'ye mümkün olan en çirkin şekilde çarpması gerekiyor. Kötü yerel şeyler yapmak, bunu gerçekleştirebilecek şeydir ve tam olarak ne Unsafeyapar.
jvdneste

34

Buraya geldim çünkü bu soruya Tutkulu Programcı'da da rastladım Çad Fowler tarafından . Bir kopyaya erişimi olmayanlar için, soru "gerçekten iyi Java programcıları" gerektiren bir pozisyon için mülakat yapan adaylar için bir tür filtre / test olarak çerçevelenir.

Özellikle şunu soruyor:

Saf Java'da, Java Sanal Makinesi'nin çökmesine neden olacak bir programı nasıl yazardınız?

Java'da 15 yılı aşkın bir süredir programladım ve bu soruyu hem şaşırtıcı hem de haksız buldum. Diğerleri işaret gibi, Java, yönetilen bir dil olarak, özel olarak tasarlanmıştır çökmesine değil . Tabii ki her zaman JVM hataları vardır, ancak:

  1. 15 yılı aşkın üretim seviyesi JRE'lerden sonra nadirdir.
  2. Bu tür hataların bir sonraki sürümde yamanması muhtemeldir, bu nedenle bir programcı olarak mevcut JRE show-stoper kümesinin ayrıntılarını çalıştırıp hatırlama olasılığınız nedir?

Diğerlerinin de belirttiği gibi, JNI üzerinden bazı yerel kodlar bir JRE'yi kilitlemenin kesin bir yoludur. Ancak yazar saf Java'da özellikle bahsetti , bu yüzden çıktı.

Başka bir seçenek JRE sahte bayt kodlarını beslemek olacaktır; bazı çöp ikili verilerini bir .class dosyasına dökmek ve JRE'den çalıştırmasını istemek kolaydır:

$ echo 'crap crap crap' > crap.class
$ java crap
Exception in thread "main" java.lang.ClassFormatError: Incompatible magic value 1668440432 in class file crap

Bu sayılır mı? Yani JRE'nin kendisi çökmedi; sahte kodu düzgün bir şekilde tespit etti, rapor etti ve çıkıldı.

Bu, yığını özyineleme yoluyla üflemek, nesne ayırma yoluyla yığın belleğinden tükenmek veya sadece atmak gibi en belirgin çözümlerle bizi terk eder RuntimeException. Ancak bu, JRE'nin bir StackOverflowErrorveya benzer bir istisna ile çıkmasına neden olur , bu da yine gerçekten bir çökme değildir .

Peki geriye ne kaldı? Yazarın gerçekten aklında ne olduğunu uygun bir çözüm olarak duymak isterim.

Güncelleme : Chad Fowler burada cevap verdi .

Not: bu başka türlü harika bir kitap. Ruby öğrenirken ahlaki destek almak için aldım.


1
Bu tür mülakat soruları, yaklaşık 1) yeterince uzun süren Java geliştiricileri için turnusol testi olarak hizmet etmektedir (1) JVM çökmesine tanıklık etmiş ve 2) bazı sonuçlar çıkarmış veya daha da iyisi (kendi kendini ilan etmiş) Java uzmanları çökmenin altında yatan nedenler. Chad Fowler ayrıca kitabında, kendi kendini ilan eden Java programcılarının "yanlış cevap bile bulamadıklarını", yani potansiyel bir sınıf sınıfının ne zaman düşünmeyi denemediklerini belirtiyor. Sonuç olarak, bu soru "JVM çökmelerini nasıl önleyebilirim?" ki bunu bilmek bile daha iyidir.
Shonzilla

1
Ben gerçekten JVM veya "Crash" kelimesini anlamıyor gibi (yığın burada taşma ", system.exit () veya diğer" Normal "kapatma sadece cevap insanlar arıyor. Bunun (sizin yaptığınız gibi) çok anormal olduğunu tanımak, daha gelişmiş bir programcıyı tanımlamak için oldukça iyi bir yoldur. İlk ifadenizi kabul ediyorum, sormak veya sormak için mükemmel ve tamamen adil bir soru bulurum - somut cevapları olmayanlar her zaman en iyisidir.
Bill K

20

Bu kod JVM'yi kötü şekillerde kilitleyecek

import sun.dc.pr.PathDasher; 

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

1
JDK 1.7'yi
realPK

7
Bu InternalErrorJDK 1.8'e bir atar . JVM artık başarısız oluyor.
Sotirios Delimanolis

18

Son kez denedim bunu yapardı:

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();
        }
    }
}

Oluşturulan günlük dosyasının ilk bölümü:

#
# 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

Bu tamamen Java kodu ile nasıl yapılacağını gösteren ve sadece tam kodu içeren sadece iki cevap biri göz önüne alındığında, downvote biraz eğleniyorum.
Hot Licks

Bir düşüşün haksız olduğunu kabul ediyorum - bu gerçek bir çökme, ancak röportaj sorusuna nasıl cevap vereceksiniz? Bunu daha önce araştırıp ezberlemediyseniz, röportajda bir cevap olarak veremezdiniz ve eğer yapabilseydiniz, yine de zayıf bir cevaba nötr olarak düşünürdüm - soruna nasıl yaklaşacağınızı göstermez. Yaptığınız şeyin jvm hata istismarları için google olduğunu ve mükemmel bir cevap olan birini uyguladığınızı umuyorum.
Bill K

@BillK - Hayır, yukarıdaki tamamen benim işim, ancak birkaç yıl önce çeşitli şeyleri denedim. Bir röportajda büyük olasılıkla "denedim / yakala ve özyineleme kullanarak yaptım, ama şimdi tam kodu tıkayamam" diyebilirim.
Hot Licks

1
Detaylar nedir (JDK sürümü, herhangi bir VM argümanı)? "11.2-b0" sürümünün ne olduğundan emin değilim. Bunu çalıştırıyorum, ancak sadece çok fazla CPU tüketiyor.
Stefan Reich

@Hot Licks: JVM çarpışma örneğindeki kavram nedir? JVM neden kod hakkında kilitleniyor. Tüm iplik ... ayrı yığın iş parçacığı var
VJS

15

Mükemmel bir JVM uygulaması asla çökmez.

Bir JVM'yi çökertmek için, JNI dışında, VM'nin kendisinde bir hata bulmanız gerekir. Sonsuz bir döngü sadece CPU tüketir. Kademesiz olarak bellek ayırmak, OutOfMemoryError'ın iyi inşa edilmiş bir JVM'de olmasına neden olur. Bu muhtemelen diğer iş parçacıkları için sorunlara neden olur, ancak iyi bir JVM yine de çökmemelidir.

VM'nin kaynak kodunda bir hata bulabilir ve örneğin VM'nin uygulanmasının bellek kullanımında bir segmentasyon hatasına neden olursanız, onu gerçekten çökertebilirsiniz.


14

JVM'yi kilitlemek istiyorsanız - Sun JDK 1.6_23 veya daha düşük sürümlerde aşağıdakileri kullanın:

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

Bu, Sun JDK'daki bir hatadan kaynaklanmaktadır - OpenJDK'da da bulunur. Bu, Oracle JDK 1.6_24'ten itibaren düzeltilmiştir.


10

Kaza ile ne demek istediğinize bağlı.

Yığın alanı bitmesini sağlamak için sonsuz bir özyineleme yapabilirsiniz, ancak bu "incelikle" çökecektir. Bir istisna alacaksınız, ancak JVM'nin kendisi her şeyi halledecek.

Yerel kodu aramak için JNI'yi de kullanabilirsiniz. Eğer doğru yapmazsanız, o zaman sert çökmesini sağlayabilirsiniz. Bu çökmeleri hata ayıklama "eğlenceli" (güven bana, imzalı bir java applet denilen büyük bir C ++ DLL yazmak zorunda kaldı). :)


6

Jon Meyer'ın Java Sanal Makinesi kitabında , JVM'nin çekirdek dökümü yapmasına neden olan bir dizi bayt kodu talimatı örneği var. Bu kitabın kopyasını bulamıyorum. Eğer dışarıda biri varsa lütfen bakın ve cevabı gönderin.


5

winxpsp2 üzerinde wmp10 jre6.0_7

Desktop.open (uriToAviOrMpgFile)

Bu, yumurtlanmış bir ipliğin yakalanmamış bir Fırlatılabilir atmasına neden olur ve etkin noktayı çöker

YMMV


5

Bozuk donanım herhangi bir programı kilitleyebilir. Bir kez aynı kurulumla diğer makinelerde iyi çalışırken belirli bir makinede tekrarlanabilir bir uygulama çökmesi yaşadım. Makinenin RAM'in hatalı olduğu ortaya çıktı.


5

mümkün olan en kısa yol :)

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

Çökmez. Derleme zamanı hatası verir Exception in thread "main" java.lang.StackOverflowError at Test.main. Ben jdk1.8.0_65 kullanıyorum
Quazi Irfan

5

Bir kilitlenme değil, bir kilitlenmeye kullanımın kabul edilen cevabından daha yakın System.exit

JVM'yi arayarak durdurabilirsiniz

Runtime.getRuntime().halt( status )

Dokümanlara göre: -

msgstr "bu yöntem kapatma kancalarının başlatılmasına neden olmaz ve çıkışta sonlandırma etkinleştirilmişse davetsiz sonlandırıcıları çalıştırmaz".



4

Eğer hafızanız bitmiş gibi davranmak isterseniz,

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

JVM yerel yöntemleri (yerleşik olanları) çağırarak bir hata dosyası dökümü neden birkaç yolu biliyorum, ama muhtemelen en iyi nasıl bunu bilmiyorum. ;)


4

Bir kilitlenmeyi işlenmeyen bir durum nedeniyle (örneğin Java İstisnası veya Hatası yok) işlem durdurma olarak tanımlarsanız, Java içinden (sun.misc.Unsafe sınıfını kullanma izniniz yoksa) bu yapılamaz. Bu yönetilen kodun bütün mesele.

Yerel koddaki tipik çökmeler, işaretçilerin yanlış bellek alanlarına (boş adres veya yanlış hizalanmış) yönlendirilmesiyle oluşur. Başka bir kaynak, yasadışı makine talimatları (opcodes) veya kütüphane veya çekirdek çağrılarından gelen işlenmemiş sinyaller olabilir. JVM veya sistem kitaplıklarında hata varsa her ikisi de tetiklenebilir.

Örneğin, JITed (oluşturulan) kod, yerel yöntemler veya sistem çağrıları (grafik sürücüsü) gerçek çökmelere neden olan sorunlara sahip olabilir (ZIP işlevlerini kullandığınızda ve bellek tükendiğinde oldukça yaygındı). Bu durumlarda JVM'nin kaza işleyicisi devreye girer ve durumu boşaltır. Ayrıca bir işletim sistemi çekirdek dosyası da oluşturabilir (Windows'ta Dr. Watson ve * nix'te çekirdek dökümü).

Linux / Unix'te, bir JVM çökmesini çalışan işleme bir Sinyal göndererek kolayca yapabilirsiniz. Not: SIGSEGVHotspot bu sinyali yakaladığı ve çoğu yerde NullPointerException olarak yeniden attığı için bunun için kullanmamalısınız. Yani bir SIGBUSörnek göndermek daha iyidir .


3

JNI büyük bir çökme kaynağıdır. Ayrıca C / C ++ ile yazılması gerektiğinden JVMTI arayüzünü kullanarak da çökebilirsiniz.


2

Sonsuz olarak daha fazla iş parçacığı (daha fazla iş parçacığı oluşturur, hangi ...) üreten bir iş parçacığı işlemi oluşturursanız, sonunda JVM kendisi bir yığın taşması hatasına neden olur.

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();
    }
}

Bu bana çıktı verdi (5 dakika sonra koçunuzu izleyin)

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

"Çökme" ile JVM'nin aniden iptal edilmesi anlamına gelirse, örneğin JVM'nin hs_err_pid% p.log dosyasına yazmasına neden olur, bunu bu şekilde yapabilirsiniz.

-Xmx argümanı küçük bir değere ayarlayın ve JVM'ye outofmemory'de kilitlenmeye zorlamasını söyleyin:

 -Xmx10m -XX:+CrashOnOutOfMemoryError

Açık olmak gerekirse, yukarıdaki ikinci arg olmadan, jvm bir OutOfMemoryError ile sonlanır , ancak jvm'i "çökmez" veya aniden iptal etmez.

Bu teknik, böyle bir hs_err_pid günlüğü nerede yazılması gerektiğini denetleyen JVM -XX: ErrorFile arg sınama çalışırken yararlı oldu. Böyle bir kazayı zorlamanın yollarını bulmaya çalışırken bu yazıyı burada bulmuştum. Daha sonra yukarıdakilerin ihtiyacım için en kolay olduğunu düşündüğümde, bu listeye eklemek istedim.

Son olarak, FWIW, herhangi biri, argümanlarınızda zaten bir -Xms değeri ayarlandığında (yukarıdakinden daha büyük bir değere) bunu test edebilirse, bunu da kaldırmak veya değiştirmek isteyeceksiniz veya bir kilitlenme değil, sadece basitçe jvm başlatılamıyorsa, "İlk yığın boyutu maksimum yığın boyutundan daha büyük bir değere ayarlandı" bildiriliyor. (JVM'yi bazı uygulama sunucuları gibi hizmet olarak çalıştırıyorsanız açık olmaz. Yine beni ısırdı, bu yüzden paylaşmak istedim.)


0

Bu sonsuz döngü için aynı işleve özyinelemeli çağrı olarak değiştirirseniz, yığın taşması istisnası alırsınız:

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

public void causeStackOverflow() {
    causeStackOverflow();
}

0

Şimdi yapıyorum, ama nasıl emin değilim ... :-) JVM (ve benim app) bazen sadece tamamen kaybolur. Hiçbir hata atılamadı, hiçbir şey kaydedilmedi. Herhangi bir uyarı olmadan çalışmadan anında çalışmamaya geçer.


Donanımınızı, özellikle de belleği kontrol etmeye başlayın!
Thorbjørn Ravn Andersen

Ne yazık ki, aksi takdirde gayet iyi çalışan birden fazla makinede. Bunu yapan sadece bu belirli bir uygulama (ve bellek veya işlemci yoğun değil).
Brian Knoblauch

0

En kısa? CTRL + BREAK tetiklemek için Robot sınıfını kullanın. Konsolu kapatmadan programımı kapatmaya çalışırken bunu fark ettim ('çıkış' işlevselliği yoktu).


Eski soru - Umarım birisi gelecekte cevabınızdan yararlanır.
dbmitch

0

Bu önemli mi?

long pid = ProcessHandle.current().pid();
try { Runtime.getRuntime().exec("kill -9 "+pid); } catch (Exception e) {}

Yalnızca Linux ve Java 9 için çalışır.

Herhangi bir nedenle alamıyorum, ProcessHandle.current().destroyForcibly();JVM'yi öldürmez ve mevcut sürecin imha edilmesine izin verilmezjava.lang.IllegalStateException mesajını atar .


0

JVM kilitlenmesini çoğaltmaya çalışırken bu sorunla karşılaşıyorum.

Jni çalışıyor, ancak farklı platformlar için ayarlanması gerekiyor. Sonunda, JVM çökmesini yapmak için bu kombinasyonu kullanın

  1. Uygulamayı bu JVM seçenekleriyle başlatın -XX:+CrashOnOutOfMemoryError
  2. long[] l = new long[Integer.MAX_VALUE];OOM'u tetiklemek için a kullanın

Daha sonra JVM çökecek ve çökme günlüğünü oluşturacaktır.


-2

Bir 'Çökme' jvm / programını normal sonlandırmadan kesen bir şeyse, İşlenmeyen bir istisna bunu yapabilir.

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
  }

Yani, ne tür bir CRASH'a bağlı?!


3
İstisna atmak bir çarpışma değildir.
Quazi Irfan

İşlenmeyen çalışma zamanı istisnaları olsa da, hangi çöker vardır ArithmeticExceptionolduğunu
orta
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.