JVM体系-性能调优

因为第三章是《深入理解JVM》,《深入理解Java虚拟机》中对这部分的阐述更完整,所以在另一篇博客上对这部分进行梳理。本博客主要对各种性能调优参数进行记录。

堆大小设置:

-Xms:设置堆的最小可用内存
-Xmx:设置堆的最大可用内存
-Xmn:设置新生代的大小
-XX:PretenureSizeThreshold设置进入老年代的对象容量最小大小
-Xss:设置每个线程的堆栈大小
-XX:NewRatio:设置年轻代(包括Eden和两个Survivor区)与年老代的比值
-XX:survivorRatio:设置新生代中Eden区和两个survivor区的比值。-XX:survivorRatio=8表示E:S:S=8:1:1
-XX:PermSize:表示非堆区(永久代)初始内存分配大小,默认为物理内存的1/64
-XX:MaxPermSize 设置永久代最大值,默认为物理内存的1/4
-XX:MaxTenuringThreshold=10:设置新生代中对象晋升到老年代中的年龄阈值。
-XX:+/-UseTLAB:开启虚拟机使用TLAB

回收器选择

在这里插入图片描述

  1. -XX:MaxGCPauseMillis=100:设置每次年轻代垃圾回收的最长时间,如果无法满足此时间,JVM会自动调整年轻代大小,以满足此值。
    
  2. -XX:ParallelGCThreads=20:配置并行收集器的线程数,即:同时多少个线程一起进行垃圾回收。此值最好配置与处理器数目相等。
    
  3. -XX:MetaspaceSize:设置元空间大小。、-XX:MaxMetaspaceSize:设置元空间最大允许大小,默认不受限制,JVM Metaspace会进行动态扩展。
    

JVM内存状况查看方法参数

  1.  输出GC日志到控制台:-XX:+PrintGc、-XX:+PrintGCDetails、-XX:+PrintGcTimeStamps、-XX:+PrintGcApplicationStoppedTime。打印gc简要信息、详细信息、时间信息、gc造成的应用暂停时间
    
  2. 输出到指定的文件:-Xloggc:gc.log将指定的gc信息输出到gc.log文件。-verbose:gc、-XX:+PrintTenuringDistribution这两个是用来GC跟踪分析的参数

线程状态分析的一些操作

  1.  kill -3 [pid],在Linux根据进程号执行操作,将线程相关信息输出到console上	 
    
  2.  top命令,查看CPU资源的消耗情况,其中内容包括us(用户进程处理占比)、sy(内核线程处理占比)、ni(被nice命令改变优先级的任务占比)、id(cpu空闲时间占比)、wa(等待io占比)
    
  3.  pidstat:pidstat 1 2,在控制台每隔1s输出当前活动进程的cpu消耗状况,共2次
    

性能调优的步骤在这里插入图片描述

1.衡量系统现状。当前系统的请求次数、响应时间、资源消耗等信息
2.设定调优目标:根据用户能接受的响应速度或系统拥有的机器以及所支持的用户量制定。
3.寻找性能瓶颈:找出造成当前系统性能不足的瓶颈点
4.性能调优:根据分析场景需求,结合一些优化的技巧指定优化策略。
5.衡量是否达标:如果达标就结束此次调优,不达标则继续看是否产生了新的性能瓶颈。

CPU消耗分析

CPU主要用来中断、内核、用户进程的任务处理。三个重要概念:

  1. 上下文切换:线程采用分配时间片轮转的方式运行,同一时间每个CPU只能有一个线程运行,线程到达运行时间就会进行切换,同时保存当前线程的执行状态,恢复即将执行的线程的状态。 典型场景:文件IO操作,网络IO操作,锁等待,线程进入阻塞或休眠。
  2. 运行队列:每个CPU核都维护一个可运行的线程队列,队列值越大表示线程消耗越长时间才能执行完。8个线程4个CPU,则每个CPU运行队列有2个线程
  3. 利用率:CPU在用户进程、内核、中断处理、IO等待、空闲的使用百分比。

