由浅入深了解JVM-堆

jvm中堆主要是用来存放对像,并且作为jvm中最大的一块,因此垃圾回收时主要作用于堆,因此堆也被称作GC堆。关于垃圾回收,从两个角度去思考:什么样的对象被当作垃圾来处理?如何去处理?在考虑这两个问题之前一定要先了解一下堆的结构。

堆结构

堆可以处于物理上不连续的内存空间中,只要逻辑上是连续的即可。堆的内部结构按照传统的做法分成新生代和老年代。如下图所示:
在这里插入图片描述

新生代则有Eden区,S0区,S1区组成。其中Eden区在新生代中占的比例最大,题外话:Eden这个词来自圣经,是一个逍遥自在美好的区域,所有的对象刚创建出来的时候都存放在Eden区。From Sruvivor和To Survivor区通过被称作S0区和S1区,即生存0区和生存1区。新生代中各个区域的占比为:8:1:1 。之因为Eden区占比大是由于每次垃圾回收时大部分对象都被回收掉,只有少数的需要移到S0区。
老年代没有进行划分,当对象的存活时间达到一定的值时,由新生代过渡到老年代。
堆的结构介绍完,回到开头提出的问题,什么样的对象被当作垃圾来处理?其实这个问题很简单,当然是“死掉”的对象,那么怎样判断一个对象存活?下面介绍一下。

对象存活判断算法

虚拟机机制决定将死掉的对象当作垃圾进行回收,我了解到的算法有两种:引用计数法和可达性分析法。

引用计数法

这种方法是通过给对象添加一个引用计数器,每当有一个地方引用它时计数器加1,引用释放时计数减1,当计数器为0时可以回收。这种算法实现简单,判断高效,容易理解。但是无法解决对象相互循环引用的问题,所以主流的Java虚拟机中没有使用该方法,但在微软COM和Python等语言中被广泛使用。

可达性分析法

这种思想是通过一系列称为“GC Root”的对象(如系统类加载器、栈中的对象、处于激活状态的线程等)作为起点,基于对象引用关系,开始向下搜索,所走过的路径称为引用链,当一个对象到GC Root没有任何引用链相连,证明对象是不可用的,这样的对象认为是“死掉”的,可以进行回收。举个场景:堆中有10个对象Object1,Object2…Object10,其中Object1的引用变量test1作为method1()的局部变量,而method1()被加载到栈中正在被执行,这里假设Object1对像里又引用了其他的对象,这时如果通过可达性分析法去找GC Root的话,test1就是所谓的GC Root.如下图所示:

在这里插入图片描述
从图中可见,Object1-Object5 都是可达的,剩下的灰色的就是所谓的不可达,即没有GC Root,这些对象就会被当作垃圾在下一次回收时进行回收。那么如何去处理这些垃圾呢?继续往下看。。。

垃圾回收

垃圾回收方法有很多种,我所了解的,也是面试过程最经常问的,主要有三种:标记清除法、复制算法、标记整理算法。

标记清除法

顾名思意先标记再清除,即首先标记出所有需要回收的对象,在标记完成后统一回收掉所有被标记的对象,如下图所示。此方法简单,容易实现,但是标记和清除过程的效率都不高;另外是空间问题,标记清除之后会产生大量不连续的内存碎片,空间碎片过多可能会导致无法为一个大的对象分配内存,从而不得触发一次垃圾回收动作。
在这里插入图片描述

如下图所示

复制算法

这种算法是标记清除的改进方法,是通过将可用内存按容量划分为大小相等的两块,每次只使用其中的一块。当一块内存用完了,就将还存活着的对象复制到另外一块上,然后清理掉前一块。JVM堆中新生代便采用复制算法,这里为了说明原理,暂且以S0区和S1区互相复制为例(Eden区的复制在下文讲解),如下图所示。这种方法将内存缩小为一半,性价比低,持续复制长生存期的对象则导致效率低下
在这里插入图片描述
上图介绍了S0和S1之间互相复制的场景,但真正的jvm复制过程是包括Eden区的。当Eden区满时,还存活的对象会被复制到其中一个Survivor区;当新生代中Eden区和其中一个Surviovr区都满时,触发回收操作,会将Eden和使用的Survivor区还存活的对象,复制到另外一个Survivor区,然后对Eden和用过的Survivor区进行清理。如果另外一个Survivor区没有足够的内存存储时,则会进入老年代。
这里针对哪些对象会进入老年代有这样的机制:对象每经历一次复制,年龄加1,达到晋升年龄阈值后,转移到老年代,而老年代的清理算法则采用下面要讲到的“标记整理算法”。

标记整理算法

这种算法一般用于回收生存周期较长的对象,因而在老年代使用比较合适。标记过程与“标记-清除”算法一样,但后续步骤不是直接对可回收对象进行清理,而是让所有存活的对象都向一端移动,然后直接清理掉端边界以外的内存,即清理之前先对内存进行整理,如下图所示。这种方法不仅不需要对内存进行一分为二,还解决了大量内存碎片的问题。

在这里插入图片描述
讲到这差不多垃圾回收的原理都讲完了,总而言之不管是新生代还是老年代,所采用的方法都是建立在标记清除算法之上。新生代中对象生命周期一般比较短,因此主要使用复制算法,而老年代中对象生存周期比较长因而使用标记整理算法。

至此,jvm的堆工作原理介绍完了,至于其他部分的工作原理请参考如下博文
由浅入深了解JVM-内存结构
由浅入深了解JVM-虚拟机栈
由浅入深了解JVM-程序计数器

猜你喜欢

转载自blog.csdn.net/hongyinanhai00/article/details/113821955