一、垃圾回收策略主要由:引用计数法,可达性分析
引用计数法:
在对象中添加一个引用计数器,每当一个地方引用它时,计数器就加一;当引用失效时,计数器值就减一;任何时刻计数器为零的对象就是不可能再被使用的
缺陷:循环引用的对象就不会被回收。A引用B,B引用A。
所以,Java虚拟机没有采用引用计数法。它采用的是可达性分析算法。
可达性分析:
可达性分析又被成为“GC Roots”的对象作为起点;从起点向下搜索,所走过的路径称之为引用链,当一个对象到GC Roots没有任何引用链相连接,代表此对象不可达。
可达性分析就是JVM首先枚举根节点,找到一些为了保证程序能正常运行所必须要存活的对象,然后以这些对象为根,根据引用关系开始向下搜寻,存在直接或间接引用链的对象就存活,不存在引用链的对象就回收。
那么在java可以作为GC Roots的对象可以是那些:
1、方法区静态属性引用的对象
全局对象的一种,Class对象本身很难被回收,回收的条件非常苛刻,只要Class对象不被回收,静态成员就不能被回收。
2、方法区常量池引用的对象
也属于全局对象,例如字符串常量池,常量本身初始化后不会再改变,因此作为GC Roots也是合理的。
3、在虚拟机栈中引用的对象,
例如各个线程被调用的方法堆栈中使用到的参数、局部变量、临时变量等。
4、JNI本地方法栈中引用的对象
和上一条本质相同,无非是一个是Java方法栈中的变量引用,一个是native方法(C、C++)方法栈中的变量引用。
5、被同步锁持有的对象
被synchronized锁住的对象也是绝对不能回收的,当前有线程持有对象锁呢,GC如果回收了对象,锁不就失效
二、垃圾回收经典算法
1.标记清除法
在gc时候,首先扫描时对需要清理的无用对象进行标记,然后将这些对象直接清理。
缺点:内存碎片,因为物理上的不连续,如果清理了两个1lkb的对象,再添加一个2kb的对象,是无法放入这两个位置的。
2.标记整理法
标记整理算法就是在标记清除算法的基础上多加一个整理的过程,把空闲的空间进行上移,从而解决内存碎片的问题。
缺点:每一次垃圾清除都要频繁的移动存活对象,效率十分低下。
3.复制算法
复制算法是将空间一分为二,在清理时,将需要保留的对象复制到第二块区域上,复制的时候直接紧凑排列,然后把原来的一块区域清空。
缺点:空间变小,每次需要移动存活的对象,效率低下。
4.分代算法
整个过程大致分为以下几个步骤:
(1)当 Eden 满了后,进行 Minor GC,将需要保存的数据复制到 S0 中;
(2)然后清空 Eden 和 S1 区域,需要保留的对象目前在 S0 中;
(3)下一次当 Eden 满了后,进行Minor GC,将原来 S0 存在的数据复制到S1中,将 Eden 中需要保存的数据也复制到 S1 中;
(4)清空 Eden 和 S0 区域,需要保存的对象目前都在 S1 中;
(5)Eden+S0 复制到 S1;
(6)Eden+S1 复制到 S0;
(7)Eden+S0 复制到 S1;
周而复始...
三、垃圾回收器
1.serial收集器:
单线程收集器,在进行垃圾回收时,他会持有所有的应用程序线程,冻结所有应用程序线程,使用单个垃圾回收线程来进行垃圾回收。回收时间100ms左右,回收新生代
新生代采用复制算法,老年采用整理算法,STW。
2.Serial-Old收集器:
单线程收集器,和serial收集器搭配使用,他针对的是老年代
3.ParallelNew收集器
Serial收集器的多线程版本,与CMS垃圾回收器通常一起使用。
收集算法:新生代采用复制算法,老年代采用标记-整理算法。
4.Parallel scavenge收集器
多线程收集器
其它垃圾回收器关注的是缩短垃圾回收对用户线程的缩短时间。而该垃圾回收器关注的是吞吐量(缩短用户停顿时间对那些高交互性比如web项目看中的)
新生代采用复制算法,老年代采用整理算法
5.Parallel Old收集器
Parallel Scavenge的老年代版本,和Parallel Scavenge一起使用,算法是标记整理算法
6.CMS收集器
收集算法:采用标记-删除算法,只应用于老年代。
一种以获取最短回收停顿时间为目标的收集器。基于标记清除算法。整个过程分为四步:
(1)初始标记:只标记根节点直接关联的引用对象,需要暂停用户线程(时间短);
(2)并发标记:标记其他引用对象,可以跟用户线程并发同时执行;
(3)重新标记:暂停用户线程,对并发标记期间新增加的引用关系变化再次标记(时间短);
(4)并发清除:跟用户线程并发进行。嗷嗷
缺点:
并发标记和并发清除时会和用户线程竞争资源。
无法清除浮动垃圾(由于用户线程还在执行,所以要预留一定空间给用户线程使用,所以一定不能在老年代已经占用100%情况下在进行垃圾回收)
内存碎片
7.G1收集器
采用标记整理算法,
可预测停顿(使用者明确指定一个长度为N毫秒的时间片段内,消耗在垃圾收集上的时间不得超过N毫秒),
其它垃圾收集器都是整个新生代或者老年代。G1不是这样,它将java堆划分陈多个大小相等的独立区域。虽然还保留新生代老年代的概念。但是新生代,老年代不再是物理隔阂。