垃圾回收算法
我们先简要看一下 四种主要的垃圾回收算法
看到这不禁感慨一下 人家1960年 都搞出GC算法了 太强了
评价标准
既然有这么多算法 那就跟各个牌子的游戏本一样 有个比较,这里我们重点介绍一下 垃圾回收算法的评价标准
这几个标准非常重要是 是后面理解很多东西的基石,大家一定要好好理解
首先大家要理解 垃圾回收也是一个线程 在运行,它的本质和其他我们工作的线程没什么区别,既然是线程就会有占用 就会有阻塞。
一旦触发垃圾回收线程开始垃圾回收的时候, 部分阶段 比如fullgc 会停止所有的用户线程。
也就是说 你的java服务器里的用户线程 全体罚站。
这个过程叫STW ,
所以如果你的系统是频繁用户交互的 那你就要注意 不能使用STW过长的垃圾回收策略
于是有了下面三个评价标准:
这三个标准 就和 分布式理论里面那个CAP一样 不可兼得。
比如吞吐量 是 用户代码时间 / cg时间+其他所有时间。 这个比例可能很出色,但是分子和分母都越来越大 那cg时间就越来越长
cg时间越来越长 STW最大时间就越来越大
四种垃圾回收算法
标记清除算法
最好理解 最简单 最无脑的算法
我们之前说了GC root 根节点 可达性分析法 ,去寻找那些有根的对象 和断根的对象, 找到那些断线的 需要回收的对象 就清除掉。
就这么简单。
它的优点是 简单 迅速。
但是缺点很多 就是会产生大量的 内存碎片, 这些内存碎片 就是清除之后 留下来的空间 大小不一
复制算法
复制算法 把内存区域 分成2块:from 、 to。 只有一块可以存储对象、
看到这还不知道它怎么清理, 它的一个缺点就已经暴露了—— 你的内存使用打半折 只有一般能用
首先往from区里面 存对象 ,这时候to 是空的
那么它需要gc的时候 还是用可达性算法 把非断根的那些不需要回收的对象找出来 把这些良好市民 都移动到 to
然后把from 清空。
它的好处就是没有内存碎片 因为移动到to 就整齐排列了
标记整理
标记整理 就是 标记清除的进化版。
首先它还是一块内存区域 不会像复制算法一样 一分为二。
然后 我们刚才讲了 标记整理是 找到那些需要清除的 然后清除掉就完事了
而标记整理 他们清除之后 把剩下的对象空间重新排列 , 这样那些内存碎片也就都被挤出来了。
但是
就是因为比标记清除多了整理者一步 意味着它的性能不佳。
(单是整理这一步 可能要内存上 大部分的对象地址空间都要移动 , 还记得那个经典问题 arraylist 和linkedlist之间的性能选择吗。 类似于你在动态数组中做增删操作 )
分代回收算法
这个就是前几种算法组合 也是现代最流行的算法
首先他把堆分为 新生代 和老年代
新生代顾名思义 就是刚创建出来的对象。
老年代就是存活时间较长的对象
新生代 中又分为 Eden区(伊甸园区 上帝刚创造人类的地方) S0区 S1区
我们来讲下这个过程。 首先刚开始 所有区域 都是空的 :
创建第一个对象:
当程序创建第一个对象时,这个对象会被分配到年轻代中。假设这个对象是一个新的实例,它被分配到年轻代的 Eden 区域。
对象存活和垃圾回收:
随着时间的推移,堆内存中会不断创建新的对象。这些对象可能被引用,也可能成为垃圾,即不再被引用。当年轻代的 Eden 区域填满时,会触发一次 Minor GC(年轻代垃圾回收)。在 Minor GC 中,存活的对象会被移动到存活区域(Survivor Space),而不再被引用的对象将被标记为可回收的垃圾。被标记为垃圾的对象将被回收释放。
晋升到老年代:
如果对象在年轻代经过几轮的 Minor GC 后仍然存活,它将被晋升到老年代。通常,对象在老年代存活时间更长,因此老年代的垃圾回收频率相对较低。
Full GC:
当老年代内存空间不足时,会触发 Full GC。Full GC 会对整个堆内存进行垃圾回收,包括年轻代和老年代。在 Full GC 中,所有的存活对象都会被标记并移动,而不再被引用的对象将被回收释放。Full GC 的成本通常比 Minor GC 更高,因为它需要对整个堆内存进行扫描和处理。
Out of Memory:
如果堆内存无法满足程序的内存需求,将会发生 Out of Memory 错误。这通常发生在无法为新对象分配内存空间时,或者在进行垃圾回收后仍然无法腾出足够的内存空间时。此时,程序无法继续执行,并抛出 Out of Memory 错误。
垃圾回收器
首先新手要理解一点 垃圾回收器都是组合使用的 出来G1外,这些乱七八糟的组合大家不用死记硬背。
这里介绍经典的2种组合,以及最新的垃圾回收器 G1:
如果你关注暂停时间 不想太长 就用 PN+CMS
CMS这个回收器 它的特点是并发执行,允许用户现场和gc线程同时跑, 但是它用的是标记清除算法 容易产生内存碎片