JVM垃圾收集算法(1)

概述

垃圾收集需要完成三件事:

  • 哪些内存需要回收?
  • 什么时候回收?
  • 如何回收?
判断对象存活
引用计数算法?

在对象中添加一个引用计数器,每当有一个地方引用,计数器加一;当引用失效时,计数器减一;任何时刻计数器为零的对象就是不可能再被使用。

缺点:单纯的引用计数很难解决对象之间相互循坏引用的问题,必须配合大量额外处理才能保证正确的工作。

可达性分析算法

基本思路:通过一系列被称为"GC Roots"的根对象作为起始节点集,如果某个对象到GC Roots间没有任何引用链相连,则证明此对象是不可能再被使用的。

在java体系中,可作为GC Roots的对象包括以下:

  • 在虚拟机栈中引用的对象。如正在运行的方法所使用的参数、局部变量、临时变量等
  • 在方法区中类静态属性引用的对象。
  • 在方法区中常量引用的对象。如字符串常量池里的引用
  • 在本地方法栈中JNI(Native方法)引用的对象。
  • jvm内部的引用,如基础数据类型对应的Class对象,一些常驻的异常对象,还有类加载器。
  • 所有被同步锁(synchronized关键字)持有的对象。
  • 反映jvm内部情况的注册回调、本地代码缓存等。
  • 其他
引用关系

由强到弱:

  • 强引用:只要此关系存在,垃圾收集器永远不会回收掉。
  • 软引用:描述还有用,非必须的对象。在系统将要发生内存溢出异常前,会对此进行二次回收。
  • 弱引用:描述非必须对象。只能生存到下一次垃圾收集器发生为止。
  • 虚引用:不对其生存关系造成影响。
死亡标记

即使可达性分析算法中判断为不可达的对象,不代表此对象要死亡,最多会经历两次标记过程:可达性分析后发现没有引用链,将会被第一次标记;随后进行一次分析,筛选的条件是此对象是否有必须执行finalize()方法。假如 1、对象没有覆盖此方法,或者 2、此方法已经被虚拟机调用过,那么会被视为“没必要执行”。

如果这个对象被被判定为有必要执行,对象会被放置在一个队列中,执行finalize()方法,并不承诺一定会等待它运行结束(如果死循环,会导致其他回收处于等待,回收子系统崩溃);finalize()方法是对象逃脱死亡的最后一次机会,稍后收集器会进行第二次标记,如果想不被回收,需要与引用链建立关系,如果还没有建立关系会被回收。

回收方法区

方法区的垃圾收集主要分两部分:废弃的常量和不再使用的类型(该类所有实例已被回收;加载该类的加载器已被回收;该类对应的class对象没有被引用)。

垃圾收集算法
分代收集理论

两个分代假说:

  • 弱分代假说:绝大多数对象都是周期短的;
  • 强分代假说:熬过越多次垃圾收集过程的对象就越难以消亡;

收集器应该将java堆划分出不同的区域,然后将回收对象依据回收对象的年龄(年龄即对象熬过垃圾收集过程的次数)分配到不同的区域之中存储。–因而有了"Minor GC" "Major GC"等回收类型的划分。

  • 部分收集(Partial GC):指目标不是完整收集整个java堆的垃圾收集,其中又分为:
    • 新生代收集(Minor GC):…
    • 老年代收集(Major GC):…
    • 混合收集(Mixed GC):指目标是收集整个新生代以及部分老年代的垃圾收集。目前只有Gi收集器会有这种行为。
  • 整堆收集(Full GC):收集整个java堆和方法区的垃圾收集。

目前商用java虚拟机中:在新生代中,每次垃圾收集会有大批量对象死去,而每次回收后存活的少量对象,将会逐步晋升到老年代中存放。

标记-清除算法

最早最基础的垃圾算法:(先标记再清除)首先标记出所有需要回收的对象,在标记完成后,统一回收掉所有被标记的对象;也可以反过来标记,标记要存活的对象,统一回收未被标记的对象。

缺点:执行效率不稳定,会随着对象的数量增长而降低;内存空间的碎片化问题,标记清除后产生大量不连续的内存碎片,可能导致较大对象无法找到足够的连续内存而不得不提前触发另一次垃圾收集。

标记-复制算法

目前的商用java虚拟机大多采用此算法收回新生代。
为了解决标记-清楚大量回收对象时效率不高的问题,针对新生代的对象大部分熬不过第一轮收集,提出“半区复制”算法,将内存按容量划分为等大的两块,每次使用其中一块;当其中一块快用完了进行回收,将还存活的移到另外一边。

优点:解决了空间碎片的问题,每次复制时,只要移动堆顶指针,按顺序分配即可。

缺点:如果很多对象时存活的,会产生大量内存复制的开销;另外因为半区的划分,可用内存缩小为一半,空间浪费。

优化:HotSpot虚拟机优化了复制分代策略,分配了8:1:1的内存。每次新生代中可用内存为整个新生代容量的90%,另外10%用于存放上一次存活的对象。如果10%不够存放,将通过分配担保机制直接进入老年代。

标记-整理算法

标记-复制算法可能存在较多复制的情况,效率降低。以及极端所有都存活的情况存在,所以老年代一般不采用复制算法,而使用整理算法。

思路:标记存活对象,然后让这些对象都想内存一端移动,直接清理掉边界以外的内存。

区别:相比较清除算法,只是是否移动活动对象,如果移动的对象很多,暂停用户程序更新引用对象是个复杂的操作,回收较复杂。如果不移动,只能依赖更为复杂的内存分配器和访问器来解决,内存分配较复杂。但是,也有相对应的优点:标记-清除相比较延迟更低,标记-整理相比较吞吐量更大。

vx公众号:java学习库

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值