中国领先的IT技术网站
|
|
创建专栏

来人啊给我炸了那个Java虚拟机

深度长文,非常非常长,执行这些程序可能导致机器完全死机,请遵照指示安全开车。

作者:大蕉|2017-09-04 16:51


你指尖跃动的电光,是我此生不灭的信仰,唯我超电磁炮永世长存。

瞬间爆炸,完成单杀。

深度长文,非常非常长,执行这些程序可能导致机器完全死机,请遵照指示安全开车。

JVM中分了两大块,公共区域和栈私有区域。公共区域中有堆,用来放对象的。还有方法区,用来放一些类信息啊,方法信息啊或者一些运行时的常量信息的。栈私有区域中有分为,PC寄存器(下一条操作指令地址),栈(临时的指针和数值)和本地方法区(native方法调用会用到)。

今天教大家怎么花式搞死Java虚拟机,顺便大概知道一下GC是啥,先了解一下JVM内存的结构吧。

真实的GC信息是长这样的。

  1. PSYoungGen      total 3072K, used 128K 
  2.     eden space 2560K, 5% used  
  3.     survivor  space 
  4.         from space 512K, 0% used  
  5.          to   space 512K, 0% used  
  6.  
  7. ParOldGen       total 6656K, used 408K 
  8.     object space 6656K, 6% used   
  9.  
  10. PSPermGen       total 4096K, used 3039K    
  11.     object space 4096K, 74% used  

一般的GC过程都是这样的,最先产生的对象,是可能最先就要消灭嘛~对象先在Eden区出生,过一段时间GC扫描,如果对象还能用,那就丢到Survivor区。如果再过一段时间还能用,那就继续丢到OldGen区。PerGem区呢,只会放一些Class类啊,方法啊,1.7之前字符串常量池也是放这里,只有Full GC的时候会进行回收。

有小伙伴就会问了,那为毛Survivor有两个区,from和to?这是其中一个GC策略,每次GC在对Survivor区扫描的时候呢,会把有用的从from 直接 复制到to区,这两个区是互相备份的,这样就减少了内存碎片的信息收集了,这样from-to-from-to来回来回好几次,才把他们丢到老年代。

好了,开始花式吊打JVM了,先指定一下我们今天的JVM配置,大家自己配上,啊。

  • -Xmx10m
  • -XX:MaxPermSize=5m
  • -XX:MaxDirectMemorySize=5m
  • -XX:+PrintGCDetails

