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

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


瞬间爆炸,完成单杀。


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


640?wx_fmt=png


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


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


0?wx_fmt=png


真实的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对象,堆满了就直接爆炸了。





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


下一次,想看到些什么呢?下面留言告诉我吧。你的转发点赞留言就是对我最大的支持~




赞赏一下,开心一周

0?wx_fmt=jpeg


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值