如何使Java虛擬機器崩潰

滕瑞發表於2015-03-14

最初見到這個問題是在《我程式設計,我快樂——程式設計師職業規劃之道》上,我的第一反應是構造一個OutOfMemoryError或者StackOverflowError。為了快速產生結果,在JVM引數進行如下設定後:

-verbose:gc -Xms20M -Xmx20M -Xmn10M -XX:+PrintGCDetails -XX:SurvivorRatio=8

下面的程式碼即可很快產生OutOfMemoryError:

import java.util.ArrayList;
import java.util.List;

public class Crash {
    public static void main(String[] args) {
        List<Object> list = new ArrayList<Object>();

        while(true) {
            list.add(new Object());
        }
    }
}

輸出效果

[GC (Allocation Failure) [PSYoungGen: 7330K->992K(9216K)] 7330K->5297K(19456K), 0.0094021 secs] [Times: user=0.00 sys=0.00, real=0.01 secs] 
[GC (Allocation Failure) --[PSYoungGen: 9184K->9184K(9216K)] 13489K->19416K(19456K), 0.0171858 secs] [Times: user=0.06 sys=0.00, real=0.02 secs] 
[Full GC (Ergonomics) [PSYoungGen: 9184K->0K(9216K)] [ParOldGen: 10232K->9893K(10240K)] 19416K->9893K(19456K), [Metaspace: 2524K->2524K(1056768K)], 0.1437797 secs] [Times: user=0.23 sys=0.00, real=0.14 secs] 
[Full GC (Ergonomics) [PSYoungGen: 7706K->8077K(9216K)] [ParOldGen: 9893K->7783K(10240K)] 17600K->15860K(19456K), [Metaspace: 2524K->2524K(1056768K)], 0.1274782 secs] [Times: user=0.25 sys=0.00, real=0.13 secs] 
[Full GC (Allocation Failure) [PSYoungGen: 8077K->8076K(9216K)] [ParOldGen: 7783K->7783K(10240K)] 15860K->15859K(19456K), [Metaspace: 2524K->2524K(1056768K)], 0.0685814 secs] [Times: user=0.16 sys=0.00, real=0.07 secs] 
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
    at java.util.Arrays.copyOf(Arrays.java:3210)
    at java.util.Arrays.copyOf(Arrays.java:3181)
    at java.util.ArrayList.grow(ArrayList.java:261)
    at java.util.ArrayList.ensureExplicitCapacity(ArrayList.java:235)
    at java.util.ArrayList.ensureCapacityInternal(ArrayList.java:227)
    at java.util.ArrayList.add(ArrayList.java:458)
    at Demo.main(Demo.java:9)
[Full GC (Ergonomics) [PSYoungGen: 8192K->0K(9216K)] [ParOldGen: 7783K->492K(10240K)] 15975K->492K(19456K), [Metaspace: 2550K->2550K(1056768K)], 0.0036032 secs] [Times: user=0.06 sys=0.00, real=0.00 secs] 
Heap
 PSYoungGen      total 9216K, used 164K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
  eden space 8192K, 2% used [0x00000000ff600000,0x00000000ff6290e8,0x00000000ffe00000)
  from space 1024K, 0% used [0x00000000ffe00000,0x00000000ffe00000,0x00000000fff00000)
  to   space 1024K, 0% used [0x00000000fff00000,0x00000000fff00000,0x0000000100000000)
 ParOldGen       total 10240K, used 492K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)
  object space 10240K, 4% used [0x00000000fec00000,0x00000000fec7b000,0x00000000ff600000)
 Metaspace       used 2557K, capacity 4486K, committed 4864K, reserved 1056768K
  class space    used 275K, capacity 386K, committed 512K, reserved 1048576K

至於產生StackOverflowError,一個沒有返回的遞迴函式即可實現。

但從某種意義上說,這些並沒有讓JVM崩潰,而是丟擲一個異常,Java虛擬機器規範裡面已經明確定義了這些異常發生的情況。

在真正的黑客眼裡,JVM內部的Bug才是他們的答案。但JVM內部的Bug最終會被修復,對於一般愛好者,可以找找以前的Java Bug Database,然後用舊版本的JVM驗證。

不過還有另外一種容易引起JVM崩潰的地方就是JNI,雖然是外部程式碼引起的,但應該也算是JVM機制上的一種缺陷吧。

以下演示一下JNI導致Java虛擬機器崩潰的例子。語文學得不好,直接上程式碼:

JvmCrash.java

class JvmCrash
{
    public static native void greeting();
}

JvmCrashTest.java

class JvmCrashTest
{
    public static void main(String[] args)
    {
        JvmCrash.greeting();
    }
    static
    {
        System.loadLibrary("JvmCrash");
    }
}

JvmCrash.h (由javah工具自動生成的)

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class JvmCrash */

#ifndef _Included_JvmCrash
#define _Included_JvmCrash
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     JvmCrash
 * Method:    greeting
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_JvmCrash_greeting
  (JNIEnv *, jclass);

#ifdef __cplusplus
}
#endif
#endif

JvmCrash.c

#include "JvmCrash.h"
#include <stdio.h>

JNIEXPORT void JNICALL Java_JvmCrash_greeting(JNIEnv* env, jclass cl)
{
    printf("Hello Crash World!\n");
    int i = 0;
    int j = 1 / i;
}

然後,我們就能得到一個Fatal Error而不是一個Exception了!

#
# A fatal error has been detected by the Java Runtime Environment:
#
#  EXCEPTION_INT_DIVIDE_BY_ZERO (0xc0000094) at pc=0x10001020, pid=2388, tid=4988
#
# JRE version: Java(TM) SE Runtime Environment (8.0_40-b26) (build 1.8.0_40-b26)
# Java VM: Java HotSpot(TM) Client VM (25.40-b25 mixed mode, sharing windows-x86 )
# Problematic frame:
# C  [JvmCrash.dll+0x1020]
#
# Failed to write core dump. Minidumps are not enabled by default on client versions of Windows
#
# An error report file with more information is saved as:
# C:\Documents and Settings\Administrator\workspace\JNICrash\hs_err_pid2388.log
#
# If you would like to submit a bug report, please visit:
#   http://bugreport.java.com/bugreport/crash.jsp
# The crash happened outside the Java Virtual Machine in native code.
# See problematic frame for where to report the bug.
#
Hello Crash World! 

相關文章