我正在读一本关于编程技巧的书,其中作者问受访者,“你是如何使 JVM 崩溃的?” 我认为您可以通过编写一个最终会耗尽所有内存的无限 for 循环来做到这一点。
有人有什么想法吗?
我不会将抛出 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* 文件。
杰尼. 事实上,对于 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();
}
}
这个类必须在引导类路径上,因为它使用的是受信任的代码,所以像这样运行:
java -Xbootclasspath/p:. 碰撞
编辑:简化版与出风头的建议:
Field f = Unsafe.class.getDeclaredField("theUnsafe");
f.setAccessible(true);
Unsafe unsafe = (Unsafe) f.get(null);
unsafe.putAddress(0, 0);
我来到这里是因为我在Chad Fowler的The Passionate Programmer中也遇到了这个问题。对于那些无法获得副本的人来说,这个问题被设计为一种过滤/测试,用于面试需要“真正优秀的 Java 程序员”的职位的候选人。
具体来说,他问:
你将如何用纯 Java 编写一个会导致 Java 虚拟机崩溃的程序?
我用 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 本身并没有崩溃。它正确地检测到虚假代码,报告它并退出。
这给我们留下了最明显的解决方案,例如通过递归破坏堆栈、通过对象分配耗尽堆内存或简单地抛出RuntimeException
. 但这只会导致 JRE 以StackOverflowError
或类似的异常退出,这又不是真正的 crash。
那么还剩下什么?我真的很想听听作者真正想到的正确解决方案。
更新:乍得福勒在这里回应。
PS:这是一本非常棒的书。我在学习 Ruby 时选择了它以获得精神上的支持。
此代码将以令人讨厌的方式使 JVM 崩溃
import sun.dc.pr.PathDasher;
public class Crash
{
public static void main(String[] args)
{
PathDasher dasher = new PathDasher(null) ;
}
}
上次我尝试这样做会这样做:
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。在构建良好的 JVM 中,无限分配内存只会导致 OutOfMemoryError。这可能会导致其他线程出现问题,但一个好的 JVM 仍然不应该崩溃。
如果你能在 VM 的源代码中发现一个 bug,例如在 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 小程序中调用它)。:)
最接近单一“答案”的是System.exit()
,它会立即终止 JVM,而无需进行适当的清理。但除此之外,本机代码和资源耗尽是最有可能的答案。或者,您可以在 Sun 的错误跟踪器上查找您的 JVM 版本中的错误,其中一些允许可重复的崩溃场景。在 32 位版本(我们现在通常使用 64 位)下,当接近 4 Gb 内存限制时,我们曾经遇到过半定期的崩溃。
Jon Meyer所著的Java Virtual Machine一书中有一个导致 JVM 核心转储的一系列字节码指令的示例。我找不到这本书的副本。如果有人在那里有一个,请查看并发布答案。
在带有 wmp10 jre6.0_7 的 winxpsp2 上
Desktop.open(uriToAviOrMpgFile)
这会导致生成的线程抛出未捕获的 Throwable 并崩溃热点
YMMV
损坏的硬件可能会使任何程序崩溃。我曾经有一个应用程序崩溃在特定机器上可重现,而在具有完全相同设置的其他机器上运行良好。原来那台机器的内存有问题。
最短的方法:)
public class Crash
{
public static void main(String[] args)
{
main(args);
}
}
不是崩溃,但比使用的公认答案更接近崩溃System.exit
您可以通过调用停止 JVM
Runtime.getRuntime().halt( status )
根据文档:-
“此方法不会导致关闭挂钩启动,并且如果已启用退出时终结器,则不会运行未调用的终结器”。
如果您将崩溃定义为由于未处理的情况(即没有 Java 异常或错误)而导致的进程中止,那么这不能在 Java 中完成(除非您有权使用 sun.misc.Unsafe 类)。这就是托管代码的全部意义所在。
本机代码中的典型崩溃是通过取消引用指向错误内存区域(空地址或未对齐)的指针而发生的。另一个来源可能是非法机器指令(操作码)或来自库或内核调用的未处理信号。如果 JVM 或系统库有错误,两者都可以触发。
例如,JITed(生成)代码、本机方法或系统调用(图形驱动程序)可能会出现导致真正崩溃的问题(当您使用 ZIP 函数并且内存不足时发生崩溃是很常见的)。在这些情况下,JVM 的崩溃处理程序会启动并转储状态。它还可以生成操作系统核心文件(Windows 上的 Watson 博士和 *nix 上的核心转储)。
在 Linux/Unix 上,您可以通过向正在运行的进程发送信号来轻松地使 JVM 崩溃。注意:您不应该SIGSEGV
为此使用,因为 Hotspot 会捕获此信号并在大多数地方将其作为 NullPointerException 重新抛出。所以最好发送一个SIGBUS
例子。
以下是关于导致 JVM 核心转储(即崩溃)的原因的详细说明:http: //kb.adobe.com/selfservice/viewContent.do?externalId= tn_17534
如果你想假装你的内存不足,你可以这样做
public static void main(String[] args) {
throw new OutOfmemoryError();
}
我知道几种通过调用本机方法(内置方法)导致 JVM 转储错误文件的方法,但最好你不知道如何执行此操作。;)
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分钟后,注意你的内存)
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:
#
如果“崩溃”是指 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 作为服务运行,例如使用一些应用程序服务器,这不会很明显。再一次,它咬了我,所以我想分享它。)
最短?使用 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
消息destroy of current process not allowed。
如果将无限 for 循环更改为对同一函数的递归调用,则会出现堆栈溢出异常:
public static void main(String[] args) {
causeStackOverflow();
}
public void causeStackOverflow() {
causeStackOverflow();
}
我现在正在这样做,但不完全确定如何...... :-) JVM(和我的应用程序)有时会完全消失。没有抛出错误,没有任何记录。从工作到完全不运行,没有任何警告。
尝试复制 JVM 崩溃时遇到此问题。
Jni 可以工作,但需要针对不同的平台进行调整。最终,我使用这种组合使 JVM 崩溃
-XX:+CrashOnOutOfMemoryError
long[] l = new long[Integer.MAX_VALUE];
触发 OOM然后JVM会崩溃并生成崩溃日志。
如果“崩溃”是任何中断 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
}
所以,这取决于什么类型的崩溃?!