it-swarm-vi.tech

Làm thế nào để bạn đánh sập một JVM?

Tôi đang đọc một cuốn sách về các kỹ năng lập trình trong đó tác giả hỏi người được phỏng vấn, "Làm thế nào để bạn đánh sập một JVM?" Tôi nghĩ rằng bạn có thể làm như vậy bằng cách viết một vòng lặp vô hạn mà cuối cùng sẽ sử dụng hết bộ nhớ.

Có ai có ý kiến ​​gì không?

141
Shivasubramanian A

Điều gần nhất với một "câu trả lời" duy nhất là System.exit() sẽ chấm dứt JVM ngay lập tức mà không cần dọn dẹp đúng cách. Nhưng ngoài ra, mã nguồn và cạn kiệt tài nguyên là câu trả lời có khả năng nhất. Ngoài ra, bạn có thể tìm kiếm trình theo dõi lỗi của Sun để tìm lỗi trong phiên bản JVM của bạn, một số trong đó cho phép các tình huống sự cố lặp lại. Chúng tôi thường gặp sự cố bán định kỳ khi tiếp cận giới hạn bộ nhớ 4 Gb trong các phiên bản 32 bit (hiện tại chúng tôi thường sử dụng 64 bit).

6
Leigh Caldwell

Tôi sẽ không gọi ném OutOfMemoryError hoặc StackOverflowError một sự cố. Đây chỉ là những ngoại lệ bình thường. Để thực sự sụp đổ a VM có 3 cách:

  1. Sử dụng JNI và sự cố trong mã gốc.
  2. Nếu không có trình quản lý bảo mật nào được cài đặt, bạn có thể sử dụng sự phản chiếu để đánh sập VM. Đây là VM cụ thể, nhưng thông thường a VM lưu trữ một loạt các con trỏ tới tài nguyên riêng trong các trường riêng (ví dụ: một con trỏ tới đối tượng luồng gốc được lưu trữ trong một trường dài trong Java.lang.Thread). Chỉ cần thay đổi chúng thông qua sự phản chiếu và VM sẽ sớm bị sập.
  3. Tất cả các máy ảo đều có lỗi, vì vậy bạn chỉ cần kích hoạt một.

Đối với phương pháp cuối cùng tôi có một ví dụ ngắn, nó sẽ đánh sập một Điểm nóng Mặt trời VM yên tĩnh độc đáo:

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

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

Điều này dẫn đến tràn ngăn xếp trong GC, do đó bạn sẽ không nhận được StackOverflowError mà là một sự cố thực sự bao gồm tệp hs_err *.

172
ralfs

JNI . Trong thực tế, với JNI, sự cố là chế độ hoạt động mặc định. Bạn phải làm việc chăm chỉ hơn để không bị sụp đổ.

123
Dan Dyer

Dùng cái này:

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

Lớp này phải nằm trên đường dẫn khởi động vì nó đang sử dụng mã đáng tin cậy, vì vậy hãy chạy như thế này:

Java -Xboot classpath/p :. Tai nạn

56
Dave Griffiths

Tôi đến đây vì tôi cũng gặp phải câu hỏi này trong Lập trình viên đam mê , bởi Chad Fowler. Đối với những người không có quyền truy cập vào một bản sao, câu hỏi được đóng khung như một loại bộ lọc/bài kiểm tra cho các ứng viên phỏng vấn cho một vị trí yêu cầu "thực sự tốt Java".

Cụ thể, anh hỏi:

Làm thế nào bạn có thể viết một chương trình, bằng Java thuần, điều đó sẽ khiến Java Máy ảo bị sập?

Tôi đã lập trình trong Java trong hơn 15 năm và tôi thấy câu hỏi này vừa khó hiểu vừa không công bằng. Như những người khác đã chỉ ra, Java, như một ngôn ngữ được quản lý, được thiết kế cụ thể không bị sập. Tất nhiên luôn có các lỗi JVM, nhưng:

  1. Sau hơn 15 năm JRE cấp sản xuất, thật hiếm.
  2. Bất kỳ lỗi nào như vậy có khả năng sẽ được vá trong phiên bản tiếp theo, vậy khả năng bạn là một lập trình viên có thể chạy vào và nhớ lại các chi tiết của bộ trình dừng JRE hiện tại như thế nào?

Như những người khác đã đề cập, một số mã gốc thông qua JNI là một cách chắc chắn để đánh sập JRE. Nhưng tác giả đã đề cập cụ thể trong Java thuần túy, vậy là xong.

Một tùy chọn khác là cung cấp mã byte không có thật của JRE; đủ dễ dàng để đổ một số dữ liệu nhị phân rác vào tệp. class và yêu cầu JRE chạy nó:

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

Có tính không? Ý tôi là bản thân JRE đã không gặp sự cố; nó đã phát hiện đúng mã giả, báo cáo và thoát.

Điều này cho chúng ta các loại giải pháp rõ ràng nhất như thổi ngăn xếp thông qua đệ quy, hết bộ nhớ heap thông qua phân bổ đối tượng hoặc chỉ đơn giản là ném RuntimeException. Nhưng điều này chỉ khiến JRE thoát ra với StackOverflowError hoặc ngoại lệ tương tự, một lần nữa không thực sự là một sự cố.

