垃圾回收相关

一.如何判断对象已经死亡

   1.引用计数 无法处理循环引用的情况

   2.可达性分析 目前主流实现方式。

     原理: 从GC Roots对象作为起点开始查询,如果有引用链说明对象存活,反之就是已经死亡。

    GC Roots对象:

    (1)栈(栈帧)中引用的对象。

      (2) 方法区中类静态变量引用的对象。

    (3)方法区中常量引用的对象。(final 修饰的属性)

      (4) 本地方法栈中引用的对象。

    引用类型:

     (1) 强引用: new创建的,该类引用GC不能回收。

       (2)  软引用: 有用非必须,可回收也快不回收,一般用在缓存中。只有在内存紧张的时候才回收。

     (3)弱引用: GC会回收,所有生存周期是一个GC

       (4)  虚引用: 可以忽略的引用,对对象没有任何影响。

   可达性分析过程

   两次标记:

   (1)第一次检查不可达后,如果该对象覆盖了finalize()方法,切没有被调用过,就把对象放到 F-Queue队列中,虚拟机的Finalizer线程会去依次调用改队列中的所有对象的finalize()方法(只调用不等待返回,以免影响系统)。

  (2)GC对 F-Queue队列进行二次标记,如果在finalize()被其他对象引用了,就移出队列。否则,才会被回收。

   备注每个对象的finalize()只会被调用一次。所以上面2中的情况只能进行一次。官方不建议使用这种方式复活对象。

  二 垃圾回收

  1.方法区回收

   回收内容:

   (1)废弃常量 string对象

   (2)无用的类:判断条件 a.类的对象都被回收 b.加载类的ClassLoader已经被回收 c. 该类的java.lang.Class对象没有任何地方引用,也不能通过反射来创建。

  参数:

  -verbose:class 

  -XX:+TraceClassLoading:查看类加载信息

  -XX:+TraceClassUnLoading:查看类卸载信息

   2.回收算法

(1)标记清除(Mark-Sweep)

  会产生内存碎片,且效率不高。适合存活比例比较高的区域

(2)分代复制

效率高,内存存在浪费。不会产生碎片。适合存活比例低的区域,比如新生代,90%的对象都会被回收,只把存活的复制到其他地方,统一清除原来的区域。

(3)标记整理

   在标记清除的基础上增加整理功能,消除内存碎片。

三 HotSpot算法实现

1.使用可达性分析来判断对象是否已死。

2.为了尽量减少系统停顿(stop the world)设置安全点(系统特定的状态,这个状态下GC不会引起问题)和安全区

  原理:GC在需要中断线程的时候设置一个标志,每个线程都去遍历这个标志,如果标志是需要中断,就挂起自己。

3.垃圾收集器:

年轻代:Serial ,ParNew , Parallel Scavenge

老年代:CMS, Serial Old , Parallel Old

G1:既可以在年轻代也可以在老年代。

Serial

单线程收集器  stop the world 造成系统停顿,体验很差

参数:

-XX:SurvivorRatio :设置 Eden和Survivor的比例 默认是8

-XX:PretenureSizeThreshold : 新对象大于这个值直接放入老年代。

-XX:MaxTenuringThreshold: 年龄最大值,大于这个就会复制到老年代。

Serial old

单线程,标记整理算法

ParNew

Serial的多线程版本,其他跟Serial都一样 

ParNew old

多线程,标记整理。

CMS收集器

1.5推出的目前应用最广的一个收集器,获取最短停顿时间。

步骤

(1)初始标记 

  stop the world 只记录GC Roots直接关联的对象。

(2)并发标记

跟用户线程并发执行,进行可达性分析,也就是GC Roots Tracing过程。

(3)重新标记

stop the world 由于上面是并发执行,在这个过程中有可能产生新的垃圾对象,所有需要重新标记。

(4)并发清除

 跟用户线程并发执行

分析:(2)(4)是耗时最长的,现在可以并发执行从而减少系统停顿。

缺点:

(1)并发清除过程中的垃圾无法回收,只能等下一次

(2)基于标记清除实现的,会产生内存碎片,CMS也支持整理,但是,整理过程没法并发,会加大停顿时间。

 G1收集器

 目前没有大规模商用。唯一一个即支持新生代又支持老年代的收集器。

基本思路:

把内存划分区域,不再是物理区分新生代和老年代,每个区域计算回收价值(回收大小和回收时间的比值)。优先回收价值高的。回收过程跟CMS类似,只是是以region为单位的。

GC日志如何查看:

对象年龄问题:

每复制一次age就加一,超过设定值就会移动到老年代。

年龄动态判定:Survivor空间中年龄相同的对象总行大于空间一半,就把比这些大的对象移动到老年代。

名词解释:

Minor GC: 回收新生代

Full GC:新生代和老年代都进行回收。

回收过程:


1.最初eden和survivor都是空的,新创建的对象放到eden中,当eden满了后会触发minor GC,把存活的对象放到s1中,并清理掉不被使用的对象。这个时候Eden是空的和s1中包含存活的对象。
2. 当慢慢的eden又被填满后,会再次触发minor GC,这个时候会把eden中存活的对象放到s2中,同时s1中存活的也会复制到s2中,然后清理垃圾对象。
3.上面2中的步骤会不断反复。
4.对象在第一次被复制的时候会设置age=1,以后没复制一次就加1,当上面的步骤不断的累加使得age大于设定的值-XX:MaxTenuringThreshold的时候,就会转入到老年代。转入老年代的方法不止这种情况,另外还有两种,
(1)每次minor GC的时候如果被复制对象大于要放到的s空间,就会放入老年代。
(2)新对象创建的时候如果大于设定值-XX:PretenureSizeThreshold会直接放入老年代,此参数只对Serial及ParNew两款收集器有效

(3)Survivor空间中年龄相同的对象总行大于空间一半,就把比这些大的对象移动到老年代。

所以老年代是存放较大对象和存活时间足够长的对象。
5.老年代满了的时候回触发 full gc,使用的是标记-清除方式,JVM先扫描一遍,把要删除的对象做上标记,然后阻塞所有用户进程进行清除,并且把存活对象集中一下,避免造成过多内存碎片。

 

猜你喜欢

转载自www.cnblogs.com/fymc/p/11073992.html