JVM 调优深度剖析:优化 Java 应用的全方位攻略(一)

这是我们就业陪跑训练营学员总结的文章,我觉得不错,和大家分享一下。

Java 应用中,JVM 调优至关重要。本文聚焦 JVM 调优,涵盖主要目标、思路与方法。目标包括减少 GC 停顿、提高吞吐量等; 思路有多步,如选垃圾回收器、设堆内存等。方法分系统参数配置(含不同回收器应用场景与参数调节)、代码优化(避免过度创建对象等)、架构设计(水平扩展等)。掌握这些,可为 Java 应用高效运行助力。

关注我,下一篇为你继续分享:《调优监控工具的使用与调优后实现的优化目标》

1 JVM 调优的主要目标

  1. 减少垃圾回收GC)的停顿时间:在垃圾回收过程中,应用程序会暂停执行(Stop-The-World,STW),过长的 GC 停顿时间会影响应用的响应时间和性能。

  2. 提高吞吐量:在批量处理或高负载任务中,提升每单位时间内处理的数据量。

  3. 减少内存泄漏和频繁的 FullGC:通过监控和调优减少频繁的 Full GC 和内存泄漏,提升系统的稳定性。

  4. 减少CPU和内存的占用:尽量减少 JVM 占用的系统资源,提高资源利用效率。

  5. 提高应用的响应速度:对用户交互频繁的应用(如 Web 应用),需通过调优减少响应延迟,增强用户体验。

2 JVM 调优的思路可以归纳为以下几个步骤:

  1. 选择合适的垃圾回收器
  2. 合理设置堆内存大小
  3. 调整新生代和老年代的内存比例
  4. 调节GC停顿时间
  5. 调节JIT编译优化
  6. 减少内存碎片和内存泄漏
  7. 优化线程管理和线程堆栈大小

3 调优方法分类

(一)系统参数配置及原因

堆内存大小调整:

  • 指令:通过-Xms 和-Xmx 来实现,一般建议将初始堆大小和最大堆大小设置为相同的值。
  • 原因:当堆内存大小发生变化时,JVM 需要进行一些额外的操作来调整内存布局。例如,在堆内存收缩时,JVM 需要移动对象来释放内存空间;在堆内存扩展时,JVM 需要向操作系统申请更多的内存。这些操作都会带来一定的性能开销。如果初始堆大小和最大堆大小相同,就可以避免这种因内存重新分配而产生的开销,使得内存管理更加稳定和高效。

垃圾回收器选择:

  • 根据不同应用场景选择合适的垃圾回收器。

Serial GC(-XX:+UseSerialGC)

  • 这是最基本的单线程垃圾回收器。它在进行垃圾回收时会暂停所有应用程序线程,直到垃圾回收完成。适用于小型应用程序或者对暂停时间不太敏感的单 CPU 环境。例如,在启动 Java 程序时可以这样设置:

    • java -XX:+UseSerialGC YourMainClass
  • 使用场景

    • 简单的文件加密工具项目
    • 项目描述:开发一个简单的命令行工具,用于对本地文件进行加密。用户通过命令行参数指定要加密的文件路径和加密算法(如 AES),工具读取文件内容,加密后将结果输出到新文件。
    • 使用 Serial GC 的原因:这个工具相对简单,主要涉及文件读取、加密操作和文件写入。它是一个单线程的应用(加密操作本身也是单线程执行更安全),内存占用相对较小。在加密过程中,即使垃圾回收暂停了应用程序,由于操作相对短暂,用户也不太会察觉到明显的延迟。而且对于这种小型工具应用,Serial GC 的简单性使得它更容易集成和调试。例如,在开发过程中,如果出现内存泄漏等问题,通过简单的调试工具和 Serial GC 的单线程回收机制,可以比较容易地定位问题。- 调节 GC 停顿时间:对于 Serial 收集器,由于其单线程的特性,很难直接调节停顿时间。不过,可以通过调整堆内存大小(-Xms 和 -Xmx 参数)来间接影响停顿时间。如果堆内存设置得较小,那么 GC 的工作范围也会相对较小,可能会减少停顿时间,但同时也可能会导致 GC 更加频繁。

