ฉันกำลังอ่านหนังสือเกี่ยวกับทักษะการเขียนโปรแกรมที่ผู้เขียนถามผู้สัมภาษณ์ว่า "คุณทำ JVM ผิดพลาดได้อย่างไร" ฉันคิดว่าคุณสามารถทำได้โดยเขียน infinite for-loop ที่จะใช้หน่วยความจำหมด
ใครมีความคิดบ้าง
ฉันกำลังอ่านหนังสือเกี่ยวกับทักษะการเขียนโปรแกรมที่ผู้เขียนถามผู้สัมภาษณ์ว่า "คุณทำ JVM ผิดพลาดได้อย่างไร" ฉันคิดว่าคุณสามารถทำได้โดยเขียน infinite for-loop ที่จะใช้หน่วยความจำหมด
ใครมีความคิดบ้าง
คำตอบ:
สิ่งที่ใกล้เคียงที่สุดกับ "คำตอบ" เดียวคือการSystem.exit()
ยกเลิก JVM ทันทีโดยไม่มีการล้างข้อมูลที่เหมาะสม แต่นอกเหนือจากนั้นรหัสท้องถิ่นและทรัพยากรที่อ่อนล้าเป็นคำตอบที่เป็นไปได้มากที่สุด อีกทางหนึ่งคุณสามารถดูข้อบกพร่องของ Sun ในการติดตาม JVM ในเวอร์ชันของคุณซึ่งบางสถานการณ์อนุญาตให้ทำซ้ำได้ เราเคยเกิดปัญหาแบบกึ่งปกติเมื่อใกล้ถึงขีด จำกัด หน่วยความจำ 4 Gb ภายใต้รุ่น 32 บิต (โดยทั่วไปเราใช้ 64 บิตในขณะนี้)
ฉันจะไม่เรียกการขว้าง OutOfMemoryError หรือ StackOverflowError นี่เป็นข้อยกเว้นปกติ ในการที่จะชน VM จริงๆมี 3 วิธี:
สำหรับวิธีสุดท้ายฉันมีตัวอย่างสั้น ๆ ซึ่งจะทำให้ Sun Hotspot VM เงียบดี:
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();
}
}
คลาสนี้ต้องอยู่ใน boot classpath เนื่องจากใช้รหัสที่เชื่อถือได้ดังนั้นให้เรียกใช้ดังนี้:
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
เช่นนั้น
ฉันมาที่นี่เพราะฉันเจอคำถามนี้ในThe Passionate Programmerโดย Chad Fowler สำหรับผู้ที่ไม่สามารถเข้าถึงสำเนาคำถามจะถูกจัดเรียงเป็นตัวกรอง / การทดสอบสำหรับผู้สมัครสัมภาษณ์ตำแหน่งที่ต้องการ "โปรแกรมเมอร์ Java ที่ดีจริงๆ"
เขาถามว่า:
คุณจะเขียนโปรแกรมในจาวาบริสุทธิ์ที่จะทำให้ Java Virtual Machine พังได้อย่างไร
ฉันตั้งโปรแกรมใน 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 เองไม่ได้ล้มเหลว ตรวจพบรหัสปลอมรายงานและออกอย่างถูกต้อง
ใบนี้เรามีชนิดที่ชัดเจนที่สุดของการแก้ปัญหาเช่นพัดผ่านสแต็ค recursion 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 คุณจะต้องค้นหาข้อบกพร่องใน VM เอง การวนซ้ำไม่สิ้นสุดใช้งาน CPU เพียงอย่างเดียว การจัดสรรหน่วยความจำอย่างไม่ จำกัด ควรทำให้ OutOfMemoryError อยู่ใน JVM ที่สร้างขึ้นมาอย่างดี นี่อาจเป็นสาเหตุของปัญหาสำหรับเธรดอื่น แต่ JVM ที่ดียังคงไม่ควรผิดพลาด
หากคุณพบข้อผิดพลาดในซอร์สโค้ดของ VM และตัวอย่างเช่นทำให้เกิดข้อผิดพลาดในการแบ่งเซกเมนต์ในการใช้หน่วยความจำของการใช้งานของ VM ดังนั้นคุณสามารถผิดพลาดได้จริง
หากคุณต้องการที่จะพัง 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 Virtual Machineโดย Jon Meyer มีตัวอย่างของชุดคำสั่ง bytecode ที่ทำให้ JVM เป็น core dump ฉันหาสำเนาของหนังสือเล่มนี้ไม่พบ หากมีใครมีกรุณาค้นหาและโพสต์คำตอบ
บน winxpsp2 w / wmp10 jre6.0_7
Desktop.open (uriToAviOrMpgFile)
ซึ่งทำให้เธรด spawned เพื่อโยน Throwable ที่ไม่ถูกตรวจจับและหยุดฮอตสปอต
YMMV
ฮาร์ดแวร์ที่ใช้งานไม่ได้อาจมีปัญหาโปรแกรมใด ๆ ฉันเคยมีปัญหาแอพที่ทำซ้ำบนเครื่องเฉพาะในขณะที่ทำงานได้ดีบนเครื่องอื่นด้วยการตั้งค่าเดียวกัน ปรากฎว่าเครื่องมี RAM ผิดพลาด
วิธีที่สั้นที่สุด :)
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
ไม่ใช่ความผิดพลาด แต่ใกล้เคียงกับความผิดพลาดมากกว่าคำตอบที่ยอมรับจากการใช้ System.exit
คุณสามารถหยุด JVM ได้โดยโทร
Runtime.getRuntime().halt( status )
ตามเอกสาร: -
msgstr "วิธีนี้ไม่ทำให้ตะขอสำหรับปิดเครื่องเริ่มต้นขึ้นและไม่เรียกใช้ตัวปิดการทำงานที่ไม่ได้เรียกใช้หากการเปิดใช้งานขั้นสุดท้ายเมื่อออก"
นี่คือคำอธิบายโดยละเอียดเกี่ยวกับสิ่งที่ทำให้ JVM ไปที่ core dump (เช่น crash): 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) นี่คือจุดทั้งหมดของรหัสที่ได้รับการจัดการ
ความผิดพลาดทั่วไปในโค้ดเนทีฟเกิดขึ้นโดยการพอยน์เตอร์อ้างอิงไปยังพื้นที่หน่วยความจำผิด (ที่อยู่ว่างหรือพลาด) แหล่งอื่นอาจเป็นคำสั่งเครื่องที่ผิดกฎหมาย (opcodes) หรือสัญญาณที่ไม่สามารถจัดการได้จากการเรียกใช้ไลบรารีหรือเคอร์เนล ทั้งสองสามารถถูกเรียกใช้หาก JVM หรือไลบรารีระบบมีข้อบกพร่อง
ตัวอย่างเช่นรหัส JITed (สร้างขึ้น) วิธีดั้งเดิมหรือการเรียกของระบบ (ไดรเวอร์กราฟิก) อาจมีปัญหาที่ทำให้เกิดปัญหาจริง (เป็นเรื่องปกติที่จะเกิดความผิดพลาดเมื่อคุณใช้ฟังก์ชั่น ZIP และหน่วยความจำหมด) ในกรณีดังกล่าวตัวจัดการความผิดพลาดของ JVM จะเริ่มต้นและทิ้งสถานะ มันยังสามารถสร้างไฟล์ core OS (Dr. Watson บน Windows และ core dump บน * nix)
บน Linux / Unix คุณสามารถทำให้ JVM พังได้ง่ายโดยส่งสัญญาณไปยังกระบวนการที่กำลังทำงานอยู่ หมายเหตุ: คุณไม่ควรใช้SIGSEGV
สิ่งนี้เนื่องจาก Hotspot จับสัญญาณนี้และโยนมันเป็น NullPointerException ในสถานที่ส่วนใหญ่ ดังนั้นจะเป็นการดีกว่าถ้าส่งSIGBUS
ตัวอย่าง
JNI เป็นแหล่งของการขัดข้องขนาดใหญ่ คุณสามารถผิดพลาดโดยใช้อินเตอร์เฟส JVMTI เนื่องจากต้องเขียนใน C / C ++ เช่นกัน
หากคุณสร้างกระบวนการของเธรดที่วางไข่เธรดได้ไม่ จำกัด (ซึ่งจะสร้างเธรดเพิ่มเติมซึ่ง ... ) ในที่สุดคุณจะทำให้เกิดข้อผิดพลาดสแต็กล้นใน 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 นาทีดู ram ของคุณ)
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:
#
หากโดย "crash" คุณหมายถึงการยกเลิก JVM อย่างกระทันหันเช่นจะทำให้ JVM เขียนลงใน hs_err_pid% p.logมันคุณสามารถทำได้ด้วยวิธีนี้
ตั้งค่า -Xmx arg เป็นค่าเล็ก ๆ และบอก JVM เพื่อบังคับให้เกิดความผิดพลาดใน outofmemory:
-Xmx10m -XX:+CrashOnOutOfMemoryError
เพื่อให้ชัดเจนโดยไม่มีอาร์กิวเมนต์ที่สองข้างต้นก็จะส่งผลให้ jvm ยุติด้วย OutOfMemoryError แต่มันจะไม่ "ผิดพลาด" หรือยกเลิก jvm ทันที
เทคนิคนี้พิสูจน์แล้วว่ามีประโยชน์เมื่อฉันพยายามทดสอบ JVM -XX: ErrorFile arg ซึ่งควบคุมตำแหน่งที่ควรเขียนบันทึก hs_err_pid ฉันได้พบโพสต์นี้ที่นี่ในขณะที่พยายามหาวิธีที่จะบังคับความผิดพลาดดังกล่าว เมื่อภายหลังฉันพบว่าการทำงานข้างต้นเป็นวิธีที่ง่ายที่สุดสำหรับความต้องการของฉันฉันต้องการเพิ่มลงในรายการที่นี่
ท้ายที่สุด FWIW หากใครก็ตามสามารถทดสอบสิ่งนี้เมื่อพวกเขามีค่า -Xms ที่ตั้งค่าไว้ใน args ของคุณ (เป็นค่าที่สูงกว่าบางส่วนด้านบน) คุณจะต้องลบหรือเปลี่ยนแปลงสิ่งนั้นหรือคุณจะไม่ผิดพลาด แต่เพียง ความล้มเหลวในการเริ่มต้น jvm โดยรายงาน "ขนาดฮีพเริ่มต้นที่ตั้งเป็นค่าที่ใหญ่กว่าขนาดฮีพสูงสุด" (นั่นจะไม่ชัดเจนหากใช้ JVM เป็นบริการเช่นกับแอพเซิร์ฟเวอร์บางตัวอีกครั้งมันกัดฉันดังนั้นฉันจึงต้องการแบ่งปันมัน)
หากคุณเปลี่ยน infinite สำหรับลูปเป็นการเรียกซ้ำไปยังฟังก์ชันเดียวกันคุณจะได้รับข้อยกเว้นสแต็กโอเวอร์โฟลว์:
public static void main(String[] args) {
causeStackOverflow();
}
public void causeStackOverflow() {
causeStackOverflow();
}
ตอนนี้ฉันกำลังทำอยู่ แต่ไม่แน่ใจทั้งหมดว่า ... :-) JVM (และแอปของฉัน) บางครั้งก็หายไปอย่างสมบูรณ์ ไม่มีข้อผิดพลาดเกิดขึ้นไม่มีการบันทึก เปลี่ยนจากการทำงานเป็นไม่ทำงานทันทีโดยไม่มีการเตือน
ที่สั้นที่สุด? ใช้คลาส Robot เพื่อเรียกใช้ 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 / โปรแกรมจากการยกเลิกตามปกติแสดงว่าข้อยกเว้น Un-handling สามารถทำสิ่งนี้ได้
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
เป็น