编程进阶网编程进阶网
  • 基础组成体系
  • 程序编程原理
  • 异常和IO系统
  • 六大设计原则
  • 设计模式导读
  • 创建型设计模式
  • 结构型设计模式
  • 行为型设计模式
  • 设计模式案例
  • 面向对象思想
  • 基础入门
  • 高级进阶
  • JVM虚拟机
  • 数据集合
  • Java面试题
  • C语言入门
  • C综合案例
  • C标准库
  • C语言专栏
  • C++入门
  • C++综合案例
  • C++专栏
  • HTML
  • CSS
  • JavaScript
  • 前端专栏
  • Swift
  • iOS入门
  • 基础入门
  • 开源库解读
  • 性能优化
  • Framework
  • 方案设计
  • 媒体音视频
  • 硬件开发
  • Groovy
  • 常用工具
  • 大厂面试题
  • 综合案例
  • 网络底层
  • Https
  • 网络请求
  • 故障排查
  • 专栏
  • 数组
  • 链表
  • 栈
  • 队列
  • 树
  • 递归
  • 哈希
  • 排序
  • 查找
  • 字符串
  • 其他
  • Bash脚本
  • Linux入门
  • 嵌入式开发
  • 代码规范
  • Markdown
  • 开发理论
  • 开发工具
  • Git管理
  • 百宝箱
  • 开源协议
  • 技术招聘
  • 测试经验
  • 职场提升
  • 技术模版
  • 关于我
  • 目标清单
  • 学习框架
  • 育儿经验
  • 我的专栏
  • 底层能力
  • 读书心得
  • 随笔笔记
  • 职场思考
  • 中华历史
  • 经济学故事
  • 基础组成体系
  • 程序编程原理
  • 异常和IO系统
  • 六大设计原则
  • 设计模式导读
  • 创建型设计模式
  • 结构型设计模式
  • 行为型设计模式
  • 设计模式案例
  • 面向对象思想
  • 基础入门
  • 高级进阶
  • JVM虚拟机
  • 数据集合
  • Java面试题
  • C语言入门
  • C综合案例
  • C标准库
  • C语言专栏
  • C++入门
  • C++综合案例
  • C++专栏
  • HTML
  • CSS
  • JavaScript
  • 前端专栏
  • Swift
  • iOS入门
  • 基础入门
  • 开源库解读
  • 性能优化
  • Framework
  • 方案设计
  • 媒体音视频
  • 硬件开发
  • Groovy
  • 常用工具
  • 大厂面试题
  • 综合案例
  • 网络底层
  • Https
  • 网络请求
  • 故障排查
  • 专栏
  • 数组
  • 链表
  • 栈
  • 队列
  • 树
  • 递归
  • 哈希
  • 排序
  • 查找
  • 字符串
  • 其他
  • Bash脚本
  • Linux入门
  • 嵌入式开发
  • 代码规范
  • Markdown
  • 开发理论
  • 开发工具
  • Git管理
  • 百宝箱
  • 开源协议
  • 技术招聘
  • 测试经验
  • 职场提升
  • 技术模版
  • 关于我
  • 目标清单
  • 学习框架
  • 育儿经验
  • 我的专栏
  • 底层能力
  • 读书心得
  • 随笔笔记
  • 职场思考
  • 中华历史
  • 经济学故事
  • 8.1彻底搞透彻内存模型
  • 8.2JVM内存回收清理机制
  • 8.3JVM监控和故障处理
  • 8.4JVM如何执行方法调用
  • 8.5JVM如何实现invoked
  • 8.6JVM性能调优的策略
  • 8.7内存上升排查和优化
  • 8.8JVM即时编译器原理
  • 8.9JVM性能如何调优实践

8.6JVM性能调优的策略

目录介绍

  • 01.性能调优重要性
  • 02.性能调优感受和心得
  • 03.制定性能调优标准
    • 3.1 为什么要做性能调优
    • 3.2 什么时候介入调优
    • 3.3 有哪些参考因素
    • 3.4 响应时间衡量标准
    • 3.5 资源分配使用率
  • 04.制定性能调优分析
    • 4.1 制定调优总思路
    • 4.2 微基准性能测试
    • 4.3 宏基准性能测试
    • 4.4 为何性能测试不稳定
    • 4.5 输出性能分析报告
  • 05.性能调优可以做那些
    • 5.1 应用优化策略
    • 5.2 系统优化策略
    • 5.3 时间/空间调优策略

