GC常用参数
- -Xmn -Xms -Xmx -Xss
年轻代 最小堆 最大堆 栈空间大小 - -XX:+UseTLAB
使用TLAB,默认打开 - -XX:+PrintTLAB
打印TLAB的使用情况 - -XX:TLABSize
设置TLAB大小 - -XX:+DisableExplictGC
System.gc()不管用 ,FGC - -XX:+PrintGC
- -XX:+PrintGCDetails
- -XX:+PrintHeapAtGC
- -XX:+PrintGCTimeStamps
- -XX:+PrintGCApplicationConcurrentTime (低)
打印应用程序时间 - -XX:+PrintGCApplicationStoppedTime (低)
打印暂停时长 - -XX:+PrintReferenceGC (重要性低)
记录回收了多少种不同引用类型的引用 - -verbose:class
类加载详细过程 - -XX:+PrintVMOptions
- -XX:+PrintFlagsFinal -XX:+PrintFlagsInitial
必须会用 - -Xloggc:opt/log/gc.log
- -XX:MaxTenuringThreshold
升代年龄,最大值15 - 锁自旋次数 -XX:PreBlockSpin 热点代码检测参数-XX:CompileThreshold 逃逸分析 标量替换 …
这些不建议设置
JVM调优第一步:了解JVM常用命令行参数
public class HelloGC {
public static void main(String[] args) {
System.out.println("hello gc~");
List<byte[]> list = new LinkedList<>();
for (; ; ) {
list.add(new byte[1024 * 1024]);
}
}
}
区分概念:内存泄漏memory leak,内存溢出out of memory
内存泄漏就是有一块内存,我们用不到 但是GC也无法回收
内存溢出是分配内存时,发现内存不够用啦
这俩其实没啥必然的联系:
泄漏不一定导致溢出,内存总空间够大的话,浪费一点也不致命;
内存溢出不一定是由内存泄漏导致,内存总空间比较小,程序的对象又很多时,正常的使用也可能内存溢出
-XX:+PrintCommandLineFlags
输出JVM的默认参数:
-XX:InitialHeapSize=132253248
-XX:MaxHeapSize=2116051968
-XX:+PrintCommandLineFlags
-XX:+UseCompressedClassPointers
-XX:+UseCompressedOops
-XX:-UseLargePagesIndividualAllocation -
XX:+UseParallelGC
-Xmn10M -Xms40M -Xmx60M -XX:+PrintCommandLineFlags -XX:+PrintGC
- -Xmn 年轻代空间的最大值,-XX:MaxNewSize
- -Xms 堆空间的最小值,-XX:InitialHeapSize,即初始大小
- -Xmx 堆空间的最大值,-XX:MaxHeapSize
一般把Xms和Xmx设置成一样的大小,避免堆的扩容和缩容所带来的资源消耗 - -XX:+PrintGC 打印GC信息
关于打印GC的参数还有:
-XX:+PrintGCDetails 打印详细信息,这个下面详细看
-XX:+PrintGCTimeStamps 时间戳
-XX:+PrintGCCause 原因
执行后输出:
GC就是Young GC,年轻代的GC
-XX:InitialHeapSize=41943040 -XX:MaxHeapSize=62914560 -XX:MaxNewSize=10485760 -XX:NewSize=10485760 -XX:+PrintCommandLineFlags -XX:+PrintGC -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:-UseLargePagesIndividualAllocation -XX:+UseParallelGC
hello gc~
[GC (Allocation Failure) 8161K->6880K(39936K), 0.0027479 secs]
[GC (Allocation Failure) 14289K->14000K(39936K), 0.0024422 secs]
[GC (Allocation Failure) 21544K->21168K(39936K), 0.0031436 secs]
[GC (Allocation Failure) 28523K->28352K(39936K), 0.0025764 secs]
[Full GC (Ergonomics) 28352K->28242K(54784K), 0.0111999 secs]
[GC (Allocation Failure) 35611K->35538K(54784K), 0.0056506 secs]
[GC (Allocation Failure) 42869K->42706K(54784K), 0.0019941 secs]
[Full GC (Ergonomics) 42706K->42580K(60416K), 0.0571264 secs]
[GC (Allocation Failure) 49911K->49876K(60416K), 0.0116782 secs]
[Full GC (Ergonomics) 49876K->49749K(60416K), 0.0036178 secs]
[Full GC (Ergonomics) 57071K->56918K(60416K), 0.0036742 secs]
[Full GC (Ergonomics) 57946K->57942K(60416K), 0.0029635 secs]
[Full GC (Allocation Failure) 57942K->57924K(60416K), 0.0095233 secs]
Exception in thread “main” java.lang.OutOfMemoryError: Java heap space
at character07.HelloGC.main(HelloGC.java:11)
-XX:+UseConcMarkSweepGC -XX:+PrintCommandLineFlags -XX:+PrintGC
-XX:+UseConcMarkSweepGC 使用CMS,观察CMS的GC
输出相比默认垃圾回收期PS+PO会更为详尽,
并且多了CMS Initial Mark
GC日志格式
关于上图中的Times含义:在linux中的times代表:
Heap Dump的含义(Linux)
eden space 5632K, 94% used [0x00000000ff980000,0x00000000ffeb3e28,0x00000000fff00000)
后面的内存地址指的是,起始地址,使用空间结束地址,整体空间结束地址
年轻代的total = eden + 1个survivor
调优前的基础概念
- 吞吐量throughput:用户代码执行时间 /(用户代码执行时间 + 垃圾回收时间)
(可以理解为干正经事的时间占总时间的比例)
吞吐量越大,JVM花在GC上的时间越少 - 响应时间:STW越短,响应时间越好
这俩指标几乎是不可能兼得的,
想要更高的吞吐量,就是要GC总体占用更少的CPU时间,这样的话,一旦需要STW,会STW很久(典型代表:CMS);
想要更快的响应时间,就是要STW很短,需要有线程去不断的标记处理对象,但是这样的话,吞吐量势必会降低,因为GC占用更多的CPU时间了.
所谓调优,首先确定,追求啥?
吞吐量优先,还是响应时间优先?还是在满足一定的响应时间的情况下,要求达到多大的吞吐量
问题:
吞吐量优先:科学计算/数据挖掘,一般选择PS + PO
响应时间优先:网站 GUI API (1.8 选择G1)
什么是调优
- 根据需求进行JVM规划和预调优
- 优化运行JVM环境(慢,卡顿,一般是GC的STW过长了)
- 解决JVM运行过程中出现的各种问题(CPU 内存使用过高告警,OOM)
并发理解:
- QPS
- TPS
淘宝双11并发历年最高54万,据说12306并发比淘宝更高,号称上百万
调优,从规划开始
- 调优,从业务场景开始,没有业务场景的调优都是耍流氓
- 无监控(压力测试,能看到结果),不调优
- 步骤:
- 熟悉业务场景,选择优化的维度(没有最好的垃圾回收器,只有最合适的垃圾回收器)
- 响应时间、停顿时间 [CMS G1 ZGC] (需要给用户作响应)
- 吞吐量 = 用户时间 /( 用户时间 + GC时间) [PS]
- 选择回收器组合
- 计算内存需求(经验值,不一定是越大越好, 有的从1.5G升到16G甚至还慢了很多)
- 选定CPU(越高越好)
- 设定年代大小、升级年龄
- 设定日志参数
- -Xloggc:/opt/xxx/logs/xxx-xxx-gc-%t.log -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=5
-XX:GCLogFileSize=20M -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintGCCause
使用5个GC日志文件,每个文件最大20M,超过5个时最新的文件会把最老的覆盖掉 - 或者每天产生一个日志文件
- -Xloggc:/opt/xxx/logs/xxx-xxx-gc-%t.log -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=5
- 观察日志情况
-
案例1:垂直电商,最高每日百万订单,处理订单系统需要什么样的服务器配置?
这个问题比较业余,因为很多不同的服务器配置都能支撑(1.5G 16G 都有可能啊)
我们做一个假设吧,1小时360000个订单。集中时间段, 100个订单/秒,(找一小时内的高峰期,可能是1000订单/秒)。我们就要找到这个最高峰的时间,保证你的架构能够承接的住。
大多数情况下,是靠经验值,然后做压测。
如果非要计算的话,你预估一下,一个订单对象产生需要多少内存?512K * 1000 = 500M内
专业一点的问法:要求响应时间在多少时间的情况下,比如100ms,我们去挑一个市面上性价比比较高的服务器,做压测去测试,再不行加内存,再不行,就上云服务器… -
案例2:12306遭遇春节大规模抢票应该如何支撑?(架构问题)
12306应该是中国并发量最大的秒杀网站:号称并发量最高100W
架构模型:CDN -> LVS -> NGINX -> 业务系统 -> 100台机器,每台机器1W并发(单机10K问题),目前这个问题已解决,主要是用redis
.
业务流程:普通电商订单 -> 下单 ->订单系统(IO)减库存 ->生成订单,等待用户付款
12306的一种可能的模型,是异步来进行的: 下单 -> 减库存 和 订单(redis kafka) 同时异步进行 ->等付款,付完款,持久化到Hbase, MySQL等等
.
减库存最后还会把压力压到一台服务器,怎么办?
可以做分布式本地库存 + 单独服务器做库存均衡
大流量的处理方法:分而治之,每台机器只减自己机器上有的库存
流量倾斜的问题怎么解决?比如有的机器上已经没库存了,有的机器上还剩很多?
这时候你还需要一台单独的服务器,去做所有服务器的平衡,如果某台服务器没库存了,从别的机器上挪一些过去。 -
怎么得到一个事务会消耗多少内存?
用压测来确定
实际中一般是弄台机器,看能承受多少TPS?是不是达到目标?扩容或调优,让它达到目标即可
优化环境
-
有一个50万PV的资料类网站(从磁盘提取文档到内存)原服务器32位,1.5G的堆,用户反馈网站比较缓慢,因此公司决定升级,新的服务器为64位,16G的堆内存,结果用户反馈卡顿十分严重,反而比以前效率更低了
- 为什么原网站慢?
很多用户浏览数据,很多数据load到内存,内存不足,频繁GC,STW长,响应时间变慢 - 为什么会更卡顿?
内存越大,FGC时间越长 - 咋办?
PS -> PN + CMS 或者 G1
- 为什么原网站慢?
-
系统CPU经常100%,如何调优?(面试高频)
CPU100%那么一定有线程在占用系统资源,- 找出哪个进程cpu高(top)
- 该进程中的哪个线程cpu高(top -Hp)
- 导出该线程的堆栈 (jstack)
- 查找哪个方法(栈帧)消耗时间 (jstack)
工作线程占比高 | 垃圾回收线程占比高
-
系统内存飙高,如何查找问题?(面试高频)
- 导出堆内存 (jmap)
- 分析 (jhat jvisualvm mat jprofiler … )
-
如何监控JVM
- jstat jvisualvm jprofiler arthas top…