阅读本文假设你对java内存模型已有一些了解。java
先来看看jvm内存模型,以下图算法
线程隔离的区域随线程而生,随线程而灭;程序计数器可保存着虚拟机字节码指令的地址(能够看作是当前线程所执行的字节码的行号指示器);栈中的栈帧(与方法关联)随着方法的进入和退出执行压栈出栈操做。既然每个栈帧与方法关联,那每一个栈帧分配多少内存基本上在类结构肯定下来就已知。能够看出线程隔离的区域内存的分配和回收具备肯定性,当方法结束或线程结束时,内存天然就进行回收了,不须要咱们去考虑回收的问题。而在java堆和方法区来讲,一个接口可能有多个实现类,咱们只有在程序运行期间才知道建立了哪些对象,也即在运行时动态分配内存,而且是否回收是根据对象是否还可用进行回收判断的,java的GC关注的就是这部分的内存。jvm
当对象已经“死亡“(即不可能再被任何途径使用的对象)。spa
那么判断对象是否存活的算法有哪些?线程
(1)引用计数算法:给对象添加一个引用计数器,每当有一个地方引用它,计数器加1;引用失效时,计数器减1,任何计数器为0的对象就是不可能再被使用的。该算法实现简单,判断效率高,可是Java虚拟机里并无选用该算法来管理内存,由于该算法很难解决对象相互循环引用问题(相互引用的对象除了它们俩之间没有其余别的引用)。orm
(2)可达性分析算法:经过一系列的“GC Roots”做为对象的起点,从这些节点开始向下搜索,搜索走过的路径称为引用链,当一个对象到GC Roots没有任何引用链相连(即不可达),则该对象是不可用的。以下图对象五、6为可回收对象。对象
在Java中,可做为GC Roots 的对象包括如下:blog
1) 虚拟机栈(栈帧中的本地变量表)中引用的对象;接口
2) 方法区中类静态属性引用的对象;内存
3) 方法区中常量引用的对象;
4) 本地方法栈中JNI(Java Native Interface)引用的对象。
可是,请注意,不可达对象并不是是“非死不可”,宣告对象“死亡”要通过至少两次标记过程,没有引用链到达GC Roots标记第一次,并进行筛选,筛选的条件是此对象是否有必要执行finalize()方法。当对象没有重写finalize()方法时或finalize()已被虚拟机调用过,虚拟机将这两种状况视为没有必要执行。Finalize() 方法是对象自我拯救的最后机会,资源从新与引用链上的任何一个对象创建关联,在第二次标记它将被移除出即将回收的集合。
方法区上的回收时机
垃圾回收包含两部份内容:废弃常量和无用的类。
废弃常量:与java堆对象回收类似,没有再被任何对象引用,就会被回收;
无用的类:知足如下条件,即为无用类,可被回收。
1) 该类的全部实例都已经被回收,java堆中不存在该类的任何实例;
2) 加载该类的ClassLoader已经被回收;
3) 该类对应的java.lang.Class对象没有在任何地方被引用,没法在任何地方经过反射访问该类的方法。
(1)标记-清除算法:算法分为“标记”和“清除”两个阶段,首先标记出全部要回收的对象,而后统一回收被标记的对象,因为死亡的对象大部分不是连续的,该算法会较多的内存碎片,在下一次对较大对象分配内存时可能会由于没法分配足够大的连续空间而再次进行垃圾回收;
(2)复制算法:将内存进行分区,一块区域用完就进行垃圾回收,把存活对象复制到另外一块区域,而后清空原来区域。在新生代中就是使用复制算法,把内存以8:1:1分配,分别称为Eden,From Survivor,To Survivor,至于为何要8:1:1分配,能够参考如下两个连接 http://ifeve.com/jvm-yong-generation/ https://dsxwjhf.iteye.com/blog/2201687
(3)标记-整理算法:与标记清理类似,但后续不是直接清理而是先移到一端后在进行清理,避免了该内存里的内存碎片产生。
(4)分代回收算法:新生代使用复制算法,老年代使用标记-整理算法
书暂时看到这部分,下次再来说讲垃圾回收算法在jvm的基本实现及具体的垃圾回收器。