==============java
读书笔记系列
==============算法
接下来的几篇笔记主要介绍一下咱们最常谈论的垃圾回收以及内存分配策略。Java 技术体系中所提倡的自动内存管理最终能够概括为自动化地解决了两个问题:给对象分配内存以及回收分配给对象的内存。既然是“自动化”,那为何还要去了解呢?答案很简单:当须要排查各类内存溢出、内存泄露问题时,当垃圾收集成为系统达到更高并发量的瓶颈时,咱们就须要对这些“自动化”的技术实施必要的监控和调节。本篇咱们先从如何判断 Java 对象已死开始。缓存
图1. 如何判断对象已死并发
图2. JDK 1.2 以后引用的分类高并发
在堆里面存放着 Java 世界中几乎全部的对象实例,垃圾回收器在堆进行回收前,第一件事就是肯定这些对象的“存活”状态。咱们称不可能再被任何途径使用的对象为“死去”的对象。this
给对象添加一个引用计数器,每当有一个对象引用它时,计数器值就加 1;当引用失效时,计数器值就减 1;任什么时候刻计数器为 0 的对象就是不可能再被使用的。线程
客观地说,引用计数算法(Reference Counting)的实现简单,断定效率也很高。可是,至少主流的 Java 虚拟机里面没有选用引用计数算法来管理内存,其中最主要的缘由是它很难解决对象之间相互循环引用的问题。3d
好比:对象 objA
和对象 objB
都有字段 instance
,赋值令 objA.instance = objB
及 objB.instance = objA
,除此以外,这两个对象再无任何引用,实际上这两个对象已经不可能再被访问,可是它们由于互相引用着对方,致使它们的引用计数都不为 0,因而引用计数算法没法通知 GC
收集器回收它们。code
经过一系列的称为“GC Roots” 的对象做为起始点,从这些节点开始向下搜索,搜索所走过的路径称为引用链(Reference Chain),当一个对象到 GC Roots 没有任何引用链相连(用图论的话来讲,就是从 GC Roots 到这个对象不可达)时,则证实此对象是不可用的。cdn
可做为 GC Roots 的对象包括如下几种:
JNI
(即通常说的 Native
方法)引用的对象不管是经过引用计数算法判断对象的引用数量,仍是经过可达性分析算法判断对象的引用链是否可达,断定对象是否存活都与“引用”有关。在 JDK 1.2 前:若是 reference
类型的数据中存储的数值表明的是另一块内存的起始地址,就称这块内存表明着一个引用。因此此时对象只有被引用和没被引用两个状态。咱们但愿描述这样一类对象:当内存空间还足够时,则能保留在内存之中;若是内存空间在进行垃圾收集后仍是很是紧张,则能够抛弃这些对象。不少系统的缓存功能都符合这样的应用场景。
在 JDK 1.2 后,Java 对引用的概念进行了扩充,将引用分为强引用(Strong Reference)、软引用(Soft Reference)、弱引用(Weak Reference)、虚引用(Phantom Reference)4 种。
即便在可达性分析算法中不可达的对象,也并不是是“非死不可”的,这时候他们暂时处于“缓刑”阶段,要真正宣告一个对象死亡,至少要经历两次标记过程:若是对象在进行可达性分析后发现没有与 GC Roots 相链接的引用链,那它将会被第一次标记而且进行一次筛选,筛选的条件是此对象是否有必要执行 finalize()
方法。当对象没有覆盖 finalize()
方法,或者 finalize()
方法已经被虚拟机调用过,虚拟机将这两种状况都视为“没有必要执行”。
若是这个对象被断定为有必要执行 finazlize()
方法,那么这个对象将会放置在一个叫作 F-Queue
的队列之中,并在稍后由一个虚拟机自动创建的、低优先级的 Finalizer
线程去执行它。这里所谓的“执行”是指虚拟机会触发这个方法,但并不承诺会等待它运行结束,这样作的缘由是,若是一个对象在 finalize()
方法中执行缓慢,或者发生了死循环(更极端的状况),将极可能会致使 F-Queue
队列中其余对象永久处于等待,设置致使整个内存回收系统崩溃。finalize()
方法是对象逃脱死亡命运的最后一次机会,稍后 GC 会对 F-Queue
中的对象进行第二次小规模的标记,若是对象要在 finalize()
中成功拯救本身——只要从新与引用链上的任何一个对象创建关联便可,譬如把本身(this
关键字)赋值给某个变量或者对象的成员变量,那么在第二次标记时它将被移除出“即将回收”的集合;若是对象这时候尚未逃脱,那基本上它就真的被回收了。
注:任何一个对象的 finalize()
方法都只会被系统自动调用一次,若是对象面临下一次回收,它的 finalize()
方法不会再次被执行。
方法区(永久代)的垃圾收集主要回收两部份内容:废弃常量和无用的类。
废弃常量:没有任何地方引用的常量值或者字面值。
“无用的类”断定条件:
ClassLoader
已被回收。java.class.Class
对象没有在任何地方被引用,没法再任何地方经过反射访问该类的方法。如何判断一个对象已死呢?就是没有被任何对象有效引用的状况,即单独的对象即可觉得“死亡”状态。借用《海贼王·狂热行动》中的一句台词:在 Java 世界中,一个对象,是注定没法存活的!(原台词:在大海上,一我的,是注定没法生存的!)