首先咱的主类长这样。

  1. public class BlowUpJVM {  

既然说了是花式,今天的过程是这样的。

  • - [√] 栈深度溢出
  • - [ ] 永久代内存溢出
  • - [ ] 本地方法栈溢出
  • - [ ] JVM栈内存溢出
  • - [ ] 堆溢出
  1. public static void  testStackOverFlow(){ 
  2.       BlowUpJVM.testStackOverFlow(); 

栈不断递归,而且没有处理,所以虚拟机栈就不断深入不断深入,栈深度就这样爆炸了。

  • - [ ] 栈深度溢出
  • - [√] 永久代内存溢出
  • - [ ] 本地方法栈溢出
  • - [ ] JVM栈内存溢出
  • - [ ] 堆溢出
  1. public static void testPergemOutOfMemory1(){ 
  2.    //方法一失败 
  3.     List<String> list = new ArrayList<String>(); 
  4.  
  5.    while(true){ 
  6.       list.add(UUID.randomUUID().toString().intern()); 
  7.    } 

打算把String常量池堆满,没想到失败了,JDK1.7后常量池放到了堆里,也能进行垃圾回收了傲。

马上第二次尝试,使用cglib,用Class把老年代取堆满,嗯,说走咱就走啊。

  1. public static void testPergemOutOfMemory2(){ 
  2.    try { 
  3.       while (true) { 
  4.          Enhancer enhancer = new Enhancer(); 
  5.          enhancer.setSuperclass(OOM.class); 
  6.          enhancer.setUseCache(false); 
  7.          enhancer.setCallback(new MethodInterceptor() { 
  8.             @Override 
  9.             public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { 
  10.                return proxy.invokeSuper(obj, args); 
  11.             } 
  12.          }); 
  13.          enhancer.create(); 
  14.       } 
  15.    } 
  16.    catch (Exception e){ 
  17.       e.printStackTrace(); 
  18.    } 

虚拟机成功gg了,那JDK动态代理产生的类能不能撑爆呢?

  1. public static void testPergemOutOfMemory3(){ 
  2.    while(true){ 
  3.    final OOM oom = new OOM(); 
  4.    Proxy.newProxyInstance(oom.getClass().getClassLoader(), oom.getClass().getInterfaces(), new InvocationHandler() { 
  5.          public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 
  6.             Object result = method.invoke(oom, args); 
  7.             return result; 
  8.          } 
  9.       }); 
  10.    } 

答案是不行!会进行回收。JDK动态代理产生的类信息,不会放到永久代中,而是放在堆中。

  • - [ ] 栈深度溢出
  • - [ ] 永久代内存溢出
  • - [√] 本地方法栈溢出
  • - [ ] JVM栈内存溢出
  • - [ ] 堆溢出
  1. public static void testNativeMethodOutOfMemory(){ 
  2.    int j = 0; 
  3.    while(true){ 
  4.       Printer.println(j++); 
  5.       ExecutorService executors = Executors.newFixedThreadPool(50); 
  6.       int i=0; 
  7.       while(i++<10){ 
  8.          executors.submit(new Runnable() { 
  9.             public void run() { 
  10.             } 
  11.          }); 
  12.       } 
  13.    } 

这个的原理就是不断创建线程池,而每个线程池都创建10个线程,这些线程池都是在本地方法区的,久而久之,本地方法区就爆炸了。

  • - [ ] 栈深度溢出
  • - [ ] 永久代内存溢出
  • - [ ] 本地方法栈溢出
  • - [√] JVM栈内存溢出
  • - [ ] 堆溢出
  1. public static void testStackOutOfMemory(){ 
  2.     while (true) {   
  3.             Thread thread = new Thread(new Runnable() {   
  4.                    public void run() { 
  5.                           while(true){ 
  6.                       } 
  7.                    }   
  8.             });   
  9.             thread.start();   
  10.      }   

线程的创建会直接在JVM栈中创建,但是本例子中,没看到爆炸,主机先挂了,不是JVM挂了,真的是主机挂了,无论在mac还是在windows,都挂了。温馨提示,这个真的会死机的。。

  • - [ ] 栈深度溢出
  • - [ ] 永久代内存溢出
  • - [ ] 本地方法栈溢出
  • - [ ] JVM栈内存溢出
  • - [√] 堆溢出
  1. public static void testOutOfHeapMemory(){ 
  2.    List<StringBuffer> list = new ArrayList<StringBuffer>(); 
  3.    while(true){ 
  4.       StringBuffer B = new StringBuffer(); 
  5.       for(int i = 0 ; i < 10000 ; i++){ 
  6.          B.append(i); 
  7.       } 
  8.       list.add(B); 
  9.    } 

好了终于到了最简单的环节,不断往堆中塞新增的StringBuffer对象,堆满了就直接爆炸了。

妥妥的。小伙伴们拿回去好好玩吧,就酱。

【本文为51CTO专栏作者“大蕉”的原创稿件,转载请通过作者微信公众号“一名叫大蕉的程序员”获取授权】

戳这里,看该作者更多好文

【编辑推荐】

  1. 外媒速递:如何为你的React项目选择最佳JavaScript测试工具
  2. 细说 Java 的深拷贝和浅拷贝
  3. Java帝国之宫廷内斗
  4. 云计算市场明年见分晓 |青云QingCloud以IoT、AI赋能,全面构筑未来人类社会基石
  5. 如何用30行JavaScript代码编写神经网络异或运算器
【责任编辑:武晓燕 TEL:(010)68476606】

点赞 0
分享:
大家都在看
猜你喜欢

热门职位+更多

× 学习达标赢Beats耳机