垃圾回收机制

垃圾回收机制
Java语言不直接操作内存,所以Java程序员在编写程序的时候无法手动释放内存,所以就需要JVM帮助清理不需要的对象,腾出空闲内存,垃圾回收机制应运而生。
学习垃圾回收机制主要需要清楚以下几个问题。

  1. 哪些对象是可回收的?
  2. 怎么回收这些对象,使用什么算法?各自的优劣有哪些?
  3. 目前有哪些垃圾收集器?各自的使用场景,优劣。
  4. finalize()方法
  5. 之前工作中是否有遇到垃圾回收机制引发的问题,比如频繁的Full GC,如何定位解决?
哪些对象是可回收的

当虚拟机中对象过多而无法申请的足够的内存存储新的对象时,会触发一次GC,但怎样去识别哪些对象可回收,哪些对象不可回收呢。
引用计数法是指当对象A引用对象B时,B的计数值加1,在回收的时候会计算每个对象的计数值是否为0。如果计数值为0,则认为可以回收。乍一看这种方法没有什么问题,简单易懂。但这种方法无法处理循环引用的问题,循环引用是指对象A引用对象B的同时,对象B也引用了对象A。除此之外,再没有其他对象存在这两个对象的引用。这样,这两个对象无法被使用,但由于相互引用,计数器值不为0,就不会被识别为可回收的对象,从而造成内存泄露。
可达性分析是虚拟机从根节点进行枚举,找出哪些对象是可达的,不可达的对象即为可回收的对象。根结点选取一般是类静态属性引用的对象、栈上引用的对象、方法区引用的对象,本地方栈中JNI引用的对象。该方法能够很好的解决循环引用的问题。目前的虚拟机大多都使用这种分析算法去标记哪些对象是可回收的。

垃圾回收算法

当前虚拟机较多使用的几种垃圾回收算法有标记清除算法、标记整理算法、复制算法。
标记清除算法是虚拟机将内存中可回收的对象标记出来,从而进行回收。因为可回收的对象不一定处于连续的内存空间,所以使用该算法会造成大量的内存碎片产生,当有较大的对象申请内存时,明明还有足够的空闲内存,却无法存放该对象,从而触发Full GC。
标记整理算法从名称上即可知,该方法是在上一个算法基础上作出了改进,虚拟机会现标记出可回收的对象,然后将不可回收的对象统一移动到一侧,然后清空后边的内存空间。该算法不会产生空间碎片。目前较多的是将两种算法混合使用,即经过一定次数的标记算法清理后触发一次标记整理算法。
复制算法是将内存分为两块,每次只使用其中一块内存,回收时首先将存活的对象复制到另一块内存,然后清空当前内存。该算法的弊端显而易见。只有一半的内存可以利用,永远有一块内存处于空闲状态,这样可能会频繁的触发GC。
根据大量的数据统计,大多数的对象都是朝生夕死,只有少数的对象会较长时间存在于内存中。所以复制算法将内存默认按照8:1:1分为eden,from,to三块区域,每次使用其中两块内存,当进行回收的时候,把存活的对象复制到空闲的一块内存中,然后清空当前的内存。这样可以更好的利用内存,不至于大量的浪费。
虽然大多数时候to区域足够存放存活的对象,但也有例外的时候。所以,复制算法需要一个担保机制来确保有足够的内存存放存活的对象。担任担保的角色就是老年代。当to区域不足以存放这些存活的对象时,就会触发担保机制,将存活对象直接移动到老年代。所以这个算法的另一个弊端就是可能会造成对象过早进入老年代。

垃圾收集器

在HotSpot虚拟机中,存在Serial、Serial Old、ParNew、Parallel Old、Parallel Scavenge、CMS、G1收集器。每个收集器有各自的功能,各有优劣。

Serial收集器是新生代使用的一个单线程收集器,使用复制算法,该收集器以单线程的方式进行垃圾回收。通常,在单核环境中使用该收集器的效果较好。
Serial Old 收集器是老年代使用的单线程收集器,使用标记-整理算法,原理与Serial收集器相同。可与Parallel Scavenge收集器配合使用,也是作为CMS收集器的后备预案。
ParNew收集器相当于Serial收集器的多线程版本,使用复制算法,目前的服务器大多都是多核超线程的服务器,所以使用多线程版本的收集器效果会更好。ParNew收集器是目前除了Serial收集器外,唯一一个可以与CMS配合使用的新生代收集器。
Parallel Old收集器是老年代使用的多线程收集器,使用标记-整理算法。相当于Serial Old的多线程版本。可与Parallel Scavenge配合使用。
Parallel Scavenge收集器是一款新生代的收集器,使用复制算法。该收集器进行垃圾回收的侧重点于其他收集器不同,该收集器的关注点是达到一个可控制的吞吐量。

