引用类型
-
强引用:发生 gc 的时候不会被回收java
-
软引用:有用但不是必须的对象,在发生内存溢出以前会被回收算法
-
弱引用:有用但不是必须的对象,在下一次 GC 时会被回收服务器
-
虚引用(幽灵引用/幻影引用):没法经过虚引用得到对象markdown
用 PhantomReference 实现虚引用,虚引用的用途是在 gc 时返回一个通知多线程
垃圾辨别方法
- 引用计数器
- 为每一个对象建立一个引用计数,有对象引用时计数器 +1,引用被释放时计数 -1
- 当计数器为 0 时就能够被回收。缺点是不能解决循环引用的问题
- 可达性分析
- 从 GC Roots 开始向下搜索,搜索所走过的路径称为引用链
- 当一个对象到 GC Roots 没有任何引用链相连时,则证实此对象是能够被回收的
GC Roots,GC 的根集合, 是一组必须活跃的引用并发
可做为 GC Roots 的对象有:性能
- 虚拟机栈(栈帧中的本地变量表)中引用的对象
- 方法区中类静态属性引用的对象
- 方法区中常量引用的对象
- 本地方法栈中 JNI(即通常说的 native 方法)中引用的对象
垃圾收集算法
-
引用计数(Reference Counting)spa
- 原理是此对象有一个引用,即增长一个计数,删除一个引用则减小一个计数
- 垃圾回收时,只用收集计数为 0 的对象
- 缺点:没法处理循环引用问题
-
标记-清除(Mark-Sweep)线程
- 第一阶段从引用根节点开始标记全部被引用的对象
- 第二阶段遍历整个堆,把未标记的对象清除
- 缺点:此算法须要暂停整个应用,同时,会产生内存碎片
-
复制(Copying)指针
- 把内存空间划为两个相等的区域,每次只使用其中一个区域
- 垃圾回收时,遍历当前使用区域,把正在使用中的对象复制到另一个区域中。每次只处理正在使用中的对象
- 由于复制成本比较小,同时复制过去之后还能进行相应的内存整理,不会出现“碎片”问题
- 缺点:须要两倍内存空间
-
标记-整理(Mark-Compact)
- 第一阶段从引用根节点开始标记全部被引用对象
- 第二阶段遍历整个堆,将全部存活的对象都向一端移动,而后直接清除掉端边界之外的内存
- 此算法避免了“标记-清除”的碎片问题,同时也避免了“复制”算法的空间问题
-
分代(Generational Collecting)
- 基于对对象生命周期分析后得出的垃圾回收算法
- 把堆中对象分为年青代、年老代、持久代(JDK8 不存在持久代)
- 按照对象经历的 GC 次数来肯定是哪一代
- 对不一样代使用不一样的算法进行回收
- 如今的垃圾回收器通常使用此算法,是多种基本回收算法的组合使用
分代回收算法
起源:研究发现,大部分 java 对象只存活一小段时间,而存活下来的小部分 java 对象则会存活很长一段时间
简单来讲,将堆分红两部分,年轻代用来存放新对象,当对象存活时间够长时,移动到年老代
堆的分代
-
年轻代 Young Generation
- 默认占总空间的 1/3(经过 -XX:NewRatio 指定年轻代和老年代比例)
- 分为 Eden、To Survivor、From Survivor 三个区,默认占比 8:1:1(经过 -XX:SurvivorRatio 指定)
-
年老代 Tenured Generation
-
持久代 Perm Generation(JDK8后不存在)
- 即方法区,用于存放静态文件,现在Java类、方法等
- 持久代对垃圾回收没有显著影响
- 在 JDK8 中,废弃了持久代,改用元空间(metaspace)实现方法区,属于本地内存
分代收集
-
年轻代回收器
-
假设大部分对象都存活很短期,须要频繁采用耗时较短的垃圾回收算法
-
新生代垃圾收集器通常采用复制算法,优势是效率高,缺点是内存利用率低
-
垃圾收集器有:Serial、ParNew、Parallel Scavenge
-
年老代回收器
- 假设老年代中的对象大几率继续存活,真正触发老年代 gc 时,表明假设出错或堆空间已耗尽,通常须要全堆扫描,全局垃圾回收
- 老年代收集器通常采用的是标记-整理的算法进行垃圾回收
- 垃圾收集器有:Serial Old、Parallel Old、CMS
-
整堆回收器
G1:兼顾吞吐量和停顿时间的 GC 实现,JDK 9 之后的默认 GC 选项
回收过程
新对象存放在年轻代的 Eden 分区,Eden 空间耗尽时,触发 gc,通常使用复制算法
年老代空间占用到达某个值以后就会触发全局垃圾收回,通常使用标记整理算法
- 把 Eden 和 From Survivor 存活的对象放入 To Survivor 区
- 清空 Eden 和 From Survivor 分区
- From 和 To 交换指针,保证下次 gc 前To Survivor 为空
- Survivor 分区的对象,通过一次复制年龄就 +1,年龄到达 15时(默认 15),Survivor 分区升级为老生代。对象也会直接进入年老代
gc 类型
-
Minor GC
- 通常状况下,当新对象生成,而且在 Eden 申请空间失败时,就会触发Minor GC
- 在年轻代 Eden 区域进行GC,清除不存活对象,而且把尚且存活的对象移动到 Survivor 区。而后整理 Survivor 的两个区
- 很频繁的 gc,不影响老年代
-
Full GC
对整个堆进行整理,包括Young、Tenured和Perm。Full GC比Scavenge GC要慢,所以应该尽量减小Full GC。有以下缘由可能致使Full GC:
- Tenured 被写满
- Perm 域被写满(JDK8 以前)
- System.gc( ) 被显示调用
- 上一次 GC 以后堆的各域分配策略动态变化
垃圾收集器
收集器分类
-
串行收集器
-
并行收集器
-
对年轻代进行并行垃圾回收,能够减小垃圾回收时间。通常在多线程多处理器机器上使用
使用 -XX:+UseParallelGC 打开
-
并行收集器 jdk5 引入,在 jdk6 中进行了加强,可对堆年老代进行并行收集
使用 -XX:+UseParallelOldGC 打开
-
若是年老代不使用并发收集,而使用单线程进行垃圾回收,会制约扩展能力
-
并发收集器
- 能够保证大部分工做都并发进行(应用不中止),垃圾回收只暂停不多的时间
- 此收集器适合对响应时间要求比较高的中、大规模应用
- 使用 -XX:+UseConcMarkSweepGC 打开
常见收集器
- Serial:最先的单线程串行垃圾回收器
- Serial Old:Serial 垃圾回收器的老年版本,一样也是单线程的,能够做为 CMS 垃圾回收器的备选预案
- ParNew:是 Serial 的多线程版本
- Parallel :
- 和 ParNew 收集器相似,是多线程的收集器
- Parallel 是吞吐量优先的收集器,能够牺牲等待时间换取系统的吞吐量
- Parallel Old:
- 是 Parallel 老生代版本
- Parallel 使用复制算法,Parallel Old 使用标记-整理算法
- CMS:一种以得到最短停顿时间为目标的收集器,很是适用 B/S 系统
- G1:一种兼顾吞吐量和停顿时间的 GC 实现,是 JDK 9 之后的默认 GC 选项
CMS 收集器
-
CMS:Concurrent Mark-Sweep
-
CMS 使用标记-清除的算法
- 在 gc 时候会产生大量的内存碎片
- 当剩余内存不能知足程序运行要求时,系统将会出现 Concurrent Mode Failure
- 临时 CMS 会采用 Serial Old 回收器进行垃圾清除,此时的性能将会被下降