几个CPU消耗问题(性能瓶颈):

  1. us过高:us值过高表示应用消耗了大部分的CPU,执行时没有挂起动作,导致CPU没机会去调度执行其他线程,导致线程饿死的现象。应该找到具体消耗CPU的线程执行的代码
    原因:1.线程一直处于可运行状态。
    2.频繁的GC造成。每次请求都分配很多资源,导致访问量大的时候内存不足,导致不断GC。
    解决方法:1.通过Linux命令找到消耗CPU严重的线程和ID,将ID转为16进制
    2.通过 kill -3 [pid] 或者jstack来dump 出应用线程的信息
    3.通过第1步的十六进制值找到对应的nid值得线程,进行优化处理。
    4.对线程进行优化处理,如添加 Thread.Sleep,来释放CPU执行权,降低Cpu消耗
  2. sy值过高:表示花费了大量时间在线程切换上
    原因:1.启动的线程比较多,且线程多数处于不断阻塞和执行的变化过程,导致需要不断切换执行的线程,大量的上下文切换。2.锁竞争激烈
    解决方法:1.通过kill -3 [pid] 或者jstack来dump 出应用线程的信息
    2.查看线程的状态信息、锁信息,来找出等待状态或者锁竞争过多的线程进行优化。
    对于为了高并发而启动大量线程的情况,可以采用协程来支持更高的并发量。
    (补充)协程:协程是比线程更轻量的存在,一个线程可以有多个协程,且只能在一个线程内执行,协程并没有增加线程数量,只是在线程的基础之上通过分时复用的方式运行多个协程,而且协程的切换在用户态完成,切换的代价比线程从用户态到内核态的代价小很多。
  3. 文件IO消耗分析:将数据放入文件缓存区,直到内存不够或者需要释放内存给用户进程。这样导致可用 的物理内存不多但是缓存用了很多。
    造成IO消耗严重的原因:1.多个线程在写大量的数据到同一文件,导致文件变的很大。
    2.磁盘设备本身处理速度慢
    3.文件系统慢或者操作文件本身就很大
    怎么跟踪文件IO消耗情况? 1.pidstat指令 如 pidstat -d -t -p [pid] 1 100
    2.iostat 直接输入这个指令查看设备的IO历史状况
    1.先通过pidstat直接找到文件IO操作多的线程
    2.用jstack来dump出线程信息,找到对应的代码进行优化
    解决方法:1.异步写文件。将写文件的同步动作改成异步动作,避免应用由于写文件慢而性能下降太多
    2.批量读写
    3.限流:将IO消耗控制到一个能接受的范围。当超过这个范围后,可以一段时间内部写或者塞入到队列中
    4.限制文件大小:为文件设置大小,超出范围就生成新的文件。
    5.采用缓冲区的方式来读取文件内容,避免不断的与系统进行交互。
  4. 网络IO消耗分析
    原因:1.同时需要接受或者发送的包太多
    2.内存消耗方面,消耗了过多的堆内存
    命令参数: 通过 sar指令来分析网络IO的消耗状况
    sar -n FULL 1 2 ,执行以1s为频率,输出两次网络IO的消耗情况
    解决办法:原因1:限流,限制发送包的频率
    原因2: 1.释放不必要的引用:代码持有了不需要的对象引用导致对象不能被gc
    2.使用对象缓存池:创建对象实例要消耗一定的cpu和内存,可以使用对象缓存池来降低堆内存的使用
    3.采用合理的缓存失效算法:如果缓存池放入太多对象且一直持有反而导致full gc增多,可以采用FIFO、LRU、LFU算法来控制缓存池的对象数目。
    4.合理使用SoftReference和WeakReference,因为这两个引用在内存不足的时候会被回收,可以减少堆内存的消耗。
  5. 内存消耗分析:查看swap和物理内存消耗的指令:
    1.vmstat:在这里插入图片描述
    2.sar:查看内存的消耗状况 sar -r 查看内存的消耗状况
    3.top:查看进程消耗的内存量,即cup资源消耗情况
    4.pidstat:查看进程消耗的内存量
    **对物理内存的消耗:**通过top命令和jstat来查看进程占用内存的大小
    对JVM内存的消耗:1.如果是JVM外的物理内存,就分析程序的线程数和直接内存的使用情况
    2.如果是堆,结合分析工具来分析程序具体对象占用内存情况。

程序执行慢的原因

资源消耗不多但程序执行慢。

  1. 锁竞争激烈:数据库连接池只有10个,但线程数有50个,导致其他40个需要去等待资源。
    解决方法:1.使用并发包中的类:多采用lock-free、nonblocking算法来减少多线程下的资源的锁竞争
    2.Treiber算法
    3.使用Michael-Scott非阻塞队列算法:CAS+AutomicReference
    4.减少锁的使用和锁粒度
    5.拆分锁,把独占锁拆分成多把锁如ConcurrentHashMap
    6.去除读写操作的互斥锁,在修改对象时加锁,并复制对象进行修改,修改完毕后将对象 指针指向最新的对象,读取的时候就不加锁–copyOnWrite
  2. 未充分使用硬件资源:比如双核CPU中只有单线程串行操作,导致CPU资源没充分利用
  3. 数据量增长:数据量大幅度上涨后导致资源的读写速度下降。

调优

1.JVM调优

JVM调优主要是内存管理的调优,包括代的大小、GC策略等等
  1. 代大小的调优在这里插入图片描述
    1.避免新生代大小设置太小:会导致一、minor GC的次数更加频繁。二、可能导致minor gc的对象直接进入老年代,占据老年代的空间,导致触发full gc。
    2.避免新生代大小设置太大:导致一、老年代变小,可能导致full gc 频繁。二、minor gc 耗时增加
    3.避免survivor区过大或者过小:-XX:survivorRatio过大表示 Eden区变大,导致survivor过小,可能导致超出survivor空间的对象没有被minor gc回收而直接进入老年代。-XX:survivorRatio过小表示Eden区过小,minor gc的触发次数增加,但是survivor区变大表示可以存储更多的minor gc 后存活的对象,避免它进入老年代。
    4.合理设置新生代的存活周期:设置-xx:MaxTenuringThreshold的值,来改变新生代晋升到老年代的年龄阈值。
  2. GC调优 :主要配合文章开始 ”回收器的选择“ 部分进行参数设置,垃圾回收器的选择。
  3. 程序调优:同上 “cpu消耗问题(性能瓶颈)”
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值