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

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

瞬间爆炸,完成单杀。

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

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

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

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

PSYoungGen      total 3072K, used 128K
    eden space 2560K, 5% used 
    survivor  space
        from space 512K, 0% used 
         to   space 512K, 0% used 

ParOldGen       total 6656K, used 408K
    object space 6656K, 6% used  

PSPermGen       total 4096K, used 3039K   
    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

首先咱的主类长这样。

public class BlowUpJVM {
}

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

- [√] 栈深度溢出

- [ ] 永久代内存溢出

- [ ] 本地方法栈溢出

- [ ] JVM栈内存溢出

- [ ] 堆溢出

public static void  testStackOverFlow(){
      BlowUpJVM.testStackOverFlow();
}

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


- [ ] 栈深度溢出

- [√] 永久代内存溢出

- [ ] 本地方法栈溢出

- [ ] JVM栈内存溢出

- [ ] 堆溢出

public static void testPergemOutOfMemory1(){
   //方法一失败
    List<String> list = new ArrayList<String>();

   while(true){
      list.add(UUID.randomUUID().toString().intern());
   }
}

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

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

public static void testPergemOutOfMemory2(){
   try {
      while (true) {
         Enhancer enhancer = new Enhancer();
         enhancer.setSuperclass(OOM.class);
         enhancer.setUseCache(false);
         enhancer.setCallback(new MethodInterceptor() {
            @Override
            public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
               return proxy.invokeSuper(obj, args);
            }
         });
         enhancer.create();
      }
   }
   catch (Exception e){
      e.printStackTrace();
   }
}

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

public static void testPergemOutOfMemory3(){
   while(true){
   final OOM oom = new OOM();
   Proxy.newProxyInstance(oom.getClass().getClassLoader(), oom.getClass().getInterfaces(), new InvocationHandler() {
         public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            Object result = method.invoke(oom, args);
            return result;
         }
      });
   }
}

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


- [ ] 栈深度溢出

- [ ] 永久代内存溢出

- [√] 本地方法栈溢出

- [ ] JVM栈内存溢出

- [ ] 堆溢出

public static void testNativeMethodOutOfMemory(){
   int j = 0;
   while(true){
      Printer.println(j++);
      ExecutorService executors = Executors.newFixedThreadPool(50);
      int i=0;
      while(i++<10){
         executors.submit(new Runnable() {
            public void run() {
            }
         });
      }
   }
}

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


- [ ] 栈深度溢出

- [ ] 永久代内存溢出

- [ ] 本地方法栈溢出

- [√] JVM栈内存溢出

- [ ] 堆溢出

public static void testStackOutOfMemory(){
    while (true) {  
            Thread thread = new Thread(new Runnable() {  
                   public void run() {
                          while(true){
                      }
                   }  
            });  
            thread.start();  
     }  
}

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


- [ ] 栈深度溢出

- [ ] 永久代内存溢出

- [ ] 本地方法栈溢出

- [ ] JVM栈内存溢出

- [√] 堆溢出

public static void testOutOfHeapMemory(){
   List<StringBuffer> list = new ArrayList<StringBuffer>();
   while(true){
      StringBuffer B = new StringBuffer();
      for(int i = 0 ; i < 10000 ; i++){
         B.append(i);
      }
      list.add(B);
   }
}

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


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

原文发布于微信公众号 - 一名叫大蕉的程序员(DaBananaTalk)

原文发表时间:2017-09-02

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏nice_每一天

转载 Java设计模式

设计模式; 一个程序员对设计模式的理解: “不懂”为什么要把很简单的东西搞得那么复杂。后来随着软件开发经验的增加才开始明白我所看到的“复杂”恰恰就是设计模式的精...

1252
来自专栏二进制文集

Google Protocol Buffers 序列化算法分析

分析一下 Google Protocol Buffers 的序列化原理。介绍参考 Google Protocol Buffers 数据交换协议

903
来自专栏漫漫深度学习路

pytorch学习笔记(十四): DataLoader源码阅读

pytorch 数据加载部分的 接口可以说是现存 深度学习框架中设计的最好的, 给了我们足够的灵活性。本博文就对 pytorch 的多线程加载 模块(Data...

2.1K9
来自专栏C语言及其他语言

【每日一题】问题 1209: 密码截获

Catcher是MCA国的情报员,他工作时发现敌国会用一些对称的密码 进行通信,比如像这些ABBA,ABA,A,123321,但是他们有时会在开始或结束时加入一...

1572
来自专栏逍遥剑客的游戏开发

C++的反射和序列化

1632
来自专栏博客园

.NET面试题解析(06)-GC与内存管理

转自:http://www.cnblogs.com/anding/p/5260319.html

1332
来自专栏Python中文社区

深度剖析isinstance的检查机制

通过内建方法 isinstance(object, classinfo) 可以判断一个对象是否是某个类的实例。但你是否想过关于鸭子协议的对象是如何进行判断的呢?...

901
来自专栏好好学java的技术栈

Java11震撼发布了,我们该怎么办?

Java11已经发布了,我们今天聊聊大家还停留在哪个版本呢?大家对于新版本的迅速的发布有什么想说的呢?

2072
来自专栏算法与数据结构

拓扑排序 ——个人理解,仅供参考

贴代码: #include <bits/stdc++.h> using namespace std; #define maxn 100//可以根据题目条件进行更...

2118
来自专栏君赏技术博客

Object-C中的黑魔法

在Swift中存在Option类型,也就是使用?和!声明的变量。但是OC里面没有这个特征,因为在XCODE6.3之后出现新的关键词定义用于OC转SWIFT时候可...

1881

扫码关注云+社区

领取腾讯云代金券