155

我正在读一本关于编程技巧的书,其中作者问受访者,“你是如何使 JVM 崩溃的?” 我认为您可以通过编写一个最终会耗尽所有内存的无限 for 循环来做到这一点。

有人有什么想法吗?

4

27 回答 27

179

我不会将抛出 OutOfMemoryError 或 StackOverflowError 称为崩溃。这些只是正常的例外。要真正使 VM 崩溃,有 3 种方法:

  1. 使用 JNI 并在本机代码中崩溃。
  2. 如果没有安装安全管理器,您可以使用反射使 VM 崩溃。这是特定于 VM 的,但通常 VM 在私有字段中存储一堆指向本地资源的指针(例如,指向本地线程对象的指针存储在java.lang.Thread的长字段中)。只需通过反射更改它们,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* 文件。

于 2008-09-16T17:24:17.657 回答
129

杰尼. 事实上,对于 JNI,崩溃是默认的操作模式。你必须加倍努力才能让它不崩溃。

于 2008-09-15T18:06:27.110 回答
64

用这个:

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

这个类必须在引导类路径上,因为它使用的是受信任的代码,所以像这样运行:

java -Xbootclasspath/p:. 碰撞

编辑:简化版与出风头的建议:

Field f = Unsafe.class.getDeclaredField("theUnsafe");
f.setAccessible(true);
Unsafe unsafe = (Unsafe) f.get(null);
unsafe.putAddress(0, 0);
于 2009-09-04T10:18:22.377 回答
36

我来到这里是因为我在Chad Fowler的The Passionate Programmer中也遇到了这个问题。对于那些无法获得副本的人来说,这个问题被设计为一种过滤/测试,用于面试需要“真正优秀的 Java 程序员”的职位的候选人。

具体来说,他问:

你将如何用纯 Java 编写一个会导致 Java 虚拟机崩溃的程序?

我用 Java 编程超过 15 年,我发现这个问题既令人费解又不公平。正如其他人指出的那样,Java 作为一种托管语言,专门设计为不会崩溃。当然总有 JVM 错误,但是:

  1. 经过 15 年以上的生产级 JRE,这种情况很少见。
  2. 任何此类错误都可能在下一个版本中得到修补,那么作为程序员,您有多大可能遇到并回忆当前 JRE 显示停止器的详细信息?

正如其他人所提到的,一些通过 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 本身并没有崩溃。它正确地检测到虚假代码,报告它并退出。

这给我们留下了最明显的解决方案,例如通过递归破坏堆栈、通过对象分配耗尽堆内存或简单地抛出RuntimeException. 但这只会导致 JRE 以StackOverflowError或类似的异常退出,这又不是真正的 crash

那么还剩下什么?我真的很想听听作者真正想到的正确解决方案。

更新:乍得福勒在这里回应

PS:这是一本非常棒的书。我在学习 Ruby 时选择了它以获得精神上的支持。

于 2012-07-24T17:41:10.313 回答
20

此代码将以令人讨厌的方式使 JVM 崩溃

import sun.dc.pr.PathDasher; 

public class Crash
{
     public static void main(String[] args)
     {    
        PathDasher dasher = new PathDasher(null) ;
     }
}
于 2010-05-17T14:01:35.863 回答
20

上次我尝试这样做会这样做:

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
于 2015-02-21T19:02:39.827 回答
16

一个完美的 JVM 实现永远不会崩溃。

要使 JVM 崩溃,除了 JNI 之外,您还需要在 VM 本身中找到错误。无限循环只会消耗 CPU。在构建良好的 JVM 中,无限分配内存只会导致 OutOfMemoryError。这可能会导致其他线程出现问题,但一个好的 JVM 仍然不应该崩溃。

如果你能在 VM 的源代码中发现一个 bug,例如在 VM 的实现中导致内存使用的分段错误,那么你实际上可以将它崩溃。

于 2008-09-15T18:16:20.970 回答
14

如果您想使 JVM 崩溃 - 在 Sun JDK 1.6_23 或更低版本中使用以下命令:

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

这是由于 Sun JDK 中的一个错误- OpenJDK 中也有。这是从 Oracle JDK 1.6_24 开始修复的。

于 2011-09-03T04:03:02.913 回答
10

取决于你所说的崩溃是什么意思。

您可以进行无限递归以使其耗尽堆栈空间,但这会“优雅地”崩溃。你会得到一个异常,但 JVM 本身会处理一切。

您还可以使用 JNI 调用本机代码。如果你做得不好,那么你可以让它很难崩溃。调试这些崩溃是“有趣的”(相信我,我必须编写一个大的 C++ DLL,我们从签名的 java 小程序中调用它)。:)

于 2008-09-15T18:12:25.630 回答
7

最接近单一“答案”的是System.exit(),它会立即终止 JVM,而无需进行适当的清理。但除此之外,本机代码和资源耗尽是最有可能的答案。或者,您可以在 Sun 的错误跟踪器上查找您的 JVM 版本中的错误,其中一些允许可重复的崩溃场景。在 32 位版本(我们现在通常使用 64 位)下,当接近 4 Gb 内存限制时,我们曾经遇到过半定期的崩溃。

于 2008-09-15T18:12:28.907 回答
6

Jon Meyer所著的Java Virtual Machine一书中有一个导致 JVM 核心转储的一系列字节码指令的示例。我找不到这本书的副本。如果有人在那里有一个,请查看并发布答案。

于 2008-09-15T18:48:07.930 回答
5

在带有 wmp10 jre6.0_7 的 winxpsp2 上

Desktop.open(uriToAviOrMpgFile)

这会导致生成的线程抛出未捕获的 Throwable 并崩溃热点

