jvm之分析调优

这篇具有很好参考价值的文章主要介绍了jvm之分析调优。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

写在前面

jvm调优不管是工作中还是面试中都异常重要,是衡量一个开发人员技术水平的重要指标,这也是个人的一个弱项,希望通过本文能够提高自我,也更能帮助到正在阅读文章的你,我们就开始吧!

1:重要的指标

如果是医生给病人看病,是需要根据各种指标的高低来对病人做出诊断的,分析jvm也是一样,这里我们主要看下两个指标,分别是分配速率和提升速率,解释如下:

分配速率:单位时间内eden生成的对象大小,单位m,即m/s
提升速率:单位时间内从young提升到old的对象大小,单位m,即m/s

1.1:分配速率

分配速率,我们并没有办法直接获取,但是可以通过GC日志来间接获取,方法是计算出某次GC距离上次GC新增的对象的数量,并除以两次GC间隔的时间就行了,如下的GC日志:

jvm之分析调优

第一次GC:
    生成的对象量是33280K,经历的时间是0.291s,分配速率为33.280m/0.291s=33280m/291s=114m/s
第二次GC:
    生成的对象量是38368k-5088k=33280k,经历的时间是0.446-0.291=0.155,分配速率为33.280m/0.155s=33280m/155s=214.7m/s
第三次GC:
    生成的对象量是71680k-5120k=66560k,经历的时间是0.829-0.446=0.383,分配速率为66.560m/0.383s=66560m/383s=173.7m/s
三次GC总共产生的对象量是33280K+33280K+66560k=133120k,总时间是0.829s,平均分配速率是133.120m/0.829s=133120m/829s=160.5m/s

当分配速率过高,就会导致young GC次数增多,进而造成stw时长增加,进而会降低系统的吞吐量。

1.2:提升速率

先看一个概念,提升过早,提升过早是指对象过早的从年轻代被提升到老年代,如果提升过早严重则就会导致频繁的major GC,major GC并不是为了频发回收而设计的,其更多的是一种兜底的方案,而衡量是否过早提升的的治疗就是提升速度,计算提升速度类似于分配速度,都可以通过gc日志的对象量以及时长信息反推出来,如下图:

jvm之分析调优

第一次GC:
    年轻代减少了33280k-5088k=28192k,整个堆内存减少了33280k-24360k=8920k,因此晋升到老年代的对象大小是28192k-8920k=19272k,平均提升速率19272m/291m=66m/s
第二次GC:
    年轻代减少了38368k-5120k=33248k,整个堆内存减少了57640k-46240k=11400k,因此晋升到老年代的对象大小是33248k-11400k=21848k,平均提升速率是21848m/(446-291)s=21848m/155s=140.9m/s
第三次GC:
    年轻代减少了71680k-5120k=66560k,整个堆内存减少了112800k-81912k=30888k,因此晋升到老年代的对象大小是66560k-30888k=35672k,平均提升速率是35672m/(829s-446s)=35672m/383s=93.1m/s
三次GC晋升的对象总量是19272k+21848k+35672k=76792k,总的提升速率是76792m/829s=92.63m/s

1.3:调优实战

1.3.1:young GC时间长

现象是GC stw时长特别长,400多毫秒,长的达到了540多毫秒,怀疑是发生了full GC,但通过查看GC日志,并没有,因此确定是因为发生了young GC,因为当时使用的是jdk8,配置参数如下:

-Xmx4g -Xms4g

而jdk8默认的GC策略 是并行GC,并行GC是一种吞吐量优先的GC算法,所以我们就怀疑是垃圾收集器为了维持吞吐量而牺牲掉了stw时长,而G1在jdk8已经成熟,所以就考虑使用G1垃圾收集器,修改为G1后配置参数为:

-Xmx4g -Xms4g -XX:+UseG1GC -XX:MaxGCPauseMillis=50

接着重启服务,运行观察,表现优秀,GC时长基本上维持在50ms以下,但程序运行了一天多之后,突然出现了一次超长时间的GC stw,时间达到了1.2s,相当残暴,有些不知所措,没有什么好办法,就是上网各种查G1的坑,也都没解决,那就看日志吧,修改参数如下打印GC日志:

-Xmx4g -Xms4g -XX:+UseG1GC -XX:MaxGCPauseMillis=50 -XX:+PrintGCDetails  -XX:+PrintGCStamps -Xloggc:/var/log/gc.log