01.性能调优重要性

03.制定性能调优标准

3.1 为什么要做性能调优

  • 一款线上产品如果没有经过性能测试,那它就好比是一颗定时炸弹,你不知道它什么时候会出现问题,你也不清楚它能承受的极限在哪儿。
  • 比如,当你在用某一款 App 查询某一条信息时,需要等待十几秒钟;在抢购活动中,无法进入活动页面等等。你看,系统响应就是体现系统性能最直接的一个参考因素。

3.2 什么时候介入调优

  • 在项目开发的初期
    • 在代码层面保证有效的编码,比如,减少磁盘 I/O 操作、降低竞争锁的使用以及使用高效的算法等等。遇到比较复杂的业务,充分利用设计模式来优化业务代码。
  • 在系统编码完成之后
    • 我们就可以对系统进行性能测试了。这时候,我们在提供的参考平台上进行压测,通过性能分析、统计工具来统计各项性能指标,看是否在预期范围之内。
  • 在项目成功上线后
    • 我们还需要根据线上的实际情况,依照日志监控以及性能统计日志,来观测系统性能问题,一旦发现问题,就要对日志进行分析并及时修复问题。

3.3 有哪些参考因素

  • 多次讲到了性能指标,那么性能指标到底有哪些呢?
    • 在我们了解性能指标之前,我们先来了解下哪些计算机资源会成为系统的性能瓶颈。分别是CPU,内存,I/O流,网络,异常,锁等等。
  • CPU:
    • 有的应用需要大量计算,他们会长时间、不间断地占用 CPU 资源,导致其他资源无法争夺到 CPU 而响应缓慢,从而带来系统性能问题。
    • 例如,代码递归导致的无限循环,正则表达式引起的回溯,JVM 频繁的 FULL GC,以及多线程编程造成的大量上下文切换等,这些都有可能导致 CPU 资源繁忙。
  • 内存:
    • Java 程序一般通过 JVM 对内存进行分配管理,主要是用 JVM 中的堆内存来存储 Java 创建的对象。系统堆内存的读写速度非常快,所以基本不存在读写性能瓶颈。
    • 但是由于内存成本要比磁盘高,相比磁盘,内存的存储空间又非常有限。所以当内存空间被占满,对象无法回收时,就会导致内存溢出、内存泄露等问题。
  • 磁盘 I/O:
    • 磁盘相比内存来说,存储空间要大很多,但磁盘 I/O 读写的速度要比内存慢,仍然无法与内存的读写速度相提并论。
  • 网络:
    • 网络对于系统性能来说,也起着至关重要的作用。如果你购买过云服务,一定经历过,选择网络带宽大小这一环节。
    • 带宽过低的话,对于传输数据比较大,或者是并发量比较大的系统,网络就很容易成为性能瓶颈。
  • 异常:
    • Java 应用中,抛出异常需要构建异常栈,对异常进行捕获和处理,这个过程非常消耗系统性能。
    • 如果在高并发的情况下引发异常,持续地进行异常处理,那么系统的性能就会明显地受到影响。
  • 数据库:
    • 大部分系统都会用到数据库,而数据库的操作往往是涉及到磁盘 I/O 的读写。大量的数据库读写操作,会导致磁盘 I/O 性能瓶颈,进而导致数据库操作的延迟性。
  • 锁竞争:
    • 在并发编程中,我们经常会需要多个线程,共享读写操作同一个资源,这个时候为了保持数据的原子性,我们就会用到锁。锁的使用可能会带来上下文切换,从而给系统带来性能开销。
    • JDK1.6 之后,Java 为了降低锁竞争带来的上下文切换,对 JVM 内部锁已经做了多次优化,例如,新增了偏向锁、自旋锁、轻量级锁、锁粗化、锁消除等。
    • 而如何合理地使用锁资源,优化锁资源,就需要你了解更多的操作系统知识、Java 多线程编程基础,积累项目经验,并结合实际场景去处理相关问题。