YMMV

于 2008-09-20T06:50:45.123 回答
5

损坏的硬件可能会使任何程序崩溃。我曾经有一个应用程序崩溃在特定机器上可重现,而在具有完全相同设置的其他机器上运行良好。原来那台机器的内存有问题。

于 2009-05-04T13:08:49.427 回答
5

最短的方法:)

public class Crash
{
    public static void main(String[] args)
    {
        main(args);
    }
}
于 2014-04-07T06:37:39.440 回答
5

不是崩溃,但比使用的公认答案更接近崩溃System.exit

您可以通过调用停止 JVM

Runtime.getRuntime().halt( status )

根据文档:-

“此方法不会导致关闭挂钩启动,并且如果已启用退出时终结器,则不会运行未调用的终结器”。

于 2016-06-15T15:05:12.907 回答
4

如果您将崩溃定义为由于未处理的情况(即没有 Java 异常或错误)而导致的进程中止,那么这不能在 Java 中完成(除非您有权使用 sun.misc.Unsafe 类)。这就是托管代码的全部意义所在。

本机代码中的典型崩溃是通过取消引用指向错误内存区域(空地址或未对齐)的指针而发生的。另一个来源可能是非法机器指令(操作码)或来自库或内核调用的未处理信号。如果 JVM 或系统库有错误,两者都可以触发。

例如,JITed(生成)代码、本机方法或系统调用(图形驱动程序)可能会出现导致真正崩溃的问题(当您使用 ZIP 函数并且内存不足时发生崩溃是很常见的)。在这些情况下,JVM 的崩溃处理程序会启动并转储状态。它还可以生成操作系统核心文件(Windows 上的 Watson 博士和 *nix 上的核心转储)。

在 Linux/Unix 上,您可以通过向正在运行的进程发送信号来轻松地使 JVM 崩溃。注意:您不应该SIGSEGV为此使用,因为 Hotspot 会捕获此信号并在大多数地方将其作为 NullPointerException 重新抛出。所以最好发送一个SIGBUS例子。

于 2008-09-16T17:34:51.903 回答
4

以下是关于导致 JVM 核心转储(即崩溃)的原因的详细说明:http: //kb.adobe.com/selfservice/viewContent.do?externalId= tn_17534

于 2008-09-30T08:57:43.163 回答
4

如果你想假装你的内存不足,你可以这样做

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

我知道几种通过调用本机方法(内置方法)导致 JVM 转储错误文件的方法,但最好你不知道如何执行此操作。;)

于 2009-04-28T20:47:33.447 回答
3

JNI 是一个很大的崩溃源。您也可以使用 JVMTI 接口崩溃,因为它也需要用 C/C++ 编写。

于 2009-04-28T20:53:09.003 回答
3

如果您创建一个无限产生更多线程的线程进程(产生更多线程,这...),您最终将在 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分钟后,注意你的内存)

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:
# 
于 2017-09-14T05:16:25.663 回答
3

如果“崩溃”是指 JVM 的突然中止,例如会导致 JVM 写入其 hs_err_pid%p.log,则可以这样做。

将 -Xmx 参数设置为一个很小的值并告诉 JVM 在内存不足时强制崩溃:

 -Xmx10m -XX:+CrashOnOutOfMemoryError

需要明确的是,如果没有上面的第二个参数,它只会导致 jvm以 OutOfMemoryError终止,但它不会“崩溃”或突然中止 jvm。

当我尝试测试 JVM -XX:ErrorFile arg 时,这种技术被证明是有用的,它控制应该在哪里写入这样的 hs_err_pid 日志。我在这里找到了这篇文章,同时试图找到迫使这种崩溃的方法。当我后来发现上述方法最适合我的需要时,我想将其添加到此处的列表中。

最后,FWIW,如果有人在您的参数中已经设置了 -Xms 值(比上面的值更大)时可以测试这个,您也需要删除或更改它,否则您不会崩溃,而只是简单地jvm 启动失败,报告“初始堆大小设置为大于最大堆大小的值”。(如果将 JVM 作为服务运行,例如使用一些应用程序服务器,这不会很明显。再一次,它咬了我,所以我想分享它。)

于 2019-08-28T17:05:52.767 回答
1

最短?使用 Robot 类触发 CTRL+BREAK。当我试图在不关闭控制台的情况下关闭程序时发现了这一点(它没有“退出”功能)。

于 2016-06-25T17:23:31.430 回答
1

这算不算?

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消息destroy of current process not allowed

于 2019-03-27T15:55:45.957 回答
0

如果将无限 for 循环更改为对同一函数的递归调用,则会出现堆栈溢出异常:

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

public void causeStackOverflow() {
    causeStackOverflow();
}
于 2008-09-15T18:12:28.390 回答
0

我现在正在这样做,但不完全确定如何...... :-) JVM(和我的应用程序)有时会完全消失。没有抛出错误,没有任何记录。从工作到完全不运行,没有任何警告。

于 2009-04-28T17:18:43.823 回答
0

尝试复制 JVM 崩溃时遇到此问题。

Jni 可以工作,但需要针对不同的平台进行调整。最终,我使用这种组合使 JVM 崩溃

  1. 使用此 JVM 选项启动应用程序-XX:+CrashOnOutOfMemoryError
  2. 使用 along[] l = new long[Integer.MAX_VALUE];触发 OOM

然后JVM会崩溃并生成崩溃日志。

于 2020-03-26T02:26:29.113 回答
-2

如果“崩溃”是任何中断 jvm/程序正常终止的东西,那么未处理的异常可以做到这一点。

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
  }

所以,这取决于什么类型的崩溃?!

于 2016-01-31T07:44:49.097 回答