码迷,mamicode.com
首页 > 其他好文 > 详细

JVM垃圾回收

时间:2021-05-24 07:35:23      阅读:0      评论:0      收藏:0      [点我收藏+]

标签:方法   经历   object   garbage   接下来   参数   选择   静态变量   mamicode   

引言

垃圾回收(GC,Garbage Collection)
在笔者上一篇文章中(JVM内存模型),介绍了JVM内存模型以及JVM运行时的数据区,堆是JVM内存区域里面最大的一块区域,用于存放实例数据,因此这一块区域是垃圾回收的重点区域,而堆为了提高垃圾回收效率,又被分为了年轻代和老老年,年轻代又被分为了eden区、survivor区。
技术图片

判断垃圾

接下来我们就讨论Jvm是怎么回收堆这部分内存的。在进行回收前垃圾收集器第一件事情就是确定哪些对象还存活,哪些已经死去。下面介绍两种基础的回收算法(找垃圾)。

引用计数法

给对象添加一个引用计数器,每当有一个地方引用它时计数器就+1,当引用失效时计数器就-1,。只要计数器等于0的对象就是不可能再被使用的。
此算法在大部分情况下都是一个不错的选择,也有一些著名的应用案例(据说python使用的是此算法),但是Java虚拟机中是没有使用的。
优点:实现简单、判断效率高。
缺点:当几个对象存在互相循环引用,但这几个对象组成了一个圆环,没有任何对象指向这个圆环了,所以这个整体应该被回收,但它的引用计算不等于0,造成无法进行回收

  Object a = new Object();
  Object b = new Object();
  a=b;
  b=a;
  a=b=null; //这样就导致gc无法回收他们。

根可达算法

虚拟机规定一个GC ROOT标准,当从这些GC ROOT往下引用查找的时候,能够引用得到,则不是垃圾,如果从任何GC ROOT都无法引用到某一个对象,则这个对象,就会标记为垃圾
主流的商用程序语言(Java、C#等)在主流的实现中,都是通过可达性分析来判定对象是否存活的。
技术图片
图中Object1-5不是垃圾,Object6/7/8会被认为是垃圾

在Java语言中,可作为GC Roots 的对象包括下面几种

  1. 虚拟机栈(栈帧中的本地变量表)中引用的对象
  2. 方法区中静态变量引用的对象
  3. 方法区中常量引用的对象
  4. 本地方法栈(即一般说的 Native 方法)中JNI引用的对象

GC分代

为了使 JVM 能够更好的管理堆内存中的对象,包括内存的分配以及回收。
堆的内存模型大致为:
技术图片
Java 中的堆也是 GC 收集垃圾的主要区域。GC 分为两种:Minor GC(或称为 young GC)、Full GC ( 或称为 Major GC )。

新生代
新生代几乎是所有 Java 对象出生的地方,即 Java 对象申请的内存以及存放都是在这个地方。Java 中的大部分对象通常不需长久存活,具有朝生夕灭的性质。(对于大对象,直接进入放在老年代)
对象优先在新生代 Eden 区中分配,如果 Eden 区没有足够的空间时,就会触发一次 Minor GC 。

当对象在 Eden ( 包括一个 Survivor 区域,这里假设是 from 区域 ) 出生后,在经过一次 Minor GC 后,如果对象还存活,并且能够被另外一块 Survivor 区域所容纳( 上面已经假设为 from 区域,这里应为 to 区域,即 to 区域有足够的内存空间来存储 Eden 和 from 区域中存活的对象 ),则使用复制算法将这些仍然还存活的对象复制到另外一块 Survivor 区域 ( 即 to 区域 ) 中,然后清理所使用过的 Eden 以及 Survivor 区域 ( 即 from 区域 ),并且将这些对象的年龄设置为1,以后对象在 Survivor 区每熬过一次 Minor GC,就将对象的年龄 + 1( 对象的当前GC年龄存在对象的headr中的),当对象的年龄达到某个值时 (默认是 15 岁,可以通过参数 -XX:MaxTenuringThreshold 来设定 ),这些对象就会成为老年代。

老生代
当对象经历过规定次数的Minor GC后,如果还有幸存活,则晋升至老年代(或者一些比较大的对象,一出生就会在老年代)。

现实的生活中,老年代的人通常会比新生代的人 "早死"。堆内存中的老年代(Old)不同于这个,老年代里面的对象几乎个个都是在 Survivor 区域中熬过来的,它们是不会那么容易就 "死掉" 了的。因此,Full GC 发生的次数不会有 Minor GC 那么频繁,并且做一次 Full GC 要比进行一次 Minor GC 的时间更长。FULL GC采用的是标记清除(或整理)算法

新生代与老年代的关系图
技术图片

垃圾回收算法

复制(Copy)

将内存按容量划分为两块,每次只使用其中一块。当这一块内存用完了,就将存活的对象复制到另一块上,然后再把已使用的内存空间一次清理掉。这样使得每次都是对半个内存区回收,也不用考虑内存碎片问题,简单高效。缺点需要两倍的内存空间。
技术图片

标记清除(Mark-Sweep)

GC分为两个阶段,标记和清除。首先标记所有可回收的对象,在标记完成后统一回收所有被标记的对象。同时会产生不连续的内存碎片。碎片过多会导致以后程序运行时需要分配较大对象时,无法找到足够的连续内存,而不得已再次触发GC。
技术图片

标记整理(Mark-Compact)

也分为两个阶段,首先标记可回收的对象,再将存活的对象都向一端移动,然后清理掉边界以外的内存。此方法避免标记-清除算法的碎片问题,同时也避免了复制算法的空间问题。
技术图片

一般年轻代中执行GC后,会有少量的对象存活,就会选用复制算法,只要付出少量的存活对象复制成本就可以完成收集。而老年代中因为对象存活率高,没有额外过多内存空间分配,就需要使用标记-清理或者标记-整理算法来进行回收。

JVM垃圾回收

标签:方法   经历   object   garbage   接下来   参数   选择   静态变量   mamicode   

原文地址:https://www.cnblogs.com/yanchuanbin/p/14762403.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!