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。

非常感谢:




posted @ 2015-10-28 18:48  长天秋水落霞孤鹜  阅读(310)  评论(0编辑  收藏  举报