第4章 垃圾回收概念与算法 第5章 垃圾收集器和内存分配
4.1 什么是垃圾回收
回收不再使用的对象所占的内存空间。垃圾回收主要针对的是堆,JVM规范没有对方法区垃圾回收做出规定,虽然方法区上也有垃圾回收但是条件较为苛刻。
4.2 如何判断哪些对象需要回收
这里所述的判断方法都是在不考虑复活和软弱虚引用的情况。
4.2.1 引用计数法
最基础的引用计数法,每个对象有一个引用计数器加一,失去一个引用减一。缺点有很多:1、循环引用 2、每个对象额外维护一个计数器 3、每次对象有新的引用指向和释放引用的时候都要统计,影响性能
4.2.2 GC可达
接下来的无论是标记清除、复制、压缩都是基于GC的可达性来判断。从一系列被称为GCRoot的节点出发判断是否能够从GCRoot找到一条引用链,如果找不到就要被清除。
4.3 找到了垃圾如何回收
4.3.1 标记-清除 Mark Sweep
把GC不可达的对象全部回收,直接简单粗暴的回收。优点是实现起来简单,回收的效率高,缺点是产生内存碎片。
4.3.2 复制
把内存区域分成两部分(暂时不考虑三部分划分),每次使用其中一部分,在垃圾回收的时候把一个区域里需要回收的对象复制到另一个区域上。优点是能够保证回收之后的空间是连续的,缺点是复制对象带来了较大的开销,所以这种算法适用于对象存活率低的内存空间,而且内存划分会带来一定的内存空间浪费。
4.3.3 压缩
把GC不可达的对象压缩到内存区域的一端,然后清楚边界另一端所有的垃圾。优点是既不会产生内存碎片也不会带来内存空间浪费,缺点是对象移动开销较大。
4.4 HotSpot是如何垃圾回收的
上述的算法各有优缺点,适用于不同的场景。HotSpot把堆内存分为两大区域:新生代和老年代。新生代的对象特点是朝生暮死,存活率低,因而可以采用复制算法。老年代的对象存活时间长,一般都是多次新生代垃圾回收之后的幸存者,甚至可以认为在整个JVM声明周期内永驻内存,使用复制算法代价过大,这种代价大是分为两个方面的:1、复制的代价 2、内存分块的代价。因而老年代使用压缩甚至标记清楚算法。
具体的对新生代,HotSpot又将其分为两个三个部分eden from to,轮换使用。
============================================================================================================================================
上面所述都是一些概念性、宏观性的垃圾回收的算法和内存划分,具体的要采用一些垃圾回收器。
5.1 串行回收器
串行回收器使用单线程进行垃圾回收,工作时只有一个垃圾回收线程。
5.1.1 新生代串行回收器
Serial收集器,最古老最悠久的垃圾回收器。
算法:不详
场景:Client。因为客户端分配的内存较小,即使stop-the-world时间也是可接受的
5.1.2 老年代串行回收器
Serial old
算法:压缩(标记-整理)
场景:Client;Parallel Scavenge搭配;CMS备胎
5.2 并行回收器
有多个垃圾收线程。
5.2.1 新生代ParNew回收器
Serial收集器的多线程版本,但是在单线程场景下效果并不好。
算法:不详
场景:Server与CMS配合
5.2.2 新生代ParallelGC
吞吐量优先垃圾收集器
算法:复制
场景:Server端,尤其是对吞吐量有要求的Server端。
5.2.3老年代ParallelOldGC
吞吐量优先
算法:压缩
场景:Server端和ParallelGC配合。
5.3 面试最爱问:CMS(老年代)
ConCurrent Mark Sweep,并发标记清除算法。这里之所以采用不被看好的标记清楚算法是为了最大程度的减小垃圾回收停顿的时间,追求极高的响应速度。
出现了!并发与并行。在垃圾回收领域,并发(Concurrent)指的是垃圾收集线程和工作线程一起工作,并行(Parallel)指的是多个垃圾回收线程一起收垃圾。
初始标记直接标记出GCRoots能直接关联的对象,在初始标记的时候需要stop-the-world,然后并发的标记出GCRoot所有能够到达的对象,这一过程是并发的即意味着标记的线程和收集的线程是一起工作的。由于是一起工作的,所有有的对象的可达性可能会在这个过程中发生转变,所以需要第二次重新标记,重新标记也需要stop-the-word,最后进行并发清理,并发两个字暗示着清理线程和工作线程也是一起的。
缺点:
- 标记-清楚算法会带来大量的内存碎片,会给大内存分配带来困难。而该算法工作的老年代有时候很会有所谓的大对象优先分配到老年代,这种内存碎片最终可能会带来full gc。
- 无法处理浮动垃圾。并发提高了效率但是也带来了浮动垃圾,所谓浮动垃圾是指在并发清理阶段工作线程产生的新的垃圾。从另一个角度来说,垃圾收集和用户线程同时存在就不能等空间全部使用才进行垃圾回收,必须留有一定的盈余空间。
- CPU敏感,多个线程同时工作势必会降低工作线程的效率降低吞吐量。
场景:老年代
5.4 我全都要:G1
肩负替代CMS的使命,面向服务端。可以自己管理整个堆的上的垃圾回收,虽然也采用了分代算法去审视整个堆内存,但是不要求新生代、老年代的内存是完全连续的。G1收集器把堆内存划分成大小相等彼此独立的Region,新生代和老年代是一系列不连续的Region的集合。
垃圾回收的前几个阶段和CMS是十分类似的。