java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > JVM垃圾回收

JVM垃圾回收机制和垃圾回收器详细解说

作者:lose_rose777

这篇文章主要介绍了JVM垃圾回收机制和垃圾回收器,为了让程序员更加专注于代码的实现,而不用过多的考虑内存释放的问题,所以在Java语言中,有了自动的垃圾回收机制,也是我们常常提及的GC,需要的朋友可以参考下

什么是垃圾回收机制

为了让程序员更加专注于代码的实现,而不用过多的考虑内存释放的问题,所以在Java语言中,有了自动的垃圾回收机制,也是我们常常提及的GC(Garbage Collection)

有了这个垃圾回收机制之后,程序员只需要考虑内存的申请即可,内存的释放由系统自动识别完成。在垃圾回收的时候,不同的对象引用类型,GC会采用不同的回收时机,换句话说自动垃圾回收算法就会变得非常重要,如果因为算法的不合理,导致内存资源一直没有释放,同样也可能导致内存资源一直没有被释放,同样也可能会导致内存溢出

对象什么时候可以被垃圾回收

简单用一句话来说:人如果一个或者多个对象没有任何的引用指向它,那么这个对象现在就是垃圾,如果定位成垃圾,那么就有可能被垃圾回收器回收

如果要定位什么是垃圾,有两种方式来确定,第一个是引用计数法,第二个是可达性分析算法

引用计数法

一个对象被引用一次,在当前对象头上递增一次引用次数 ,如果这个对象的引用次数为0,代表这个对象可以被回收

String demo = new String("123");

String demo = null;

当对象间出现了循环引用的话,则引用计数法就会失效,如下图:

虽然a和b都为null,但是由于a和b存在循环引用,这样a和b永远都不会被回收

引用计数法的优点:

缺点:

可达性分析算法

现在的虚拟机采用的都是通过可达性分析算法来确定哪些内容是垃圾

首先会存在一个根节点(GC Roots),引出它下面指向的下一个节点,在以下一个节点开始为开始找到它下面的节点,依次往下类推,直到所有节点全部遍历完毕。这个思想类似于算法中的并查集

如上图,X和Y就是GC Roots无法到达的节点,那么X和Y就可以被认为是垃圾

根对象是那些肯定不能当作垃圾回收的对象,就可以当作根对象。一般有四种类型可以选做根对象:

虚拟机栈(栈帧中的本地变量表)中引用的对象

方法区中类静态属性引用的对象

方法区中常量引用的对象

本地方法栈中JNI(即一般说的Native方法)引用的对象(不常用)

JVM垃圾回收算法有哪些

刚刚已经讲解完了如何定义垃圾,那么定义完成之后的垃圾有一系列的处理算法

标记清除算法

标记清除算法,是将垃圾回收分为两个阶段,分别是标记和清除

标记清除算法有一定的劣势:

复制算法

复制算法的核心就是,将原来的内存空间一分为二,每次都是用其中的一块内存。在垃圾回收的时候,将正在使用的对象复制到另一个内存空间中,然后将该内存空间清空,交换两个内存的角色,完成垃圾的

如果内存中的垃圾对象较多,需要复制的对象就较少,这种情况下适合使用该 方式并且效率比较高,反之,则不适合

复制算法的执行流程:

优点:

缺点:

标记整理算法

标记整理算法是在标记清除算法的基础上,做了优化和改进的算法。和标记清除算法一样,也是从根节点开始,对对象的引用进行标记,在清理阶段,并不是简单的直接清理可回收对象,而是将存活对象都向内存的另一端移动,让然后清理边界以外的垃圾,从而解决了碎片化的问题

标记整理算法的执行流程:

优缺点:

分代收集算法

在Java8时,堆被分成两份新生代和老年代(1:2),在Java7的时候,还存在一个永久代,Java8将其移动到本地内存的元空间中

对于新生代,内部被分为三个区域:Eden区,两个大小完全相同的survivor区S0(from)和S1(to)(8:1:1)

分代收集算法执行流程:

新建的对象都会先分配代eden区,当eden区内存不足的时候,会标记eden区和from区(现阶段还没有)的存活对象

