Java教程

垃圾回收机制

本文主要是介绍垃圾回收机制,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

堆内存的分配与回收

由于目前收集器都采用分代算法,因此Java堆可以分为新生代和老年代。
堆
分代回收原理:对象首先被分配在新生代,经历过垃圾回收后若仍存活,则移到老年代。但这样老年代很快会被填满,导致触发Major GC,性能受影响。
于是将新生代分为Eden区和Survivor区,Eden满时触发Minor GC,将存活的对线移至Survivor区。这样直到对象经历过一定次数的回收仍存活时,才将其移到老年代,这样就减少了Major GC(可视为Full GC)。

那为什么要设置俩Survivor区?答案是为了解决内存碎片问题。
单一Survivor区:
单Survivor
双Survivor区(保证有一个Survivor区为空,实现连续存储):
双Survivor
对于需要大量连续内存空间对象(如数组),为避免频繁复制导致效率降低,直接将其分配到老年代。

判断对象是否可回收

引用计数法

给对象添加一个引用计数器,为0时则可回收。
简单、高效,但难以解决对象间循环引用的问题,因此不被使用。

可达性分析法

以GC Roots对象为起点,向下搜索引用链。若一个对象与GC Roots没有任何引用路径,则判定为可回收。

可作为GC Roots的对象

  • 虚拟机栈(栈帧中的局部变量表)中引用的对象
  • 本地方法栈中引用的对象
  • 方法区中静态属性引用的对象
  • 方法区中常量引用的对象
  • 所有被同步锁持有的对象

死缓

对象被判断为不可达后,会被第一次标记,同时进行筛选:
①对象重写了finalize方法 ②对象的finalize方法未被虚拟机调用过
当满足任一条件,则执行finalize方法并放入队列进行二次标记,这之后才会被真正回收。

引用的种类

强引用

最普遍的引用。
内存不足时,JVM也不会回收强引用对象,只会抛出OutOfMemoryError。

软引用

内存够用时,JVM不会回收;内存不足时,JVM回收软引用对象。

弱引用

垃圾回收器一旦发现只有弱引用的对象,就会进行回收。
不过垃圾回收线程的优先级很低,不一定很快能发现这些对象。

虚引用

主要用来跟踪对象被垃圾回收的活动,必须和引用队列联合使用。

后两个很少使用,一般通过弱引用来加速垃圾回收速度,避免内存溢出。

垃圾收集算法

标记-清除算法

标记不能回收的对象,然后同一回收掉所有没标记的对象。
缺点:①效率不高 ②内存碎片问题

标记-复制算法

将内存分为大小相同的两块,每次使用其中的一块。当这一块的内存使用完后,就将还存活的对象复制到另一块去,然后再把使用的空间一次清理掉。这样就使每次的内存回收都是对内存区间的一半进行回收。
标记-复制

标记-整理算法

标记后,不是直接回收未标记对象,而是将标记对象向一端移动,然后清理掉边界外的内存。
标记-整理

综合应用

新生代中,每次收集都有大量对象被回收,于是选用“标记-复制”算法,复制少量存活对象即可完成回收。
老年代中,对象存活率较高,且没有额外空间进行分配担保,于是使用“标记-整理”算法进行回收。

垃圾收集器

Serial收集器

单线程进行收集,并且必须暂停其他所有工作线程。
新生代采用标记-复制算法,老年代采用标记-整理算法。

ParNew收集器

Serial收集器的多线程版本。

Parallel Scavenge收集器

JDK1.8 默认收集器,关注CPU吞吐量,提供了很多参数供用户找到最合适的停顿时间或最大吞吐量。

CMS 收集器

使用标记-清除算法,关注用户线程的停顿时间。

G1 收集器

面向服务器的垃圾收集器,主要针对配备多颗处理器及大容量内存的机器,以极高概率满足 GC 停顿时间要求的同时,还具备高吞吐量性能特征。
使用Region划分内存空间,根据优先级来进行区域回收。

这篇关于垃圾回收机制的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!