你指尖跃动的炸那电光,是拟机我此生不灭的信仰,唯我超电磁炮永世长存。炸那
瞬间爆炸,拟机完成单杀。炸那
深度长文,拟机非常非常长,炸那执行这些程序可能导致机器完全死机,拟机请遵照指示安全开车。炸那
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对象,堆满了就直接爆炸了。
妥妥的。小伙伴们拿回去好好玩吧,就酱。
【本文为专栏作者“大蕉”的原创稿件,转载请通过作者微信公众号“一名叫大蕉的程序员”获取授权】
戳这里,看该作者更多好文