总览算法
本文会介绍垃圾回收的如下几个方面。缓存
- 为何要垃圾回收
- 在哪里回收
- 哪些对象须要回收
- 怎么回收
- HotSpotJVM中有哪些具体的回收器能够直接用。
在开始讲垃圾回收以前,先经过一张图快速回忆一下运行时内存多线程
为何须要垃圾回收并发
- 由于程序在运行的过程当中,对象实例,变量会占据愈来愈多的内存,若是不及时的清理,会形成性能降低和内存耗尽的问题。
从哪里回收性能
- 堆和方法区。堆里面再也不使用的对象实例,方法区里面的再也不使用的常量和类。
如何判断一个对象须要回收呢?url
- 有两种算法。
- 引用计数法,就是对象被引用一次就+1, 当引用为0的时候就进行回收。
这种算法缺点是当两个对象互相引用的时候,就不会被回收。spa
- 可达性分析法:经过对象是否可以经过GC ROOT的引用链可达 判断 对象是否能够被回收。
上图就是一个可达性分析的常见场景,对象三何时回被回收?先上结果:对象三是软可达,正常状况下在内存不足的时候会被回收。.net
这里须要理解两个概念线程
-
- 一个是哪些数据能够做为GC ROOT,虚拟机栈中本地变量表引用的对象
- 方法区中
- 类静态变量引用的对象
- 常量引用的对象
- 本地方法栈中JNI引用的对象
- 方法区中
- 一个是哪些数据能够做为GC ROOT,虚拟机栈中本地变量表引用的对象
-
- 一个是对象引用有哪几种类型。
为了更加灵活的控制对象的生命周期,将对象引用分为四个级别。3d
-
-
- 强引用( Strong Reference ):这是最多见的引用,不会被GC,除非你显示的置为null,才会GC。
-
Object o=new Object();
-
-
- 软引用( Soft Reference ):用来描述有用但非必须的对象,内存不足的时候回GC,多用于caching/pooling中,好比下面例子网页缓存吃紧的时候回收内存。
-
SoftReference<String> sr = new SoftReference<String>(new String("hello")); System.out.println(sr.get());
-
-
- 弱引用( Weak Reference ):也是用来描述非必须的对象,在下一次GC的时候就会回收,跟内存是否不足无关。
-
Object obj = new Object(); WeakReference<Object> WeakRef =new WeakReference(obj); Object objg = WeakRef.get();
-
-
- 虚引用( Phantom Reference ) : 不会影响对象的生命周期,也没法经过虚引用获取对象实例,仅用于在发生GC的时候接受一个系统通知。
- 必须和引用队列一块儿使用,主要用来判断对象是否将被垃圾回收器回收。
- 垃圾回收器准备回收虚引用对象时,会将这个引用加入到与之关联的引用队列中,你经过引用队列判断到某个引用将要被回收就能够当即采起行动。
- 虚引用( Phantom Reference ) : 不会影响对象的生命周期,也没法经过虚引用获取对象实例,仅用于在发生GC的时候接受一个系统通知。
-
ReferenceQueue<String> queue = new ReferenceQueue<String>(); PhantomReference<String> pr = new PhantomReference<String>(new String("hello"), queue); System.out.println(pr.get());
理解了上面两个概念以后,咱们在思考下面一个问题,若是一个对象的引用有多个,怎么经过可达性决定回收周期呢?
有两个原则。
-
-
- 单条引用链上,由最弱的一个引用类型决定
- 多条引用链之间,由最强的一个引用类型决定
-
上图中对象三是一个软可达对象,由于左半边的链最弱的是弱引用,右边的链最弱的是软引用,左右两边最强的是软引用。
知道了有对象须要回收以后,咱们怎么回收呢?有四种方法
- 标记清除(Mark-Sweep)算法
先须要回收的对象作标记,以后扫描,将有标记的回收。这种算法效率不高,会产生碎片。最终致使大对象分配空间时没法找到足够大的内存而频繁触发垃圾回收。
- 复制(Copying)算法
为了解决标记-清除算法碎片的缺点,提出了复制算法,将内存容量按照大小分红两份,每次使用其中的一半,当内存耗尽以后将存活对象有序的复制到另外一半中,并清理当前内存。
这种算法也有缺点:浪费通常空间;效率也跟存活对象多少有关,存活对象多算法效率会将大大下降。
- 标记整理(Mark-Compact)算法
为了解决复制算法浪费空间的缺点,并利用标记清理的优势,提出了这个算法,就是先标记回收对象,完了以后会将存活对象向同一端移动,而后清理须要回收的对象,这样就保证了空间的连续性。
- 分代收集(Generational Collection)算法
从回收对象生命周期不一样的角度讲内存划分为不一样的区域:新生代和老年代,并结合了前面的复制算法和标记整理算法。
新生代中存放的是:新生成的对象实例,也是垃圾回收对象最多的区域,用到的是复制算法。
老年代会存放的是:在新生代中经历过屡次垃圾回收仍然存在的对象;另外新生代生成大对象空间不够时也会在老年代中分配。这里采用的是标记整理算法对老年代区域进行清理。
其中,新生代又细分为三个区:Eden去,From Survivor区,To Survivor区,比例是8:1:1。
每次会使用Eden和一块Survivor区(90%新生代内存)服务对象,在触发回收机制时会采用复制算法,将两块区中存活的对象复制到另外10%的内存中,而后清理待回收对象。
这里考虑一个有趣的问题,若是老年代的对象有引用到新生代的,这种状况怎么处理的呢?用的是写屏障,会将这种类型的全部引用都放到一个集合中,标记时须要处理这个集合。
HotSpot JVM基于上面的垃圾回收算法实现的垃圾收集器有哪几种呢?
- Serial GC/Serial Old GC
- 二者都属于单线程垃圾收集器,在垃圾收集时,必须暂停全部用户线程。
- Serial用于新生代,采用复制算法,Serial Old用于老年代,采用标记-整理算法。
- ParNew GC
- 是Serial的多线程版本,使用多线程进行垃圾收集。
- Parallel Scavenge GC/Parallel Old GC
- 都是吞吐量优先的多线程垃圾收集器
- Parallel Scavenge GC用于新生代,采用复制算法; Parallel Old GC 用于老年代,采用标记-整理算法。
- CMS GC
- Current Mark Sweep,一种以获取最小垃圾回收停顿时间为目标的收集器,并发,采用标记-清除算法。
- G1 GC基于CMS有很多的改进。基于标记-整理算法,能够比较精确的控制停顿。
- 具体使用哪一种收集器,在JVM中能够经过下面的参数指定。
参考资料:
https://blog.csdn.net/zhangerqing/article/details/8214365
https://blog.csdn.net/wxfx888/article/details/78074145