Parallel GC(-XX:+UseParallelGC)

  • 它使用多个线程来进行垃圾回收,能够更有效地利用多核 CPU 资源,从而提高垃圾回收的效率。在进行垃圾回收时仍然会暂停应用程序线程。可以通过以下方式启用:

    • java -XX:+UseParallelGC YourMainClass
  • 使用场景

    • 大数据日志分析项目
    • 项目描述:在一个大型企业的服务器集群中,需要对海量的系统日志和业务日志进行分析。日志数据每天会产生数 TB,应用程序需要从分布式存储系统(如 HDFS)中读取日志文件,进行数据清洗(如去除无用的日志字段、格式化日期等),然后通过一些统计分析算法(如计算某个业务操作的频率、用户访问的峰值时间等)来提取有价值的信息。
  • 使用 Parallel GC 的原因:

    • 这个项目是一个典型的后台数据处理应用,运行在多核服务器环境。在数据处理过程中,会产生大量的临时对象,如日志数据的解析对象、统计数据的中间结果对象等。Parallel GC 可以利用多核 CPU 的优势,通过多个线程并行进行垃圾回收。例如,当一个批次的日志数据处理完成后,Parallel GC 能够快速地回收这些临时对象所占用的内存,为下一批次的日志处理腾出空间,从而提高整个日志分析系统的吞吐量,满足企业对海量日志快速分析的需求。
  • 调节 GC 停顿时间:

    • 控制垃圾收集的吞吐量:
    • 可以使用 -XX:MaxGCPauseMillis 参数来设置期望的最大 GC 停顿时间。JVM 会尽量调整垃圾收集的频率和强度,以满足这个停顿时间的要求。不过需要注意的是,设置这个参数可能会导致吞吐量下降。例如,如果将 -XX:MaxGCPauseMillis 设置为 50 毫秒,JVM 会尝试通过调整新生代和老年代的大小、GC 的频率等方式来将 GC 停顿时间控制在 50 毫秒以内。
    • 调整新生代大小: 通过 -Xmn 参数来设置新生代的大小。增大新生代的大小可以减少 Minor GC 的频率,但每次 Minor GC 的时间可能会增加。因为在新生代空间较大的情况下,垃圾收集器需要处理更多的对象。相反,减小新生代的大小会增加 Minor GC 的频率,但每次 Minor GC 的时间可能会缩短。CMS(Concurrent Mark - Sweep)GC(-XX:+UseConcMarkSweepGC)

CMS(Concurrent Mark - Sweep)GC (-XX:+UseConcMarkSweepGC)

  • CMS 垃圾回收器的目标是尽量减少垃圾回收时应用程序的暂停时间。它主要有四个阶段:初始标记、并发标记、重新标记和并发清除。其中初始标记和重新标记阶段会暂停应用程序线程,但时间相对较短;并发标记和并发清除阶段与应用程序线程并发执行。启用 CMS 垃圾回收器可以使用以下指令:
    • java -XX:+UseConcMarkSweepGC YourMainClass
  • 不过,CMS 垃圾回收器也有一些缺点,比如它会占用更多的 CPU 资源用于并发执行,并且可能会产生内存碎片。可以通过-XX:CMSInitiatingOccupancyFraction参数来设置在老年代空间使用达到多少百分比时触发 CMS 垃圾回收。例如,设置为 70%:
    • java -XX:+UseConcMarkSweepGC -XX:CMSInitiatingOccupancyFraction = 70 YourMainClass
  • 高流量电商网站后端项目
    • 项目描述:开发一个电商网站的后端服务,包括商品管理、订单处理、用户认证等多个模块。这个后端服务需要同时处理大量的用户请求,如用户查询商品信息、添加商品到购物车、下单支付等操作。
    • 使用 CMS GC 的原因:对于电商网站这种对响应时间要求极高的应用,用户在浏览商品和下单过程中的体验至关重要。如果垃圾回收导致应用程序长时间暂停,用户可能会遇到页面加载缓慢或者操作无响应的情况。CMS GC 的并发标记和清除阶段可以在很大程度上减少这种暂停时间。例如,当大量用户在促销活动期间访问网站时,CMS GC 可以在后台并发地进行垃圾回收,同时保证前端应用能够及时响应用户的各种操作请求,如更新购物车数量、查询订单状态等,从而提高用户的满意度和购物体验。
  • 调节 GC 停顿时间:
    • 调整初始标记和重新标记阶段的参数:可以通过 -XX:+CMSScandStartInitialMarkAtBoot 参数,在启动时就开始初始标记阶段,这样可以减少应用程序运行过程中的停顿。对于重新标记阶段,可以通过 -XX:ParallelCMSThreads 参数来设置并行的线程数,增加线程数可以加快重新标记的速度,从而减少停顿时间。
    • 调整 CMS 的触发阈值:使用 -XX:CMSInitiatingOccupancyFraction 参数来设置老年代使用达到多少百分比时触发 CMS 收集。如果将这个参数设置得较低,CMS 收集会更频繁,但每次收集的停顿时间可能会较短;如果设置得较高,CMS 收集的频率会降低,但每次收集的停顿时间可能会因为老年代中对象过多而变长。

