性能测试瓶颈分析与系统调优(8)java应用程序jvm调优方向

七、java程序调优分析

7.1 jvm核心知识

类加载器(class loader)负责将编译好的class字节码文件加载到内存中,是的jvm可以实例化或以其它方式使用加载后的类

方法区/元数据:方法区就是我们常说的永久代区域,里面存储这Java类信息、常量池、静态变量等数据,方法区占用的内存区域在jvm中时线程共享的

在jdk1.8及以后的版本中,方法区已经被移除,取而代之的时元数据区和本地内存,类的元数据信息直接存放到jvm管理的本地内存中。需要注意的时,本地内存不是虚拟机运行时数据区的一部分,也不是java虚拟及规范中定义的内存区域。常量池、静态变量等数据则存放到了java堆(heap)中

Java虚拟机栈&本地方法栈&程序计数器 线程私有的,和线程运行代码直接相关

堆区:java是一门面向对象的程序设计语言,而jvm堆区时真正存储java对象实例的内存区域,堆的大小不等于jvm课同时存储这么多数据,因为要做垃圾回收

简单概念:搞卫生--把家里的东西挪动一下打扫,所以需要空闲的位置。

一起请求通过网络过来了,对应到某一个端口号,java进程绑定了端口号,操作系统通知java程序来处理,java启动线程,对应到一个主线程,这个线程就会去处理这个请求。

一个请求过来了,根据路径url行解析,找到对应的业务代码去解析,解析后不会去立马处理,解析完毕后先存起来,,安排另外一个线程,去执行业务代码。

这里的话,就是一个多线的概念。

7.2java程序运行逻辑分析

源文件需要编辑,通过jvm运行

线程处理请求逻辑理解

Tomcat比作一个医院,可以理解为:服务器,ip地址和对应的端口号

线程--为最终处理请求的单位,那个科室

请求不是随便发的,iP地址端口号,请求参数要正确,可以理解为要去那个医院,路要走对、

线程池,可以理解为不同的科室,比如内科,外壳,呼吸科,耳鼻喉科灯

我们的请求到了服务器(你到了医院),主线程处理解析(医院有人过来找你,了结你的情况,要挂什么科)然安排线程去处理请求(哪怕科室的人去给你看病)

线程池:线程池--最大容量,初始化大小

线程池,一个一个线程放在一起,可以理解一下鱼池

线程创建不是无限制,cpu资源是有限的,如果cpu处理不过,创建再多线程是没有用的。

任何一种程序,对于宝贵资源,都会采用池的一种技术,池的技术:预先的,这些线程空在这里,1个线程池有10个线程,来1个请求,分1个线程去处理请求,来10,取10个线程,来11个,等一等。

线程池,有大小,有线程池,就可以设置提前创建一些线程,不用请求来了,再去创建线程。

线程池的线程,用10个线程,用了1个,剩余9个,请求处理完,线程回归为10

线程运行的时候,创建的对象占用堆内存,代码逻辑放在元数据,线程运行时,程序计数器,栈记录:代码执行的顺序,位置。

创建的对象,会占用堆内存,java程序堆内存不够的,是不能同时处理很多请求的,

线程的运行会消耗cpu资源,线程越多,对cpu的消耗越大

7.3jvm资源监控工具

#设置512MB

Java -Xmx512m -jar jar包

jps 和jcmd 快速查看当前服务器上启动了那些java进程

jmap 此工具在jdk安装目录的bin文件夹里面,用用户查看jvm的内存信息

jmap[option]<pid>

例如:jmap -heap 2350

MaxHeapSIZE = 994050048 (948.0MB)

7.4程序瓶颈分析

实战1:性能测试过程--服务器资源够用,但是程序性能达到了瓶颈:

可疑原因:内存配置太小,没有榨干服务器内存资源。或者时测试场景中的并发量不够(要监控测试过程中,jvm自身内存是否被用完,如果没有用完,就加大并发量)

通过查看jvm的内存配置,是否用到机器的大部分内存

验证:加大并发--排查性能测试并发不够的问题

加大内存--在加大内存之后,性能是否有提升

内存参数调整:-Xms 堆内存最小值,例如:Xms4g

-Xmx 堆内存最大值

java原生命令(springboot)直接在java命令后,记得空格

java -Xmx2G -jar xxx.jar

Tomcat 配置 windows写在bin目录setenv.bat,没有就创建

linux 写在bin目录setenv.sh

Jmeter,总共启动三组线程,第一组1个用户,延迟1秒启动,在1秒内启动这些线程,持续运行60s,然后1s内关闭这些线程

第二组线程3个,第三组线程5个用户

不指定堆内存启动项目:命令为:

java -jar

-Dspring.datasource.url="jdbc:mysql://192.168.1.7:3306/novel-plus?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true" -Dspring.datasource.username=root -Dspring.datasource.password=zhaoWEILI1314520@ novel-front-3.5.4.jar

此时的java堆内存,最大为948M

压测结果:累计发起146227个请求,异常48281个,错误率33.1%,其中吞吐量最大的时候为:3.32K,最小为166.2,平均为1.33K

服务器资源的使用情况如下

调整堆内存的大小2G,启动命令为:

java -Xmx2G -jar

-Dspring.datasource.url="jdbc:mysql://192.168.1.7:3306/novel-plus?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=false" -Dspring.datasource.username=root -Dspring.datasource.password=zhaoWEILI1314520@ novel-front-3.5.4.jar

通过jcmd,和jmap命令检查,发现配置成功,jmeter脚本不变,再次堆服务器进行压测

