垃圾收集器与内存分配策略

概述

GC当前高度自动化,but当GC成为系统高并发瓶颈时,就需要对GC进行必要的监控和调节。针对JVM,GC主要针对**堆**。通过**垃圾收集器**,按照**垃圾收集算法**,对内存进行分配和回收。

判断对象存亡

- **引用计数法reference counting** 有引用,计数器加1,引用失效,计数器减1,计数器为0的对象不再被使用的。但是难以解决Java中的互相循环引用问题。 - **根搜索算法GC root tracing** 以GC Root为根节点,向下搜索,无引用链的对象,证明不可用。 可作为GC Root的对象有: - 虚拟机栈引用对象 - 方法区中类静态属性引用对象 - 方法区常量引用对象 - JNI引用对象
  • 引用
    传统定义:如果引用类型存储的数值代表另一块内存的起始地址,就称这块内存代表一个引用。
    JDK1.2以后,引用进行了划分:

    • 强引用,普遍存在
    • 软引用,内存溢出之前,进行回收,仍然溢出,抛出异常,参照SoftReference
    • 弱引用,下一次GC之前存活,WeakReference
    • 虚引用,主要是获得GC通知,不影响生存周期。
  • 对象缓刑
    第一次标记,GC Root标记的不可用对象,并且判断对象是否覆盖finalize()或finalize()方法已被JVM调用过。如果是,标记为没有必要执行。
    第二次标记,如果判定为有必要执行,则将此对象放到一个F-Queue中,JVM自动启动一个低优先级线程处理。为了防止死循环或执行缓慢等意外情况,GC还会对F-Queue中对象进行第二次小规模标记。如果对象仍然没有重新与引用链上建立关联,则GC掉。

  • 回收方法区
    性价比比低很多。主要回收“废弃常量”,对于hotspot虚拟机,提供了参数进行配置,尤其是大量使用反射,动态代理,CGLib等bytecode框架时,都需要JVM具备类卸载功能,保证永久代不会溢出。判断“废弃常量”需满足:

    • 该类所有实例被回收
    • 加载该类的classloader被回收
    • 该类对象的java.lang.class对象没有任何引用,防止反射

垃圾收集算法

- 标记-清除算法Mark-Sweep 标记待清除对象,参照上节**对象缓刑**,清除对象,缺点较明显: - 效率低 - 容易产生大量不连续内存碎片,当程序需要分配大对象时并无足够连续内存时,提前触发下次GC
  • 复制算法Coping
    - 内存分两块,一块用完时将存活对象复制到另一块,并清理当前块。JVM都采用这种算法回收新生代。
    - 98%对象朝生夕死,HotSpot虚拟机将内存分为一块Eden(80%),两块Survivor(20%),每次使用Eden和其中一块survivor。回首时,将存放对象放到另一块survivor上。当多余10%内存对象存活时,依赖老年代内存,进行分配担保。

  • 标记-整理算法
    复制算法,对象存活率较高时,需要执行较多的复制操作,效率较低。标记整理算法,是标记出存活对象,GC时,让存活对象向内存一端移动,然后直接清理掉端边界以外内存。

  • 分代收集算法
    当前商业JVM都采用分代收集,把Java堆分为新生代和老年代。新生代中,存活对象少,采用复制算法。老年代对象存活率高,没有额外空间进行分配担保,必须使用“标记-清理”或“标记-整理”算法进行回收。

垃圾收集器

> 根据收集算法,有多种收集器的实现,没有最好,只有最合适
  • Serial收集器
    单线程,暂停其它工作线程,依然是JVM运行在Client模式的默认收集器,简单而高效。在用户桌面应用场景中,收集几十甚至上百M的新生代,停顿时间会控制在100毫秒以内,可接受。

  • ParNew收集器
    Serial收集器的多线程版本,单核效果比Serial低。

  • Parallel Scavenge收集器
    新生代收集器,复制算法,“吞吐量优先”,可配置停顿时间和吞吐量大小,可打开GC自适应调节策略,自动调节新生代,Eden与Subvivor区比例。

  • Serial Old收集器
    是Serial收集器的老年代版本,单线程,“标记-整理”算法

  • Parallel Old收集器
    多线程,“标记-整理”,注重吞吐量及CPU资源敏感的场合,优先考虑Parallel Scavenge收集器加Parallel Old收集器

  • CMS收集器
    Concurrent Mark Sweep,最短回收停顿时间,重视响应速度,“标记-清除”,分4部:
    - 初始标记
    - 并发标记
    - 重新标记
    - 并发清除
    其中,初始和重新标记,单线程,缺点:
    - CPU资源敏感
    - 无法处理浮动垃圾
    - 标记清除算法,清理不及时,触发Full GC

  • G1收集器
    Garbage First,“标记-整理”,收集毫秒级,将整个Java堆分队多个region,维护一个优先列表,最大限度提升收集效率

原文地址:https://www.cnblogs.com/lknny/p/6931354.html