记一次模拟jvm的OOM

来源:互联网 发布:高校教师 知乎 编辑:程序博客网 时间:2024/05/08 01:53

模拟一次jvm的OOM异常

在学习jvm的时候,听前辈建议自己手打一遍周志明大侠写的深入理解jvm虚拟机上的代码。这本书要多看几遍,看了是1年前,那个时候刚接触java也就1年,看的是云里雾里。现在看了第二遍,能理解一些jvm中内存分区,gc的分类,基本异常造成的原因,其中尝试openjdk下载失败了几次,下一次读的时候把这个作为点。

代码

代码和jvm的参数都是来自于周大侠的书,本次gc回收器采用默认的,没有设置。直接贴上。

/** * Created by carey on 2016/7/9 0009. * jvm运行参数 * -verbose:gc -Xms20M -Xmx20M -Xmn10M -XX:+PrintGCDetails -XX:SurvivorRatio=8 * 堆大小设置为20M,新生代10M,新生代中可用的是9M,剩余1M用来垃圾收集器回收的交换区 * */public class OutOfMemory {    static class  OOMObject{    }    public static void main(String[] args) {        List<OOMObject> list = new ArrayList<OOMObject>();        while(true){            list.add(new OOMObject());        }    }}

启动参数的含义为堆大小固定,20M。其中新生代10M,老生代剩下10M,新生代中8:1:1的配置,实际可用9M

日志分析

不同机子,打印出来的日志肯定不一样。我以自己的为例子。

第一条输出的小gc日志

[GC [PSYoungGen: 7768K->1016K(9216K)] 7768K->5289K(19456K), 0.0129323 secs] [Times: user=0.02 sys=0.00, real=0.01 secs]

具体分析:

这次的小gc实际时间为0.01s

       新生代gc:垃圾收集工作前可用的空间为7768K,收集后剩下了1016K,回收了6752K           位置      工作前被占用的空间   工作后被占空的空间   总共的空间       回收的空间           新生代:     7768K                  1016K         9216K            6752K           整个堆       7768K                  5289K         19456K           2479K(这才是真正被回收掉的空间)           新生代被回收的空间 —— 整个堆回收的空间 = 新生代被交换到老生带的空间。
总共的空间和jvm设置的参数是匹配的。这一次小gc有2479K真的被回收了,有4273K从新生代转到了老生代。### 第一条输出的大gc日志`[Full GC [PSYoungGen: 9208K->0K(9216K)] [ParOldGen: 10232K->9816K(10240K)] 19440K->9816K(19456K) [PSPermGen: 3042K->3041K(21504K)], 0.3411768 secs] [Times: user=0.45 sys=0.00, real=0.34 secs]`具体分析:这次大gc实际时间为0.34s。大gc的时候明显是大于小gc。
            位置      工作前被占用的空间   工作后被占空的空间  总共的空间   回收的空间           新生代:     9208K                  0K           9216K       9208K           老生代       10232K                9816K        10240K        416k           整个堆       19440K                  9816K      19456K       9624K(这才是真正被回收掉的空间)           方法区       3042K                   3041K      21504K         1K           新生代升级到老生代的空间为9208K+416K-9624k = 0

一次大gc肯定会引发一次小gc。大gc会回收方法区,而小gc不会。
从系统时间上来看,这次的大gc并不是第一次执行的大gc。

疑惑点

  1. 这段代码看上起没有可以被回收的变量,为什么还是会有空间能够被回收?
  2. gc的日志不是按照时间顺序来的,应该是会用队列或者数组来实现,难道是有多条线程用来打印?
  3. 从后面几条的日志来看,一次大gc还没有完成,就开始了另外一次gc。
  4. 为什么在这里大gc中方法区也能被回收?

如果有大神知道,请告诉小弟,不甚感激~

0 0