本文共 8294 字,大约阅读时间需要 27 分钟。
本文主要为《深入理解Java虚拟机》第三章的读书记录笔记,同时伴有一些网络上资料的总结。
Java堆是JVM主要的内存管理区域,里面存放着大量的对象实例和数组。在垃圾回收算法和垃圾收集器之前,首先要做的就是判断哪些对象已经“死去”,需要进行回收即不可能再被任何途径使用的对象。
引用计数法是这样:给对象中添加一个引用计数器,每当有一个地方使用它时,计数器值就加1。当引用失效时,计数器就减1。任何时刻计数器为0的对象就是不可能再被使用的。
现在主流的Java虚拟机都没有使用引用计数法,最主要的原因就是它很难解决对象之间互相循环引用的问题。
可达性分析的基本思路:通过一系列称为"GC Roots"的对象作为起点,从这些节点开始向下搜索,如果从GC Roots到一个对象不可达,则证明此对象是不可用的,如下图所示。
Java语言中,可作为GC Roots的对象包括下面几种:
对于Java程序而言,对象基本都位于堆内存中,简单来说GC Roots就是有被堆外区域引用的对象。
在JDK 1.2以前的版本中,若一个对象不被任何变量引用,那么程序就无法再使用这个对象。也就是说,只有对象处于(reachable)可达状态,程序才能使用它。
从JDK 1.2版本开始,对象的引用被划分为4种级别,从而使程序能更加灵活地控制对象的生命周期。这4种级别由高到低依次为:强引用、软引用、弱引用和虚引用。
强引用是使用最普遍的引用,如下的方式就是强引用:
Object strongReference = new Object();复制代码
举例来说,
public void test() { Object strongReference = new Object(); // 省略其他操作 }复制代码
class Obj { pulic static Object strongReference = new Object();}复制代码
如果对象只具有软引用,则
// 强引用 String strongReference = new String("abc"); String str = new String("abc"); // 软引用 SoftReferencesoftReference = new SoftReference (str);复制代码
软引用可以和一个引用队列(ReferenceQueue)联合使用。如果软引用的对象被垃圾回收,JVM就会把这个软引用加入到与之关联的引用队列中。
ReferenceQueuereferenceQueue = new ReferenceQueue<>(); // 强引用 String str = new String("abc"); SoftReference softReference = new SoftReference<>(str, referenceQueue); // 消除强引用 str = null; // Notify GC System.gc(); System.out.println(softReference.get()); // abc Reference reference = referenceQueue.poll(); System.out.println(reference); //null复制代码
注意:
也就是说,垃圾收集线程会在虚拟机抛出OutOfMemoryError之前回收软引用对象,而且虚拟机会尽可能优先回收长时间闲置不用的软引用对象。对那些刚构建的或刚使用过的较新的软对象会被虚拟机尽可能保留。
应用场景:
浏览器的后退按钮。按后退时,这个后退时显示的网页内容是重新进行请求还是从缓存中取出呢?这就要看具体的实现策略了。
这时候就可以使用软引用,很好的解决了实际的问题:
// 获取浏览器对象进行浏览 Browser browser = new Browser(); // 从后台程序加载浏览页面 BrowserPage page = browser.getPage(); // 将浏览完毕的页面置为软引用 SoftReference softReference = new SoftReference(page); // 消除强引用 page = null; // 回退或者再次浏览此页面时 if(softReference.get() != null) { // 内存充足,还没有被回收器回收,直接获取缓存 page = softReference.get(); } else { // 内存不足,软引用的对象已经回收 page = browser.getPage(); // 重新构建软引用 softReference = new SoftReference(page); }复制代码
相比较软引用,只具有弱引用的对象拥有更短暂的生命周期。在垃圾回收器线程扫描它锁管辖的内存区域的过程中,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。不过,由于垃圾回收器是一个优先级很低的线程,因此不一定会很快发现那些只具有弱引用的对象。
String str = new String("abc"); WeakReferenceweakReference = new WeakReference<>(str); // 消除强引用 str = null;复制代码
同样,弱引用可以和一个引用队列(ReferenceQueue)联合使用,如果弱引用的对象被垃圾回收,JVM就会把这个弱引用加入到与之关联的引用队列中。
ReferenceQueuequeue = new ReferenceQueue<>(); String str = new String("abc"); WeakReference weakReference = new WeakReference<>(str, queue); str = null; System.gc(); try { // 休息几分钟,等待上面的垃圾回收线程运行完成 Thread.sleep(6000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(weakReference.get()); // null System.out.println(queue.poll()); // java.lang.ref.WeakReference@22a71081复制代码
虚引用顾名思义,就是形同虚设。与其他几种引用都不同,虚引用并不会决定对象的生命周期。如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收器回收。
应用场景:
虚引用主要用来跟踪对象被垃圾回收器回收的活动。 虚引用与软引用和弱引用的一个区别在于:
虚引用必须和引用队列(ReferenceQueue)联合使用。当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会在回收对象的内存之前,把这个虚引用加入到与之关联的引用队列中。
String str = new String("abc"); ReferenceQueue queue = new ReferenceQueue(); // 创建虚引用,要求必须与一个引用队列关联 PhantomReference pr = new PhantomReference(str, queue);复制代码
程序可以通过判断引用队列中是否已经加入了虚引用,来了解被引用的对象是否将要进行垃圾回收。如果程序发现某个虚引用已经被加入到引用队列,那么就可以在所引用的对象的内存被回收之前采取必要的行动。
标记-清除算法分为“标记”和“清除”两个阶段,执行过程如下图所示:
标记-清除算法主要有两个不足:
复制算法的大致思路如下,其执行过程如下图所示:
复制算法的代价就是将内存缩小为原来的一半。
现在的商业虚拟机都是采用复制算法来回收新生代。
标记-整理算法分为“标记”和“整理”两个阶段,执行过程如下图所示:
分代收集算法就是降Java堆分为新生代和老年代,根据其各自的特点采用最适当的收集算法。
JVM垃圾收集器发展历程大致可以分为以下四个阶段: Serial(串行)收集器 -> Parallel(并行)收集器 -> CMS(并发)收集器 -> G1(并发)收集器
下图展示了7种作用域不同分代的收集器,如果两个收集器之间存在连续,就说明它们可以搭配使用。下面逐一介绍这些收集器的特性、基本原理和使用场景。
Serial类收集器是一个单线程的收集器:
Parallel类收集器就是Serial收集器的多线程版本:
Parallel Scavenge收集器还有一个开关参数-XX: UseAdaptiveSizePolicy,打开这个开关后就不用手动指定新生代的大小(-Xmn),Eden与Survivor区的比例(-XX:SurvivorRatio)等细节参数了,JVM会动态调整这些参数已提供最合适的停顿时间或者最大吞吐量。
CMS(Concurrent Mark Sweep)收集器是一种以获取最短回收停顿时间为目标的收集器。它是一个基于标记-清除算法实现的,运作过程分为4个步骤:
同优秀的CMS一样,G1也是关注最小停顿时间的垃圾回收器,也同样适合大尺寸堆内存,官方也推荐用G1来代替选择CMS。
4.4.1 G1相对于CMS的改进
4.4.2 G1与CMS的区别
(1)堆内存模型的不同
G1之前的JVM堆内存模型,堆被分为新生代,老年代,永久代(1.8之前,1.8之后是元空间),新生代中又分为Eden和两个Survivor区。
(2)应用分代的不同
G1可以在新生代和老年代使用,而CMS只能在老年代使用。
(3)收集算法的不同
G1是复制+标记-整理算法,CMS是标记清除算法。
4.4.3 G1收集器的工作流程
G1收集器的工作流程大致分为如下几个步骤:
4.4.4 G1的GC模式
G1提供了两种GC模式,Young GC和Mixed GC,两种都是完全Stop The World的
(1)YoungGC
(2)MixedGC
当越来越多的对象晋升到老年代old region时,为了避免堆内存被耗尽,虚拟机会触发一次mixed gc,该算法并不是一个old gc,除了回收整个young region,还会回收一部分的old region。这里需要注意:是一部分老年代,而不是全部老年代,可以选择哪些old region进行收集,从而可以对垃圾回收的耗时时间进行控制
G1没有fullGC概念,需要fullGC时,调用serialOldGC进行全堆扫描(包括eden、survivor、o、perm)。
转载地址:http://rufvi.baihongyu.com/