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ı?
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ı?
Yanıtlar:
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).
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:
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.
java.lang.OutOfMemoryError: GC overhead limit exceeded
JNI . Aslında, JNI ile çökme varsayılan çalışma modudur. Çökmemesini sağlamak için çok çalışmalısın.
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
Field f = Unsafe.class.getDeclaredField( "theUnsafe" ); f.setAccessible( true ); unsafe = (Unsafe) f.get( null );
Benim için harika çalıştı.
Unsafe
tanımı gereği "güvensiz" dir. Bu biraz hile.
getDeclaredField
üretimi de dahil olmak üzere, Linux x64 üzerinde JDK 8u131 yılında hile hs_err_pid*.log
bir mesafede SIGSEGV
.
Unsafe
bir 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 Unsafe
yapar.
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:
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 StackOverflowError
veya 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.
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) ;
}
}
InternalError
JDK 1.8'e bir atar . JVM artık başarısız oluyor.
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
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.
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.
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ı). :)
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.
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
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ı.
mümkün olan en kısa yol :)
public class Crash
{
public static void main(String[] args)
{
main(args);
}
}
Exception in thread "main" java.lang.StackOverflowError at Test.main
. Ben jdk1.8.0_65 kullanıyorum
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".
JVM'nin çekirdek dökümü (ör. kilitlenme) için neyin neden olduğuna dair ayrıntılı bir açıklama: http://kb.adobe.com/selfservice/viewContent.do?externalId=tn_17534
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. ;)
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: SIGSEGV
Hotspot 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 .
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:
#
"Çö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.)
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();
}
Ş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.
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).
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 .
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
-XX:+CrashOnOutOfMemoryError
long[] l = new long[Integer.MAX_VALUE];
OOM'u tetiklemek için a kullanınDaha sonra JVM çökecek ve çökme günlüğünü oluşturacaktır.
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ı?!
ArithmeticException
olduğunu