收集器提供了两个参数用于精确控制吞吐量
-XX:MaxGCPauseMillis 最大垃圾收集停顿时间
-XX:GCTimeRatio 吞吐量大小
吞吐量=运行用户代码时间/(运行用户代码时间+垃圾收集时间)
设置 -XX:UseAdaptiveSizePolicy,打开该开关后,虚拟机会根据系统运行情况自适应调节。

CMS收集器是一款真正实现了与业务线程并行的收集器,STW时间得到有效的控制,CMS收集器的机制:
初始标记->并发标记->重新标记->并发清理

初始标记和重新标记需要STW。
初始标记只是标记能直接关联到GC Roots的对象。
重新标记是为了修正并发标记中产生变动的标记记录。
缺点:
对CPU敏感,因为会与用户线程并发执行,需要抢占资源。

使用标记-清除算法,会产生空间碎片。
可以设置执行多次不压缩的清理,然后进行一次代压缩的清理

CMS收集器无法等到老年代完全填满再进行回收,需要预留一部分内存留作并发标记时使用。
如果预留的空间不足,就会出现失败,系统将使用Serial Old重新进行垃圾收集。

该收集器使用的算法就是标记算法,用于老年代。该收集器有一个问题就是可能会出现浮动垃圾,即在并行的过程中又有新的垃圾生成,这块儿垃圾本次GC无法回收,会一直存在到下次回收。
G1收集器也是一款实现与业务并行的收集器,该收集器与其他的收集器的不同之处在于它并不是以新生代,老年代来划分内存,而是将整块内存划分为一个一个小块,在清理的时候会查找清理效率最高的块儿进行优先清理。

并行与并发
分代收集
G1收集器不需要其他收集器配合就可以管理整个GC堆。

空间整合
G1收集器整体来看使用标记-整理算法,局部使用复制算法。

可预测的停顿
可以让使用者指定在一定时间内进行回收。

初始标记->并发标记->最终标记->筛选回收

G1收集器使用remembered Set来避免全表扫描,每一个Region有一个对应的Remembered Set与之对应,虚拟机发现程序对引用的对象进行写操作时,会检查引用的对象是否在同一个Region中,如果不是,则记录到Remembered Set之中。
并行:指多条垃圾收集线程共同工作,此时的用户线程仍然处于STW状态。
并发:指垃圾收集线程可以与用户线程同时执行。
finalize()方法

finalize方法是java为了向C++程序员妥协而产生的一个方法,当一个对象回收的时候可能会执行该方法,但并不一定,finalize方法执行后也不是一定会回收该块儿内存。在垃圾回收的时候,虚拟机检测到该块儿内存可以回收,会执行该对象的finalize()方法,但此时并不会回收该块儿内存,而是将其标记为可清理的,只到下一次回收时,该对象仍然没有其象引用,才会对其进行回收。 这其中就有一种可能,就是在执行finalize()方法时,该对象进行了自我拯救,重新与其他对象 GC Roots链建立了引用关系,这样在后边 下一次的真正回收的时候,则不会再一次执行finalize()方法。所以,在finalize()方法中释放资源的操作是不可取的,我们也不应该依赖于该方法的执行。

可达性分析发现对象没有于GC Roots相连接的引用链,那他将会被第一次标记并且进行一次筛选
筛选的条件是此对象是否有比较执行finalize()方法,如果虚拟机已经执行过该方法,或者没有
覆盖重写该方法。虚拟机视为“没有必要执行“
如果对象判断为有比较执行,那么对象将会放到F-Queue队列中,并在稍后由虚拟机创建的线程中
执行,这里的“执行“是指虚拟机会触发这个方法,但不承诺会等待他执行结束。
HotSpot的算法实现

枚举根结点 由于虚拟机查找根结点的时候需要STW,并且从整个应用内存中查找根结点会很耗时,所以虚拟机实现是增加了oopMap的数据结构来存储根结点,这样在垃圾回收的时候只需要扫描该数据结构就能找到根结点。而不需要扫描整个内存。
安全点 由于每一条指令都生存oopMap的话,内存消耗会很大,所以只有在某一点的时候,才会生成oopMap,这样的点成为安全点,程序执行时,并不是任何位置都可以停下来执行GC,而是需要所有线程到达安全点,才可以执行GC。中断业务线程的方式有两种:抢先式中断和主动式中断。
抢先式中断是指需要GC的时候,会直接中断所有线程,然后恢复未跑到安全点的线程,让其执行到安全点。目前几乎没有虚拟机使用这样的机制去实现响应GC事件。
主动式中断是指需要GC的时候,并不会直接中断线程,而是设置一个标记,所有线程去查询这个标记,当发现需要GC的时候,便运行到安全点主动中断等待GC。
安全区域 当有线程处于阻塞或者休眠的时候,无法主动执行到安全点,所以便有了安全区域的概念,安全区域是指在一块儿区域内,对象引用关系没有发生变化,在该块儿区域内的任何位置开始GC都是安全的。

垃圾回收相关问题实践

代码块部分文字摘自 《深入理解Java虚拟机》 周至明

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值