【Java虚拟机】JVM调优和分析案例综合实战

本文涉及的产品
性能测试 PTS,5000VUM额度
日志服务 SLS,月写入数据量 50GB 1个月
简介: 【Java虚拟机】JVM调优和分析案例综合实战

1.什么是JVM性能优化

jvm性能优化涉及到两个很重要的概念:吞吐量和响应时间。jvm调优主要是针对他们进行调整优化,达到一个理想的目标,根据业务确定目标是吞吐量优先还是响应时间优先。

吞吐量:用户代码执行时间/(用户代码执行时间+GC执行时间)。

响应时间:整个接口的响应时间(用户代码执行时间+GC执行时间),STW时间越短,响应时间越短。

调优方法论

监控JVM性能

对JVM的运行情况进行监控,以了解应用程序的瓶颈和性能瓶颈

可以使用JVM自带的工具,如jstat、jmap、jstack等,或者第三方工具,如VisualVM、JProfiler等

压测基准指标

对程序进行压测,得出接口对应的吞吐量、响应时间等

外部现象

对用户体验来说,就是响应速度

可以用压测工具jmeter进行压测得出相关性能指标

内部现象:

分析GC情况,是JVM性能调优的重要因素,需要掌握GC的工作机制和GC日志的含义

可以使用JVM自带的GC日志或者第三方工具,如GCEasy等来分析GC情况,了解GC的频率、时间、内存占用等情况

调整JVM参数

通过调整堆大小、GC算法、线程池大小等参数来提高应用程序的性能

注意:不同的应用程序和环境可能需要不同的JVM参数配置,比如IO密集型和CPU密集型应用

二次压测分析

通过调整jvm参数后,二次压测看性能指标提升还是下降

内部:GC日志,看吞吐量,GC次数,停顿时间变化

外部:接口对应的吞吐量、响应时间是否更优

其他优化方式

优化代码

通过避免不必要的对象创建、减少同步操作、使用缓存等方式来优化代码。

注意:代码优化应该遵循“先正确,再优化”的原则,不应该牺牲代码的可读性和可维护性

使用并发编程

使用多线程、线程池等方式来提高并发性能,比如调整线程池的队列长度,存活线程数量等

注意:并发编程需要考虑线程安全和锁竞争等问题,需要进行正确的设计和实现

使用缓存

可以使用本地缓存、分布式缓存等方式来提高数据访问性能

注意:缓存需要考虑缓存一致性和缓存失效等问题,需要进行正确的设计和实现

避免IO阻塞

使用异步IO、NIO等方式来提高IO性能,比如前面学的CompletableFuture异步任务编排

注意:IO编程需要考虑并发性和可靠性等问题,需要进行正确的设计和实现

分布式+集群技术

使用负载均衡+集群技术,提升单节点的处理能力

2.JVM调优之压测环境准备

  • SpringBoot 编写的jar的程序,接口一个返回随机组成的100个以内的对象的list (使用JDK17)
/**
 * @author lixiang
 * @date 2023/5/8 21:44
 */
@Slf4j
@RestController
@RequestMapping("/spring-test")
public class SpringTestController {
    @RequestMapping("query")
    public Map<String, Object> query() throws InterruptedException {
        int num = (int) (Math.random() * 100) + 1;
        //申请5MB内存
        Byte[] bytes = new Byte[5 * 1024 * 1024];
        List<Product> productList = new ArrayList<>();
        for (int i = 0; i < num; i++) {
            Product product = new Product();
            product.setPrice((int) Math.random() * 100);
            product.setTitle("商品编号" + i);
            productList.add(product);
        }
        Thread.sleep(5);
        Map<String, Object> map = new HashMap<>(16);
        map.put("data", productList);
        return map;
    }
}
  • Jmeter压测工具准备,测试计划 200并发,循环500次

3.JVM性能优化之堆大小配置

  • 堆大小配置,FullGC次数的性能影响
  • 性能优化初始值
-Xms1g # 配置初始堆内存1G
-Xmx1g # 配置最大堆内存1G
-XX:+UseG1GC # 使用G1回收器
-XX:MaxGCPauseMillis=200 # 设置最大停顿时间200ms
-XX:G1HeapRegionSize=32M # 设置G1每个region块大小为32M
-XX:ActiveProcessorCount=8 # 设置JVM使用的CPU核数限制为8
-XX:+HeapDumpOnOutOfMemoryError # 当JVM发生OOM时,自动生成DUMP文件
-XX:HeapDumpPath=heapdump.hprof # DUMP文件路径
-XX:+PrintCommandLineFlags # 监控开启
-Xlog:gc=info:file=portal_gc.log:utctime,level,tags:filecount=50,filesize=100M 
  # Xlog:指定日志输出方式为日志文件。
  # gc*:指定日志输出类型为GC相关的日志。
  # info:指定输出日志的级别为info级别。
  # file=portal_gc.log:指定日志输出的文件名为portal_gc.log。
  # utctime:指定日志输出的时间戳使用UTC时间。
  # level,tags:指定日志输出的格式包含级别和标签信息。
  # filecount=50:指定最多保存50个日志文件。
  # filesize=100M:指定每个日志文件的大小为100MB。
  • 机器配置为:8核16G 500M带宽

