目录
堆配置:
-Xmx
- 堆大小
- 可取值:1024k的倍数,必须 >2M
- 等价于:-XX:MaxHeapSize
-Xms
- 堆初始大小
- 可取值:1024k的倍数,必须 >1M
- 等于 新生代和老年代之和
-Xmn
- 新生代最大值、初始值
- 等价于:-XX:NewSize(初始值)、-XX:MaxNewSize(最大值)
-Xss
- 线程栈大小
- 默认值Linux(64-bit):1M
- 等价于-XX:ThreadStackSize
-XX:NewRatio
- young/old
- 默认2
-XX:MaxDirectMemorySize
- New I/O size
- 默认值0
-XX:MaxMetaspaceSize
- 元空间最大值
- 默认无限制,只受内存空间大小的限制
-XX:MaxTenuringThreshold
- 最大老年代转化年纪
- 默认值:parallel GC为15,CMS为6
-XX:SurvivorRatioratio**
- 默认8,计算公式 Sur=Y/(Ratio+2)
-XX:+UseTLAB
- TLAB:线程本地缓冲区,用于堆中对象内存分配
- -XX:TLABSize,设置大小
垃圾回收器算法
复制算法 | 标记清除 | 标记整理 | |
---|---|---|---|
指针调整 | 需要 | 不需要 | 需要 |
效率 | 高效 运行简单 | 略低 两遍扫描(标记+清除) | 最低 两遍扫描(标记+整理) |
适用 | 新生代(Eden+Sur) | 老年代(老年代+方法区) | 老年代(老年代+方法区+H块) |
空间利用率 | 50% | 100% | 100% |
缺点 | 空间利用率低 | 有内存碎片 | 执行较复杂 |
解决方案 | 新生代8:1:1 空间利用率和效率的最佳权衡 | 标记整理 | |
内存碎片 | 无 | 有碎片 | 无 |
为什么采用分代收集?
堆的不同分代,对象的生存周期具有不同的特点。针对不同分代中的对象分别的采用不同的算法,朝生夕死的对象采用复制算法;长期存活的对象采用标记清除、标记整理算法。
- 有效的提高了不同分代的清除效率
- 将三种收集算法进行有效组合,充分利用每种算法的优势
分代回收器对比
新生代
Serial | Parallel Scavenger | ParNew | G1 | |
---|---|---|---|---|
设置 | -XX:+UseSerialGC | -XX:+UseParallelGC 自动 -XX:+UseParallelOldGC 显式 -XX:-UseParallelOldGC为 Serial Old | -XX:+UseParNewGC 只能和-XX:+UseConcMarkSweepGC使用 | -XX:+UseG1GC |
老年代 | Serial Old | Parallel Old & Serial Old | CMS ( Serial Old 在JDK8弃用) | G1 |
算法 | 复制算法 | 复制算法 | 复制算法 | 复制算法 & 标记整理 |
类型 | 单线程 常用于客户端 | 多线程 JDK8默认回收器 可用于客户端、服务器 | 多线程 | 多线程 |
适用 | 几十M~几百M | 几百M~几个G | 几个G~几十G | 十几G~一百多G |
缺陷 | STW耗时长 | 可停顿时间设置很鸡肋 | ||
解决 | 使用parallel GC | 使用CMS或G1 |
老年代
Serial Old | Parallel Old | CMS | G1 | |
---|---|---|---|---|
设置 | -XX:+UseSerialGC | -XX:+UseParallelOldGC 自动-XX:+UseParallelGC | -XX:+UseConcMarkSweepGC 只能和ParNew一起 | -XX:+UseG1GC |
类型 | 单线程 | 多线程 | 多线程 | 多线程 |
算法 | 标记整理 | 标记整理 | 标记清除 | 复制算法 & 标记整理 |
效率 | 低 | 较高 | 高 | 高 |
内存利用率 | 100% | 100% | 100% | O、H块:100% E、S块:< 100% |
缺点 | CPU敏感 有浮动垃圾 | 每个region都有不能存储对象的内存,内存大小要求较高 | ||
解决方案 | 浮动垃圾:提前触发回收 现在的硬件CPU核心数已不是问题 使用G1回收器 | 无 | ||
适用 | 几十M~几百M | 几百M~几个G | 几个G~几十G | 十几G~一百多G |
目标 | 解决Parallel GC的追求吞吐量基础上的高延迟问题 | 可预测可控制回收模型 |
分代回收器具体参数配置:
CMS:
配置
提前触发CMS回收:
- -XX:CMSTriggerRatio,默认75%
并发可中断预处理:
- -XX:+CMSScavengeBeforeRemark,默认禁用
解决漏标:
漏标的产生是在并发标记过程,用户线程并发的产生新的垃圾。解决在重新标记Remark阶段停顿标记漏标的垃圾
算法:Incremental update ,通过引用增加的改变,从根开始再扫描。(关注引用增加)
重新标记阶段只扫描dirty的对象,dirty是在并发预处理阶段完成的。
- 并发预处理:将漏标对象标记为dirty状态,缩减remark的扫描范围
- 并发可中断预处理:默认禁用。做些remark的工作,尽可能进行一次YGC减少remark的扫描时间
新对象避免标记:
一个统一的区域记录新对象,以避免被标记
并发线程数:
- -XX:ConcGCThreads
默认不处理永久代垃圾回收
- -XX:CMSPermGenSweepingEnabled,开启永久代回收
- -XX:CMSInitiatingPermOccupancyFraction,永久代触发回收百分比阈值,默认80%
- -XX:CMSClassUnloadingEnabled,回收时可对class进行卸载
Parallel Scanvenge GC
自适应堆大小:
-XX:+UseAdaptiveSizePolicy
- 默认开启
- 为求满足
最大停顿时间
和吞吐量
要求,会自动适当减小、增加堆大小
设置最大停顿时间:
-XX:MaxGCPauseMillis=milliseconds
- 默认无最大限制
- 不建议设置。因为若设置的小,GC的频率会提升,并没有针对GC的选择性和优化
追求最大吞吐量
- -XX:GCTimeRatio=,默认值99
- 吞吐量计算: / (1 + ),N越大,吞吐量越大
并发回收线程数:
-XX:ParallelGCThreads
- 默认由核心数参考决定
并行压缩??
- parallel compaction:并行压缩比单个线程做major回收更具有扩展性,可设置并发线程数
- -XX:+UseParallelGC配置,默认开启
- -XX:-UseParallelOldGC配置,默认关闭
默认堆大小:
- Client:
- 堆最大值:
- 物理内存> 192M,1/2物理内存
- 物理内存> 1G,1/4物理内存
- 堆初始值:
- 1/64物理内存,不小于8M
- 新生代大小:
- 1/3堆大小
- 堆最大值:
- Server:
- 堆最大值:
- 物理内存>4G,1/4的物理内存
- 其他,与Client类似
- 堆最大值:
过程:
- 先符合最大停顿时间,超过则自适应减小堆大小
- 再满足吞吐量,不满足则自适应增大堆大小
- 最后,在以上两点满足情况下,自适应减小堆大小(最大限值由-Xmx设置)
G1:
配置
region大小设置:
- -XX:G1HeapRegionSize,默认在heap大小基础上的工效学决定
- 可取值:1 MB ~ 32 MB
- 目标是创建2048个region在堆中
设置最大停顿时间:
- -XX:MaxGCPauseMillis=milliseconds
- 默认200,指200ms
- 建议设置。G1会根据回收价值优先筛选回收,并有一些优化方法
分配担保空间预留:
- -XX:G1ReservePercent,默认10。指10%
触发百分比阈值:
- -XX:InitiatingHeapOccupancyPercent,默认45。指45%
- 堆占比达到45%,开启GC
并发线程数:
- -XX:ParallelGCThreads=n
- STW的工作线程数
- 可取值:
- 逻辑线程数 <= 8,逻辑线程数
- 逻辑线程数 > 8,5/8逻辑线程数
- -XX:ConcGCThreads=m
- 并发标记工作线程数
- 可取值:
- 1/4的ParallelGCThreads
解决漏标
产生原理和CMS相同,都是在并发标记阶段由用户线程产生的新的垃圾。解决由SATB(snapshot at the beginning)算法比对识别漏标
- SATB存储在每个region块的头部
新对象避免被标记
产生原因同漏标,解决由TAMS()记录每个region的新对象
- TAMS存储在每个region块的头部
region间引用避免全扫
在每个region中的Remembered Set log 存储其它块中相关联的对象引用
永久代(JDK1.7) & 元空间(JDK1.8) 的回收
G1中只在此空间满时进行Full GC回收,其他情况是不对永久代、元空间进行回收的
大对象
在G1中,如果一个对象的内存占用超过region的一半,那就被看作是大对象
优势
- 可适用更大范围堆内存的回收
- 将堆拆分为E、S、O、H不同区块,建立可预测可控制的停顿时间模型。程序允许的时间范围内,获得更高效的回收结果
- 筛选回收:根据不同区块的回收价值,设置不同的回收优先级
- 解决了CMS的内存碎片问题
- 解决了Parallel GC的停顿时间比较鸡肋的问题
ZGC
ZGC Feature
- 类似G1,但无分代概念
- 可预测低延迟并发垃圾回收器:Z Garbage Collector
- 平台支持:Linux从JDK11开始,MacOS从JDK14开始
- 目前是试验版本,非商业版本
- 回收目标:
- 适用从8M到16T范围的堆回收
- 停顿时间10ms以内,只有几毫秒
- 停顿时间不会随着堆大小、存活数量的增加而增加。只与GCRoots有关
- 功能特点:
- [Returning Unused Memory to the Operating System]
- 支持Linux大页面:大页面通常会产生更好的性能(在吞吐量、延迟和启动时间方面),并且没有真正的缺点,只是它的设置稍微复杂一些
- Transparent Huge Pages On Linux
- 支持非一致性内存访问:NUMA 全称 Non-Uniform Memory Access
- 使用有色指针(要求内存小于4TB,效率非常高)
- 使用加载屏障
Large Page支持:
- Linux在kernel 2.6以后支持
- 查看是否支持:cat /proc/meminfo | grep Huge
- 大页面:通过优化Translation-Lookaside Buffers(翻译-查看缓冲区)来减少多次内存访问从而提高内存读取性能。缺点是需要更大的内存页大小,可能会挤占其他应用造成其他应用分页过多从而减慢整个系统的速度
NUMA:
- -XX:+UseNUMA, 默认禁用
- 只有在ZGC 和 parallel GC时支持
- NUMA:通过机器的非一致内存架构来减少应用的内存延迟,拆分内存并不强制内存一致性。
如何选择垃圾回收器?
-
先让JVM自己去适配一个垃圾回收器。因为JVM会根据系统的硬件条件去自动选择。
-
如果JVM自动选择的垃圾回收器不能满足性能要求,再尝试调整堆大小及堆中的新、老分区比例。若依然不能满足再去选择回收器
-
先选择新生代回收器:因为新生代的垃圾回收器是应用程序中最常使用到的,并且新生代的垃圾回收器的选择往往决定了老年代的垃圾回收器选择。
参考维度:单或多用户应用、数据集级别、机器的单或多处理器和线程的硬件条件、系统吞吐量、响应速度要求
新生代的三个垃圾回收器具有不同运行、性能特点:
- serial GC:适用单用户、小数据集(100M以内)、单处理器机器;
- 串行GC没有多线程通信开销,单线程或单用户应用中有很好的性能表现
- 多处理器机器也能使用,但数据集一定不能太大,在100M最好
- parallel GC:并发回收器,适用中、大型数据集
- 适用于多用户、多线程、多处理器,数据集较大,注重系统吞吐量
- 并行回收,同serial GC无区别,只是serial GC的多线程版本
- 可控制停顿时间和系统吞吐量,堆大小做自适应性调整
- CMS、G1:并发回收器,适用数据集更加广泛达几十G,更注重系统响应速度
- 适用于多用户、多线程、多处理器,数据集大
- 并发回收,不同于serial GC,很短的停顿时间
- serial GC:适用单用户、小数据集(100M以内)、单处理器机器;
JVM 自适配收集器过程:
- JVM 首先根据不同的硬件条件(处理器、内存大小、操作系统)自适应判断适用
客户端模式运行时编译器
或服务器模式运行时编译器
- 再根据不同的模式选择适用的垃圾收集器
- 判断为
服务器模式运行时编译器
模式的基础条件有:- 大于等于两个物理处理器
- 大于等于2GB物理内存
现在jdk1.8中,确定为
服务器模式运行时编译器
模式一般会默认parallel GC收集器,具体堆大小会依据具体的硬件情况不同而不同。(可参考parallel GC的默认值)
Concurrent mode failed
并发模式失败,发生在回收器的并发阶段,由于并发、内存不足、显示调用GC、诊断工具使用等原因导致垃圾回收被中断,从而发生并发模式失败。
发生回收器:
CMS、G1
如何解决:
- 调整堆的大小和分区比例
- 修改提前触发堆百分比
handler promotion failed
每次回收,都会发生新生代对象向老年代晋升的现象,所以会有晋升失败的可能。
具体判断过程
system.gc()
不建议在生产中使用,甚至在循环中应用。可能会导致并发模式失败。
可禁用:-XX:+DisableExplicitGC。由JVM决定回收的必要性。
OOM相关配置:
-XX:+HeapDumpOnOutOfMemoryError:
- 默认禁用
-XX:HeapDumpPath=path
- 默认当前工作路径
- -XX:HeapDumpPath=/var/log/java/java_heapdump.hprof
-XX:LogFile=path
- 默认当前工作路径
查看默认参数值
java -XX:+PrintFlagsFinal <GC options> -version | grep MaxHeapSize
例如:
➜ ~ java -XX:+PrintFlagsFinal -version | grep MaxHeapSize
uintx MaxHeapSize := 2147483648 {product}
java version "1.8.0_131"
Java(TM) SE Runtime Environment (build 1.8.0_131-b11)
Java HotSpot(TM) 64-Bit Server VM (build 25.131-b11, mixed mode)
Q & A
-
CMS可以和Serial配合吗?
不可以,JDK8已经禁用了和Serial的配合