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[] args) throws 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[] alloc1, alloc2, alloc3, alloc4;
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 垃圾回收器
输出的信息:
{Heap before GC invocations=0 (full 0):
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)
No shared spaces configured.
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.
}
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垃圾回收器
垃圾回收大致的共有四种常见的回收机制,主要是:
- 串行垃圾回收器(Serial Garbage Collector)
- 并行垃圾回收器(Parallel Garbage Collector)
- 并发标记扫描垃圾回收器(CMS Garbage Collector)
- G1垃圾回收器(G1 Garbage Collector)
- 具体的哪一种,在什么场合使用,我们在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。
非常感谢: