一、概述
问题:
- 垃圾回收回收什么(what)
- 垃圾什么时候回收(when)
- 怎么回收(how)
回收什么
我们java内存中有:java堆,java栈,方法区,直接内存
其中java栈是跟随线程生命周期的,所以不需要我们回收。
我们主要的回收目标:java堆、方法区、直接内存
方法区主要回收类和常量
什么时候回收
当对象没有引用的时候:
1、引用计数法:当对象被引用时,计数+1,当计数为0时,可被回收
缺点:会出现循环引用的情况,导致多个对象一直不能回收,比如python
2、可达性分析法:根据gcroot节点,分析一个对象是否可达,若不可达,则可以回收
GC root:java虚拟机栈、java本地方法栈、方法区中的类静态变量、方法区中的类常量对象
有引用:java中有4中引用关系
- 强引用:Object obj = new Object();不会被回收
- 软应用:softReference,当java将要发生内存溢出之前,会被回收
- 弱引用:WeakReference 下次执行垃圾回收时,会被回收
- 虚引用:PhantormReference 和没有引用一样,只不过当回收时,会给对象一个finalize()的通知
二、垃圾回收算法
- 标记清除法
- 复制法
- 标记整理法
- 分区法
- 分代法
标记清除法
标记哪些可被回收,然后直接清除
缺点:标记和清除效率低、产生碎片化的内存
复制法
把内存分为2个相同大小的区域,一次只用一个,把存活的对象复制到内外一个内存空间,然后清除所有的对象
优点:没有内存碎片,如果存活率低的话提高效率
缺点:如果存活的对象多,效率低
适合于存活率低的年轻代的算法
标记整理法
先标记存活的对象,然后移动到一边,再清除剩余的内存
优点:减少碎片化
适用于大多数对象长时间没有被回收的情况,老年代
分代法
上面2个的综合,根据不同对象的特征分为年轻代和老年代
年轻代分为:eden、Survivor from和Survivor to。默认8:1
分区法
把内存分为很多相同大小的区。G1中使用。可单独对其中部分区进行内存回收。减少stw时间,提升效率。
三、垃圾回收器
- serial new
- parnew
- parallel scavenge
- serial old
- parallel old
- CMS
- G1
serial new单线程回收器
年轻代的单线程回收器,使用的复制算法,垃圾回收时是单线程执行的
年轻代client的默认回收器
parnew多线程回收器
和serial相比,回收时是多线程的,使用的复制算法
年轻代server的默认回收器
组合:parnew+CMS配合使用
parallel scavenge
和parnew类似,使用复制算法,只不过parallel scavenge跟关注吞吐量,更适用于计算型的后台程序;而其他回收器更关注stw的时间;更适用于响应的服务
组合:parallel scavenge+ parallel old 更关注吞吐量
serial old单线程回收器
老年代的单线程回收器,使用的标记整理算法,垃圾回收时是单线程执行的
老年代client的默认回收器
parallel old多线程回收器
和serial相比,回收时是多线程的,使用的标记整理算法
CMS回收器
老年代的回收器,更强调stw时间。
过程
- 初始标记:标记GC root
- 并发标记:标记存活对象
- 再次标记:把这个过程中有变动的标记下
- 并发清除
其中只有初始标记和再次标记是stw的,其他都是和用户线程并行的,stw时间很短
问题:
1、并发占用正常线程,cpu吞吐量下降
2、并发标记需要额为占用内存,不能等内存满了再开始回收,如果设置阈值过高,可能发生fail的请求,引发fullgc。使用parallel old兜底。
3、没有整理产生碎片,可以设置每次回收整理一次,或者N次回收整理一次内存对象
G1回收器
jdk1.7引入,适用于年轻代和老年代,引入分区概念,初始标记-并发标记-最终标记-筛选回收。可以一次只回收一个或几个有价值的区。减少stw时间。
三、什么时候执行垃圾回收
YGC:
1、当eden满了的时候
FULL GC:
1、system.gc() 不一定立马回收
2、当老年代快满了的时候
3、方法区快满了的时候
4、通过Minor GC后进入老年代的平均大小大于老年代的可用内存
5、当某次Minor GC后进入老年代的大小大于老年代的可用内存时
四、什么时候进入老年代
1、eden和from的存活对象放不下to的时候,部分对象进入老年代
2、当分配一个大对象的时候,直接进入老年代。字符串、字符数组等,可设置
3、当年轻代的对象年龄太大的,进入老年代。默认是15,一次回收+1;或者年轻代同年龄的对象占比大于50%时
五:其他
Oopmap:存储GCroot,方便查询gc root对象
安全点、安全区:当这段程序执行时没有对象的变动时,被编辑为安全点/区。才能执行垃圾回收动作。