垃圾回收一般指回收方法区和堆区的内存
一、判断对象已死
1、引用计数法?
2、可达性分析算法!
GC Roots作为对象起点,从节点开始搜索,搜索走过的路径称为引用链。当对象到GCRoots没有任何引用链时,即对象不可达时,对象即可回收。
GC Roots对象:
虚拟机栈(栈帧中的本地变量表)中引用的对象;
方法区中类静态属性引用的对象
方法区中常量引用的对象
本地方法栈中JNI(Native方法)引用的对象
即以下对象会成为GC Roots
• System class:被bootstrap或者system类加载器加载的类,比如rt.jar里的java.util.*;
• JNI local:native代码里的local变量,比如用户定义的JNI代码和JVM的内部代码;
• JNI global:native代码里的global变量;
• Thread block:当前活跃的线程block中引用的对象;
• Thread:已经启动并且没有stop的线程;
• busy monitor:被调用了wait()或者notify()或者被synchronized同步的对象,如果是synchronized方法,那么静态方法指的类,非静态方法指的是对象;
• java local:local变量,比如方法的入参和方法内创建的变量;
• native stack:native代码里的出入参数,比如file/net/IO方法以及反射的参数;
• finalizable:在一个队列里等待它的finalizer 运行的对象;
• unfinalized:一个有finalize方法的对象,还没有被finalize,同时也没有进入finalizer队列等待finalize;
• unreachable:不会被触碰到的对象,在MAT里被标记为root用来retain object,否则是不会在分析中出现的;
• java stack frame:java栈帧包含了本地变量,当dump被解析时且在preferences里设置过把栈帧当做对象,这时才会产生;
• unknown:位置的root类型。
下图表示可回收对象:
3、引用的类型
强引用(Strong reference)
软引用(Soft reference)
弱引用(Weak reference)
虚引用(Phantom reference)
4、对象死亡判断
经历两次GC标记过程;
finalize方法不可靠
二、方法区回收
永久代垃圾回收部分:废弃常量和无用的类
无用的类:
此类所有实例已经全部被回收;
加载该类的ClassLoader被回收;
该类对应的java.lang.Class对象没有在任何地方被引用;
三、垃圾回收
1、回收算法 :
1)、标记-清除算法;
2)、复制算法
3)、标识-整理算法
4)、分代收集算法
2、安全点(safe point)&安全区域
到达安全点时,线程才能停下来进行GC。
安全点一般指程序长时间执行的时刻,比如方法调用、循环跳转,异常跳转;
3、垃圾收集器
种类:Hotspo虚拟机可使用的垃圾回收器,连线表示可以共存
- 、串行收集器:
适用于client模式下。
回收过程如下:
2)ParNew收集器:
Server模式,新生代使用
-XX:+UseConMarkSweepGC,新生代默认ParNew;
3)Parallenge Scanvenge
目标 :达到可控的吞量
4)Serial Old
收回老年代
5)Parallen Old
+Parallenge Scanvenge
6)CMS收集器
7)G1
垃圾回收日志格式:
参数总结:
4、内存分配和回收策略
1)、对象优先分配在Eden区,当不足时,发起Minor GC;
测试代码:
public class TestAllocation { private static final int _1MB = 1024 *1024; public static void main(String[] args) { /** * VM 参数:-verbose:gc -Xms20M -Xmx20M -Xmn10M -XX:+PrintGCDetails -XX:SurvivorRatio=8 */ byte[] a,b,c,d; a = new byte[2*_1MB]; b = new byte[2*_1MB]; c = new byte[2*_1MB]; d = new byte[4*_1MB]; } }
1
2
3
4
5
6
7
8
9
10
11
12
13
输出日志 :
```java
Heap
PSYoungGen total 9216K, used 6651K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
eden space 8192K, 81% used [0x00000000ff600000,0x00000000ffc7eec8,0x00000000ffe00000)
from space 1024K, 0% used [0x00000000fff00000,0x00000000fff00000,0x0000000100000000)
to space 1024K, 0% used [0x00000000ffe00000,0x00000000ffe00000,0x00000000fff00000)
PSOldGen total 10240K, used 4096K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)
object space 10240K, 40% used [0x00000000fec00000,0x00000000ff000010,0x00000000ff600000)
PSPermGen total 21248K, used 2990K [0x00000000f9a00000, 0x00000000faec0000, 0x00000000fec00000)
object space 21248K, 14% used [0x00000000f9a00000,0x00000000f9ceb8d8,0x00000000faec0000)
可见JDK6默认的回收器是UseParallelGC ,即Parallel Scanvenge回收器。上面没有发生GC。根据surivorRatio=8,-Xmn=10,知道新生代分配为10M,eden区为8M,而from和to分别为1M,当abc三个对象分配时,都分配到了eden,当d分配时,因为eden区已经不够用,理论上应该发生GC回收,但因为from区实际上并没有空间存放回收的对象,因此,d直接分配在了老年代上而没有进行GC。
加上-XX:+UseSerialGC,即使用串行回收器,输出
1 | [GC [DefNew: 6487K->159K(9216K), 0.0041662 secs] 6487K->6303K(19456K), 0.0042166 secs] [Times: user=0.00 sys=0.02, real=0.01 secs] |
[GC [DefNew: 6487K->159K(9216K), 0.0041662 secs] 6487K->6303K(19456K), 0.0042166 secs]
表示进行了一次Minor回收,GC发生在新生代。新生代所占内存为eden+from的和。
可见分配d变量内存时,发起Minor GC,把a,b,c三个变量复制到了Old区,因为6M的大小无法放入Survivor区;最后d分配到了新生代的Eden上。
2)、大对象直接分配在老年代
可以设置-XX:+PretenureSizeThreshold=xx来强制大于多少M的大对象直接分配在老年代,避免在Eden区和两个Survivor区之间复制大对象的消耗;
需要注意的是Parallel Scavenge回收器不认识这个参数;
示范代码:
public class TestPretenureSizeThreshold {
private static final int _1MB = 1024 *1024;
public static void main(String[] args) { /** * VM 参数:-verbose:gc -Xms20M -Xmx20M -Xmn10M -XX:+PrintGCDetails -XX:SurvivorRatio=8 -XX:+UseParNewGC -XX:PretenureSizeThreshold=3145728 */ byte[] a = new byte[4*_1MB]; } }
3)、长期存活的对象进入老年代 经过一次Minor GC后,对象扔存活,移动到Survivor区,对象的年龄age + 1; 对象每在Survivor区经过一次GC, age就会+1; 默认15次GC后就移入到老年代中; -XX:MaxTenuringThreshold用来设置对象进入老年代的阈值; 设置-XX:MaxTenuringThreshold=1时,如下测试:1
2
3
4
5
6
7
8
9
10
11
12
13
14
可以看到,直接分配4M的对象在老年区:
```java
Heap
par new generation total 9216K, used 507K [0x00000000f9a00000, 0x00000000fa400000, 0x00000000fa400000)
eden space 8192K, 6% used [0x00000000f9a00000, 0x00000000f9a7ee98, 0x00000000fa200000)
from space 1024K, 0% used [0x00000000fa200000, 0x00000000fa200000, 0x00000000fa300000)
to space 1024K, 0% used [0x00000000fa300000, 0x00000000fa300000, 0x00000000fa400000)
tenured generation total 10240K, used 4096K [0x00000000fa400000, 0x00000000fae00000, 0x00000000fae00000)
the space 10240K, 40% used [0x00000000fa400000, 0x00000000fa800010, 0x00000000fa800200, 0x00000000fae00000)
compacting perm gen total 21248K, used 2990K [0x00000000fae00000, 0x00000000fc2c0000, 0x0000000100000000)
the space 21248K, 14% used [0x00000000fae00000, 0x00000000fb0eb878, 0x00000000fb0eba00, 0x00000000fc2c0000)
No shared spaces configured.GC日志如下:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20public class TestTenuringThreshold {
private static final int _1MB = 1024 *1024;
public static void main(String[] args) {
/**
* VM 参数:-verbose:gc -Xms20M -Xmx20M -Xmn10M -XX:+PrintGCDetails -XX:SurvivorRatio=8 -XX:+UseParNewGC -XX:MaxTenuringThreshold=1
*/
byte[] a ,b,c;
a = new byte[_1MB/4];
b = new byte[4*_1MB];
c = new byte[4*_1MB];
//---注释掉以下代码以便观察,注释掉只会进行1次回收,a还在survior的from区
//没注释掉因为c分配时,进行2次回收,对象年龄达到超过1次,a被回收到tenured区,
//新的c分配在eden,旧的c回收,b也移入tenured区
c = null;
c = new byte[4*_1MB];
}
}而当设置-XX:MaxTenuringThreshold=15时,GC日志如下:1
2
3
4
5
6
7
8
9
10
11
12[GC [ParNew: 4695K->440K(9216K), 0.0052751 secs] 4695K->4536K(19456K), 0.0053095 secs] [Times: user=0.00 sys=0.00, real=0.01 secs]
[GC [ParNew: 4700K->25K(9216K), 0.0006787 secs] 8796K->4548K(19456K), 0.0007060 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
Heap
par new generation total 9216K, used 4285K [0x00000000f9a00000, 0x00000000fa400000, 0x00000000fa400000)
eden space 8192K, 52% used [0x00000000f9a00000, 0x00000000f9e28fd8, 0x00000000fa200000)
from space 1024K, 2% used [0x00000000fa200000, 0x00000000fa206748, 0x00000000fa300000)
to space 1024K, 0% used [0x00000000fa300000, 0x00000000fa300000, 0x00000000fa400000)
tenured generation total 10240K, used 4522K [0x00000000fa400000, 0x00000000fae00000, 0x00000000fae00000)
the space 10240K, 44% used [0x00000000fa400000, 0x00000000fa86aa08, 0x00000000fa86ac00, 0x00000000fae00000)
compacting perm gen total 21248K, used 2999K [0x00000000fae00000, 0x00000000fc2c0000, 0x0000000100000000)
the space 21248K, 14% used [0x00000000fae00000, 0x00000000fb0ede98, 0x00000000fb0ee000, 0x00000000fc2c0000)
No shared spaces configured.4)、动态对象年龄判断 当Survivor空间的相同年龄的所有对象的大小总和等于Survivor空间的一半时,也会进行GC;1
2
3
4
5
6
7
8
9
10
11
12[GC [ParNew: 4695K->440K(9216K), 0.0052751 secs] 4695K->4536K(19456K), 0.0053095 secs] [Times: user=0.00 sys=0.00, real=0.01 secs]
[GC [ParNew: 4700K->25K(9216K), 0.0006787 secs] 8796K->4548K(19456K), 0.0007060 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
Heap
par new generation total 9216K, used 4285K [0x00000000f9a00000, 0x00000000fa400000, 0x00000000fa400000)
eden space 8192K, 52% used [0x00000000f9a00000, 0x00000000f9e28fd8, 0x00000000fa200000)
from space 1024K, 2% used [0x00000000fa200000, 0x00000000fa206748, 0x00000000fa300000)
to space 1024K, 0% used [0x00000000fa300000, 0x00000000fa300000, 0x00000000fa400000)
tenured generation total 10240K, used 4522K [0x00000000fa400000, 0x00000000fae00000, 0x00000000fae00000)
the space 10240K, 44% used [0x00000000fa400000, 0x00000000fa86aa08, 0x00000000fa86ac00, 0x00000000fae00000)
compacting perm gen total 21248K, used 2999K [0x00000000fae00000, 0x00000000fc2c0000, 0x0000000100000000)
the space 21248K, 14% used [0x00000000fae00000, 0x00000000fb0ede98, 0x00000000fb0ee000, 0x00000000fc2c0000)
No shared spaces configured.GC日志,a和b已经进和老年代中1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25public class TestTenuringThreshold2 {
private static final int _1MB = 1024 *1024;
public static void main(String[] args) {
/**
* VM 参数:-verbose:gc -Xms20M -Xmx20M -Xmn10M -XX:+PrintGCDetails -XX:SurvivorRatio=8 -XX:+UseParNewGC -XX:MaxTenuringThreshold=15
*/
byte[] a ,b,c,d;
a = new byte[_1MB/4];
b = new byte[_1MB/4];
//a+b等于512K,大于Survivor空间的一半
c = new byte[4*_1MB];
//触发回收eden区,a,b移入survior from区,c直接进入tenured区
//然后的eden区给d分配
d = new byte[4*_1MB];
//------注释以下内容观察
d = null;
//触发第2次回收,d没有引用的实例,堆上的d引用的数据直接被回收掉
//新的d分配在eden区
//当Survivor空间的相同年龄的所有对象的大小总和等于Survivor空间的一半时,也会进行GC;
d = new byte[4*_1MB];
}
}1
2
3
4
5
6
7
8
9
10
11
12[GC [ParNew: 4951K->688K(9216K), 0.0038913 secs] 4951K->4784K(19456K), 0.0039288 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[GC [ParNew: 4948K->25K(9216K), 0.0006194 secs] 9044K->4811K(19456K), 0.0006480 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
Heap
par new generation total 9216K, used 4285K [0x00000000f9a00000, 0x00000000fa400000, 0x00000000fa400000)
eden space 8192K, 52% used [0x00000000f9a00000, 0x00000000f9e28fd8, 0x00000000fa200000)
from space 1024K, 2% used [0x00000000fa200000, 0x00000000fa206748, 0x00000000fa300000)
to space 1024K, 0% used [0x00000000fa300000, 0x00000000fa300000, 0x00000000fa400000)
tenured generation total 10240K, used 4785K [0x00000000fa400000, 0x00000000fae00000, 0x00000000fae00000)
the space 10240K, 46% used [0x00000000fa400000, 0x00000000fa8ac608, 0x00000000fa8ac800, 0x00000000fae00000)
compacting perm gen total 21248K, used 2999K [0x00000000fae00000, 0x00000000fc2c0000, 0x0000000100000000)
the space 21248K, 14% used [0x00000000fae00000, 0x00000000fb0edec8, 0x00000000fb0ee000, 0x00000000fc2c0000)
No shared spaces configured.