将存活下来的对象采用复制算法复制到to中,复制完毕之后,eden和from内存得以释放

经过一段时间之后,eden区的内存又不足了,标记eden区和to区存活的对象,将存货的对象复制到from区

当幸存区对象熬过几次回收(最多15次),晋升到老年代(幸存区内存不足 或大对象会导致提前晋升)

MinorGC 、 Mixed GC 、 FullGC 的区别是什么

JVM垃圾回收器有哪些

在JVM中,实现了多种垃圾收集器,包括:

串行垃圾收集器

Serial和Serial Old串行垃圾收集器,是指使用单线程进行垃圾回收,堆内存较小,适合个人电脑

垃圾回收时,只有一个线程在工作,并且Java应用中所有线程都要暂停(STW),等待垃圾回收的完成

并行垃圾收集器

Parallel New和Parallel Old是一个并行垃圾回收器,JDK8默认使用此垃圾回收器

垃圾回收时,多个线程在工作,并且java应用中的所有线程都要暂停(STW), 等待垃圾回收的完成

CMS(并发)垃圾收集器

CMS全称 Concurrent Mark Sweep,是一款并发的、使用标记-清除算法的垃圾回收器,该回收器是针对老年代垃圾回收的,是一款以获取最短回收停顿时间为目标的收集器,停顿时间短,用户体验就好。其最大特点是在进行垃圾回收时,应用仍然能正常运行

G1垃圾收集器

应用于新生代和老年代,在JDK9之后默认人使用的G1垃圾收集器。其中划分了很多个区域,每个区域都可以充当eden,survivor,old,humongous,其中humongous专门为大对象准备的。采用的复制算法,并且注重于响应时间和吞吐量。运行时主要是分为三个阶段:新生代回收、并发标记、混合收集。如果出现并发失败(即回收速度赶不上创建新对象的速度),就会触发Full GC

下面来详细的讲解一下年轻代垃圾回收:

初始的时候,所有的区域都处于空闲状态

创建了一些对象,挑出一些空闲区域作为eden区存储这些对象

当eden区需要垃圾回收时,挑出一个空闲区域作为survivor,用复制算法复制存活对象,需要暂停用户线程

随着时间流逝,eden区的内存又有不足,将eden区以及之前幸存区中存活的对象,采用复制算法,复制到新的幸存区,其中比较老的对象晋升至老年代

下面来说一下下一个阶段年轻代垃圾回收 + 并发标记:

当老年代占用内存超过一定的阈值(默认是45%)后,触发并发标记,这个时候无需暂停用户线程

并发标记之后,会有重新标记阶段解决漏标问题,此时需要暂停用户线程

这些都完成后就知道了老年代有哪些存活对象,随后进入混合收集阶段。此时不会对所有老年代区域进行回收,而是根据暂停时间目标优先回收价值高 (存活对象少)的区域(这也是 Gabage First 名称的由来)

混合垃圾回收的执行流程:

复制完成,内存得到释放。进入到下一轮的新生代回收、并发标记、混合收集

其中H叫做大对象,如果对象非常大,就会开辟一块连续的空间存储巨型对象

强引用与软引用与弱引用与虚引用的区别

强引用:只有所有 GC Roots 对象都不通过【强引用】引用该对象,该对象才能 被垃圾回收

User user = new User()

软引用:仅有软引用引用该对象时,在垃圾回收后,内存仍不足时会再次出发垃圾回收

User user = new User();
SoftReference softReference = new SoftReference(user);

弱引用:仅有弱引用引用该对象时,在垃圾回收时,无论内存是否充足,都会回收弱引用对象

User user = new User();
WeakReference weakReference = new WeakReference(user);

虚引用:必须配合引用队列使用,被引用对象回收时,会将虚引用入队,由Reference Handler线程调用虚引用相关方法释放直接内存

User user = new User();
ReferenceQueue referenceQueue = new ReferenceQueue();
PhantomReference phantomReference = new PhantomReference(user,queue);

到此这篇关于JVM垃圾回收机制和垃圾回收器详细解说的文章就介绍到这了,更多相关JVM垃圾回收内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文