,终于在日志中找到了如下的可疑信息:

[Parallel Time: 1861.0 ms, GC Workers: 48]

提示GC线程48个,这就奇怪了,因为我们的机器是4核的,GC线程怎么可能会这么高,虽然不知道为什么这么高,但严重怀疑这就是导致GC STW时长超长的真凶了,这么多的线程争抢4核势必造成非常严重的资源争抢,导致频繁上线文切换,所以先增加如下参数,限制GC线程数:

-XX:ParallelGCThreads=4

再重启,观察,问题解决,但为什么GC线程数会是48,之后经过和运维工程师的沟通,发现,是因为应用部署在k8s的环境中,k8s限制了pod只能使用4g的内存,只能使用4核cpu,但是物理机是72核的,而G1启动的工作线程数量的策略是这样的:

1:当核数小于等于8时,取核数
2:当核数大于8是,取(核数*(5/8)+3)

所以我们这里就是(72*(5/8)+3)=48,所以根本原因是JVM看到了物理机的72核,所以本质上,造成问题的原因是k8s的资源隔离不彻底,只是限制了pod使用4核,但是却让pod中的jvm看到了72核(其实是不应该看到的))。

1.3.2:young GC过于频繁

现象是这样子的,young GC很频繁,每秒1次,有时候每秒2次,每次GC 60ms 左右,不算长,通过jstat -gcutil ${pid} 1000 1000每秒打印1次,打印1000次,如下图:

jvm之分析调优
为了进一步的印证young GC 过于频繁,我们将GC日志上传到gceasy.io , 发现其吞吐量只有93%。线上当前的配置如下:

-Xmx8g -Xms8g -Xmn2g -XX:+UseConcMarkSweepGC

很明显,young GC发生频繁,而且几乎没有full GC,所以是不存在内存泄漏问题,问题的原因是young区的内存大小太小了,所以就需要调大-Xmn,即增加年轻代的大小,以将young GC每4秒钟到5秒钟发生一次,作为优化目标来调整该参数,最终调整参数为-Xmn2.9g,但又发现gc 时长增加到了80ms左右,虽然时间也可以接受,但还是继续尝试优化这个时间,jvm使用的GC是CMS,而cms在年轻代使用的垃圾收集器是ParNew,ParNew采用的垃圾收集算法是复制算法,所以我们就希望能够减少在s0,s1之间复制对象的量来降低young gc的时间,想要实现这个效果,就需要调整参数-XX:MaxTenturingThreshold={age},这里age的默认值15,即15次young gc之后才会被提升到老年代,降低这个参数值,可以让对象提早提升到老年代就可以实现我们的目标了,但如何确定合适的年龄也是个问题,太小了,可能会导致会导致full GC,为了确定这个年轻,增加了参数-XX:PrintTenuringDistribution,配置如下:

-Xmx8g -Xms8g -Xmn2.9g -XX:+UseConcMarkSweepGC -XX:+PrintGCDetails -XX:+PrintGCDateStamps -Xloggc:d:\\test\\gc1423.log -XX:+PrintTenuringDistribution -Dfile.encoding=UTF-8

打印出不同分代对象的个数和大小信息,如下图(来自gceasy.io 分析结果):

jvm之分析调优

可以看到存活区15岁的对象个数是8501,8岁的对象个数是8565,比例为8501/8565=99.2%,可以看到只要存活区到8岁的对象,存活到15岁的比例达到了99.2%,也就是达到8岁的对象就可以直接让其晋升到老年代了,因此调整参数-XX:MaxTenturingThreshold=7,这样就节省了8次大量存活对象从一个survivor区复制到另一个survivor区的成本,优化后,gc时间维持在了71ms左右,吞吐量也提高到了97%左右。

写在后面

参考文章列表

SuperBenchmarker(简称“sb“)压力测试工具详解 。

JVM工具之Arthas使用 .

一次线上JVM Young GC调优,搞懂了这么多东西! 。

线上问题排查思路

jvm之分析调优

jvm之分析调优文章来源地址https://www.toymoban.com/news/detail-465138.html