3bbb957363654f62a6802252f09be29c.jpg

  • 设置初始堆内存和最大堆内存为1G,压测
nohup java -jar spring-test-1.0-SNAPSHOT.jar -Xms1g -Xmx1g -XX:+UseG1GC -XX:MaxGCPauseMillis=200 -XX:G1HeapRegionSize=32M -XX:ActiveProcessorCount=8 -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/usr/local/jmeter/heapdump.hprof -XX:+PrintCommandLineFlags -Xlog:gc=info:file=/usr/local/jmeter/portal_gc.log:utctime,level,tags:filecount=50,filesize=100M &


19622210a94047b69df8479d11e56281.jpg


image.jpeg

当我们设置堆内存为1G的时候,整体的吞吐量为40%以上,这已经很低了,期间Young GC发生了7451次,Full GC发生了142次

  • 设置初始堆内存和最大堆内存为2G,压测
nohup java -jar spring-test-1.0-SNAPSHOT.jar -Xms2g -Xmx2g -XX:+UseG1GC -XX:MaxGCPauseMillis=200 -XX:G1HeapRegionSize=32M -XX:ActiveProcessorCount=8 -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/usr/local/jmeter/heapdump.hprof -XX:+PrintCommandLineFlags -Xlog:gc=info:file=/usr/local/jmeter/portal_gc.log:utctime,level,tags:filecount=50,filesize=100M &

769a9244797b45309d7362166dc4899f.jpg

7a19f3a8f6534eb68083dfe1b9867b48.jpg

当把堆内存设置为4G的时候,整体的吞吐量提升到76%,Young GC发生了504,一次Full GC都没有发生。

  • 设置初始堆内存和最大堆内存为6G,压测
nohup java -jar spring-test-1.0-SNAPSHOT.jar -Xms6g -Xmx6g -XX:+UseG1GC -XX:MaxGCPauseMillis=200 -XX:G1HeapRegionSize=32M -XX:ActiveProcessorCount=8 -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/usr/local/jmeter/heapdump.hprof -XX:+PrintCommandLineFlags -Xlog:gc=info:file=/usr/local/jmeter/portal_gc.log:utctime,level,tags:filecount=50,filesize=100M &

efb541acf58544ed8a966c03d06ed30f.jpg


2b615311847f477a9847f3a0f1e58b6c.jpg

当把堆内存设置为6G的时候,整体吞吐量到达87%,Yong GC发生了196次,Full GC发生了0次。

总结:通过对堆内存的调整,发现4G是投入产出比最高的参数配置,所以当前配置可以采用4G的堆内存。

4.JVM性能优化之收集器配置

通过上面配置堆内存我们得出4G是当前机器和应用配置的最佳堆内存,这里我们不改变堆内存的大小,采用4G的堆内存,改变垃圾收集器,看看对接口吞吐量的影响。

这里我们采用ParallelGC,目前G1垃圾器在对于并发量大的应用来说,已经是最优的选择啦,我们这里用ParallelGC主要做一个对比。

nohup java -jar spring-test-1.0-SNAPSHOT.jar -Xms4g -Xmx4g -XX:+UseParallelGC -XX:MaxGCPauseMillis=200 -XX:G1HeapRegionSize=32M -XX:ActiveProcessorCount=8 -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/usr/local/jmeter/heapdump.hprof -XX:+PrintCommandLineFlags -Xlog:gc=info:file=/usr/local/jmeter/portal_gc.log:utctime,level,tags:filecount=50,filesize=100M &

cc88e75037124fd5b88427f836eeb071.jpg

9c7f34c34abc4b81bb549b58dac804ea.jpg


