JVM调优

一、概述

1、生产环境中的问题

  • 生产环境发生了内存溢出应该如何处理
  • 生产环境应该给服务器分配多少内存合适
  • 如何对垃圾回收器的性能进行调优
  • 生产环境CPU负载飙高应该如何处理
  • 生产环境应该给应用分配多少线程合适
  • 无log,如何确定请求是否执行了某行代码
  • 无log,如何实时查看某个方法的入参和返回值

2、调优基本问题

  • 为什么要调优
    • 防止OOM出现,对JVM规划和预调优
    • 解决程序运行中各种OOM
    • 减少full GC的出现频率,决绝运行慢、卡顿的文图
  • 调优的方向
    • 合理的编写代码
    • 充分并合理的使用硬件资源
    • 合理的进行JVM调优
  • 不同阶段的考虑问题
    • 上线前
    • 项目运行阶段
    • 线上运行出现OOM
  • 原则
    • 没有业务场景的调优都是耍流氓
    • 无监控、不调优

3、调优监控的依据

  • 运行日志
  • 异常堆栈
  • GC日志
  • 线程快照
  • 堆转储快照

4、性能优化的步骤

  1. 属性业务场景
  2. 性能监控(发现问题)
    • GC频繁
    • CPU load 过高
    • OOM
    • 内存泄漏
    • 死锁
    • 程序响应时间长
  3. 性能分析(排查问题)
    • 打印GC日志,通过工具分析(gceasy)
    • 使用命令行工具,jstack、jinfo、jmap等
    • 分析dump文件
    • 使用图形化工具查看,arthas、jVisualVm、jConsoles等
  4. 性能调优(解决问题)
    • 适当的增加呢次,根据业务场景使用垃圾回收器
    • 优化代码,控制内存使用
    • 增加节点,分散压力
    • 合理设置线程池的参数
    • 使用中间件

5、性能评价、测试指标

  • 停顿时间
  • 吞吐量
  • 并发数
  • 内存占用
  • 相互间关系

二、OOM案例

  • 堆溢出
  • 元空间溢出
  • GC overhead limit execceded:98%的时间都在GC
  • 线程溢出

三、性能测试工具:Jmeter

1、主界面

2、使用流程

  • 新增线程组           
  • 新增JMeter元组
  • 新增监听器
  • 运行、查看结果:调试运行,分析指标助局,挖掘性能瓶颈,评估系统性能状态

四、性能优化案例

1、调整堆大小提高服务的吞吐量

设置tomcat堆内存,前后的吞吐量的变化。主要关注Full GC的变化。

2、JVM优化-JIT优化(-XX:+DoEscapeAnalysis)

堆是分配对象的唯一选择吗 ?

可以分配到栈里面,但是在java中没有真正放在堆中,而是把对象分解成聚合量,把聚合量中的标量放入栈中。其实最后都是放入在堆中的。

TaoBaoVM,就使用GCHI技术,把生命周期长的对象,放入本地内存中。

JDK1.6之后,默认开启的。

1、逃逸分析:变量使用仅在方法那,就没有发生逃逸

  • 栈上分配:对象没有发生逃逸,就可以分配到栈上
  • 同步消除:如果存在只有一个线程调用的 synchronized,就会消除锁
  • 标量替换:把对象替换成标量(基本数据类型),存放在栈中(-XX:+EliminateAllocations)

注意,在JVM中,逃逸分析优化的点在于栈上分配的对象使用标量替换

3、合理配置堆内存

增加内存提高性能是显著的,但是增加了内存就会导致Full GC的时间变得很长,所以增加多少内存合适是需要考量的。

JVM官方给出的意见,如下图所示:

  • 堆空间:full GC之后老年代占用内存空间的3~4倍(可以多次Full GC 平均之后)
  • 元空间:full GC之后老年代占用内存空间的1.2~1.5倍
  • 新生代(-Xmn):full GC之后老年代占用内存空间的1~1.5倍
  • 老年代:full GC之后老年代占用内存空间的2~3倍

1、计算老年代存活对象:查看日志、强制出发FullGC

其中查看日志文件比较推荐的方式。看一下多次full GC的平均值。

2、数据分析

根据以上的经验,平时在工作上处理tomcat内存设置都是错误的,不是越大越好,而是根据老年代存活的占用内存大小而设置。

3、估算GC频率

计算出Eden大小:1024*1/3*80%(1G堆空间,1/3年轻代,80%Eden区)

计算出每秒数据大小,两个值相除,就得到大约每隔多少秒进行一次Young GC。

但是计算每秒获取数据量的大小,感觉比较难。

4、新生代和老年代的比例

默认情况下,新生代中Eden:s1:s0=8:1:1,新生代:老年代=1:3。

但是在ParallelGC情况下,查看比例位6:1:1,因为默认开启了动态分配内存中。

结论:

  • 对于面向外部的大流量、低延迟系统,不建议开启这个参数  -XX:+UseAdaptiveSizePolicy
  • 保持使用 UseParallelGC,显示设置-XX:SurvivorRatio=8
  • 使用CMS,直接关闭  -XX:+UseAdaptiveSizePolicy参数即可

5、CPU占用高排查方案(经常出现)

主要是查看哪行代码出现了问题,根据jstack进行排查。

  • top 查看哪个进程占用高
  • top -Hp 进程ID,查看哪个线程占用高
  • jstack 进程ID > 文件,查看进程中线程的堆栈信息
  • 线程ID转换成16进制,然后在文件中查看线程堆栈信息
  • 使用 jstack 进程ID | grep -A20 线程ID16进制

6、G1并发执行线程数对性能的影响

1、配置信息

当设置ConcGCThreads增加的时候,Jmeter的平均响应时间会有所提高。但是感觉这个值可以直接使用默认即可。

7、调整垃圾回收器提高服务的吞吐量

根据实际情况使用不同的垃圾回收器,例如单核服务器使用SerialGC、高内存的时候使用G1、并发量比较大的时候使用ParallelGC、低延迟的时候使用CMS等等。

8、日均百万级订单交易系统如何设置JVM参数

1、分析

注意一点,因为产生了大量的订单数据,如果一个订单对象需要使用30秒钟,但是每次执行YGC只有10秒钟,可能导致大量的订单对象进入到老年代中,这样的情况需要避免。

也就是量很大,使用时间并不是很长的对象避免出移动到老年代,这样可以防止Full GC的出现,减少STW。

  • 在大流量的秒杀系统中,可以使用分布式本地库存+单独服务器做库存均衡
  • 升级了服务器变慢了
    • 频繁GC导致STW时间长,响应时间慢
    • 升级了更卡顿,内存空间越大,做GC时间长,导致STW时间更长
    • 更换垃圾回收器:parallelGC、ParNew+GMS、G1,可以考虑实用化G1
    • 配置GC参数
      • 限制暂停时间
      • 配置并发标记线程
    • 根据进一步分析,优化内存空间的比例:jstack、jstat、jinfo、jmap
  • 内存飙高,如何排查问题
    • 导出dump文件,分析dump文件
    • 通过相关工具进行内存占比情况,查看GC日志
  • 监控JVM
    • 命令行工具
    • 界面工具
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值