G1(Garbage - First)GC(-XX:+UseG1GC)

  • G1 垃圾回收器是一种面向服务器端应用的垃圾回收器,它将堆内存划分为多个大小相等的区域(Region),在进行垃圾回收时可以优先回收垃圾最多的区域。它能够在尽量短的时间内完成垃圾回收,并且可以比较好地控制垃圾回收暂停时间。启用 G1 垃圾回收器的指令如下:
    • java -XX:+UseG1GC YourMainClass
  • 可以通过-XX:MaxGCPauseMillis参数来设置最大垃圾回收暂停时间的目标值。例如,设置最大暂停时间为 200 毫秒:
  • java -XX:+UseG1GC -XX:MaxGCPauseMillis = 200 YourMainClass
  • 使用场景
    • 大型企业级资源管理系统项目
    • 项目描述:
    • 构建一个大型企业的资源管理系统,用于管理企业内部的各种资源,包括人力资源(员工信息、考勤记录等)、物资资源(办公设备、库存物资等)和财务资源(预算、成本核算等)。这个系统有一个庞大的数据库连接池,并且会缓存大量的业务数据(如经常访问的员工信息、物资库存报表等)以提高性能。
    • 使用 G1 GC 的原因:
    • 该系统的堆内存占用很大,因为要存储大量的业务对象和缓存数据。同时,它运行在多处理器服务器环境,并且需要满足不同用户(如人力资源部门、物资管理部门、财务部门等)对系统的不同需求。有些操作(如人力资源部门批量导入员工考勤数据)对吞吐量有要求,而另一些操作(如财务部门实时查询预算使用情况)对响应时间敏感。G1 GC 可以将堆内存划分为多个区域,有针对性地回收垃圾最多的区域,有效地管理内存,并且可以通过参数控制垃圾回收暂停时间,从而很好地满足这个大型企业级资源管理系统复杂的内存管理需求。
  • 调节 GC 停顿时间:
    • 设置期望的最大停顿时间:
    • 通过 -XX:MaxGCPauseMillis 参数来设置期望的最大 GC 停顿时间,与 Parallel 收集器类似,G1 会根据这个参数来调整回收策略。例如,将 -XX:MaxGCPauseMillis 设置为 200 毫秒,G1 会尽量将每次 GC 的停顿时间控制在 200 毫秒以内。
    • 调整 Region 大小和数量:
    • 可以通过 -XX:G1HeapRegionSize 参数来调整 Region 的大小。较小的 Region 大小可以更精细地控制内存回收,但可能会增加管理成本;较大的 Region 大小则相反。同时,G1 会根据 Region 的使用情况和期望的停顿时间来动态调整回收的 Region 数量。

