文章目录
- GC演变过程
- 并发垃圾回收需要解决的问题
- 怎么确定一个垃圾?
- 并发收集存在的问题
- 三色标记法
- CMS垃圾收集器
- G1垃圾收集器
- 介绍,主要特点
- 优点
- 使用注意点
GC演变过程
在Java中,垃圾收集一直是一个非常重要的组成部分, 到目前为止,垃圾收集器已经有十种了, 在不停的优化.
那为什么需要不停的优化呢? 其实根本在于Java程序使用的内存在不断的增加, 在最开始的时候就只有几兆,几十兆, 使用串行垃圾回收器不会浪费多多少时间, 后来慢慢发展, 到现在有的Java程序需要占用几十G的内存,那使用串行垃圾回收肯定就不适用了.
到目前为止, 这十种垃圾回收器其实总共可以分为三类.这里举例进行说明.
- 串行垃圾回收
你、你女朋友、你男朋友在房间里扔线团(垃圾)玩, 玩了一段时候后, 你妈妈看见房间里垃圾太多了, 说不许动(STW), 然后进来帮你们收拾垃圾.收拾完出去,你们又可以继续玩了. - 并行垃圾回收
还是你、你女朋友、你男朋友在房间里扔线团(垃圾)玩, 但是后来你们生活改善了, 换了个200平的大房子, 现在你妈妈一个人打扫就太慢了, 就和你爸爸一起帮你们打扫, 当然打扫的时候,你们还是不能动. - 并发垃圾回收
每次你爸妈帮你们收拾垃圾的时候你们都不能动, 这个让你们很不爽, 感觉玩的不高兴, 于是和你爸妈反馈, 你爸妈为了满足你, 给你们雇了一个保姆, 就一直在你们房间, 一旦你们产生垃圾, 就帮你们进行处理, 就这样你们可以一边玩, 还有人帮你们收拾, 就一直可以玩了.
看到这几个样例,相信能很容易理解这几类的区别了.其实垃圾回收还有一个一直在优化的点. 就是STW(stop the word),对于web应用, 是非常关注这一点的, 因为如果在外部请求到来, 你的程序正好在垃圾回收, 花费了好几秒, 那就很影响客户体验了, 并发垃圾收集器就是为了解决这一点, CMS和G1都属于并发垃圾收集器.
并发垃圾回收需要解决的问题
怎么确定一个垃圾?
我们怎么确定一个对象是不是垃圾呢? 其实就是可达性分析.
通常通过建立根对象的集合,然后检查从这些根对象开始的可触及性来实现,如果正在运行的程序可以通过根对象访问到某个对象,那么这个对象就是可触及的,而无法被触及的对象被认为是垃圾.
跟对象的集合通常通过包括下面几个部分:
- 虚拟机栈中的局部变量的对象引用
- 传入本地方法栈中,没有被释放的对象引用.
- 方法区中的常量引用的对象.
- 方法区中的静态变量引用的对象.
具体实现就是从根节点开始有一个对象引用图,在追踪的过程中以某种方式打上标记,当追踪结束时,为被标记的对象就是无法触及的.
对应到我们生活中举的例子,我们往房间里扔线团, 那有线的就是正常的, 没线的就是垃圾.
并发收集存在的问题
在业务线程不断的产生垃圾的同时, 垃圾回收线程会不断的进行标识, 标识哪个对象是有用的
那我们知道线程是靠CPU调度的, 一个线程不可能一直执行, 当垃圾回收线程被挂起的时候, 已经标识好的对象的状态是有可能被改变的.遍历结束后,所有未被标记的对象都是垃圾对象,可以被回收。
比如一个对象被标识成垃圾, 然后垃圾回收线程被挂起了, 在被挂起的时候, 这个对象又重新被业务线程引用了, 又不是垃圾了, 哪这种怎么处理?
这时候就出现了一种算法, 叫做三色标记法.
三色标记法
三色标记法(Tri-color marking)是一种用于垃圾回收的算法。
它的主要思想是将所有对象分为三种颜色,所有的对象开始的时候都是白色,从程序的根集(如全局变量或寄存器)开始,逐步遍历所有与之相关联的对象,并将它们标记为灰色或黑色。
三种颜色的区别是:
-
白色: 还没有被访问到
-
灰色: 被访问到还没有完成, 比如说当前节点被访问是有引用, 但是它的孩子节点(成员变量)还没有被访问, 或者说该节点有三个孩子,其中有两个孩子被访问完成, 另一个还没有被访问到,也是灰色.
-
黑色: 被访问且完成, 当前节点和他所有的孩子节点都被访问到(不包括孙子).
之前我们说在标识的过程中, 对象的状态可能会发生变化, 接下来我们就来分析一下, 其实总结来说, 可能会发生三种变化, 还是以A、B、D这三个对象来说.
-
情况一: B->D消失.
这种情况其实没什么影响, 如果是垃圾引用消失就刚好被删除, 如果不是垃圾, 还有别的对象需要该引用, 那必然通过别的对象能够找到, 也不会被删除. -
情况二.A-D增加.
这种情况也没有影响, 因为B还是灰色, 还会扫描, 也能扫描到D. -
情况三: B-D消失, A->D增加
这种就会有影响了, 因为A对象是黑色的, 不会再扫描, B到D的引用又消失了, 那此时D就会被当做垃圾, 但是它不是, 明明还有对象A引用到它.
那这个问题怎么解决呢?
CMS解决方案:Incremental Update Reference
简单一句话就是当黑色对象指向白色对象, 将黑色对象标灰
那怎么知道黑色对象值向白色对象了呢?通过写屏障, 可以理解为一个切面操作,类似Spring里的AOP这些, 监测所有的引用变化,发现是黑色指向白色,那就把它变成灰色.
G1针对此问题的解决方案:SATB
简单来说就是做一个快照,还是监测所有的引用变化, 当发现灰色到白色的线消失时, 把这个引用存到专门的GC堆栈中.
然后回收线程需要对这个GC堆栈中的引用做特殊处理,就是看看有没有其他的对象需要这个引用,没有的话就是垃圾了.
CMS垃圾收集器
CMS是为了解决STW的第一次尝试. 也是使用并发垃圾收集的第一款垃圾收集器.
虽然CMS为了解决STW产生,但是很多长时间的STW却是由于CMS诞生的,为什么呢?
因为CMS中的垃圾回收是在某一阀值的时候开始回收,回收阀值可用指定的参数进行配置,-XX:CMSInitiatingOccupancyFraction来指定,默认为68,也就是说当老年代的空间使用率达到68%的时候,会执行CMS回收。如果内存使用率增长的很快,在CMS执行的过程中,已经出现了内存不足的情况,此时CMS回收就会失败,虚拟机将启动老年代串行回收器进行垃圾回收,这会导致应用程序中断,直到垃圾回收完成后才会正常工作,这个过程GC的停顿时间可能较长,所以-XX:CMSInitiatingOccupancyFraction的设置要根据实际的情况.
G1垃圾收集器
介绍,主要特点
终于迎来了今天的主角, G1,目前G1已经在很多生产环境中使用了, 可以大胆的学习使用.
在之前的垃圾收集器其实都是分代的,就是有明确的新生代, 老年代, 不同的垃圾收集器负责不同的代.
而G1最关键的点,也是和之前最大的不同:
物理上是分区的,逻辑上是分代的
怎么理解?就是它实际会把内存分为一块一块的,也就是物理上分区, 但是每一块都可以动态的作为Eden区,Survivior,Old区.这个视实际情况而定.
比如,某个项目新对象很多,且产生特别快, 那就把Eden区分的多一些
比如说有的项目,老年代对象多, 且需要持续使用, 不够用, 那就把Old区分的多一些.
这样还有一个好处, 就是一小块一小块进行处理,比整体处理效率更高.
比如某一小块区域快满了,就把它清掉, 不需要等整体满了再去清.
优点
使用注意点
- 不建议手工指定新老年代比例.当指定之后,G1的自动调优功能就完成不了了.
- 一般使用内存大于等于8g才建议使用G1.
因为它需要维护大量的数据结构来追踪每个区域的垃圾回收情况。这些数据结构需要占用额外的内存空间,对于小型应用程序来说可能会造成内存不足的问题。
今天的分享就到这里了,有问题可以在评论区留言,均会及时回复呀.
我是bling,未来不会太差,只要我们不要太懒就行, 咱们下期见.