并行?并发?
并行 Parallel
并行是指 多个任务在同一时间运行
在垃圾回收器执行阶段中 我一般认为在同一时间多个垃圾回收线程同时在回收垃圾叫并行
并发 Concurrent
并发是指在同一阶段同时执行 比如单核CPU 在1秒钟之内执行了多个任务(CPU同一时间只能执行一个任务),那么这一秒钟的时间我们认为这些任务是并发执行,这里需要区分下并行意思是在同一时间多任务同时执行 多核CPU
在垃圾回收器中 我一般认为 GC线程和工作线程同时执行的叫并发垃圾回收器
PS - PO
PS PO是JDK1.8默认的垃圾回收器 处理是吞吐量优先的方式 使用的是分代模型
分代模型
吞吐量
吞吐量的计算方式是 程序执行时间 /(程序执行时间+GC执行时间)
响应时间
响应时间的方式就是 GC线程产生的STW越短 响应时间越高
Parallel Scavenge
Parallel Scavenge 作用于年轻代垃圾回收器 简称PS
-XX:+UseParallelGC
采用并行执行的方式 使用PS的垃圾回收器 在服务模式Java8下默认采用PS的垃圾回收器
简单介绍PS垃圾回收器的处理流程
可以看出 在执行年轻代垃圾回收的时候 会触发一次STW停顿时间,这个时候并行回收器会使用多个线程一般等于CPU的核数处理垃圾
假设内存之前的分布为
执行PS回收时 会将 A A1 复制到S1区 如果C D分代年龄已够 那么会将CD移动至老年代
最后会清除Eden区所有数据
如果下次清理的时候新增了F G对象数据 那么执行后内存分布为
PS回收步骤:
- 新产生的对象会存在Eden区 经过一次回收后 会将Eden区存活的数据复制到 S1 区 年龄足够的 移动到老年代
- 第二次回收的时候 会将Eden区 存活的对象 和S1 区存活的对象复制到 S2区 清空 S1 和Eden 区数据,年龄足够的 移动到老年代
- 第三次回收的时候 会将Eden区 和 S2区存活的对象 复制到 S1区 将Eden区 和S2区清空 年龄足够的 移动到老年代
- .....依次类推
优点
采用的复制移动的算法实现 并且使用多线程并行的方式处理 速度效率非常快 并且 内存数据是连续的
缺点
因为采用的是复制算法,所以需要预留S1 S2内存空间,空间会形成浪费
调优方式 最低下统一说明
Paraller Old
Paraller Old 作用于老年代垃圾回收器 简称PO PO采用的是标记整理的方式 PS采用的是复制移动的方式
使用方式-XX:+UseParallelOldGC
在Java8中默认的使用PO的垃圾回收器 处理老年代垃圾
假设老年代内存回收之前如下,蓝色的为有用数据
因为PO采用的是 标记整理的方式 所以 执行PO回收之后 内存数据为
优点
回收垃圾数据之后 内存连续 且不需要开辟额外的空间 不会造成内存浪费的情况
缺点
因为采用的 标记整理的算法,执行的效率偏慢因为需要涉及要 移动每一个对象且更改每个对象的指针数据,所以效率偏低
内存设定
采用PS+PO的方式 一般都设置 heap最大大小和初始大小 ,一般最大大小和初始大小都设置为一致,如果最大大小和初始大小不一致 则会产生动态收缩过程,此过程同样需要消耗CPU以及性能 并且GC还需要计算可用大小的各种比例去做动态收缩,具体大小设定的方式 通过测试预估一个合适的值,根据业务需求 服务器配置按需配置。
内存设定 并不是越大越好,越大的内存 是会减少GC的频率 但是 会导致单次GC STW的时间过长 最好的方式是根据实际测试的结果去做设定
通过–Xms、-Xmx
设置初始值 和 最大值
PS PO 调优
PS垃圾回收器因为使用了Copying的方式 所以调优一般着重于 分区大小 以及 吞吐量目标 和 停顿时间3个方向调优
分区大小
- 首先规划的是整个年轻代和老年代的比例 默认是 1:2 可以通过
–XX:NewRatio
方式配置大小比例 或者通过-Xmn
指定年轻代大小 - 设置Survivor区和Eden区大小比例 如果你的程序年轻代数据基本每一次都会被回收,那么可以适当的调小Survivor区大小 节省内存(一般不会去调节), 如果程序每次基本上都会有部分数据停留在年轻代,那么可以适当的调大Survivor区大小 通过参数
-XX:SurvivorRatio
默认为8 也就是 8:1:1 一般不太会调整 Survivor和Eden区的比例,除非你真的知道自己在做什么
吞吐量目标和STW
执行频率 需要根据业务场景而定 我们一般会避免发生太频繁的FGC和YGC 会根据停顿时间和执行频率做一个均衡
-
通过最大停顿时间做处理,根据JVM提供的
-XX:MaxGCPauseMillis
参数去设定你能接受的最大停顿时间,JVM会尽可能的在此时间内处理完垃圾回收(但是在某些情况下无法满足所需的暂停时间),参数默认值为0 如果设置了这个值比较小可能会导致频繁的GC也就是GC的执行频率会变高 -
通过
-XX:GCTimeRatio
指定GC占用的实际为整个时间的比例, 例如-XX:GCTimeRatio=19
将垃圾收集总时间的 1/20 或 5% 设定为目标,垃圾回收时间与应用程序时间之比为1 / (1 + 对应值)
,垃圾回收所花费的时间是年轻代和老年代回收的总时间。如果未满足吞吐量目标,应该增加代的大小从而减少GC的频率 -
-XX:+UseAdaptiveSizePolicy
这种模式下 年轻代 和 survivor区 和 晋升老年代的年龄都会自适应调整 以便达到吞吐量和响应时间的平衡
其他参数
增加并行线程数-XX:ParallelGCGThreads
调整并行线程数,默认值为CPU的核数一般不用调整,等于CPU核数的时候会避免上下文切换 导致的时间消耗
修改晋升年龄 -XX:PretenureSizeThreshold
这里最大的年龄为15 因为每一个object的head上有4位代表晋升年龄 最大也就是15 默认值也为15