Vậy những gì còn lại? Tôi thực sự thích nghe những gì tác giả thực sự có trong đầu như một giải pháp thích hợp.

Cập nhật : Chad Fowler đã trả lời tại đây .

PS: đó là một cuốn sách tuyệt vời. Tôi nhặt nó lên để hỗ trợ về mặt đạo đức khi học Ruby.

32
George Armhold

Mã này sẽ đánh sập JVM theo những cách khó chịu

import Sun.dc.pr.PathDasher; 

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

Lần trước tôi đã thử điều này sẽ làm điều đó:

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

Phần đầu tiên của tệp nhật ký được tạo:

#
# 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
17
Hot Licks

Một triển khai JVM hoàn hảo sẽ không bao giờ sụp đổ.

Để đánh sập một JVM, ngoài JNI, bạn cần tìm một lỗi trong VM. Một vòng lặp vô hạn chỉ tiêu tốn CPU. Việc cấp phát bộ nhớ vô hạn chỉ gây ra OutOfMemoryError trong một JVM được xây dựng tốt. có thể sẽ gây ra sự cố cho các luồng khác, nhưng một JVM tốt vẫn không bị sập.

Nếu bạn có thể tìm thấy một lỗi trong mã nguồn của VM và ví dụ gây ra lỗi phân đoạn trong việc sử dụng bộ nhớ của việc triển khai VM, thì bạn thực sự có thể gặp sự cố.

15
Dave L.

Nếu bạn muốn đánh sập JVM - hãy sử dụng phần sau trong Sun JDK 1.6_23 trở xuống:

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

Điều này là do một bug trong Sun JDK - cũng được tìm thấy trong OpenJDK. Điều này được sửa từ Oracle JDK 1.6_24 trở đi.

14
Prabath Siriwardena

Phụ thuộc vào những gì bạn có nghĩa là bởi sự cố.

Bạn có thể thực hiện một đệ quy vô hạn để làm cho nó hết dung lượng ngăn xếp, nhưng điều đó sẽ sụp đổ "một cách duyên dáng". Bạn sẽ có một ngoại lệ, nhưng chính JVM sẽ xử lý mọi thứ.

Bạn cũng có thể sử dụng JNI để gọi mã gốc. Nếu bạn không làm điều đó đúng thì bạn có thể làm cho nó sụp đổ. Gỡ lỗi những sự cố đó là "vui vẻ" (tin tôi đi, tôi đã phải viết một C++ DLL mà chúng tôi gọi từ một applet đã ký Java). :)

10
Herms

Cuốn sách Máy ảo Java của Jon Meyer có một ví dụ về một loạt các hướng dẫn mã byte đã khiến JVM đổ rác lõi. Tôi không thể tìm thấy bản sao của cuốn sách này. Nếu bất cứ ai ở ngoài đó có một xin vui lòng tìm nó và gửi câu trả lời.

6
Soulfly

Phần cứng bị hỏng có thể sụp đổ bất kỳ chương trình. Tôi đã từng gặp sự cố ứng dụng có thể lặp lại trên một máy cụ thể trong khi vẫn chạy tốt trên các máy khác có cùng thiết lập. Hóa ra máy đó có RAM bị lỗi.

5
Michael Borgwardt

Không phải là sự cố, nhưng gần với sự cố hơn là câu trả lời được chấp nhận khi sử dụng System.exit

Bạn có thể tạm dừng JVM bằng cách gọi

Runtime.getRuntime().halt( status )

Theo các tài liệu: -

"Phương pháp này không làm cho các móc tắt máy được bắt đầu và không chạy các công cụ hoàn thiện không được yêu cầu nếu đã hoàn tất việc thoát khi hoàn thành".

5
henry

cách ngắn nhất có thể :)

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

trên winxpsp2 w/wmp10 jre6.0_7

Desktop.open (uriToAviOrMpgFile)

Điều này làm cho một chủ đề được sinh ra để ném một điểm nóng không thể ném được và gặp sự cố

YMMV

5
kitsuneymg

Nếu bạn xác định sự cố là hủy bỏ quy trình do tình huống chưa được xử lý (nghĩa là không Java Ngoại lệ hoặc Lỗi), thì điều này không thể được thực hiện từ bên trong Java (trừ khi bạn có quyền sử dụng lớp Sun.misc.Unsafe). Đây là toàn bộ điểm của mã được quản lý.

Sự cố điển hình trong mã gốc xảy ra bằng cách khử các con trỏ tham chiếu đến các vùng nhớ sai (địa chỉ null hoặc bị sai lệch). Một nguồn khác có thể là hướng dẫn máy bất hợp pháp (opcodes) hoặc tín hiệu chưa được xử lý từ các cuộc gọi thư viện hoặc kernel. Cả hai có thể được kích hoạt nếu JVM hoặc các thư viện hệ thống có lỗi.

