题
我正在读一本关于编程技能的书,作者问受访者:“您如何崩溃JVM?”我认为您可以通过编写无限的循环来做到这一点,该循环最终将消耗所有记忆。
有人有什么想法吗?
解决方案
最接近单一“答案”的是 System.exit()
它会立即终止 JVM,而不会进行适当的清理。但除此之外,本机代码和资源耗尽是最有可能的答案。或者,您可以在 Sun 的错误跟踪器上查找您的 JVM 版本中的错误,其中一些错误允许重复的崩溃情况。我们过去在 32 位版本下接近 4 Gb 内存限制时会发生半定期崩溃(现在我们通常使用 64 位)。
其他提示
我不会将抛出 OutOfMemoryError 或 StackOverflowError 称为崩溃。这些只是正常的例外情况。要真正使虚拟机崩溃,有 3 种方法:
- 使用 JNI 并在本机代码中崩溃。
- 如果未安装安全管理器,您可以使用反射来使虚拟机崩溃。这是特定于 VM 的,但通常 VM 会在私有字段中存储一堆指向本机资源的指针(例如指向本机线程对象的指针存储在一个长字段中 java.lang.Thread)。只要通过反射改变它们,虚拟机迟早会崩溃。
- 所有虚拟机都有错误,因此您只需触发一个即可。
对于最后一种方法,我有一个简短的示例,它将很好地使 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. 。事实上,对于 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:.碰撞
我来这里是因为我也遇到了这个问题 充满激情的程序员, ,查德·福勒着。对于那些无法获得副本的人来说,这个问题被认为是对面试需要“真正优秀的 Java 程序员”的职位的候选人的一种过滤/测试。
具体来说,他问:
您将如何用纯 Java 编写一个会导致 Java 虚拟机崩溃的程序?
我已经使用 Java 编程超过 15 年了,我发现这个问题既令人费解又不公平。正如其他人指出的那样,Java 作为一种托管语言,是专门设计的 不要崩溃. 。当然,JVM 错误总是存在,但是:
- 经过 15 年以上生产级 JRE 的使用,这种情况很少见。
- 任何此类错误都可能在下一个版本中得到修补,那么作为程序员,您有多大可能遇到并回忆起当前 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
或类似的异常,这又是 并不是真正的崩溃.
那么还剩下什么呢?我真的很想听听作者真正想要的正确解决方案。
更新:查德·福勒 在这里回复.
附:这是一本很棒的书。我在学习 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 仍然不应该崩溃。
如果您可以在虚拟机的源代码中找到错误,例如导致虚拟机实现的内存使用出现分段错误,那么您实际上可以使其崩溃。
如果您想让 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虚拟机 Jon Meyer 提供了一个导致 JVM 核心转储的一系列字节码指令的示例。我找不到这本书的副本。如果有人有,请查找并发布答案。
在 winxpsp2 上 w/wmp10 jre6.0_7
Desktop.open(uriToAviOrMpgFile)
这会导致生成的线程抛出未捕获的 Throwable 并导致热点崩溃
青年MMV
损坏的硬件可能会使任何程序崩溃。我曾经遇到过一个应用程序在特定机器上可重复崩溃的情况,而在具有完全相同设置的其他机器上运行良好。结果发现那台机器的内存有故障。
最短的可能的方式:)
public class Crash
{
public static void main(String[] args)
{
main(args);
}
}
不是崩溃,但比使用的接受的答案更接近崩溃 System.exit
您可以通过调用来停止 JVM
Runtime.getRuntime().halt( status )
根据文档:-
“如果启用了退出时终结,此方法不会导致关闭挂钩启动,并且不会运行未调用的终结器”。
这里详细解释了导致 JVM 核心转储的原因(即碰撞):http://kb.adobe.com/selfservice/viewContent.do?externalId=tn_17534
如果您将崩溃定义为由于未处理的情况(即,没有 Java 异常或错误),那么这不能从 Java 内部完成(除非您有权使用 sun.misc.Unsafe 类)。这就是托管代码的全部要点。
本机代码中的典型崩溃是由于取消引用指向错误内存区域(空地址或未对齐)的指针而发生的。另一个来源可能是非法机器指令(操作码)或来自库或内核调用的未处理信号。如果 JVM 或系统库有错误,则两者都会被触发。
例如,JITed(生成的)代码、本机方法或系统调用(图形驱动程序)可能会出现导致真正崩溃的问题(当您使用 ZIP 函数并且内存不足时发生崩溃是很常见的)。在这些情况下,JVM 的崩溃处理程序会启动并转储状态。它还可以生成操作系统核心文件(Dr.Windows 上的 Watson 和 *nix 上的核心转储)。
在 Linux/Unix 上,您可以通过向正在运行的进程发送信号轻松地使 JVM 崩溃。笔记:你不应该使用 SIGSEGV
为此,因为 Hotspot 会捕获此信号,并在大多数地方将其作为 NullPointerException 重新抛出。所以最好发送一个 SIGBUS
例如。
如果你想假装你的内存不足,你可以这样做
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:
#
最短?使用Robot类来触发CTRL+BREAK。当我试图在不关闭控制台的情况下关闭程序时,我发现了这一点(它没有“退出”功能)。
我现在正在做,但不完全确定如何...:-) JVM(和我的应用程序)有时会完全消失。没有抛出任何错误,也没有记录任何内容。在没有任何警告的情况下立即从工作变为根本不运行。
这算不算?
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
与消息 不允许破坏当前进程.
如果将无限 for 循环更改为对同一函数的递归调用,则会出现堆栈溢出异常:
public static void main(String[] args) {
causeStackOverflow();
}
public void causeStackOverflow() {
causeStackOverflow();
}
如果“崩溃”是任何中断 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
}
那么,这取决于什么类型的崩溃?!