• JVM探索(一)


    JVM测试的样例代码:
    import java.lang.management.ManagementFactory;
    /**
     * @author zhailzh
     * 
     * @Date 2015年10月31日——下午14:18:15
     * 
     */
    public class Main {
      private static final int _1m = 1024 * 1024;
      public static void main(String[] argsthrows Exception {
        String name = ManagementFactory.getRuntimeMXBean().getName();
        System.out.println(name);
        // get pid
        String pid = name.split("@")[0];
        System.out.println("Pid is:" + pid);
        Thread.sleep(80000);
        byte[] alloc1alloc2alloc3alloc4;
        alloc1 = new byte[2 * _1m];
        Thread.sleep(2000);
        alloc2 = new byte[2 * _1m];
        Thread.sleep(2000);
        alloc3 = new byte[2 * _1m];
        Thread.sleep(2000);
        alloc4 = new byte[4 * _1m];
        Thread.sleep(6000);
      }
    }  
    1. 我们使用的配置是:
    -Xmx20M                 最大堆的大小          
    -Xms20M                 初始堆的大小
    -Xmn10M                 新生代的大小
    -XX:+PrintHeapAtGC    打印垃圾回收前后的信息
     -XX:+UseParNewGC   使用serial/serial old 垃圾回收器
    输出的信息:
    1. {Heap before GC invocations=0 (full 0):
    2. par new generation total 9216K, used 7145K [0x00000000f9a00000, 0x00000000fa400000, 0x00000000fa400000)
    3. eden space 8192K, 87% used [0x00000000f9a00000, 0x00000000fa0fa540, 0x00000000fa200000)
    4. from space 1024K, 0% used [0x00000000fa200000, 0x00000000fa200000, 0x00000000fa300000)
    5. to space 1024K, 0% used [0x00000000fa300000, 0x00000000fa300000, 0x00000000fa400000)
    6. tenured generation total 10240K, used 0K [0x00000000fa400000, 0x00000000fae00000, 0x00000000fae00000)
    7. the space 10240K, 0% used [0x00000000fa400000, 0x00000000fa400000, 0x00000000fa400200, 0x00000000fae00000)
    8. compacting perm gen total 21248K, used 2792K [0x00000000fae00000, 0x00000000fc2c0000, 0x0000000100000000)
    9. the space 21248K, 13% used [0x00000000fae00000, 0x00000000fb0ba1d0, 0x00000000fb0ba200, 0x00000000fc2c0000)
    10. No shared spaces configured.
    11. Heap after GC invocations=1 (full 0):
    12. par new generation total 9216K, used 541K [0x00000000f9a00000, 0x00000000fa400000, 0x00000000fa400000)
    13. eden space 8192K, 0% used [0x00000000f9a00000, 0x00000000f9a00000, 0x00000000fa200000)
    14. from space 1024K, 52% used [0x00000000fa300000, 0x00000000fa387788, 0x00000000fa400000)
    15. to space 1024K, 0% used [0x00000000fa200000, 0x00000000fa200000, 0x00000000fa300000)
    16. tenured generation total 10240K, used 6144K [0x00000000fa400000, 0x00000000fae00000, 0x00000000fae00000)
    17. the space 10240K, 60% used [0x00000000fa400000, 0x00000000faa00030, 0x00000000faa00200, 0x00000000fae00000)
    18. compacting perm gen total 21248K, used 2792K [0x00000000fae00000, 0x00000000fc2c0000, 0x0000000100000000)
    19. the space 21248K, 13% used [0x00000000fae00000, 0x00000000fb0ba1d0, 0x00000000fb0ba200, 0x00000000fc2c0000)
    20. No shared spaces configured.
    21. }

    jstat监测:



    首先对jstat上面的指标进行介绍:S0、S1 代表的是幸存1区,幸存2区或者叫做from或者to区。E 代表伊甸区,P代表老年代,
    YGC垃圾回收的次数,T代表时间,FGC代表full gc。c 代表总量, u代表使用量。那么S0C表示幸存1区
    的总容量为512M,S0U表示幸存1区使用了0M,EC表示伊甸区的容量,OU老年代的使用的量。


    那我们来分析输出的日志:
    第一行:Heap before GC invocations=1 (full 0), 翻译过来就是:
    触发第一次GC调用,FullGC调用为0的时候

    第2到9行:
    par new generation total 9216K, used 7145K [0x00000000f9a00000, 0x00000000fa400000, 0x00000000fa400000)
    eden space 8192K, 87% used [0x00000000f9a00000, 0x00000000fa0fa540, 0x00000000fa200000)
    from space 1024K, 0% used [0x00000000fa200000, 0x00000000fa200000, 0x00000000fa300000)
    to space 1024K, 0% used [0x00000000fa300000, 0x00000000fa300000, 0x00000000fa400000)
    tenured generation total 10240K, used 0K [0x00000000fa400000, 0x00000000fae00000, 0x00000000fae00000)
    the space 10240K, 0% used [0x00000000fa400000, 0x00000000fa400000, 0x00000000fa400200, 0x00000000fae00000)
    compacting perm gen total 21248K, used 2792K [0x00000000fae00000, 0x00000000fc2c0000, 0x0000000100000000)
    the space 21248K, 13% used [0x00000000fae00000, 0x00000000fb0ba1d0, 0x00000000fb0ba200, 0x00000000fc2c0000)

    par new generation 表示我们使用的设置:年轻代使用的是serial垃圾回收器
    垃圾回收大致的共有四种常见的回收机制,主要是:
    1. 串行垃圾回收器(Serial Garbage Collector)
    2. 并行垃圾回收器(Parallel Garbage Collector)
    3. 并发标记扫描垃圾回收器(CMS Garbage Collector)
    4. G1垃圾回收器(G1 Garbage Collector)
    5. 具体的哪一种,在什么场合使用,我们在JVM探索(二)中进行说明。此次不作为重点

    我们知道默认使用的是串行垃圾回收器,我们首先看一下内存的模型和第一次触发
    先贴上两张图,
    图1:

    图2:

    这里说明一点:PermanentSpace就是在hotspot 的JVM的永久代,也可以说是上面的非堆的内存的部分
    我们根据jstat检测的输出的图片可以退出m,n默认的大小:


    初始化堆的大小为20M,最大的也是20M,那么我们看一下具体的分配:伊甸区为8192 ,伊甸1区1024,伊甸2区1024,所以说SurvivorRatio
    的默认值为8,不过hotSpot的文档上面说默认值为32

    文档的名称是:Memory Management in the Java HotSpot™ Virtual Machine  -------Sun Microsystems April 2006

    那么:m值呢?NewRatio 新生代的大小为: 10240:(1024*2+ 8192 )=1, 因为新生代的大小直接通过-xmn 来进行
    设定。这个就和文档上面的说的默认值不一样的。



    总的堆大小是20M,分配开来就是1024*2+8192+10240= 20M 。 
    持久代的大小为:21248,大约是20M,印象中默认的方法区或者持久带的大小和整个堆的大小差不多。

    JVM的堆中内存的分配,我们已经清楚了,现在我们看一下GC是如何触发的,先看触发前,各个区的大小:

    第2到9行:

    par new generation total 9216K, used 7145K [0x00000000f9a00000, 0x00000000fa400000, 0x00000000fa400000)
    eden space 8192K, 87% used [0x00000000f9a00000, 0x00000000fa0fa540, 0x00000000fa200000)
    from space 1024K, 0% used [0x00000000fa200000, 0x00000000fa200000, 0x00000000fa300000)
    to space 1024K, 0% used [0x00000000fa300000, 0x00000000fa300000, 0x00000000fa400000)
    tenured generation total 10240K, used 0K [0x00000000fa400000, 0x00000000fae00000, 0x00000000fae00000)
    the space 10240K, 0% used [0x00000000fa400000, 0x00000000fa400000, 0x00000000fa400200, 0x00000000fae00000)
    compacting perm gen total 21248K, used 2792K [0x00000000fae00000, 0x00000000fc2c0000, 0x0000000100000000)
    the space 21248K, 13% used [0x00000000fae00000, 0x00000000fb0ba1d0, 0x00000000fb0ba200, 0x00000000fc2c0000)

    新生代的总的大小为9216,eden为8192,from为1024,to为1024。
    (新生代的大小就是eden+一个幸存区from的大小,这个比较的有意思,为什么分为了三个部分?为什么不直接就是两部分,幸存区为什么被分为了两个?)
    数据和jstat集测的大小一样:

    我们从这个更加详细的截图中,可以看到内存的分配1001.3-> 3049.3 分配了2048,也就是2M的内存,对应的java语句就是:
    alloc1 = new byte[2 * _1m];
    首先分配的是伊甸区的内存,然后就是3049.3--->5097.3分配了2048,还是2M的内存,对应的java语句就是:
    alloc2 = new byte[2 * _1m];
    然后下一个java语句就是:
    alloc3 = new byte[2 * _1m];; 按照原来的分配方式到了7145.3
    alloc4 = new byte[4 * _1m];

    在分配4M的时候,可定会大于伊甸区的大小8192,所以触发了minor gc 

    资料或者网上搜集的资料都显示:触发minor gc的条件是 eden区满了的时候会触发一次,新生代
    的大小是eden+from的大小?为什么不是eden+from满了以后,在触发minor gc呢?

    触发一次YGC:输出的日志是:
    Heap after GC invocations=1 (full 0):
    par new generation total 9216K, used 541K [0x00000000f9a00000, 0x00000000fa400000, 0x00000000fa400000)
    eden space 8192K, 0% used [0x00000000f9a00000, 0x00000000f9a00000, 0x00000000fa200000)
    from space 1024K, 52% used [0x00000000fa300000, 0x00000000fa387788, 0x00000000fa400000)
    to space 1024K, 0% used [0x00000000fa200000, 0x00000000fa200000, 0x00000000fa300000)
    tenured generation total 10240K, used 6144K [0x00000000fa400000, 0x00000000fae00000, 0x00000000fae00000)
    the space 10240K, 60% used [0x00000000fa400000, 0x00000000faa00030, 0x00000000faa00200, 0x00000000fae00000)
    compacting perm gen total 21248K, used 2792K [0x00000000fae00000, 0x00000000fc2c0000, 0x0000000100000000)
    the space 21248K, 13% used [0x00000000fae00000, 0x00000000fb0ba1d0, 0x00000000fb0ba200, 0x00000000fc2c0000)
    No shared spaces configured.

    YGC的基本的动作:
    • 检索heap中的对象,将还能通过GC roots能够遍历到的对象copy到to区中,由于上面分配的alloc1 ,alloc2 ,alloc3均活着,所以需要copy到to里面去 
    • 如果需要copy的对象没法进入from区中,触发分配担保机制,则将其晋升到老年代,本例中即发生了这种情况,3个2MB的数组全部晋升到老生代(OU:6144)
    • 清理eden和from中无用的垃圾
    • 互换from和to空间
    申请最后一个的4M的内存的时候,还是放在了伊甸区,伊甸区的使用变为了4423.9。

    非常感谢:




  • 相关阅读:
    Quora的技术探索
    基于移动互联网的电子商务个性化推荐的一些思考
    想个法子找出性能差的SQL
    结束——2012年微软精英挑战赛
    sql 查询时间执行长的sql语句
    NET好书盘点
    最后两个编程题
    网络七层协议
    使用WCF创建Restful服务
    一步一步学Entity FrameWork 4
  • 原文地址:https://www.cnblogs.com/zhailzh/p/0242cea8863dd91ffca50596d25f7bae.html
Copyright © 2020-2023  润新知