压测结果:累计发起144860个请求,异常46934个,错误率32.4%,其中吞吐量最大的时候为:3.32K,最小为166.2,平均为1.35K

调整了jvm堆内存大小后,吞吐量不一定就立马翻倍。

实战2:性能测试过程中--服务器cpu资源够用,但是程序性能达到了瓶颈

可疑原因:程序配置的线程数太少,没有榨干服务器硬件资源。或者时测试场景中的并发量不够。

线程池的概念:程序使用线程的时候,通常是采用线程池的方式

Pool就是用来存放线程,使用线程的一个管理机制:要用的时候,从池子里面取,用完之后,放回去,同一个线程反复使用。

要用的时候,没有线程怎么办?等待,创建新的线程,池子的大小,决定总共能够存在多少线程。

参数1:初始化线程数量,启动后会初始化一定量的线程,放到池

参数2:最小线程数量,池子里面会维持有多少个线程常驻

参数3:最大线程数量,总共可以存多少个线程

请求处理的线程池大小,线程参数调整

WEB服务器--处理请求时候,会用到线程池:不限于Tomcat,jety,netty,weblogic包括其它语言也有类似的机制

SpringBoot 程序: -Dserver.tomcat.max-threads=100

原生Tomcat部署

默认情况--Tomcat:最大线程数量200

maxConnection 10000

AcceptCount:没有足够线程处理请求的时候,排队的请求个数

对于超过线程数量的请求:队列堆积,丢弃超过一定数量的请求

这里我们通过jar包中的配置文件,查看项目的线程配置

测试结果如下:共发起请求数140973,错误43038,错误率30.53%,其中吞吐量,最大为3.32k,最小为134.20,平均为1.28k

对线程数进行调整如下:

再次使用相同的jmeter脚本,进行压测,测试结果为:一共发起146091个请求,错误48156个,错误率32.96%,吞吐量最大为3.43K,最小为177.60,平均为1.36k

吞吐量有所提升,但变化不大,影响性能指标的因素有非常多个,调整其中的某一个,不一定会出现性能显著提高。

实战3:并发少,但是cpu资源占用量大:可以原因:少了线程占用了大量的系统资源,例

如:单线程占用cpu%(死循环,数据太对,循环N多次处理),1个请求会导致大量线程被创建(开发人员写业务代码也用到多线程)

排查方式1:基于top徒手取撸--学习思路:

找到最耗进cpu的进程。执行top -c,显示进程运行信息列表,键入P(大写P),进程按照cpu使用率排序。

找到最耗cpu的线程:top -HP 6577,显示第一个进程的线程运行信息列表,进入P(大写P),线程按照cpu使用率排序

继续找--线程在做什么事情,通过jstack命令取查询jvm里面的线程信息--匹配

将线程PID转换为16进制,printf”%x\n” 67890,得到67890对应的16禁止是10932,之所以要转换为16进制,是因为堆栈里,线程id是用16进制表示的

jstack查看堆栈,找到线程在干吗:

打印进程堆栈:jastack jvm 进程id

通过线程id,过滤得到线程堆栈jstack 12345|trep “10932” -C5 --color

人工执行太慢,通过脚本快速执行,快捷shell脚本

0.0自己写太麻烦,业界硬件有很多案例了

排查方式2:基于arthas阿里提供的:dashborad整体仪表盘,thread -n 5展示最忙的前5个线程

注意:某项资源没用完,不代表程序就一定有问题,也可能就是这个程序比较占用其它资源,不怎么占用该资源。

应用到具体的项目中去

开启top命令 ,对服务器进行压测,找到最耗cpu资源的进程,PID:2760

top -HP 2760,找到最耗cpu的线程,

对服务进行压测,多个线程都占用了cpu,且占用率不高,通过这个办法不能排查出问题,

因为web程序在处理时,用到了线程池,多个线程重复使用

继续找,压测,找到最耗cpu的 线程,pid是2800

线程在做什么事情,

jstack 2800

nid是一个16进制展示,这个id和top命令里面的id是对应上的

通过printf "%x\n" 十进制线程TID # 将十进制转成十六进制后,这里是af0,之所以转成16进制,因为堆栈里,线程id用16进制表示

打印进程堆栈:jstack jvm进程id

jstack 进程id |grep 转成16进制的线程id -C5 --color

grep af0 -C5 --color 匹配af0字串那行以及上下5行

这种方式属于徒手去撸,就一个一个一个找,比较慢

排查方式2,基于阿里的arthas:

dashboard 整体仪表盘

thread -n 5展示最忙的前5个线程

下载地址:https://arthas.aliyun.com/doc/download.html

下载后,传到java应用服务器上,解压 ,运行as.sh文件,直接运行会报错

安装telnet服务和客户端

yum install telnet-server

yum install telnet*

打开整体监控,对服务器进行压测,查看整体情况

想要看到具体的线程在执行什么代码,thread -n 5打印当前最忙的5个 线程,看他们在干什么,先压测,然后执行该命令

找到这种程度,接下来的事情交给开发了,让java开发,去检查对应代码 ,例如这里时175行,让开发看175代码的逻辑,为什么占用了这么多的cpu资源,代码能不能优化。

测试过程中可能是 java、go,c语言,交给专业的人员,测试不能去改代码,业务逻辑出问题,那就是自己给自己强行背锅了。

这里可以看到,该线程在执行代码时占用了15.25%的cpu资源,占用的资源比较多,通过内容描述,开发可以具体定位到那一行代码,类方法,接口,占用了较大的资源。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

当代键仙

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值