Java教程

JVM性能调优

本文主要是介绍JVM性能调优,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

文章目录

  • 前言
  • 性能调优难吗?
  • jvm调优需要懂那些知识?
    • 什么是垃圾?
    • 如何判定是垃圾?
    • jvm的内存模型?
    • jvm有那些垃圾回收算法?
    • jvm有那些垃圾回收器?
  • 找出性能问题
  • 相关命令和参数
    • jdk自动工具
    • 设定堆内存大小
    • 设定新生代大小
    • 设定垃圾回收器
    • 其他常用参数
  • 总结


前言

java是一门内存垃圾自动回收语言,大部分时候我们都是通过new关键字来创建一个java对象,然而我们并不知道它在底层是如何申请内存和释放内存,归根结底这个强大的功能是由jvm来完成。对于新手经常出现这样的问题,刚开始开发一个项目时接口响应时间很快感觉没有任何问题,但是上线一段时间后,发现程序的响应时间越来越慢,甚至有时出现卡顿现象,通俗的讲这样的问题就是性能调优的问题。为了更好的理解本文采用问答的方式进行讨论。

性能调优难吗?

答: 难,因为性能调优是综合解决方案,主要体现在:(1)代码优劣问题,(2)业务逻辑是否有待优化,(3)架构是否存在性能问题、(4)高并发下数据库是否已实现三高架构,(4)是否合理使用缓存,(5)是否做了限流熔断,(6)是否做了JVM性能调优等很多问题。所有性能调优一般是架构师负责的。本主要讨论JVM性能调优。

jvm调优需要懂那些知识?

jvm调优主要是垃圾回收的调优,所以在调优之前必须知道什么是垃圾?如何判定是垃圾?jvm的内存模型?jvm有那些垃圾回收算法?jvm有那些垃圾回收器?等知识点。

什么是垃圾?

当前创建一个对象用完之后,没有任何地方引用,则这个对象就是垃圾对象。

如何判定是垃圾?

jvm采用引用计数算法(无法解决循环依赖问题)和可达性分析算法来判断某一个对象是否是垃圾(Hostport 采用这种算法),更多细节请阅读《垃圾回收算法》

jvm的内存模型?

jvm将内存分为线程私有和线程共享两大类,其中线程私有分为java虚拟机栈,本地方法栈,程序计数器它们的生命周期和线程一致,另一部分是java堆,方法区(主要用来存放,一些元数据,常量池,静态常量等固定不变的数据),所以java堆才是我们主要调优的部分。对jvm内存模型不是很了解请阅读《java虚拟机内存模型》。

jvm有那些垃圾回收算法?

目前为止常见的jvm垃圾回收算法有,标记清除算法标记复制算法标记整理算法,各种自己的优缺点。更多细节请阅读《垃圾回收算法》

jvm有那些垃圾回收器?

jdk从1.0到现在经过了20几年的发展,研发了很多种垃圾回收器,一个比一个先进也更复杂

名称搭配作用作用区域
SerialSerial Old 、ParNew Old、CMS单线程采用标记清除算法年轻代
Serial OldParNew单线程采用标记整理算法老年代
ParNewParNew Old、Serial Old 、CMS多线程版的 Serial年轻代
ParNew OldParNew、Serial多线程版的 Serial Old老年代
Parallel ScavengeParallel Old 、Serial Old并行方式垃圾回收,主要关注吞吐量年轻代
Parallel OldParallel Scavenge并行方式垃圾回收老年代
CMSParNew、 Serial并发标记算法,主要关注停顿时间老年代
G1JDK9后才出现,将堆内存分为多个Regoion区域的全新内存模型
ZGC这个最强的垃圾回收器,在JDK16后可以使用

找出性能问题

本文主要讲解在linux环境下调优,假设当前系统只运行一个java程序。先确认出现问题的原因,则查看硬件资源,使用top命令查看CPU和内存的使用情况。

  • 1、内存已满,需要添加内存,或对项目进行负载均衡
  • 2、内存空闲还很多
  1. 使用 jps -lmv 查看java进程pid
  2. 使用 jmap -heap pid 当前java程序的对内存情况
  3. 分析当前是发生什么Minor GC 和 Full GC 次数和时间
    3.1 发生Full GC次数频繁,可能出现原因:(1)出现内存泄漏,(2)Survivor 区域内存太小,当发生YGC时存活对象直接从End区进入老年区,(3)老年代old内存太小。出现(1)时说明有对象没有被回收它的数量会递增使用jmap -histo pid 数量大或占用空间大的对象,然后到程序代码中去看修改代码;出现(2)时将 Survivor 大小比例调大,保证大部分的朝生夕灭的垃圾在年轻代就已回收;出现(3)情况时应该将整个堆大小增大,保证老年代有足够的空间。
    3.2 Minor GC过于频繁时适当调整新生代的大小(由于GC时间和内存大小成反比关系,所以年轻代的大小需要根据自己的业务找到这个平衡点)
  • 3 CPU的利用率很高
  1. 使用arthas (arthas 是阿里巴巴的一个开源工具)thread -n 10 查看当前程序进程使用CPU最高的前10个线程。
  2. 如果这些线程大部分是GC线程说明当前是频繁GC状态,需要根据第二步来适当调整堆内存大小,减少GC的次数。
  3. 如果是程序的业务线程,thread -b 查看线程的阻塞情况,最后到代码中确认是当前操作IO型还是计算密集型,该升配置的还是要升

