《Java性能优化权威指南》学习笔记

编译

编译是指生成机器码 即 二进制目标文件的过程。
Java最初是转换为类文件,虚拟机将其转为字节码。
运行时动态的转为机器码。

JIT会在运行时,将调用次数超过阈值CompileThreshold的代码编译(由方法调用计数器计数)。

JVM性能监控

重要的垃圾收集数据
堆大小。
新生代、老年代、永久代大小
Minor GC /Full GC 的持续时间、频率、空间回收量

打印垃圾收集信息 -XX+PrintGCDetails

可以启动时开启, 也可用jinfo 动态开启

常用GC启动参数

推荐 -XX:+PrintGCDetails -XX:+PrintGCDateStamps-Xloggc:<filename> -XX:+UseConcMarkSweepGC -Xmx400m -Xms400m -Xmn30m -XX:+PrintGCDetails -XX:+PrintGCDateStamps -Xloggc:G:/学习/gclog.log -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=G:/学习/dump.hprof

资料:https://www.oracle.com/technetwork/java/javase/tech/vmoptions-jsp-140102.html

-XX:PrintGCTimeStamps 打印gc的时候打印时间戳
-XX:+UseConcMarkSweepGC 使用cms垃圾回收器

-Xmn30m 新生代30M 默认 SurvivorRatio 8, eden:s0:s1为8:1:1,所以新生代为9,即30m*0.9=27m

MetaspaceSize 为本地内存。 非堆。
-Xmx400m -Xms400m 最大堆内存 400M,最小堆内存400M, 老年代=400m-30m=370m

jmap -head jpsid
在这里插入图片描述

收集的时候打印时间戳

-XX:+PrintGCTImeStamps   启动秒数
-XX:+PrintGCDateStamps  时间戳

内存分布
图片来源于网络
Memory Pools
在这里插入图片描述

新生代

Minor GC
GC后 Eden几乎是空的。
大多数新对象分配在Eden,大对象可能直接分配到老年代。(-XX:PretenureSizeThreshold
MonorailGC后 ,两个Survivor交换角色。
“足够老”的存活对象提升到老年代。
用到复制算法。
MinorGC过程中,Survivor(to)可能不足以容纳Eden+另一个Survivor(from)中的存活对象。此时就需要将Surviv偶然中的存活对象溢出?多余的对象转移到老年代–过早提升(Premature Promotion)。 — 如果老年代满了,无法容纳更大对象。(提升失败),minor GC后会导致Full GC.

Minor GC:Eden+to 存活对象–> to 不够? ----> 老年代 不够? ----> Full GC

老年代

“足够老”的存活对象提升到老年代。

永久代

类加载 ,将类的源数据信息加载到永久代(jdk8以下)
永久代满,引发垃圾收集-Full GC.
当需要加载其他类而空间不足,未使用的类就会从永久代中被卸载。
永久代垃圾回收不是stop-the-world
相关参数: -XX:PermSize 、 -XX:MaxPerSize

垃圾回收器

分代垃圾收集器

基于以下观察事实:

  1. 大多数分配对象朝生夕死。
  2. 存活时间久的对象很少引用存活时间短的对象。
分代名称 内存占用 垃圾收集频率 特点
新生代 minorGC,多次躲过回收后晋升到老年代
老年代 FullGC执行频率低,但是执行时间长。尽量
永久代 废弃常量和无用类

Serial收集器

只使用一个处理器
minor GC 、 Full GC都 stop-the-world.
标记清除、标记压缩

适用于 对停顿时间要求不高,多个JVM实例在一台机器上。

Parellel收集器-吞吐量

吞吐量大。
尽最大可能收集。
Minor GC 并行。
Full GC 并行。

CMS收集器-低延迟

停顿少,低延迟
减少stop-the-world时间。

初始标记 a 并发标记 b 重新标记 c 并发清除 d
短暂停顿,标记GC Roots可达对象① 标记可从①对象达到的存活对象 再次停顿,重新标记b阶段导致未被标记的存活对象 清除整个Java堆

G1收集器-分代、垃圾优先

分代
优先回收垃圾最多的区域。

JVM性能调优

关注稳定状态下 内存使用,程序延迟,吞吐量

选择JVM运行模式

client、server

垃圾收集器

吞吐量、延迟、内存占用 三选二,因为不能三者都满足。

调整内存大小

调整JVM堆布局

调整新生代、老年代、永久代大小

初始堆空间大小配置

名称 设置参数 说明
Java堆 -Xms和-Xmx 3-4倍FullGC后老年代空间量
永久代 -XX:permSize和-XX:MaxPermSize 1.2-1.5倍~
新生代 -Xmn 1-1.5倍~
老年代 Java堆大小减新生代大小 2-3倍~

Survivor

Survivor太小,导致to无法存放所有从Eden空间和“From”空间复制来的活跃对象,导致提升到老年代。 加速老年代内存消耗,提前Full GC.

-XX:SurvivorRatio=<ratio>

ratio表示单个survivor空间与Eden空间大小的比率。
如果是8,则表示 from:to:eden = 1:1:8
所以调大Survivor需要降低ratio。

survivor空间大小 = -Xmn/(-XX:SurvivorRatio=+2)

晋升

对象提升至老年代。
晋升阈值就是对象年龄。对象的年龄就是它所经历的Minor GC次数。
【最大晋升阈值参数】-XX:MaxTenuringThreshold=<n>
对象年龄超过n值将其提升到老年代。

不建议将【最大晋升阈值】设置为0,这会导致刚分配的对象,会在接下来的Minor GC中直接从新生代晋升到老年代,导致老年代空间迅速增长以至于Full GC。

不成熟的(未到年龄)的晋升解决方案是:使用SurvivorRatio增大Survivor空间

当Survivor空间比较紧张,JVM会使用一个低于 最大晋升阈值(MaxTenuringThreshold)的值来保证目标Survivor空间的占用。

注意:Survivor空间过小,导致JVM 内部计算的阈值过小,导致易晋升到老年代,导致Full GC频繁。

监控晋升阈值

参数:-XX:+PrintTenuringDistribution
期望Survivor大小 小于 活下来的对象。导致Survivor空间溢出,提升对象到老年代。

至少将Survivor增大到 【存活的Survivor大小】 

调整Survivor空间的容量

原则:调整Survivor空间的时候,如果新生代空间大小不变,增大Survivor则减少Eden。
因此,增大Survivor的同时,保持Eden空间不变,需要增大新生代空间。

理想状态:晋升阈值等于最大晋升阈值

如果Minor GC时间过长,就要减少新生代空间大小。

调整目标Survivor空间占用

尝试Minor GC之后仍然维持的Survivor空间占用

参数:-XX:TargetSurvivorRatio=<percent>

新生代
新生代大小决定了应用平均延迟
如果平均Minor GC持续时间大于应用程序平均延迟性要求,可以适当减小新生代空间大小;
如果Minor GC频率大于应用程序平均延迟性要求,可以适当增大新生代空间;
老年代
老年代大小决定了应用最差延迟
FullGC频率大于应用程序最大FullGC频率要求,可以适当增大老年代空间大小;
FullGC持续时间大于应用程序最差延迟性要求,可以使用CMS垃圾收集器;

posted @ 2019-10-25 14:47  thewindkee  阅读(156)  评论(0编辑  收藏  举报