到了这里,关于jvm之分析调优的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • JVM 8 调优指南:如何进行JVM调优,JVM调优参数

    这篇文章将详细介绍如何进行JVM 8调优,包括JVM 8调优参数及其应用。此外,我将提供12个实用的代码示例,每个示例都会结合JVM启动参数和Java代码。 本文已收录于,我的技术网站 ddkk.com,有大厂完整面经,工作技术,架构师成长之路,等经验分享 JVM调优是指通过调整Java虚拟

    2024年01月21日
    浏览(43)
  • JVM 11 调优指南:如何进行JVM调优,JVM调优参数

    JVM 11的优化指南:如何进行JVM调优,以及JVM调优参数有哪些”这篇文章将包含JVM 11调优的核心概念、重要性、调优参数,并提供12个实用的代码示例,每个示例都会结合JVM调优参数和Java代码 本文已收录于,我的技术网站 ddkk.com,有大厂完整面经,工作技术,架构师成长之路,

    2024年01月16日
    浏览(43)
  • JVM 21 的调优指南:如何进行JVM调优,JVM调优参数

    聊聊关于JVM 21的优化指南。这篇文章将会深入探讨如何进行JVM调优,介绍一些关键的JVM调优参数,并提供12个实用的代码示例。由于篇幅较长,我会分几个部分来详细讲解,之前写的也有33篇系列教程JVM调优实战打击也可以去围观。 JVM(Java虚拟机)调优是一个复杂但重要的任

    2024年01月24日
    浏览(40)
  • JVM实战(19)——JVM调优工具概述

    作者简介:大家好,我是smart哥,前中兴通讯、美团架构师,现某互联网公司CTO 联系qq:184480602,加我进群,大家一起学习,一起进步,一起对抗互联网寒冬 学习必须往深处挖,挖的越深,基础越扎实! 阶段1、深入多线程 阶段2、深入多线程设计模式 阶段3、深入juc源码解析

    2024年01月18日
    浏览(43)
  • JVM-JVM调优基础(理论)

    申明:文章内容是本人学习极客时间课程所写,作为笔记进行记录,文字和图片基本来源于课程资料,在某些地方会插入一点自己的理解,未用于商业用途,侵删。 原资料地址:课程资料 JVM参数 标准参数 定义:稳定的参数不会随着Java版本的变化而变化。通常以 短横线开头

    2024年02月19日
    浏览(31)
  • JVM调优(10)JVM的运行时数据区

    对于 C C++ 来说,在内存管理领域,JVM既拥有最高的权利,但是同时他们又是从事最基础工作的劳动人员,因为他们担负着每一个对象从开始到结束的维护责任。而对于Java来说,再虚拟机自动内存管理的帮助下,不再需要为每一个new操作去分配内存,不容易出现内存泄漏和内

    2024年02月07日
    浏览(34)
  • 【JVM】JVM 调优的参数都有哪些?

    设置堆的初始大小和最大大小,为了防止垃圾收集器在初始大小、最大大小之间收缩堆而产生额外的时间, 通常把最大、初始大小设置为相同的值 。 堆空间设置多少合适? 最大大小的默认值是物理内存的1/4,初始大小是物理内存的1/64 堆太小,可能会频繁的导致年轻代和老

    2024年02月12日
    浏览(30)
  • 【JVM】性能调优

    性能调优,顾名思义,就是对系统或软件的性能进行优化,以提高其运行效率和响应速度。在计算机科学中,性能调优通常涉及到硬件、操作系统、数据库、网络等多个方面。对于Java开发者来说,JVM(Java虚拟机)的性能调优是非常重要的一环,因为JVM的性能直接影响到Java程

    2024年01月18日
    浏览(37)
  • JVM性能调优

    一、JVM内存模型及垃圾收集算法      1.根据Java虚拟机规范,JVM将内存划分为:   New(年轻代) Tenured(年老代) 永久代(Perm)   其中New和Tenured属于堆内存,堆内存会从JVM启动参数(-Xmx:3G)指定的内存中分配,Perm不属于堆内存,有虚拟机直接分配,但可以通过-XX:PermSize

    2024年02月13日
    浏览(60)
  • JVM调优相关

    1.jvm中的一些工具 1.1 jps  jps 用于查看java进程运行情况,输出JVM中运行的进程状态信息 命令行参数如下: -m 输出传入main方法的参数 -l 输出main类或Jar的全限名 -v 输出传入JVM的参数    如上,bootstrap 就是tomcat进程,调用的main方法就是start方法(可以参考tomcat笔记) 1.2 jstack

    2024年02月11日
    浏览(24)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包