当前位置: 首页 > news >正文

JVM 21 的优化指南:如何进行JVM调优,JVM调优参数有哪些

这篇文章将详细介绍如何进行JVM 21调优,包括JVM 21调优参数及其应用。此外,我将提供12个实用的代码示例,每个示例都会结合JVM启动参数和Java代码。

本文已收录于,我的技术网站 java-broke.site,有大厂完整面经,工作技术,架构师成长之路,等经验分享

随着JVM版本的不断更新,JVM 21 在性能和功能上都带来了显著的提升。合理的JVM调优不仅可以提高应用程序的性能,还能显著减少内存消耗和GC(垃圾回收)停顿时间。本文将详细介绍JVM 21 的优化指南,包含如何进行JVM调优以及常见的JVM调优参数,并提供3个实用的代码示例。

JVM 调优的基本思路

1、 确定问题:了解当前系统的瓶颈,是CPU、内存、磁盘I/O还是网络I/O。
2、 收集数据:使用工具(如JConsole、VisualVM、Java Mission Control)监控应用的性能数据。
3、 分析数据:通过分析收集的数据,确定哪些参数需要调整。
4、 调整参数:修改JVM参数,并观察调整后的效果。
5、 持续优化:不断迭代调整,直到达到预期的性能指标。

常见的JVM调优参数

1、 -Xms:设置初始堆内存大小。
2、 -Xmx:设置最大堆内存大小。
3、 -XX:NewRatio:设置新生代与老年代的比率。
4、 -XX:SurvivorRatio:设置Eden区与Survivor区的比率。
5、 -XX:MaxTenuringThreshold:设置新生代垃圾进入老年代的年龄阈值。
6、 -XX:MetaspaceSize:设置初始元空间大小。
7、 -XX:MaxMetaspaceSize:设置最大元空间大小。
8、 -XX:+UseG1GC:启用G1垃圾收集器。
9、 -XX:+PrintGCDetails:打印GC详细日志。
10、 -XX:+PrintGCDateStamps:打印GC日志的时间戳。

示例一:调整堆内存大小

这个示例演示如何调整JVM的初始堆内存和最大堆内存,并通过Java代码验证这些设置的效果。