并发线程数调整

  • 指令:通过-XX:ParallelGCThreads 来调整,一般设置为与处理器数目(CPU 核心的数量)相等。
  • 原因:
    • 充分利用硬件资源

      • 如果设置的垃圾回收线程数多于处理器核心数,会导致线程之间过度竞争 CPU 资源。例如,有 8 个垃圾回收线程但只有 4 个处理器核心,那么这些线程就需要争夺有限的 CPU 时间片来执行任务,这可能会导致额外的上下文切换开销。上下文切换是指 CPU 从一个线程切换到另一个线程执行任务时,需要保存当前线程的状态并加载下一个线程的状态,这个过程会消耗一定的时间和系统资源。相反,如果设置的线程数过少,比如只有 2 个垃圾回收线程在 4 核处理器上,那么就无法充分利用所有的处理器核心,造成硬件资源的浪费,垃圾回收的效率也会降低。
    • 避免资源过度竞争和浪费

      • 当设置-XX:ParallelGCThreads的值等于处理器数目时,垃圾回收器可以为每个处理器核心分配一个线程来进行垃圾回收工作。这样,在垃圾回收阶段,每个核心都能独立地处理一部分垃圾回收任务,实现真正的并行处理。例如,如果计算机有 4 个处理器核心,设置 4 个垃圾回收线程,就像有 4 个工人同时清理垃圾一样,可以最大限度地利用硬件的并行处理能力,从而提高垃圾回收的效率。

JIT 编译器调整

  • 指令:通过-XX:+PrintCompilation 参数观察编译过程,在某些情况下通过-XX:CompileCommand 参数禁用对某个方法或类的编译。
    • java-XX:+PrintCompilation-XX:CompileThreshold=1000YourApp
    • 此处设置 JIT 编译器在方法调用达到 1000 次后进行编译,同时打印编译日志,以帮助分析性能瓶颈。
  • 原因:JIT 编译器的优化可以提高热点代码的执行速度。

内存区域划分调整

  • 指令:通过-XX:NewRatio 和-XX:SurvivorRatio 来调整新生代和老年代的比例。
    • 例如,-XX:NewRatio=4表示年轻代与年老代所占比值为 1:4,年轻代占整个堆栈的 1/5;
    • -XX:SurvivorRatio=4表示两个 Survivor 区与一个 Eden 区的比值为 2:4,一个 Survivor 区占整个年轻代的 1/6。
  • 原因:新生代和老年代的比例没有固定的标准,需要根据应用程序的特点进行调整。一般来说,新生代的比例可以在 1/3 到 1/2 之间。例如,如果堆内存大小为 4GB,可以将新生代设置为 1.5GB,老年代设置为 2.5GB。内存区域划分的调整是为了根据应用的特点合理分配内存,避免某个区域的内存不足导致频繁的垃圾回收。

(二)代码优化

在代码层面,应避免过度创建对象,以减少垃圾回收的负担。比如,尽量使用局部变量,因为调用方法时传的参数以及在调用中创建的临时变量都保存在栈中,速度较快,而其他变量如静态变量、实例变量等都在堆中创建,速度较慢,且栈中创建的变量随着方法的运行结束就消失了,不需要额外的垃圾回收。选择合适的集合类型也很关键,例如ArrayList随机遍历快,LinkedList添加删除快。同时,要优化循环操作,如for(int i =0; i<list.size(); i++){…}可替换为int length = list.size();for(int i =0; i<length; i++){…},这样在list.size()很大的时候,可以减少很多消耗。

(三)架构设计

水平扩展可以通过增加服务器节点来分担负载,提高系统的吞吐量。异步处理可以使用消息队列等技术,将耗时的操作异步执行,避免阻塞主线程。缓存优化可以减少对数据库等外部资源的访问,提高响应速度。分布式设计可以将系统的不同模块部署在不同的服务器上,提高系统的可扩展性和可用性。

好了,这期内容先和你分享到这里。

关注我,下一篇为你继续分享: 《调优监控工具的使用与调优后实现的优化目标》

欢迎关注 ❤

我们搞了一个免费的面试真题共享群,互通有无,一起刷题进步。

没准能让你能刷到自己意向公司的最新面试题呢。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/459969.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