Ví dụ mã JITed (được tạo), các phương thức gốc hoặc các cuộc gọi hệ thống (trình điều khiển đồ họa) có thể gặp sự cố dẫn đến sự cố thực sự (khá phổ biến khi gặp sự cố khi bạn sử dụng các chức năng Zip và chúng hết bộ nhớ). Trong các trường hợp đó, trình xử lý sự cố của JVM khởi động và loại bỏ trạng thái. Nó cũng có thể tạo tệp lõi hệ điều hành (Tiến sĩ Watson trên Windows và kết xuất lõi trên * nix).

Trên Linux/Unix, bạn có thể dễ dàng tạo ra sự cố JVM bằng cách gửi Tín hiệu cho quá trình đang chạy. Lưu ý: bạn không nên sử dụng SIGSEGV cho việc này, vì Hotspot bắt được tín hiệu này và ném lại dưới dạng NullPulumException ở hầu hết các nơi. Vì vậy, tốt hơn là gửi một SIGBUS chẳng hạn.

4
eckes

đây là một lời giải thích chi tiết về nguyên nhân khiến JVM bị đổ lõi (tức là sự cố): http://kb.Adobe.com/elfservice/viewContent.do?externalId=tn_17534

4
COTOHA

Nếu bạn muốn giả vờ hết bộ nhớ, bạn có thể làm

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

Tôi biết một vài cách để khiến JVM kết xuất một tệp lỗi bằng cách gọi các phương thức gốc (những phương thức được tích hợp sẵn), nhưng có lẽ tốt nhất là bạn không biết cách thực hiện điều này. ;)

3
Peter Lawrey

JNI là một nguồn lớn của sự cố. Bạn cũng có thể gặp sự cố khi sử dụng giao diện JVMTI vì điều đó cũng cần phải được viết bằng C/C++.

3
Jared

Nếu bạn tạo một quy trình xử lý vô hạn sinh ra nhiều luồng hơn (sinh ra nhiều luồng hơn, thì ...) cuối cùng bạn sẽ gây ra lỗi tràn ngăn xếp trong chính 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();
    }
}

Điều này cho tôi đầu ra (sau 5 phút, xem ram của bạn)

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:
# 
2
Lightfire228

Ngắn nhất? Sử dụng lớp Robot để kích hoạt CTRL + BREAK. Tôi phát hiện ra điều này khi tôi đang cố gắng đóng chương trình của mình mà không đóng giao diện điều khiển (Nó không có chức năng 'thoát').

1
user6022288

Tôi đang làm điều đó ngay bây giờ, nhưng không hoàn toàn chắc chắn làm thế nào ... :-) JVM (và ứng dụng của tôi) đôi khi hoàn toàn biến mất. Không có lỗi ném, không có gì đăng nhập. Đi từ làm việc đến không chạy ngay lập tức mà không có cảnh báo.

0
Brian Knoblauch

Nếu bằng cách "sập", bạn có nghĩa là hủy bỏ JVM đột ngột, chẳng hạn như sẽ khiến JVM ghi ra hs_err_pid% p.log, bạn có thể thực hiện theo cách này.

Đặt đối số-Xmx thành một giá trị nhỏ và báo cho JVM để tạo sự cố cho outofmemory:

 -Xmx10m -XX:+CrashOnOutOfMemoryError

Để rõ ràng, nếu không có đối số thứ hai ở trên, nó sẽ chỉ dẫn đến jvm chấm dứt với OutOfMemoryError, nhưng nó sẽ không "sụp đổ" hoặc hủy bỏ đột ngột jvm.

Kỹ thuật này tỏ ra hữu ích khi tôi đang thử kiểm tra JVM -XX: ErrorFile arg, điều khiển nơi ghi nhật ký hs_err_pid như vậy. Tôi đã tìm thấy bài đăng này ở đây, trong khi cố gắng tìm cách để buộc một vụ tai nạn như vậy. Khi sau đó tôi thấy những thứ trên hoạt động dễ dàng nhất cho nhu cầu của mình, tôi muốn thêm nó vào danh sách ở đây.

Cuối cùng, FWIW, nếu bất kỳ ai cũng có thể kiểm tra điều này khi họ đã đặt giá trị -Xms trong đối số của bạn (với một số giá trị lớn hơn ở trên), bạn cũng sẽ muốn xóa hoặc thay đổi, hoặc bạn sẽ không gặp sự cố mà chỉ đơn giản là jvm không khởi động được, báo cáo "Kích thước heap ban đầu được đặt thành giá trị lớn hơn kích thước heap tối đa". (Điều đó sẽ không rõ ràng nếu chạy JVM dưới dạng dịch vụ, chẳng hạn như với một số máy chủ ứng dụng. Một lần nữa, nó cắn tôi, vì vậy tôi muốn chia sẻ nó.)

0
charlie arehart

Điều này có tính không?

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

Nó chỉ hoạt động cho Linux và từ Java 9.

Vì một số lý do tôi không nhận được, ProcessHandle.current().destroyForcibly(); không giết JVM và ném Java.lang.IllegalStateException Với thông báo hủy quy trình hiện tại không được phép.

0
mszmurlo