天天看点

JVM内存模型JVM内存模型一、JVM内存模型分类JVM-GC机制GC机制流程finalize的执行过程(生命周期)

JVM内存模型

详情参考 简书 J先生有点儿屁 JVM内存模型

详情参考 csdn Steafan_ 深入理解Java-GC机制

一、JVM内存模型分类

JVM内存模型从线程维度归类分为:线程私有内存、线程共享内存、以及不在堆内的直接内存。

JVM内存模型JVM内存模型一、JVM内存模型分类JVM-GC机制GC机制流程finalize的执行过程(生命周期)

(一)直接内存

(二)线程私有型内存

​ 线程私有型内存 由三种:程序寄存器、Java栈、本地方法栈。

(三)线程共享型内存

​ 线程共享型内存 又分为两种:Java堆和本地方法区。

JVM-GC机制

Java虚拟机将堆内存进行了“分块处理”,

从广义上讲,在堆中进行垃圾回收分为新生代(Young Generation)和老生代(Old Generation);

从细微之处来看,为了提高Java虚拟机进行垃圾回收的效率,又将新生代分成了三个独立的区域(这里的独立区域只是一个相对的概念,并不是说分成三个区域以后就不再互相联合工作了),分别为:Eden区(Eden Region)、From Survivor区(Form Survivor Region)以及To Survivor(To Survivor Region),

而Eden区分配的内存较大,其他两个区较小,每次使用Eden和其中一块Survivor。

GC实现机制-Java虚拟机在什么地方进行垃圾回收

​ 堆是Java虚拟机进行垃圾回收的主要场所,其次要场所是方法区。

GC实现机制-Java虚拟机具体实现流程

JVM内存模型JVM内存模型一、JVM内存模型分类JVM-GC机制GC机制流程finalize的执行过程(生命周期)

判断一个类是否“无用”,则需同时满足三个条件:

(1)、该类所有的实例都已经被回收,也就是Java堆中不存在该类的任何实例;

 (2)、加载该类的ClassLoader已经被回收

 (3)、该类对应的java.lang.Class对象没有在任何地方被引用,无法在任何地方通过反射访问该类的方法。
           

​ 虚拟机可以对满足上述3个条件的无用类进行回收,这里说的是可以回收而不是必然回收。

GC机制流程

虚拟机通过一个对象年龄计数器来判定哪些对象放在新生代,哪些对象应该放在老生代。
           

​ 如果对象在Eden出生并经过一次Minor GC后仍然存活,并且能被Survivor容纳的话,将被移动到Survivor空间中,并将该对象的年龄设为1。

​ 对象每在Survivor中熬过一次Minor GC,年龄就增加1岁,当他的年龄增加到最大值15时,就将会被晋升到老年代中。

​ 虚拟机并不是永远地要求对象的年龄必须达到MaxTenuringThreshold才能晋升到老年代,如果在Survivor空间中所有相同年龄的对象大小的总和大于Survivor空间的一半,年龄大于或等于该年龄的对象就可以直接进入老年代,无需等到MaxTenuringThreshold中要求的年龄。

GC实现机制-Java虚拟机如何实现垃圾回收机制

(1)、引用计数算法(Reference Counting)

​ 给对象添加一个引用计数器,每当有一个地方引用它时,计数器值就加1;当引用失效时,计数器值就减1;任何时刻计数器为0的对象就是不可能再被使用的,这就是引用计数算法的核心。客观来讲,引用计数算法实现简单,判定效率也很高,在大部分情况下都是一个不错的算法。但是Java虚拟机并没有采用这个算法来判断何种对象为死亡对象,因为它很难解决对象之间相互循环引用的问题。

(2)、可达性分析算法(Reachability Analysis)

这是Java虚拟机采用的判定对象是否存活的算法。通过一系列的称为“GC Roots"的对象作为起始点,从这些结点开始向下搜索,搜索所走过的路径称为引用链(Reference Chain),当一个对象到GC Roots没有任何引用链相连时,则证明此对象是不可用的。可作为GC Roots的对象包括:虚拟机栈中引用的对象、方法区中类静态属性引用的对象、方法区中常量引用的对象。本地方法栈JNI引用的对象。
           
JVM内存模型JVM内存模型一、JVM内存模型分类JVM-GC机制GC机制流程finalize的执行过程(生命周期)

在上图可以看到GC Roots左边的对象都有引用链相关联,所以他们不是死亡对象,而在GCRoots右边有几个零散的对象没有引用链相关联,所以他们就会别Java虚拟机判定为死亡对象而被回收。

GC实现机制-何为死亡对象?

​ Java虚拟机在进行死亡对象判定时,会经历两个过程。如果对象在进行可达性分析后没有与GC Roots相关联的引用链,则该对象会被JVM进行第一次标记并且进行一次筛选,筛选的条件是此对象是否有必要执行

finalize()方法

(详情见finalize的执行过程),如果当前对象没有覆盖该方法,或者finalize方法已经被JVM调用过都会被虚拟机判定为“没有必要执行”。

​ 如果该对象被判定为没有必要执行,那么该对象将会被放置在一个叫做F-Queue的队列当中,并在稍后由一个虚拟机自动建立的、低优先级的Finalizer线程去执行它,在执行过程中JVM可能不会等待该线程执行完毕,因为如果一个对象在finalize方法中执行缓慢,或者发生死循环,将很有可能导致F-Queue队列中其他对象永久处于等待状态,甚至导致整个内存回收系统崩溃。如果在finalize方法中该对象重新与引用链上的任何一个对象建立了关联,即该对象连上了任何一个对象的引用链,例如this关键字,那么该对象就会逃脱垃圾回收系统;如果该对象在finalize方法中没有与任何一个对象进行关联操作,那么该对象会被虚拟机进行第二次标记,该对象就会被垃圾回收系统回收。值得注意的是finaliza方法JVM系统只会自动调用一次,如果对象面临下一次回收,它的finalize方法不会被再次执行。

finalize的执行过程(生命周期)

(1) 首先,大致描述一下finalize流程:当对象变成(GC Roots)不可达时,GC会判断该对象是否覆盖了finalize方法,若未覆盖,则直接将其回收。否则,若对象未执行过finalize方法,将其放入F-Queue队列,由一低优先级线程执行该队列中对象的finalize方法。执行finalize方法完毕后,GC会再次判断该对象是否可达,若不可达,则进行回收,否则,对象“复活”。

(2) 具体的finalize流程:

对象可由两种状态,涉及到两类状态空间,

​ 一是终结状态空间 F = {unfinalized, finalizable, finalized};

​ 二是可达状态空间 R = {reachable, finalizer-reachable, unreachable}。