垃圾收集器与内存回收策略

引用分析算法:

  • 引用:强 > 软 > 弱 > 虚
    • 概述:java中的引用
      • jdk1.2以前:
      • 如果refernce类型数据 存储值 代表的是另一块内存的起始地址,即这块内存代表一个引用。
      • jdk1.2以后:
      • 内存空间足够,则保留;
      • 如内存空间 进行垃圾收集后 还是非常紧张,则可抛弃。
    • 强引用:指普遍存在的:类似object Obj = new Object()
      • 收集:强引用关联的对象,垃圾收集器永远不会回收被引用的对象
      • 实现:new关键字,即继承Object
    • 软引用:指有用但并非必须的对象
      • 收集:软引用关联着的对象,在系统即将发生内存溢出之前,进行二次回收。回收完毕后还无足够内存,则抛出内存溢出异常。
      • 实现:实现SoftReference类
    • 弱引用:指非必需对象的,
      • 收集:弱引用关联的对象,下次垃圾收集进行回收
      • 实现:实现WeakReference类
    • 虚引用:幽灵引用或者幻影引用
      • 一个对象是否有虚引用:
      • 不会对其生存时间构成影响
      • 无法通过虚引用来取得对象实例。
      • 使用:设置虚引用关联的唯一目的:
      • 即在这个对象被收集器回收时收到一个系统通知。
      • 实现:实现PhantomReference类
  • 引用技术算法
    • 优点:实现简单
    • 缺点:难解决 对象之间 相互循环引用
    • 实现
      • 1.对象添加引用计数器
      • 2.当引用时,计数器值+1;
      • 3.当引用失效时,计数器值-1;
    • 任何时刻计数器为0的对象就是不可能再被使用的。
  • 可达性分析算法
    • 概念
      • GC Root节点
        • 虚拟机栈(栈贞中本地变量表)中引用的对象;
        • 方法区类静态属性引用的对象;
        • 方法区常量引用的对象;
        • 本地方法栈中jni(一般的native方法)引用的对象。
      • 引用链
        • gc root的对象作为起始点,
        • 从该点向下搜索,
        • 搜索的路径就称为引用链。
    • finalize()方法:
      • finalize()方法都只会被系统自动调用一次
      • 不是c++中的析够函数、是java刚诞生时为了时c/c++程序员更容易接受它所做出的一个妥协
      • 流程:
        • 放到F -Queue的队列之中
        • 虚拟机自动建立 低优先级 Finalizer线程去执行
        • gc将会对F -Queue队列对象进行小规模标记。
        • 如果在finalize()方法中成功自救(重新于引用链上的任何一个对象建立关联即可,如this赋值给某个类变量活着对象的成员变量)
        • 则会被移出即将回收集合;反之则被回收。
      • 缺点:运行代价高昂,不确定性,调用顺序随机性。
    • 宣告对象死亡:经历标记两次过程
      • 1.对象可达性分析后发现没有与gc roots相连接的引用链,则将会被第一次标记
      • 2.进行筛选,筛选条件(对象是否有必要执行finalize()方法)
        • 没有覆盖finalize()方法或finalize()方法已被调用过
  • 回收方法区
    • 废弃常量
    • 无用的类
      • 1.该类所有实例都已被回收,即java堆中不存在该类的任何实例。
      • 2.加载该类的ClassLoder已经被回收。
      • 3.该类对应的java.lang.Class对象没有在任何地方被引用,无法在任何地方通过反射访问该类的方法。
    • 注:大量使用反射,动态代理,CGLib等ByteCode框架,动态生成JSP以及OSGi这类频繁自定义的ClassLoader的场景需 虚拟机具备类卸载功能,以保证永久代不会溢出。
  • 垃圾收集算法
    • 标记清除
      • 过程
        • 1.标记出所有需要回收的对象
        • 2.完成后统一回收
      • 缺陷
        • 1.效率问题(标记和清除效率都不高)。
        • 2.空间问题(标记清除之后会产生大量不连续多内存碎片,空间碎片可能会导致在程序运行中需要分配较大对象无法找到足够连续内存而提前触发垃圾收集动作)。
    • 复制算法
      • 过程
        • 1.将内存容量划分为大小相等的两块,每次只使用其中的一块
        • 2.当这一块的内存用完了,就将还存活的对象复制到另外一块上面
        • 3.把已使用到内存空间一次性清理掉,每次都是对整个半区进行内存回收
      • 优点
        • 1.不用考虑内存碎片等复杂情况
        • 2.实现简单,运行高效(移动堆顶指针,按顺序分配内存即可)
      • 缺陷
        • 1.内存浪费
        • 2.对象存活率较高时就要进行复制操作,效率低下
      • IBM公司研究表明,新生代中的对象是98%短暂的,并不按1:1的比例来划分内空间.将内存分为一块较大的eden空间和两块较小的survivor空间
        • hotspot虚拟机为eden和survivo比例为8:1。即每次新生代中可用内存空间为整个新生代容量的90%(80+10),只有10%会被浪费。无法保证任何情况下都只有不多于10%都对象存活,当surivor空间不够时,需要依赖其他内存(老年代)进行分配担保。
      • 分配担保
        • 如果另外一块survivor空间没有足够空间存放上一次新生代收集下来的存活对象时,这些对象将直接通过分配担保机制进入老年代。
      • 空间分配担保
        • 在发生MinorGC之前,虚拟机会 检查老年代最大可用的连续空间 是否 大于 新生代所有对象总空间,
          • 如果不成立,则虚拟机会查看 HandlePromotionFailur设置值 是否允许担保失败。
            • 如果允许,则继续检查 老年代最大可用的连续空间 是否 大于 历次晋升到老年代对象的平均大小,
              • 如果大于,将尝试着进行一次MinorGC。尽管这次MinorGC是有风险的,
              • 如果小于,或者HandlePromotionFailue设置不允许冒险,那这时候也改为进行一次FullGC。
      • 注:新生代使用复制收集算法为了内存利用率 只使用 其中一个Surivor空间 来作为轮换备份,当出现大量对象在MinorGC后仍然存活的情况(如内存回收后新生代中所有对象都存活),需老年代进行分配担保,把Survivor无法容纳的对象直接进入老年代
  • 标记-整理算法
    • 过程
      • 1.标记出所有需要回收的对象
      • 2.存活的对象都向一端移动
      • 3.清理掉端边界以外的内存。
  • 分代收集算法
    • 过程
      • 1.把java堆分为新生代和老年代,根据各代的特点采用最适当的收集算法。
      • 2.新生代复制算法,存活率低
      • 3.老年代标记整理算法,存活率高
  • HotStop算法实现
  • 枚举根节点
  • GC Roots节点
  • 1.全局的引用(常量与静态属性)
  • 2.执行上下文(栈帧中本地变量表)
  • 一致性
  • 在分析期间执行系统像被冻结在某个时间点上。不可出现分析过程中对象引用关系还变化。如果不满足的话分析结果的准确性就无法得到保证
  • HotStop一致性
  • 使用oopMap(使虚拟机知道哪些地方存放着对象引用)的数据结果保证当执行系统停顿下来时,
  • 并不需要检查所有执行上下文和全局的引用位置。
  • 在类加载完成的时候,把对象内什么偏移量上是什么类型的数据计算出来,
  • 在jit编译过程,也会在特定位置纪录下栈和寄存器中哪些位置是引用
  • 缺点
  • 如果在oopmap中内容变化的指令非常多,
  • 如果为每一条指令都生成对应的oopmap,将需要额外空间,
  • gc的空间成本变高
  • 安全点
  • 程序执行时在安全点才能暂停停顿下来开始gc
  • 安全点的选定:
  • 以程序是否具有让程序长时间执行的特征为标准进行选定的。
  • 长时间执行的明显特点就是指令序列复用。
  • 方法调用,循环跳转,异常跳转等。
  • 具有该功能的指令才会产生安全点。
  • 线程跑到安全点的方案
  • 抢先式中断
  • gc发生时
  • 1.首先把所有线程全部中断
  • 2.如果发现有线程中断不在安全点上,即恢复线程跑到安全点上。
  • 主动式中断
  • 1.设置一个标志
  • 2.各个线程执行时回主动轮询这个标志,
  • 3.发现中断标志为真时就自己中断挂起(轮询的地方与安全点重合,创建对象需要分配内存的地方)
  • 问题
  • 当在程序不执行的时候安全点(如当前线程处于sleep或blocked状态)则无法响应jvm的中断请求。
  • 安全区域
  • 概述
  • 指在一段代码片段之中,引用关系不会发生变化。
  • 该区域点任意地方开始gc都是安全的。即安全点的扩展。
  • 实现
  • 安全区域当线程执行到安全区域点的代码时:
  • 1.首先标识经进入了安全区,该时间进行gc时,不用管标识为安全点状态的线程
  • 2.线程离开时,检查系统是否已经完成了根节点枚举(或者整个gc),
  • 3.如果完成则继续执行,反之则等待到收到可以安全离开的信号为止。

猜你喜欢

转载自www.cnblogs.com/lllllht/p/9173518.html