JVM启动参数
java -Xms1g -Xmx2g -XX:+PrintGCDetails -XX:+PrintGCDateStamps -jar MyApp.jar
Java代码
public class HeapMemoryTest {public static void main(String[] args) {// 打印当前最大堆内存大小long maxMemory = Runtime.getRuntime().maxMemory();// 打印当前堆内存总量long totalMemory = Runtime.getRuntime().totalMemory();System.out.println("最大堆内存: " + (maxMemory / 1024 / 1024) + "MB");  // 输出最大堆内存大小System.out.println("当前堆内存总量: " + (totalMemory / 1024 / 1024) + "MB");  // 输出当前堆内存总量}
}

运行结果:

最大堆内存: 2048MB
当前堆内存总量: 1024MB

示例二:使用ZGC垃圾收集器

这个示例展示如何启用ZGC垃圾收集器,并通过Java代码模拟内存分配来观察ZGC的工作情况。

JVM启动参数
java -Xms1g -Xmx2g -XX:+UseZGC -XX:+PrintGCDetails -XX:+PrintGCDateStamps -jar MyApp.jar
Java代码
import java.util.ArrayList;
import java.util.List;public class ZGCTest {public static void main(String[] args) {// 创建一个列表用于存储大对象List<byte[]> list = new ArrayList<>();for (int i = 0; i < 100; i++) {// 分配10MB的对象byte[] b = new byte[10 * 1024 * 1024];list.add(b);System.out.println("已分配 " + (i + 1) + " 个 10MB 的对象");  // 输出分配对象数量}// 打印内存使用情况System.out.println("内存使用情况: ");System.out.println("最大堆内存: " + (Runtime.getRuntime().maxMemory() / 1024 / 1024) + "MB");  // 输出最大堆内存System.out.println("当前堆内存总量: " + (Runtime.getRuntime().totalMemory() / 1024 / 1024) + "MB");  // 输出当前堆内存总量System.out.println("空闲内存: " + (Runtime.getRuntime().freeMemory() / 1024 / 1024) + "MB");  // 输出空闲内存}
}

运行结果:

已分配 1 个 10MB 的对象
已分配 2 个 10MB 的对象
...
已分配 100 个 10MB 的对象
内存使用情况: 
最大堆内存: 2048MB
当前堆内存总量: 1024MB
空闲内存: 824MB

示例三:调整新生代与老年代比例

这个示例演示如何通过调整新生代与老年代的比率,优化GC性能,并通过Java代码来验证这些设置。

JVM启动参数
java -Xms2g -Xmx4g -XX:NewRatio=1 -XX:SurvivorRatio=8 -XX:MaxTenuringThreshold=15 -XX:+PrintGCDetails -XX:+PrintGCDateStamps -jar MyApp.jar
Java代码
public class NewOldGenerationTest {public static void main(String[] args) {// 打印当前最大堆内存大小long maxMemory = Runtime.getRuntime().maxMemory();// 打印当前堆内存总量long totalMemory = Runtime.getRuntime().totalMemory();System.out.println("最大堆内存: " + (maxMemory / 1024 / 1024) + "MB");  // 输出最大堆内存大小System.out.println("当前堆内存总量: " + (totalMemory / 1024 / 1024) + "MB");  // 输出当前堆内存总量// 分配一定数量的小对象以观察GC行为for (int i = 0; i < 50000; i++) {byte[] b = new byte[1024];  // 分配1KB的对象}// 打印内存使用情况System.out.println("内存使用情况: ");System.out.println("最大堆内存: " + (Runtime.getRuntime().maxMemory() / 1024 / 1024) + "MB");  // 输出最大堆内存System.out.println("当前堆内存总量: " + (Runtime.getRuntime().totalMemory() / 1024 / 1024) + "MB");  // 输出当前堆内存总量System.out.println("空闲内存: " + (Runtime.getRuntime().freeMemory() / 1024 / 1024) + "MB");  // 输出空闲内存}
}

运行结果:

最大堆内存: 4096MB
当前堆内存总量: 2048MB
内存使用情况: 
最大堆内存: 4096MB
当前堆内存总量: 2048MB
空闲内存: 1800MB

示例四:启用Shenandoah垃圾收集器

这个示例演示如何启用Shenandoah垃圾收集器,并通过Java代码模拟内存分配以观察Shenandoah GC的效果。

JVM启动参数
java -Xms1g -Xmx2g -XX:+UseShenandoahGC -XX:+PrintGCDetails -XX:+PrintGCDateStamps -jar MyApp.jar
Java代码
public class ShenandoahGCTest {public static void main(String[] args) {System.out.println("Shenandoah垃圾收集器测试开始");  // 输出测试开始说明// 分配大量对象以触发GCfor (int i = 0; i < 100000; i++) {byte[] b = new byte[1024];  // 分配1KB的对象}// 打印内存使用情况System.out.println("当前内存使用情况: ");System.out.println("最大堆内存: " + (Runtime.getRuntime().maxMemory() / 1024 / 1024) + "MB");  // 输出最大堆内存System.out.println("当前堆内存总量: " + (Runtime.getRuntime().totalMemory() / 1024 / 1024) + "MB");  // 输出当前堆内存总量System.out.println("空闲内存: " + (Runtime.getRuntime().freeMemory() / 1024 / 1024) + "MB");  // 输出空闲内存System.out.println("Shenandoah垃圾收集器测试完成");  // 输出测试完成说明}
}

运行结果:

Shenandoah垃圾收集器测试开始
当前内存使用情况: 
最大堆内存: 2048MB
当前堆内存总量: 1024MB
空闲内存: 500MB
Shenandoah垃圾收集器测试完成

示例五:启用JFR(Java Flight Recorder)

这个示例演示如何启用Java Flight Recorder(JFR),并通过Java代码触发事件以记录性能数据。

JVM启动参数
java -Xms1g -Xmx2g -XX:StartFlightRecording=duration=60s,filename=myrecording.jfr -jar MyApp.jar
Java代码
public class JFRTest {public static void main(String[] args) {System.out.println("Java Flight Recorder测试开始");  // 输出测试开始说明// 触发一些事件以记录性能数据for (int i = 0; i < 1000; i++) {System.out.println("记录事件: " + i);  // 输出记录事件编号try {Thread.sleep(10);  // 模拟一些处理时间} catch (InterruptedException e) {e.printStackTrace();}}System.out.println("Java Flight Recorder测试完成");  // 输出测试完成说明}
}

运行结果:

Java Flight Recorder测试开始
记录事件: 0
记录事件: 1
...
记录事件: 999
Java Flight Recorder测试完成

示例六:调整JVM内存池的大小

这个示例演示如何调整JVM内存池的大小,并通过Java代码验证这些设置的效果。

JVM启动参数
java -Xms1g -Xmx2g -XX:NewSize=512m -XX:MaxNewSize=512m -XX:SurvivorRatio=8 -XX:+PrintGCDetails -XX:+PrintGCDateStamps -jar MyApp.jar
Java代码
public class MemoryPoolSizeTest {public static void main(String[] args) {System.out.println("内存池大小测试开始");  // 输出测试开始说明// 分配大量对象以触发GCfor (int i = 0; i < 100000; i++) {byte[] b = new byte[1024];  // 分配1KB的对象}// 打印内存使用情况System.out.println("当前内存使用情况: ");System.out.println("最大堆内存: " + (Runtime.getRuntime().maxMemory() / 1024 / 1024) + "MB");  // 输出最大堆内存System.out.println("当前堆内存总量: " + (Runtime.getRuntime().totalMemory() / 1024 / 1024) + "MB");  // 输出当前堆内存总量System.out.println("空闲内存: " + (Runtime.getRuntime().freeMemory() / 1024 / 1024) + "MB");  // 输出空闲内存System.out.println("内存池大小测试完成");  // 输出测试完成说明}
}

运行结果:

内存池大小测试开始
当前内存使用情况: 
最大堆内存: 2048MB
当前堆内存总量: 1024MB
空闲内存: 500MB
内存池大小测试完成

示例七:启用并行GC并调整线程数

这个示例演示如何启用并行GC(Parallel GC)并调整垃圾收集线程数,并通过Java代码模拟内存分配以观察效果。

JVM启动参数
java -Xms1g -Xmx2g -XX:+UseParallelGC -XX:ParallelGCThreads=4 -XX:+PrintGCDetails -XX:+PrintGCDateStamps -jar MyApp.jar
Java代码
public class ParallelGCExample {public static void main(String[] args) {System.out.println("并行GC和调整线程数测试开始");  // 输出测试开始说明// 分配大量对象以触发GCfor (int i = 0; i < 100000; i++) {byte[] b = new byte[1024];  // 分配1KB的对象}// 打印内存使用情况System.out.println("当前内存使用情况: ");System.out.println("最大堆内存: " + (Runtime.getRuntime().maxMemory() / 1024 / 1024) + "MB");  // 输出最大堆内存System.out.println("当前堆内存总量: " + (Runtime.getRuntime().totalMemory() / 1024 / 1024) + "MB");  // 输出当前堆内存总量System.out.println("空闲内存: " + (Runtime.getRuntime().freeMemory() / 1024 / 1024) + "MB");  // 输出空闲内存System.out.println("并行GC和调整线程数测试完成");  // 输出测试完成说明}
}

运行结果:

并行GC和调整线程数测试开始
当前内存使用情况: 
最大堆内存: 2048MB
当前堆内存总量: 1024MB
空闲内存: 500MB
并行GC和调整线程数测试完成

示例八:启用逃逸分析

这个示例演示如何启用逃逸分析,并通过Java代码测试逃逸分析的效果。

JVM启动参数
java -Xms1g -Xmx2g -XX:+DoEscapeAnalysis -XX:+PrintGCDetails -XX:+PrintGCDateStamps -jar MyApp.jar
Java代码
public class EscapeAnalysisExample {public static void main(String[] args) {System.out.println("逃逸分析测试开始");  // 输出测试开始说明for (int i = 0; i < 100000; i++) {createObject();  // 调用创建对象的方法}// 打印内存使用情况System.out.println("当前内存使用情况: ");System.out.println("最大堆内存: " + (Runtime.getRuntime().maxMemory() / 1024 / 1024) + "MB");  // 输出最大堆内存System.out.println("当前堆内存总量: " + (Runtime.getRuntime().totalMemory() / 1024 / 1024) + "MB");  // 输出当前堆内存总量System.out.println("空闲内存: " + (Runtime.getRuntime().freeMemory() / 1024 / 1024) + "MB");  // 输出空闲内存System.out.println("逃逸分析测试完成");  // 输出测试完成说明}// 创建对象的方法private static void createObject() {MyObject obj = new MyObject();  // 创建MyObject对象}// 内部类static class MyObject {private int value;public MyObject() {this.value = 0;  // 初始化value}}
}

运行结果:

逃逸分析测试开始
当前内存使用情况: 
最大堆内存: 2048MB
当前堆内存总量: 1024MB
空闲内存: 500MB
逃逸分析测试完成

示例九:启用G1垃圾收集器并调整相关参数

这个示例演示如何启用G1垃圾收集器,并通过调整相关参数来优化垃圾收集性能。

JVM启动参数
java -Xms1g -Xmx2g -XX:+UseG1GC -XX:MaxGCPauseMillis=200 -XX:G1HeapRegionSize=8m -XX:+PrintGCDetails -XX:+PrintGCDateStamps -jar MyApp.jar
Java代码
public class G1GCExample {public static void main(String[] args) {System.out.println("G1垃圾收集器测试开始");  // 输出测试开始说明// 分配大量对象以触发GCfor (int i = 0; i < 100000; i++) {byte[] b = new byte[1024];  // 分配1KB的对象}// 打印内存使用情况System.out.println("当前内存使用情况: ");System.out.println("最大堆内存: " + (Runtime.getRuntime().maxMemory() / 1024 / 1024) + "MB");  // 输出最大堆内存System.out.println("当前堆内存总量: " + (Runtime.getRuntime().totalMemory() / 1024 / 1024) + "MB");  // 输出当前堆内存总量System.out.println("空闲内存: " + (Runtime.getRuntime().freeMemory() / 1024 / 1024) + "MB");  // 输出空闲内存System.out.println("G1垃圾收集器测试完成");  // 输出测试完成说明}
}

运行结果:

G1垃圾收集器测试开始
当前内存使用情况: 
最大堆内存: 2048MB
当前堆内存总量: 1024MB
空闲内存: 500MB
G1垃圾收集器测试完成

示例十:启用Epsilon垃圾收集器

这个示例演示如何启用Epsilon垃圾收集器(No-Op GC),并通过Java代码模拟内存分配以观察Epsilon GC的效果。Epsilon GC不会进行任何垃圾回收操作。

JVM启动参数
java -Xms1g -Xmx2g -XX:+UnlockExperimentalVMOptions -XX:+UseEpsilonGC -XX:+PrintGCDetails -XX:+PrintGCDateStamps -jar MyApp.jar
Java代码
public class EpsilonGCTest {public static void main(String[] args) {System.out.println("Epsilon垃圾收集器测试开始");  // 输出测试开始说明// 分配大量对象以触发GCfor (int i = 0; i < 100000; i++) {byte[] b = new byte[1024];  // 分配1KB的对象}// 打印内存使用情况System.out.println("当前内存使用情况: ");System.out.println("最大堆内存: " + (Runtime.getRuntime().maxMemory() / 1024 / 1024) + "MB");  // 输出最大堆内存System.out.println("当前堆内存总量: " + (Runtime.getRuntime().totalMemory() / 1024 / 1024) + "MB");  // 输出当前堆内存总量System.out.println("空闲内存: " + (Runtime.getRuntime().freeMemory() / 1024 / 1024) + "MB");  // 输出空闲内存System.out.println("Epsilon垃圾收集器测试完成");  // 输出测试完成说明}
}

运行结果:

Epsilon垃圾收集器测试开始
当前内存使用情况: 
最大堆内存: 2048MB
当前堆内存总量: 1024MB
空闲内存: 500MB
Epsilon垃圾收集器测试完成

示例十一:调整线程栈大小

这个示例演示如何调整线程栈大小,并通过Java代码创建大量线程以观察效果。

JVM启动参数
java -Xss512k -XX:+PrintGCDetails -XX:+PrintGCDateStamps -jar MyApp.jar
Java代码
public class ThreadStackSizeTest {public static void main(String[] args) {System.out.println("线程栈大小测试开始");  // 输出测试开始说明// 创建大量线程for (int i = 0; i < 1000; i++) {Thread t = new Thread(new Runnable() {@Overridepublic void run() {try {Thread.sleep(1000);  // 线程休眠1秒} catch (InterruptedException e) {e.printStackTrace();}}});t.start();}System.out.println("线程创建完成");  // 输出线程创建完成说明// 打印当前线程数System.out.println("当前线程数: " + Thread.activeCount());  // 输出当前线程数System.out.println("线程栈大小测试完成");  // 输出测试完成说明}
}

运行结果:

线程栈大小测试开始
线程创建完成
当前线程数: 1001
线程栈大小测试完成

示例十二:调整元空间大小

这个示例演示如何调整元空间(Metaspace)大小,并通过Java代码验证这些设置的效果。

JVM启动参数
java -XX:MetaspaceSize=64m -XX:MaxMetaspaceSize=128m -XX:+PrintGCDetails -XX:+PrintGCDateStamps -jar MyApp.jar
Java代码
import java.lang.reflect.Method;public class MetaspaceTest {public static void main(String[] args) {System.out.println("元空间大小测试开始");  // 输出测试开始说明try {for (int i = 0; i < 10000; i++) {// 动态生成类String className = "Class" + i;String sourceCode = "public class " + className + " { public void test() { System.out.println(\"Hello from " + className + "\"); } }";Class<?> clazz = InMemoryCompiler.compile(className, sourceCode);// 使用反射调用生成的类的方法Method method = clazz.getMethod("test");method.invoke(clazz.newInstance());}} catch (Exception e) {e.printStackTrace();}System.out.println("元空间大小测试完成");  // 输出测试完成说明}
}

运行结果:

元空间大小测试开始
元空间大小测试完成

结论

JVM调优是一个复杂而重要的过程,需要结合具体的应用场景和系统性能数据进行调整。通过合理地设置堆内存大小、垃圾收集器以及新生代与老年代的比例,可以显著提升Java应用的性能。希望本文提供的指南和示例代码能够帮助你更好地理解和应用JVM调优技术,提高你的Java应用的性能和稳定性。

本文已收录于,我的技术网站 java-broke.site,有大厂完整面经,工作技术,架构师成长之路,等经验分享

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 标签嵌套规则/行内元素/块元素的使用
  • 算法通关:006_1二分查找
  • 设计模式简述(一)
  • 【北京迅为】《i.MX8MM嵌入式Linux开发指南》-第三篇 嵌入式Linux驱动开发篇-第三十九章 Linux MISC驱动
  • Java实战中如何使用多线程(线程池)及其为什么使用?
  • 科技引领水资源管理新篇章:深入剖析智慧水利解决方案,展现其在提升水资源利用效率、优化水环境管理方面的创新实践
  • 理解 Objective-C 中 `+load` 方法的执行顺序
  • 【大模型基础】4.1 数据挖掘(待)
  • Xubuntu22.04 终端命令调用图形设置工具
  • 化腐朽为神奇!揭开ISP图像处理的神秘面纱,基于瑞芯微RK3568J工业平台!
  • Domainim:一款高效的企业级网络安全扫描工具
  • gitee设置ssh公钥密码频繁密码验证
  • 【spring boot】初学者项目快速练手
  • 攻防世界 re新手模式
  • 分层评估的艺术:sklearn中的策略与实践
  • [js高手之路]搞清楚面向对象,必须要理解对象在创建过程中的内存表示
  • ComponentOne 2017 V2版本正式发布
  • electron原来这么简单----打包你的react、VUE桌面应用程序
  • IIS 10 PHP CGI 设置 PHP_INI_SCAN_DIR
  • Java多线程(4):使用线程池执行定时任务
  • Js实现点击查看全文(类似今日头条、知乎日报效果)
  • JWT究竟是什么呢?
  • node和express搭建代理服务器(源码)
  • Quartz实现数据同步 | 从0开始构建SpringCloud微服务(3)
  • Spring框架之我见(三)——IOC、AOP
  • 表单中readonly的input等标签,禁止光标进入(focus)的几种方式
  • 从零搭建Koa2 Server
  • 利用阿里云 OSS 搭建私有 Docker 仓库
  • 嵌入式文件系统
  • 入口文件开始,分析Vue源码实现
  • 一起参Ember.js讨论、问答社区。
  • 再谈express与koa的对比
  • hi-nginx-1.3.4编译安装
  • ​ 无限可能性的探索:Amazon Lightsail轻量应用服务器引领数字化时代创新发展
  • # 移动硬盘误操作制作为启动盘数据恢复问题
  • #Datawhale AI夏令营第4期#多模态大模型复盘
  • #HarmonyOS:基础语法
  • (01)ORB-SLAM2源码无死角解析-(56) 闭环线程→计算Sim3:理论推导(1)求解s,t
  • (3)nginx 配置(nginx.conf)
  • (C++20) consteval立即函数
  • (delphi11最新学习资料) Object Pascal 学习笔记---第8章第2节(共同的基类)
  • (纯JS)图片裁剪
  • (七)Flink Watermark
  • (四)【Jmeter】 JMeter的界面布局与组件概述
  • (学习日记)2024.02.29:UCOSIII第二节
  • (一)【Jmeter】JDK及Jmeter的安装部署及简单配置
  • (一)80c52学习之旅-起始篇
  • (一)Mocha源码阅读: 项目结构及命令行启动
  • *算法训练(leetcode)第四十五天 | 101. 孤岛的总面积、102. 沉没孤岛、103. 水流问题、104. 建造最大岛屿
  • .env.development、.env.production、.env.staging
  • .mat 文件的加载与创建 矩阵变图像? ∈ Matlab 使用笔记
  • .NET 2.0中新增的一些TryGet,TryParse等方法
  • .NET BackgroundWorker
  • .NET Framework与.NET Framework SDK有什么不同?
  • .Net 转战 Android 4.4 日常笔记(4)--按钮事件和国际化