相关命令和参数

jdk自动工具

  • jsp 列出正在运行的虚拟机进程
  • jstat 监视虚拟机运行状态信息
  • jmap 查询对信息和生成堆存储快照
  • jstack 生成虚拟机当前时刻的线程快照,帮助定位线程出现长时间停顿的原因 (这个一般用arthas工具替代)

设定堆内存大小

  • -Xms:启动JVM时的堆内存空间。
  • -Xmx:堆内存最大限制。
  • -Xmn:设置年轻代大小整个堆大小=年轻代大小 + 年老代大小 + 持久代大小,Sun官方推荐配置为整个堆的3/8
  • -XX:PermSize=128M设置持久代大小
  • -XX:MaxPermSize=128M设置持久代最大值,此值可以设置与-XX:PermSize相同,防止持久代内存伸缩,持久代设置很重要,一般预留其使用空间的1/3.

设定新生代大小

  • -XX:NewRatio:新生代和老年代的占比。
  • -XX:NewSize:新生代空间。
  • -XX:SurvivorRatio:伊甸园空间和幸存者空间的占比。
  • -XX:MaxTenuringThreshold:对象进入老年代的年龄阈值。

设定垃圾回收器

  • -XX:+UseSerialGC 开启串行收集器
  • -XX:+UseParallelGC 开启年轻代并行收集器,JDK5.0以上,JVM会根据系统配置自行设置,所以无需再设置此值
  • -XX:+UseParallelOldGC开启老年代并行收集器
  • -XX:+UseConcMarkSweepGC开启老年代并发收集器(简称CMS),可以和UseParallelGC一起使用
  • -XX:CMSInitiatingOccupancyFraction=70老年代内存使用比例到多少激活CMS收集器,这个数值的设置有很大技巧基本上满足(Xmx-Xmn)*(100-CMSInitiatingOccupancyFraction)/100>=Xmn否则会出现“Concurrent Mode Failure”,promotionfailed,官方建议数值为68
  • -XX:+UseCMSCompactAtFullCollection:使用并发收集器时,开启对年老代的压缩

其他常用参数

  • -Xss: 设置每个线程的堆栈大小,设置每个线程的堆栈大小。JDK5.0以后每个线程堆栈大小为1M,以前每个线程堆栈大小为256K。更具应用的线程所需内存大小进行调整。在相同物理内存下,减小这个值能生成更多的线程。但是操作系统对一个进程内的线程数还是有限制的,不能无限生成,经验值在3000~5000左右,这个参数对性能的影响比较大的
  • -XX:MaxTenuringThreshold=0:设置垃圾最大年龄。如果设置为0的话,则年轻代对象不经过Survivor区,直接进入年老代。对于年老代比较多的应用,可以提高效率。如果将此值设置为一个较大值,则年轻代对象会在Survivor区进行多次复制,这样可以增加对象再年轻代的存活时间,增加在年轻代即被回收的概论,linux64的java6默认值是15
  • -XX:ParallelGCThreads=设置并行垃圾回收的线程数。此值可以设置与机器处理器数量相等(逻辑cpu数),这个不确定是物理、还是逻辑使用默认就好
  • -XX:MaxGCPauseMillis=指定垃圾回收时的最长暂停时间,单位毫秒,如果指定了此值的话,堆大小和垃圾回收相关参数会进行调整以达到指定值,设定此值可能会减少应用的吞吐量
  • -XX:GCTimeRatio=设定吞吐量为垃圾回收时间与非垃圾回收时间的比值,公式为1/(1+N)。例如,-XX:GCTimeRatio=19时,表示5%的时间用于垃圾回收。默认情况为99,即1%的时间用于垃圾回收
  • -XX:+UseAdaptiveSizePolicy:设置此选项后,并行收集器会自动选择年轻代区大小和相应的Survivor区比例,以达到目标系统规定的最低相应时间或者收集频率等,此值建议使用并行收集器时,一直打开
  • -XX:+DisableExplicitGC:禁止 java 程序中的 full gc, 如System.gc() 的调用. 最好加上, 防止程序在代码里误用了对性能造成冲击
  • -XX:+PrintGCDetails 打应垃圾收集的情况
  • -XX:+PrintGCTimeStamps 打应垃圾收集的情况
  • -XX:+PrintGCApplicationConcurrentTime:打印每次垃圾回收前,程序未中断的执行时间。可与上面混合使用
  • -XX:+PrintGCApplicationStoppedTime 打应垃圾收集时 , 系统的停顿时间
  • -XX:+PrintGC 打印GC情况
  • -XX:PrintHeapAtGC:打印GC前后的详细堆栈信息

总结

性能调优是一门很需要技术的工作,同一个程序不同环境调优方式也有可能不同,所以性能调优不仅要知道原理还需要和丰富的经验相结合

这篇关于JVM性能调优的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!