jvm整理 | 垃圾回收

1、可达性算法分析

      GC Roots:虚拟机栈中引用的对象、方法区中类静态属性引用的对象、方法区中常量引用的对象、Native方法中引用的对象、java虚拟机内部的引用(基本数据类型的Class对象,异常对象,系统类加载器)、被Synchronized 持有的对象

2、引用

      如果reference 类型的数据中存储的数值代表的是另外一块内存的起始地址,就称改reference数据是代表某块内存,某个对象的引用。

      强引用:程序中普遍的引用赋值,只要强引用关系还在,就不会回收掉被引用的对象

      软引用:描述一些还有用,但是非必须的对象。在系统将要发生内存溢出前,会先把这些对象列进回收范围进行第二次回收,如果这次还没有足够的内存,才会抛出内存异常。举例:反射缓存 SoftReference<ReflectionData<T>>

      弱引用:描述非必须的对象,被弱引用关联的对象只能生存到下一次垃圾收集发生为止。当垃圾收集器开始工作,无论当前内存是否足够,都会回收掉只被弱引用关联的对象   举例: WeakReference<ThreadLocal<?>>

      虚引用:为一个对象设置虚引用关联的唯一目的只是为了能在这个对象被收集器回收时收到一个系统通知

3、垃圾收集算法

     分代收集理论建立在三个假说之上

    弱分代假说:绝大多数对象都是朝生夕灭的

    强分代假说:熬过越多次垃圾收集过程的对象就越难消灭

    跨代引用假说:跨代引用相对于同代引用来说占极少数 [记忆集(Remembered Set)]:建立在新生代上的全局数据结构,把老年代分为若干小块,标识出老年代哪块内存会存在跨代引用,当发生MinorGC时,只有包含了跨代引用的小块内存里的对象才会被加入到GC Roots进行扫描

    

    标记-清除:标记存活或需要回收的对象,统一清除

    缺点:1、执行效率不稳定,随着堆中对象数量的增加而降低。2、内存碎片化,碎片太多会导致分配大对象是无法找到连续的内存而在此不得不提前触发垃圾收集

    标记-复制:将内存划分为两块,每次只使用其中一块,当一块内存使用完了,就将还存活的对象复制到另一块上面去,然后把已使用过的内存空间一次性清理掉(适用的新生代,HotSpot默认Eden和Suivivor的大小比例是8:1)

   缺点:1、内存中大量对象都是存活的,会有大量的复制时间开销。2、可用内存缩小

   标记-整理:让所有存活的对象都向内存空间一端移动,然后直接清理掉边界以外的内存

   缺点:1、移动存活对象并更新引用这些对象的地方将会是一种极为负重的操作,需要暂停用户程序才能进行

(CMS:多数时间标记清除,内存碎片足够大才会标记整理)

 

4、Hotspot的算法实现细节

   根节点枚举:由于目前主流Java虚拟机使用的都是准确式垃圾收集,所以当用户线程停顿下来之后,其实并不需要一个不漏地检查完所有执行上下文和全局的引用位置,虚拟机应当是有办法直接得到哪些地方存放着对象引用的。在HotSpot的解决方案里,是使用一组称为OopMap的数据结构来达到这个目的。一旦类加载动作完成的时候,HotSpot就会把对象内什么偏移量上是什么类型的数据计算出来,也会在特定的位置记录下栈里和寄存器里哪些位置是引用。这样收集器在扫描时就可以直接得知这些信息了(可以跳过其他信息的扫描),并不需要真正一个不漏地从方法区等GC Roots开始查找。

OopMap:记录了栈上本地变量到堆上对象的引用关系

我们知道,一个线程意味着一个栈,一个栈由多个栈帧组成,一个栈帧对应一个方法,一个方法里有多个安全点,gc发生时,程序首先运行到最近的一个安全点停下来,然后跟新自己的OopMap,记录栈上那些位置代表着引用,根节点枚举的时候,递归遍历每个栈帧的OopMap,通过引用找到这些对象(GCRoots)

 

安全点:安全点的选取是以“是否具有让程序长时间执行的特征”为标准。因为每个指令执行的时间都非常短。长时间执行”的最明显特征就是指令序列的复用,例如方法调用、循环跳转、异常跳转等都属于指令序列复用,所以只有具有这些功能的指令才会产生安全点。

安全区域:安全区域是指能够确保在某一段代码片段之中,引用关系不会发生变化

记忆集与卡表:记忆集是一种用于记录从非收集区域指向收集区域的指针集合的抽象数据结构,简单的做法就是用非收集区域中所有含跨代引用的对象数组来实现,但是占用空间个维护成本都很高昂。而在垃圾收集场景中,收集器只需要通过记忆集判断出某一块非收集区域是否存在指向了收集区域的指针就可以了,并不需要了解这些跨代指针的全部细节。

                         卡表:卡精度的方式去实现记忆集(卡精度-每个记录精确到一块内存区域,该区域内有对象含有跨代引用)

读写屏障:HotSpot虚拟机里是通过写屏障(Write Barrier)技术维护卡表状态的。

                  写屏障可以看作在虚拟机层面对“引用类型字段赋值”这个动作的AOP切面[插图],在引用对象赋值时会产生一个环形(Around)通知,供程序执行额外的动作,也就是说赋值的前后都在写屏障的覆盖范畴内。在赋值前的部分的写屏障叫作写前屏障(Pre-WriteBarrier),在赋值后的则叫作写后屏障(Post-Write Barrier)

并发标记: 三色标记法:白色-尚未被垃圾收集器访问过,在可达性分析刚开始的阶段,所有的对象都是白色的,结束阶段仍然是白色,就代表不可达  

                                        黑色-已经被垃圾收集器访问过,且这个对象的所有引用都已经扫描。(如果有其他对象引用指向黑色,无须重新扫描,黑不可能直接变白)

                                        灰色-介于黑白之间,本身被垃圾收集器访问过,但是对象上至少存在一个引用还没被扫描过

                                        关于可达性分析,可以看做是对象图上一股以灰色为波峰的波纹从黑向白推进

                    并发标记可能出现两种后果。一种是把原本消亡的对象错误标记为存活,这不是好事,但其实是可以容忍的,只不过产生了一点逃过本次收集的浮动垃圾而已,下次收集清理掉就好。另一种是把原本存活的对象错误标记为已消亡,这就是非常致命的后果了,程序肯定会因此发生错误

                    当且仅当以下两个条件同时满足时,会产生“对象消失”的问题,即原本应该是黑色的对象被误标为白色:

                    ·赋值器插入了一条或多条从黑色对象到白色对象的新引用;

                    ·赋值器删除了全部从灰色对象到该白色对象的直接或间接引用。

                     因此,我们要解决并发扫描时的对象消失问题,只需破坏这两个条件的任意一个即可。由此分别产生了两种解决方案:增量更新(Incremental Update)原始快照(Snapshot At The Beginning,SATB)

                     增量更新要破坏的是第一个条件,当黑色对象插入新的指向白色对象的引用关系时,就将这个新插入的引用记录下来,等并发扫描结束之后,再将这些记录过的引用关系中的黑色对象为根,重新扫描一次。

                     原始快照要破坏的是第二个条件,当灰色对象要删除指向白色对象的引用关系时,就将这个要删除的引用记录下来,在并发扫描结束之后,再将这些记录过的引用关系中的灰色对象为根,重新扫描一次。

(CMS是基于增量更新来做并发标记的,G1、Shenandoah则是用原始快照来实现。)

https://www.jianshu.com/p/12544c0ad5c1

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值