3.4 响应时间衡量标准

  • 响应时间是衡量系统性能的重要指标之一,响应时间越短,性能越好,一般一个接口的响应时间是在毫秒级。在系统中,我们可以把响应时间自下而上细分为以下几种:
    • 服务端响应时间:服务端包括 Nginx 分发的请求所消耗的时间以及服务端程序执行所消耗的时间;
    • 网络响应时间:这是网络传输时,网络硬件需要对传输的请求进行解析等操作所消耗的时间;
    • 客户端响应时间:对于普通的 Web、App 客户端来说,消耗时间是可以忽略不计的,但如果你的客户端嵌入了大量的逻辑处理,消耗的时间就有可能变长,从而成为系统的瓶颈。

3.5 资源分配使用率

  • 通常由 CPU 占用率、内存使用率、磁盘 I/O、网络 I/O 来表示资源使用率。
    • 这几个参数好比一个木桶,如果其中任何一块木板出现短板,任何一项分配不合理,对整个系统性能的影响都是毁灭性的。

04.制定性能调优策略

4.1 制定调优总思路

  • 面对日渐复杂的系统,制定合理的性能测试,可以提前发现性能瓶颈,然后有针对性地制定调优策略。
    • 总结一下就是“测试 - 分析 - 调优”三步走。

4.2 微基准性能测试

  • 微基准性能测试可以精准定位到某个模块或者某个方法的性能问题
    • 特别适合做一个功能模块或者一个方法在不同实现方式下的性能对比。例如,对比一个方法使用同步实现和非同步实现的性能。

4.3 宏基准性能测试

  • 宏基准性能测试是一个综合测试,需要考虑到测试环境、测试场景和测试目标。
    • 看测试场景。需要确定在测试某个接口时,是否有其他业务接口同时也在平行运行,造成干扰。如果有,请重视,因为你一旦忽视了这种干扰,测试结果就会出现偏差。

4.4 为何性能测试不稳定

  • 在做性能测试时发现,每次测试处理的数据集都是一样的,但测试结果却有差异。
    • 这是因为测试时,伴随着很多不稳定因素,比如机器其他进程的影响、网络波动以及每个阶段 JVM 垃圾回收的不同等等。
    • 我们可以通过多次测试,将测试结果求平均,或者统计一个曲线图,只要保证我们的平均值是在合理范围之内,而且波动不是很大,这种情况下,性能测试就是通过的。

4.5 输出性能分析报告

  • 在完成性能测试之后,需要输出一份性能测试报告,帮我们分析系统性能测试的情况。
    • 其中测试结果需要包含测试接口的平均、响应时间,CPU、内存、I/O、网络 IO 使用率,JVM 的 GC 频率等。
  • 通过观察这些调优标准,可以发现性能瓶颈,再通过自下而上的方式分析查找问题。
    • 首先从操作系统层面,查看系统的 CPU、内存、I/O、网络的使用率是否存在异常,再通过命令查找异常日志,最后通过分析日志,找到导致瓶颈的原因;
    • 还可以从 Java 应用的 JVM 层面,查看 JVM 的垃圾回收频率以及内存分配情况是否存在异常,分析日志,找到导致瓶颈的原因。
    • 如果系统和 JVM 层面都没有出现异常情况,我们可以查看应用服务业务层是否存在性能瓶颈,例如 Java 编程的问题、读写数据瓶颈等等。

05.性能调优可以做那些

5.1 应用优化策略

  • 优化代码
    • 应用层的问题代码往往会因为耗尽系统资源而暴露出来。例如,我们某段代码导致内存溢出,往往是将 JVM 中的内存用完了,这个时候系统的内存资源消耗殆尽。
  • 优化设计
    • 面向对象有很多设计模式,可以帮助我们优化业务层以及中间件层的代码设计。优化后,不仅可以精简代码,还能提高整体性能。
  • 优化算法
    • 好的算法可以帮助我们大大地提升系统性能。例如,在不同的场景中,使用合适的查找算法可以降低时间复杂度。

5.2 系统优化策略

  • 操作系统调优
  • JVM调优

5.3 时间/空间调优策略

  • 时间换空间
    • 有时候系统对查询时的速度并没有很高的要求,反而对存储空间要求苛刻,这个时候我们可以考虑用时间来换取空间。
  • 空间换时间
贡献者: yangchong211
上一篇
8.5JVM如何实现invoked
下一篇
8.7内存上升排查和优化