相关实践学习
通过性能测试PTS对云服务器ECS进行规格选择与性能压测
本文为您介绍如何利用性能测试PTS对云服务器ECS进行规格选择与性能压测。
相关文章
|
2月前
|
存储 Java 开发者
Java Map实战:用HashMap和TreeMap轻松解决复杂数据结构问题!
【10月更文挑战第17天】本文深入探讨了Java中HashMap和TreeMap两种Map类型的特性和应用场景。HashMap基于哈希表实现,支持高效的数据操作且允许键值为null;TreeMap基于红黑树实现,支持自然排序或自定义排序,确保元素有序。文章通过具体示例展示了两者的实战应用,帮助开发者根据实际需求选择合适的数据结构,提高开发效率。
64 2
|
2天前
|
存储 监控 算法
深入探索Java虚拟机(JVM)的内存管理机制
本文旨在为读者提供对Java虚拟机(JVM)内存管理机制的深入理解。通过详细解析JVM的内存结构、垃圾回收算法以及性能优化策略,本文不仅揭示了Java程序高效运行背后的原理,还为开发者提供了优化应用程序性能的实用技巧。不同于常规摘要仅概述文章大意,本文摘要将简要介绍JVM内存管理的关键点,为读者提供一个清晰的学习路线图。
|
15天前
|
安全 Java 开发者
Java 多线程并发控制:深入理解与实战应用
《Java多线程并发控制:深入理解与实战应用》一书详细解析了Java多线程编程的核心概念、并发控制技术及其实战技巧,适合Java开发者深入学习和实践参考。
|
2月前
|
存储 消息中间件 安全
JUC组件实战:实现RRPC(Java与硬件通过MQTT的同步通信)
【10月更文挑战第9天】本文介绍了如何利用JUC组件实现Java服务与硬件通过MQTT的同步通信(RRPC)。通过模拟MQTT通信流程,使用`LinkedBlockingQueue`作为消息队列,详细讲解了消息发送、接收及响应的同步处理机制,包括任务超时处理和内存泄漏的预防措施。文中还提供了具体的类设计和方法实现,帮助理解同步通信的内部工作原理。
JUC组件实战:实现RRPC(Java与硬件通过MQTT的同步通信)
|
1月前
|
存储 Java 关系型数据库
在Java开发中,数据库连接是应用与数据交互的关键环节。本文通过案例分析,深入探讨Java连接池的原理与最佳实践
在Java开发中,数据库连接是应用与数据交互的关键环节。本文通过案例分析,深入探讨Java连接池的原理与最佳实践,包括连接创建、分配、复用和释放等操作,并通过电商应用实例展示了如何选择合适的连接池库(如HikariCP)和配置参数,实现高效、稳定的数据库连接管理。
53 2
|
1月前
|
Java 关系型数据库 数据库
面向对象设计原则在Java中的实现与案例分析
【10月更文挑战第25天】本文通过Java语言的具体实现和案例分析,详细介绍了面向对象设计的五大核心原则:单一职责原则、开闭原则、里氏替换原则、接口隔离原则和依赖倒置原则。这些原则帮助开发者构建更加灵活、可维护和可扩展的系统,不仅适用于Java,也适用于其他面向对象编程语言。
25 2
|
2月前
|
存储 算法 Java
Java虚拟机(JVM)的内存管理与性能优化
本文深入探讨了Java虚拟机(JVM)的内存管理机制,包括堆、栈、方法区等关键区域的功能与作用。通过分析垃圾回收算法和调优策略,旨在帮助开发者理解如何有效提升Java应用的性能。文章采用通俗易懂的语言,结合具体实例,使读者能够轻松掌握复杂的内存管理概念,并应用于实际开发中。
|
2月前
|
开发框架 Java 程序员
揭开Java反射的神秘面纱:从原理到实战应用!
本文介绍了Java反射的基本概念、原理及应用场景。反射允许程序在运行时动态获取类的信息并操作其属性和方法,广泛应用于开发框架、动态代理和自定义注解等领域。通过反射,可以实现更灵活的代码设计,但也需注意其性能开销。
48 1
|
2月前
|
监控 架构师 Java
JVM进阶调优系列(6)一文详解JVM参数与大厂实战调优模板推荐
本文详述了JVM参数的分类及使用方法,包括标准参数、非标准参数和不稳定参数的定义及其应用场景。特别介绍了JVM调优中的关键参数,如堆内存、垃圾回收器和GC日志等配置,并提供了大厂生产环境中常用的调优模板,帮助开发者优化Java应用程序的性能。
|
2月前
|
存储 监控 算法
JVM调优深度剖析:内存模型、垃圾收集、工具与实战
【10月更文挑战第9天】在Java开发领域,Java虚拟机(JVM)的性能调优是构建高性能、高并发系统不可或缺的一部分。作为一名资深架构师,深入理解JVM的内存模型、垃圾收集机制、调优工具及其实现原理,对于提升系统的整体性能和稳定性至关重要。本文将深入探讨这些内容,并提供针对单机几十万并发系统的JVM调优策略和Java代码示例。
55 2