JVM中内存模型
内存模型与运行时数据区
重点的存储数据的是堆和方法区。对于虚拟机栈,本地方法栈,程序计数器都是线程私有的。
堆区、非堆区的划分
堆区分为两大块,一个是old区,一个是Young区
Young区分为两大块,一个是Survivor(s0+s1)一样大,也可以称为From和To
如下图:
一般对象和数组的创建会在堆中分配内存空间,但是在堆中存在多个区域,具体的会分配到那个区中?
对象创建所在的区域
一般情况下新建的对象都会分配到Eden区,由于Eden区的内存空间有限,如果内存空间达到了临界值,那么这个时候需要对Eden内存空间进行清理,就是所谓的垃圾收集(Garbage Collect),这样的GC称为Minor GC,Minor GC是Young区的GC。
经过GC之后,有些对象就会被清理掉,有些对象可能还存活着,对于存活对象需要将其复制到Survivor区,然后在清理掉Eden中的对象。
Survivor区
Survivor区可以分为:S0+S1(From+To)
在同一个时间点,S0或S1只能有一个区域是存在数据的,另外是一个空的。
模拟GC:
- 比如这个时候只有Eden区和From区中有对象,To是空的。
- 此时进行GC操作,From区中对象的年龄就会+1,我们知道Eden区中所有存活的对象会被复制到To区,From区中还能存活的对象会有两个去处。
- 若对象年龄达到之前设置的临界值,此时对象会被移动到Old区,没有达到临界值会被复制到To区
- 此时Eden区和From区已经被清空(被GC的对象肯定没了,没有被GC的对象都有各自的去处)
- 这时候From和To交换角色,之前的From变成了To,之前的To变成了From
- 也就是说无论如何都要保证名为To的Survivor区域是空的
- Minor GC会一直重复这样的过程,To区被填满,会将所有的对象复制到老年代中。
Old区
一般Old区都是年龄比较大的对象,或者超过了这个临界值,在Old区也会有GC的操作,Old区的GC称为Major GC。
如何理解Minor/Major/Full GC
Minor GC:新生代
Major GC:老年代
Full GC:新生代+老年代
Garbage Collect
对于一个对象而言,只有确定它是垃圾才能被回收。Java是自动做内存管理和垃圾回收的,自动垃圾回收机制就是寻找Java堆中的对象,并对对象进行分类判别,寻找正在使用的对象和不使用的对象,然后将这些不使用的对象清除。
如何判断一个对象是不是垃圾
- 引用计数法
对于某个对象而言,只要应用程序中持有该对象的引用,就说明该对象不是垃圾,反之说明就是垃圾。
每当一个对象被引用计数器就+1。当引用失效时计数器就-1.当对象的计数器为0时。该对象就是就是一个不被引用的对象,即“死亡”。
但是当对象之间发生相互循环引用的问题的时候就会保存两个对象的指针。这样就引入了可达性分析。
- 可达性分析
通过GC Root的对象,开始向下寻找,看某个对象是否可达。走过的路径为“引用链”,如果一个对象没有任何引用链可到达GC Roots,那么该对象就是不可用的,即使该对象还与其他对象相关联。
经可达性分析算法所标记出的对象,会进行一次筛选(根据finalize方法)。若经过筛选,判定可回收,那么就会立即回收;若判定没有必要回收,那么就将对象放入F-Queue队列中,进行二次筛查。
二次筛查会执行对象的finalize()方法。若对象在这个过程重新与引用链上的任何一个对象建立关联,那么该对象就会从回收集合中移除。否则,对象会被回收。
(可以作为GC Root:类加载器、Thread、虚拟机栈的本地变量表、static成员、常量引用、本地方法栈的变量)
虚拟机栈(栈帧中的本地变量表)中引用的对象
方法区中类静态属性引用的对象
方法区中常量引用的对象
本地方法栈中JNI(即一般说的Native方法)引用对象
垃圾收集算法
标记清除(Mark-Sweep)
- 标记:找出内存中需要回收的对象,并且把它们标记出来
此时堆中所有的对象都会被扫描一遍,从而才能确定需要回收的对象,比较耗时
- 清除:清除掉被标记的需要回收的对象,释放出对应的内存空间
缺点:标记清除会在内存空间中产生大量的空间碎片,空间碎片太多可能会导致以后在程序运行过程中需要分配较大对象时,无法找到足够的连续的内存而不得不提前出发另一次垃圾收集动作。
标记和清除的两个过程都比较耗时,效率不高。
会产生大量的空间碎片。
复制(copying)
将内存划分为两块相等的区域,每次只使用其中一块
当其中一块内存使用完了,就将还存活的对象复制到另外一个上,然后在将已经使用的内存空间一次清4除掉。
缺点:空间利用率低
复制收集算法在对象存活率较高时就要进行较多的复制操作,效率会变低。更关键的是,还要浪费50%的空间,就需要有额外的空间进行分配担保,以应对被使用的内存中所有的对象都有100%存活的极端情况,所以老年代一般不能直接选用这种算法。
标记-整理(Mark-Compact)
标记整理:标记过程任然与“标记-清除”算法一样,但是不是直接将可回收对象进行清理,而是让所有存活的对象都向一端移动,然后直接清理掉端边界以外的内存。
让所有的存活对象都向一端移动,清理掉边界以外的存在
分代收集算法
在堆内存中使用的算法
Young区:复制算法(对象在被分配之后,可能生命周期比较短,Young区复制效率高)
Old区:标记清除或标记整理(Old区对象存活时间较长,来回复制是没有必要的)