这是我参与8月更文挑战的第21天,活动详情查看:8月更文挑战
垃圾收集器
JDK1.8 默认的垃圾回收器 :Parallel Scavenge + Parallel Old ,所以调优主要针对的就是它。
useParallelGc
新生代垃圾收集器
新生代的垃圾回收器都是基于复制算法
-
1.Serial:单线程的垃圾回收器,STW (stop-the-world)停止所有线程。
- 优点
是客户端模式下默认的新生代收集器,简单高效(相比于其他收集器的单线程)
内存占用最小
最高的单线程收集效率:没有线程交互的开销
-
2.ParNew
-
Serial 的 多线程并行版本,在单线程情况下效率比不上Serial。
处理器核心越多,它的效率越高,因为是并行。默认开启收集线程数 = 处理器核心数。
-
和CMS 配合使用。
-
-
Parallel Scavenge
- 和ParNew非常相似,但是关注点不同:达到一个可控制的吞吐量:运行用户代码时间/(运行用户代码时间+运行垃圾收集时间)这个值默认99,越高越好。
- 和Parallel Old搭配使用
老年代垃圾收集器
老年代的垃圾收集器都是基于 标记压缩算法
-
1.Serial Old
Serial的老年代版本。
-
2.Parallel Old
Parallel Secavenge 的老年代版本,与Parallel Secavenge 搭配使用,组成吞吐量优先的搭配组合
-
3.CMS
CMS
ConcurrentMarkSweep,以获取最短回收停顿时间为目标的收集器。基于标记清理算法(不是标记压缩)
用于服务端,关注服务器响应时间,希望系统停顿时间尽量少,垃圾回收运行期间用户线程也能运行
四个步骤
-
初始标记
- 找到GC Roots能关联的类,这个阶段也会STW ,但是时间很短。
-
并发标记
-
并发的可达性分析,基于三色标记算法,这个最耗时
-
垃圾回收线程和业务线程同时执行,
- 产生的并发问题
1.当业务线程使用对象的时候垃圾线程也标记了这个对象,那么垃圾线程会认为这个线程存活对象,但是业务线程用完后就不再使用了,这个垃圾还是标记为存活对象
2.垃圾线程标记对象为垃圾,但是这时业务线程又使用了这个对象。那么这个在使用的对象就会标记称垃圾。
-
-
重新标记
- 修正漏标和错标,这个阶段STW,但是时间也不长
-
并发清除
- 使用标记清除算法,将标记的对象直接删除,和用户线程并发执行。由于使用清除算法,会产生碎片
- 为何不适用压缩算法? 如果使用压缩算法,会涉及到用户线程正在使用,情况更加复杂,用户线程不停顿很难处理
- 可能会出现浮动垃圾:在清除阶段用户产生的新垃圾,但是CMS在本次垃圾回收中无法处理,只能等到下次CMS回收。
并发模式失败
CMS 无法处理浮动垃圾可能会"Concurrent Mode Failure”失败,导致Full GC
失败原因:
1.对象提升到老年代的速度很快,使得CMS 不能保持老年代足够的空间
2.大量碎片化导致没有足够的空间容纳提升上来的新对象。
年老代将进行垃圾收集以释放可用空间,同时也会整理压缩以消除碎片,这个操作需要停止所有的java应用线程,并且需要执行相当长时间。
- 三色标记
- 黑色:对象已经被收集器访问过,并且它的所有引用也已经访问过,代表它是线程安全的。
- 灰色:表示对象已经被垃圾收集器访问过,但是至少存在一个引用还没有被扫描过
- 白色:这个对象还没有被扫瞄过。