JVM 垃圾收集与内存分配

判断对象是否还活着

引用计数法

  • 给对象添加引用计数器,添加加1,引用失效减1,如果为0就是不可使用的。问题是不能解决互相引用带来的问题

可达性分析法

  • 以GC Roots为起点,判断到一个对象是否有引用链,就是从开始节点向下能否搜索到,如果搜索不到就是不可达,可以被回收
  • GC Roots有以下
  1. 虚拟机栈中的引用对象
  2. 方法区中的类静态属性的引用对象
  3. 方法区中的常量引用对象
  4. 本地方法栈是JNI(Native方法)的引用对象

什么是引用

  • 就是reference类型中存储的数值代表另外一块内存中的起始地址就称这块内存代表着一个引用(侠义的引用)
  • 强引用:类似Object obj=new Object()这类的,垃圾永不回收的引用对象 SoftReference
  • 软引用:在内存发生溢出异常前,进行第二次回收的,如果回收后还不足,才会抛出溢出异常
  • 弱引用: 能存活到下一次垃圾回收之前,与内存是否足够无关 WeakReference
  • 虚引用:最弱唯一的目的就是在被回收时收到一个系统通知,不能通过引用获取对象和操作影响对象 PhantomReference

消除对象

  • 一个对象至少要经历两次标记:如果不可达进行第一次标记并筛选是否要执行finalize()方法,当对象没有finalize()或被执行过都认为不需要执行。如果判断需要执行会把这个对象放在F-Queue队列中并在稍后虚拟机会以一个底线程去执行,所以这是对象自救的最后一次机会

回收方法区

  • 判断一个类是否有用
  1. 类所有的实例都回收
  2. 加载类的ClassLoader已回收
  3. 对应的java.lang.Class对象没有任何地方被引用也无法通过反射访问该类的方法
  • 是否回收类HotSpot虚拟机提供了-Xnoclassgc参数进行控制

垃圾回收算法

标记-清除法

  • 标记阶段:慢
  • 清除阶段 :慢,有内存碎片

复制算法

  • 把内存一分为二(大小相等)当一块用完了,把存活的复制到另一块上
  • 缺点:内存只用了一半:
  • 优化:不分为相等的两部分而是分为Eden Survivor 默认比例8:1,当survivor不够时,老年代来担保,提醒 Survivor 分为From和To 每次复制完互换

标记-整理法

  • 解决复制算法对象存活率高时的问题,先标记-然后让存活的向一端移动,以解新内存碎片的问题

分代收集算法

  • 不同的代用不同的方法,针对性的回收

HotSpot实现

  • 可达性分析:
  1. 因为分析准确所以要产生停顿,为了减少停顿,使用准确式GC ,就是虚拟机知道那些地主存着对象引用,使用OopMap数据结构来达到这个目的,这样GC直接扫描就可以了。
  2. 为了不产生大量的OopMap,只是在特殊的位置(安全点)记录这些信息,程序到了安全点才会停顿,考虑如果安全点少GC等的时间长,如果多大频繁。
  3. 在安全点时让所有的线程都到这个点,有两种办法一个是抢断式中断,就是所有的都停下来把那些不在安全点的继续运行到安全点,几乎没有用。另一个是主动式中断,就是打标记当线程到这个标记就自己产生中断(身陷)
  • 安全区域
  1. 安全点不能解决程序没有获取CPU时无法产生中断的问题,以所以产生安全区域,就是此区域任何地方都是安全点
  2. 执行到安全区域时,标记进来了,然后发起GC,当要离开时如果没有完成根节点的枚举,等待,收到完成的信号后才可以离开

垃圾收集器

  • Serial收集器
  1. 收集时必需暂停其它的所有线程
  • ParNew 收集器
  1. 就是Serial收集器的多线程版本 控制参数与Serial一样,同样是目前能与CMS老年收集器一起使用的年轻代收集器
  2. 可以通过-XX:ParallelGCThreads来限制垃圾收集器的线程数
  • Parallel Scavenge 收集器
  1. 并行多线程收集器 它的目标是达到一个可控的吞吐量=运行用户代码的时间/(用户代码时间+垃圾回收的时间)适用后始运逄面不需要太多交互的任务
  2. -XX:MaxGCPauseMills 垃圾停顿时间 大于0 的毫秒数时间短是以牺牲吞吐量和新生代的空间来换的
  3. -XX:GCTimeRatio 设置吞吐量大小(0-100)
  4. -XX:+UseAdaptiveSizePolicy 这是一个是否需要手动设置参数的开关,GC会自适应调整策略,但是要设置一个优化的目标就是上面两个参数
  • Serial Old 收集器
  1. 单线程老年代收集器,使用标记-整理,当CMS收集器失败时使用
  • Parallel Old 收集器
  1. 对应Parallel Scavenge收集器,因为Parallel Scavenge无法与CMS收集器一起工作,让吞吐量优化的收集器可以洛地使用
  • CMS 收集器
  1. 是一种以获取最短停顿时间为目标的收集器 一般的b/s系统 上使用,以和用户线程一起工作
  2. 4个步骤
    初始标记 :要停顿
    并发标记
    重新标记:要停顿
    并发清除
  3. 缺点:对Cpu非常敏感,会占用用户资源
    无法处理浮动垃圾 ,如果失败会产生Full GC ,如果运行内存不足会产生失败并用血腥和的Serial Old 进行回收
    产生在碎片,如果空间不够时并提前触发Full GC 提供一个-XX:+UseCMSCompactAtFullCollection默认开,就是进行Full GC时整理内存碎片
    -XX:CMSFullGCsBeforeCompaction 执行多少次不压缩整理后,执行一次内存整理,默认是0,每次都执行
  • G1 收集器
  1. 前沿成果,生产环境中没有大量使用
  2. 并行与并发 分代收集 空间整合 可预测停顿(看书了解)

GC 日志

  • GC , Full GC 停顿类型,如果手动调用System.gc()会产生Full Gc(System)
  • [ 前数的数字是代表GC 发生的时间参考是虚拟机启动以来的秒数
  • DefNew 新生代发生 ParNew 使用ParNew收集的新生代,PSYoungGen 是Parallel Scavenge对应的新生代
  • Tenured 老年代 Perm 永久代 名称也与收集器对应
  • a->b(c) d secs a是已使用的,b是已使用的 c是总容量 d 表示该区域GC使用的时间,单位是秒有的会给时更详细的时间
  • 墙钟时间 包含阻塞时间

分配策略

优化分配在Eden上

  • -XX:+PrintGCDetails 告诉虚拟机发生垃圾回收果打印回收日志
  • Minor GC 新生代GC 频繁 速度快
  • Major GC/Full GC 老年代 比新生代慢10倍以上

大对象直接进入老年代

  • 就是很长的字符串或者数组
  • -XX:PretenureSizeThreshold 大于这个参数对象直接在老年代分配,只对Serial 和ParNew收集器有效
  • -XX:MaxTenuringThreshold 晋升到老年代年龄设置
  • 如果Survivor相同年龄的对象大小占总空间的一半,也直接进行老年代
  • 如果有担保 那么如果空间不足会进行Full GC腾出更多的空间
    ** 参考深入理解Java虚拟机第三章 **
原文地址:https://www.cnblogs.com/ants_double/p/10755382.html