[MySQL#6] 表的CRUD (1) | Create | Retrieve(查) | where

目录 1. 插入 1.1 单行数据 - 全列插入 指定列插入 1.2 多行数据 - 全列插入 指定列插入 1.3 更新 1.4 替换 2. 查找 2.1 select 列 2.2 where 条件 具体案例 2.3 结果排序 总结关键字执行顺序 2.4 筛选分页结果 CRUD : Create(创建)&#xff0c;Retrieve(读取)&…

[机器学习]集成学习

1 集成学习 强强联合、弱弱变强Bagging&#xff08;平权投票&#xff09;&#xff1a;随机森林Boosting&#xff08;加权投票&#xff09;&#xff1a;Adaboost、GBDT、XGBoost、LightGBM 2 随机森林 3 Adaboost 放大错误数据&#xff0c;缩小正确数据

第三十三篇:TCP协议如何避免/减少网络拥塞,TCP系列八

一、流量控制 一般来说&#xff0c;我们总是希望数据传输得更快一些&#xff0c;但是如果发送方把数据发送得太快&#xff0c;接收方可能来不及接收&#xff0c;造成数据的丢失&#xff0c;数据重发&#xff0c;造成网络资源的浪费甚至网络拥塞。所谓的流量控制&#xff08;fl…

在Excel中如何快速筛选非特定颜色

Excel中的自动筛选是个非常强大的工具&#xff0c;不仅可以筛选内容&#xff0c;而且可以筛选颜色&#xff0c;例如筛选A列红色单元格。但是有时希望筛选除了红色之外的单元格&#xff08;下图右侧所示&#xff09;&#xff0c;其他单元格的填充色不固定&#xff0c;有几种颜色…

数据结构---链表(一)【不带头单向非循环】

文章目录 链表概念链表的使用LinkedList 的几种遍历方式单链表的模拟实现&#xff08;不带头&#xff09;链表面试题 观察ArrayList 顺序表的源码发现&#xff0c;底层是使用数组实现的。由于其底层是一段连续空间&#xff0c;当在ArrayList任意位置插入或者删除元素时&#xf…

Pytorch(一)

一.PyTorch环境配置及安装 1.1 工具安装 1.1.1 Anaconda下载 清华大学镜像站下载&#xff0c;版本为Anaconda3-5.2.0-Windows-x86_64&#xff08;对应python3.6.5&#xff09; Index of /anaconda/archive/ | 清华大学开源软件镜像站 | Tsinghua Open Source Mirror 1.1.2…

关于我的数据结构与算法——初阶第二篇(排序)

&#xff08;叠甲&#xff1a;如有侵权请联系&#xff0c;内容都是自己学习的总结&#xff0c;一定不全面&#xff0c;仅当互相交流&#xff08;轻点骂&#xff09;我也只是站在巨人肩膀上的一个小卡拉米&#xff0c;已老实&#xff0c;求放过&#xff09;。 排序的概念及其运…

AI驱动的低代码未来:加速应用开发的智能解决方案

引言 随着数字化转型的浪潮席卷全球&#xff0c;企业对快速构建应用程序的需求愈发强烈。然而&#xff0c;传统的软件开发周期冗长、成本高昂&#xff0c;往往无法满足快速变化的市场需求。在此背景下&#xff0c;低代码平台逐渐成为开发者和企业的优选方案&#xff0c;以其“低…

三周精通FastAPI:21 子依赖项和路径操作装饰器依赖项

官方文档&#xff1a;https://fastapi.tiangolo.com/zh/tutorial/dependencies/sub-dependencies/#_6 子依赖项 FastAPI 支持创建含子依赖项的依赖项。 并且&#xff0c;可以按需声明任意深度的子依赖项嵌套层级。 FastAPI 负责处理解析不同深度的子依赖项。 第一层依赖项 …

模具生产管理系统软件:提升制造业效率的新利器

引言 我们都知道&#xff0c;企业面临着提高生产效率、降低成本和提升产品质量的压力。模具生产作为制造过程中至关重要的一环&#xff0c;如何有效管理和优化模具生产过程&#xff0c;成为企业关注的重点。模具生产管理系统应运而生&#xff0c;能够为企业提供实时监控、流程…

MySQL中,如何定位慢查询?定位到的慢SQL如何分析?

目录 1. 慢查询发生的场景&#xff1f; 2. MySQL中&#xff0c;如何定位慢查询&#xff1f; 2.1 详细解释 3. 定位到的慢SQL如何分析&#xff1f; 3.1 详细说明 1. 慢查询发生的场景&#xff1f; 2. MySQL中&#xff0c;如何定位慢查询&#xff1f; 介绍一下当时产生问题…

「C/C++」C++ 设计模式 之 单例模式(Singleton)

✨博客主页何曾参静谧的博客&#x1f4cc;文章专栏「C/C」C/C程序设计&#x1f4da;全部专栏「VS」Visual Studio「C/C」C/C程序设计「UG/NX」BlockUI集合「Win」Windows程序设计「DSA」数据结构与算法「UG/NX」NX二次开发「QT」QT5程序设计「File」数据文件格式「PK」Parasoli…

华为云开源项目Sermant正式成为CNCF官方项目

近日&#xff0c;云原生计算基金会&#xff08;CNCF&#xff09;正式接纳由华为云发起的云原生无代理服务网格项目Sermant。Sermant的加入&#xff0c;极大地丰富了云原生微服务治理技术的探索、创新和发展&#xff0c;为CNCF社区注入了新的活力。 Sermant是华为云在微服务治理…

用sdcc给51单片机编译C程序

学习单片机大部分人用的是Keil uVision&#xff0c;虽然好用&#xff0c;可大部分人用的是盗版&#xff0c;其实单片机程序小的话&#xff0c;完全可以用文本编辑器&#xff08;推荐notepad)编写&#xff0c;然后用免费的sdcc来编译&#xff0c;下面介绍一下大致的过程。 sdcc…

Ajax:表单 模板引擎

Ajax&#xff1a;表单 & 模板引擎 form 表单form 属性 Ajax操控表单事件监听阻止默认行为收集表单数据 模板引擎art-template{{}}语法原文输出条件输出循环输出过滤器 原理 form 表单 在HTML中&#xff0c;可以通过<form>创建一个表单&#xff0c;收集用户信息。而采…

【水下生物数据集】 水下生物识别 深度学习 目标检测 机器视觉 yolo(含数据集)

一、背景意义 随着全球海洋生态环境的日益变化&#xff0c;水下生物的监测和保护变得愈发重要。水下生物种类繁多&#xff0c;包括螃蟹、鱼类、水母、虾、小鱼和海星等&#xff0c;它们在海洋生态系统中扮演着关键角色。传统的水下生物监测方法通常依赖于人工观察&#xff0c;效…

[vulnhub]Kioptrix: Level 1.2 (#3)

https://www.vulnhub.com/entry/kioptrix-level-12-3,24/ 主机发现端口扫描 使用nmap扫描网段类存活主机 因为靶机是我最后添加的&#xff0c;所以靶机IP是169 nmap -sP 192.168.75.0/24 Starting Nmap 7.94SVN ( https://nmap.org ) at 2024-10-29 13:16 CST …

TVM前端研究--Relay

文章目录 深度学习IR梳理1. IR属性2. DL前端发展3. DL编译器4. DL编程语言Relay的主要内容一、Expression in Relay1. Dataflow and Control Fragments2. 变量3. 函数3.1 闭包3.2 多态和类型关系3.3. Call4. 算子5. ADT Constructors6. Moudle和Global Function7. 常量和元组8.…

Ubuntu UFW防火墙规则与命令示例大全

在服务器安全领域&#xff0c;防火墙是守护网络安全的坚实盾牌。UFW&#xff08;Uncomplicated Firewall&#xff09;&#xff0c;即“不复杂的防火墙”&#xff0c;是一个运行在iptables之上的防火墙配置工具&#xff0c;它为Ubuntu系统默认提供了一个简洁的命令行界面&#x…

Linux高阶——1026—验证内存映射mmap函数使用

1、验证共享映射后修改文件内容&#xff0c;是否能够同步 先创建一个映射文件&#xff0c;写入数据 分为四个步骤 1、打开映射文件 设文件描述符&#xff0c;使用open函数 int fd; if((fdopen("mapfile",O_RDWR))-1) { perror("open failed");exit…