jvm垃圾回收
垃圾对象算法
引用计数
对象在创建时保存一个自身被引用的计数,初始值为1。每次被新的变量引用,该值加1。相反,则减去1。当该值等于0时,占用空间被系统回收。
缺点:无法处理循环引用对象。
可达性分析
从这些节点上向下搜索,所走过的路径称为引用链,当一个对象没有任何引用链与GCRoots连接时就说明此对象不可用,也就是对象不可达!
GCRoots对象
1、虚拟机栈中引用对象(栈帧中本地变量表)
2、方法中类的静态属性引用对象
3、方法区中常量引用对象
4、native方法引用的对象
可达性分析算法整个清理流程
1、第一次标记:对象经过可达性分析,没有GCRoots引用链,则进行第一次标记筛选,筛选条件是:该对象是否必要执行finalize()方法:没有覆盖finalize方法或该方法已经执行过。如果有必要执行,则该对象放在F-Queue队列,并稍后在由虚拟 机建立的低优先级 Finalizer 线程中触发该对象的 finalize()方法,但不保证一定等 待它执行结束。
2、第二次标记:GC 对 F-Queue 队列里的对象进行第二次标记,如果在第二次标记 时该对象又成功被引用,则会被移除即将回收的集合,否则会被回收!
垃圾回收算法
标记清除
标记-清除算法将垃圾回收分为两个阶段:标记阶段和清除阶段。
在标记阶段首先通过根节点(GC Roots),标记所有从根节点开始的对象,未被标记的对象就是未被引用的垃圾对象。然后,在清除阶段,清除所有未被标记的对象。
适用场合:
存活对象较多的情况下比较高效
适用于年老代
缺点:
容易产生内存碎片,再来一个比较大的对象时(典型情况:该对象的大小大于空闲表中的每一块儿大小但是小于其中两块儿的和),会提前触发垃圾回收
扫描了整个空间两次(第一次:标记存活对象;第二次:清除没有标记的对象
复制
从根集合节点进行扫描,标记出所有的存活对象,并将这些存活的对象复制到一块儿新的内存上去,之后将原来的那一块儿内存全部回收掉.
现在的商业虚拟机都采用这种收集算法来回收新生代
适用场合:
存活对象较少的情况下比较高效
扫描了整个空间一次(标记存活对象并复制移动)
适用于年轻代(即新生代):基本上98%的对象是"朝生夕死"的,存活下来的会很少
缺点:
需要一块儿空的内存空间
需要复制移动对象
标记压缩
复制算法的高效性是建立在存活对象少、垃圾对象多的前提下的。
这种情况在新生代经常发生,但是在老年代更常见的情况是大部分对象都是存活对象。如果依然使用复制算法,由于存活的对象较多,复制的成本也将很高。
标记-压缩算法是一种老年代的回收算法,它在标记-清除算法的基础上做了一些优化。
首先也需要从根节点开始对所有可达对象做一次标记,但之后,它并不简单地清理未标记的对象,而是将所有的存活对象压缩到内存的一端。之后,清理边界外所有的空间。这种方法既避免了碎片的产生,又不需要两块相同的内存空间,因此,其性价比比较高。
分代收集
分代收集算法就是目前虚拟机使用的回收算法,将内存分为各个年代。一般情况下将堆区划分为老年代(Tenured Generation)和新生代(Young Generation),在堆区之外还有一个代就是永久代(Permanet Generation,1.8开始叫元空间)。
在不同年代使用不同的算法,从而使用最合适的算法,新生代存活率低,可以使用复制算法。而老年代对象存活率高,没有额外空间对它进行分配担保,所以只能使用标记清除或者标记整理算法。
常见的垃圾回收器
serial(年轻代)
使用复制算法!垃圾回收的时候会STW(STOP THE WORLD)暂停用户线程,垃圾回收线程以单线程的方式回收垃圾!一般内存小的时候使用(小于100m)
serial old(老年代)
serial在老年代的垃圾回收器实现,使用标准压缩算法!
parallel scavenge(年轻代)
使用复制算法,在serial的基础上进行了改进,垃圾回收线程以多线程的方式并行回收垃圾!(内存几百兆,几个g的时候使用)
parallel old(老年代)
parallel在老年代的垃圾回收器实现!使用标记压缩算法!
parallel new(年轻代)
使用复制算法,为了配合cms而实现的垃圾回收器!
CMS(老年代)
使用标记清除算法!
CMS(ConcurrentMarkSweep)垃圾回收过程:
1.初始标记
STW用户线程,垃圾回收线程标记根对象,这个时间比较短暂!
2.并发标记
用户线程跟垃圾回收线程一起工作,垃圾回收线程标准根对象以外的存活对象!
3.重新标记
STW用户线程,垃圾回收线程标记因并发标记过程产生的一些垃圾对象或者重新被使用的对象,这个时间比较短暂!
4.并发清除
用户线程跟垃圾回收线程一起工作!这个过程还是会因为用户线程工作产生新的对象,这些对象只能在下次垃圾回收的时候被回收!
CMS存在一定的不可解觉的问题,比如浮动垃圾,内存碎片化,所以没有哪个版本的jdk默认垃圾回收器为cms,都要手动指定!
G1(逻辑上分代,物理不分代)
将堆分成一块块Region区,region的命名也分eden,survivor,old,humongous !使用的是标记压缩算法!
java1.9默认的垃圾回收器
ZGC(逻辑跟物理都不分代)
java11开始出现的垃圾回收器!据说很厉害,有兴趣的可以自行了解下!
对象分配过程
其中要注意的是对象的产生首先是尝试放在栈中(栈上分配)!