今天在书上看到一个做者提出一个问题“怎样经过编写Java代码让Jvm崩溃”,我看了以后也不懂。带着问题查了一下,百度知道里面有这样一个答案:java
1 package jvm; 2 3 public class Crash { 4 public static void main(String[] args) { 5 6 //Object[] o = {“abc”};初始值赋值,不会有影响。 7 Object[] o = null; 8 9 while (true) { 10 o = new Object[] { o }; 11 //输出的话,jvm就不会崩溃。 12 //System.out.println(o); 13 } 14 } 15 }
程序运行十几秒以后,控制台会出现这样的错误:数组
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at jvm.Crash.main(Crash.java:10)
很明显,超出内存空间错误。jvm
我将原程序随意改了一下,如赋初始值等,对程序无影响。函数
但是我将死循环中的o输出在控制台的时候,jvm竟然一直都不崩,为何输出的话,就不会超出内存空间呢?测试
我看来,原程序可以使Jvm崩溃,是由于死循环中,经过旧对象,不断建立出新的对象,即创造的对象是互相引用的,因此GC是不会回收它们的,形成堆栈溢出。this
仿照这个例子,我写了一个简单的类,模仿例子程序中的数组,以下:spa
1 package jvm; 2 3 public class JvmBean { 4 5 JvmBean bean = new JvmBean(this); 6 7 public JvmBean(JvmBean bean){ 8 this.bean = bean; 9 } 10 }
而后简单测试,以下:code
1 package jvm; 2 3 public class MyCrash { 4 5 public static void main(String[] args) { 6 JvmBean j = null; 7 while(true){ 8 j = new JvmBean(j); 9 //不管输出不输出,jvm都会崩溃 10 //System.out.println(j); 11 } 12 } 13 }
结果即是控制台输出以下的错误:对象
Exception in thread "main" java.lang.StackOverflowError at jvm.JvmBean.<init>(JvmBean.java:5) at jvm.JvmBean.<init>(JvmBean.java:5) at jvm.JvmBean.<init>(JvmBean.java:5) at jvm.JvmBean.<init>(JvmBean.java:5) at jvm.JvmBean.<init>(JvmBean.java:5)
一长串的"at jvm.JvmBean.<init>(JvmBean.java:5)",后面的被我省略了。blog
结果看来,一样也形成了jvm崩溃,但是错误类型跟例子程序的不一样,说堆栈溢出错误,而且不管是否输出,错误都同样发生,为何呢?
因为评论的两位老兄的热心指点,两个问题都水落石出了!
这里过一下整个流程。
第一个异常 结合天添老兄说的,Exception in thread "main" java.lang.OutOfMemoryError: Java heap space at jvm.Crash.main(Crash.java:10)是由于程序没法申请到足够的内存的时候抛出的异常,Object数组o不断指向新的Object数组,数组元素是原来的Object数组,这使得Object维数愈来愈高。不断申请内存空间,最终致使超出jvm中堆的最大值。堆内存溢出。为何输出打印,时间会延长呢?yahokuma老兄一言惊醒梦中人!输出打印的话,虚拟机并非不会崩溃,而是崩溃的时间大大延长了。而崩溃时间延长实际上是假象,是由于输出属于IO事件,每次输出CPU都被中断,IO很耗时,因此,感受上才会时间延长。
第二个异常,yahokuma 老兄在下面评论中已经说的很清楚了,我这里搬过来——“类内部的静态属性 > 静态块 > 对象属性 > 构造方法。注意这一点,那就是说 bean属性会先于JvmBean的构造函数被初始化。在你main函数中,new一个 JvmBean的构造函数以前,类内部的JvmBean对象要优先被初始化,这个类内部的属性bean的内部一样也包含了一个JvmBean对象须要被初始化,成循环调用,造 成了栈溢出。”因此异常才会是这个——Exception in thread "main" java.lang.StackOverflowError
我把原JvmBean改一下
1 package jvm; 2 3 public class JvmBean { 4 5 JvmBean bean = null; 6 7 public JvmBean(JvmBean bean){ 8 this.bean = bean; 9 } 10 }
这样最终获得的结果跟第一个例子同样了。
如何使Jvm崩溃呢?若是想使它堆内存空间不足,形成典型的内存泄漏,能够建立对象,使它们不断向深层次引用。产生Exception in thread "main" java.lang.OutOfMemoryError: Java heap space 这样的错误。若是想使他们栈空间不足,最简单的,就是在方法里,如构造方法里不断申请新的内存空间就够了,如我第二个错误例子的示范。