【JVM】垃圾回收机制、算法和垃圾回收器

打印 上一主题 下一主题

主题 958|帖子 958|积分 2874

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

x
什么是垃圾回收机制

为了让程序员更加专注于代码的实现,而不用过多的考虑内存开释的问题,以是在Java语言中,有了自动的垃圾回收机制,也是我们常常提及的GC(Garbage Collection)
有了这个垃圾回收机制之后,程序员只需要考虑内存的申请即可,内存的开释由体系自动辨认完成。在垃圾回收的时间,不同的对象引用类型,GC会采用不同的回收机遇,换句话说自动垃圾回收算法就会变得非常紧张,如果由于算法的不合理,导致内存资源一直没有开释,同样也大概导致内存资源一直没有被开释,同样也大概会导致内存溢出
对象什么时间可以被垃圾回收


简朴用一句话来说:人如果一个大概多个对象没有任何的引用指向它,那么这个对象现在就是垃圾,如果定位成垃圾,那么就有大概被垃圾回收器回收
如果要定位什么是垃圾,有两种方式来确定,第一个是引用计数法,第二个是可达性分析算法
引用计数法

一个对象被引用一次,在当前对象头上递增一次引用次数 ,如果这个对象的引用次数为0,代表这个对象可以被回收
  1. String demo = new String("123");
复制代码

  1. String demo = null;
复制代码
 
当对象间出现了循环引用的话,则引用计数法就会失效,如下图:

 
固然a和b都为null,但是由于a和b存在循环引用,如许a和b永远都不会被回收
引用计数法的优点:


  • 及时性较高,无需等到内存不敷的时间,才开始回收,运行时根据对象的计数器是否为0,就可以直接回收
  • 在垃圾回收过程中,应用无需挂起。如果申请内存时,内存不敷,则立即报OOM错误
  • 地区性,更新对象的计数器时,只是影响到该对象,不会扫描全部对象
缺点:


  • 每次对象被引用时,都需要去更新计数器,有一点时间开销
  • 浪费 CPU 资源,纵然内存够用,仍然在运行时进行计数器的统计
  • 无法解决循环引用问题,会引发内存走漏 (最大的缺点)
可达性分析算法

现在的虚拟机采用的都是通过可达性分析算法来确定哪些内容是垃圾
起首会存在一个根节点(GC Roots),引出它下面指向的下一个节点,在以下一个节点开始为开始找到它下面的节点,依次往下类推,直到全部节点全部遍历完毕。这个头脑雷同于算法中的并查集

如上图,X和Y就是GC Roots无法到达的节点,那么X和Y就可以被以为是垃圾
根对象是那些肯定不能看成垃圾回收的对象,就可以看成根对象。一样平常有四种类型可以选做根对象:


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



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



  • 方法区中常量引用的对象



  • 本地方法栈中JNI(即一样平常说的Native方法)引用的对象(不常用)
JVM垃圾回收算法有哪些

刚刚已经解说完了怎样定义垃圾,那么定义完成之后的垃圾有一系列的处置惩罚算法
标志扫除算法

标志扫除算法,是将垃圾回收分为两个阶段,分别是标志扫除


  • 根据可达性分析算法得出了垃圾进行标志
  • 对这些标志为可回收的内容进行垃圾回收

标志扫除算法有一定的劣势:


  • 服从比较低,标志和扫除两个动作都是需要遍历全部的对象,并且GC的时间,需要停止应用程序,对于交互性要求比较高的应用而言这个不是很推荐
  • (紧张)通过标志扫除算法清理出来的内存,碎片化比较严重,由于被回收的对象大概存在于内存的各个角落,以是清理出来的内存不是很连贯
复制算法

复制算法的焦点就是,将原来的内存空间一分为二每次都是用此中的一块内存。在垃圾回收的时间,将正在使用的对象复制到另一个内存空间中,然后将该内存空间清空交换两个内存的脚色,完成垃圾的
如果内存中的垃圾对象较多,需要复制的对象就较少,这种情况下适合使用该 方式并且服从比较高,反之,则不适合

复制算法的实行流程: 


  • 将内存地区分成两个部门,每次只是操作此中的一个
  • 当进行垃圾回收的时间,将正在使用的内存地区中的存活对象移动到未使用的内存地区。当移动完成之后,对这一部门内存进行一次性扫除
优点:


  • 在垃圾对象多的情况下,服从较高
  • 清理后,内存无碎片
缺点:


  • 分配的2块内存空间,在同一个时刻,只能使用一半,内存使用率较低
标志整理算法

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

标志整理算法的实行流程:


  • 标志垃圾
  • 需要清楚的向右走,不需要清楚的向左走
  • 扫除界限以外的垃圾
优缺点:


  • 解决了标志扫除算法的碎片化的问题
  • 标志压缩算法多了一步,对象移动内存位置的步调,其服从也有有一定的影响
  • 复制算法标志完就复制,但标志整理算法得等把全部存活对象 都标志完毕,再进行整理
分代收集算法

在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 的区别是什么 


  • MinorGC【young GC】发生在新生代的垃圾回收,暂停时间短(STW:暂停全部应用程序线程,等待垃圾回收的完成)
  • Mixed GC 新生代 + 老年代部门地区的垃圾回收,G1 收集器特有
  • FullGC 新生代 + 老年代完备垃圾回收,暂停时间长(STW),应努力避免
JVM垃圾回收器有哪些

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


  • 串行垃圾收集器
  • 并行垃圾收集器
  • CMS(并发)垃圾收集器
  • G1垃圾收集器
串行垃圾收集器

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


  • Serial作用于新生代,曹勇复制算法
  • Serial Old作用于年轻代,采用标志-整理算法
垃圾回收时,只有一个线程在工作,并且Java应用中全部线程都要暂停(STW),等待垃圾回收的完成

并行垃圾收集器

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


  • Parallel New作用于新生代,采用复制算法
  • Parallel Old作用于老年代,采用标志-整理算法
垃圾回收时,多个线程在工作,并且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 对象都不通过【强引用】引用该对象,该对象才能 被垃圾回收
  1. User user = new User()
复制代码

 软引用:仅有软引用引用该对象时,在垃圾回收后,内存仍不敷时会再次出发垃圾回收
  1. User user = new User();
  2. SoftReference softReference = new SoftReference(user);
复制代码
 

弱引用:仅有弱引用引用该对象时,在垃圾回收时,无论内存是否充足,都会回收弱引用对象
  1. User user = new User();
  2. WeakReference weakReference = new WeakReference(user);
复制代码

虚引用:必须配合引用队列使用,被引用对象回收时,会将虚引用入队,由Reference Handler线程调用虚引用相关方法开释直接内存
  1. User user = new User();
  2. ReferenceQueue referenceQueue = new ReferenceQueue();
  3. PhantomReference phantomReference = new PhantomReference(user,queue);
复制代码


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

光之使者

金牌会员
这个人很懒什么都没写!
快速回复 返回顶部 返回列表