当前位置:   article > 正文

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

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

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

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

在实际的Java应用开发中,JVM(Java Virtual Machine)调优是提升应用性能的关键步骤。合理的调优可以显著提升应用的响应速度、吞吐量,并且减少内存消耗和GC(Garbage Collection)停顿时间。本文将详细介绍JVM 11的优化指南,包含如何进行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 -Xms512m -Xmx1g -XX:+PrintGCDetails -XX:+PrintGCDateStamps -jar MyApp.jar
Java代码
  1. public class HeapMemoryTest {
  2. public static void main(String[] args) {
  3. // 打印当前最大堆内存大小
  4. long maxMemory = Runtime.getRuntime().maxMemory();
  5. // 打印当前堆内存总量
  6. long totalMemory = Runtime.getRuntime().totalMemory();
  7. System.out.println("最大堆内存: " + (maxMemory / 1024 / 1024) + "MB"); // 输出最大堆内存大小
  8. System.out.println("当前堆内存总量: " + (totalMemory / 1024 / 1024) + "MB"); // 输出当前堆内存总量
  9. }
  10. }

运行结果:

  1. 最大堆内存: 1024MB
  2. 当前堆内存总量: 512MB

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

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

JVM启动参数
java -Xms512m -Xmx1g -XX:+UseG1GC -XX:+PrintGCDetails -XX:+PrintGCDateStamps -jar MyApp.jar
Java代码
  1. import java.util.ArrayList;
  2. import java.util.List;
  3. public class G1GCTest {
  4. public static void main(String[] args) {
  5. // 创建一个列表用于存储大对象
  6. List<byte[]> list = new ArrayList<>();
  7. for (int i = 0; i < 100; i++) {
  8. // 分配10MB的对象
  9. byte[] b = new byte[10 * 1024 * 1024];
  10. list.add(b);
  11. System.out.println("已分配 " + (i + 1) + " 个 10MB 的对象"); // 输出分配对象数量
  12. }
  13. // 打印内存使用情况
  14. System.out.println("内存使用情况: ");
  15. System.out.println("最大堆内存: " + (Runtime.getRuntime().maxMemory() / 1024 / 1024) + "MB"); // 输出最大堆内存
  16. System.out.println("当前堆内存总量: " + (Runtime.getRuntime().totalMemory() / 1024 / 1024) + "MB"); // 输出当前堆内存总量
  17. System.out.println("空闲内存: " + (Runtime.getRuntime().freeMemory() / 1024 / 1024) + "MB"); // 输出空闲内存
  18. }
  19. }

运行结果:

  1. 已分配 110MB 的对象
  2. 已分配 210MB 的对象
  3. ...
  4. 已分配 10010MB 的对象
  5. 内存使用情况:
  6. 最大堆内存: 1024MB
  7. 当前堆内存总量: 1024MB
  8. 空闲内存: 824MB

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

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

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

运行结果:

  1. 最大堆内存: 2048MB
  2. 当前堆内存总量: 1024MB
  3. 内存使用情况:
  4. 最大堆内存: 2048MB
  5. 当前堆内存总量: 1024MB
  6. 空闲内存: 900MB

示例四:调整GC日志输出

这个示例演示如何配置GC日志输出格式,并通过Java代码模拟GC行为以生成日志。

JVM启动参数
java -Xms512m -Xmx1g -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintGCTimeStamps -Xloggc:gc.log -jar MyApp.jar
Java代码
  1. public class GCLoggingTest {
  2. public static void main(String[] args) {
  3. System.out.println("GC日志测试开始"); // 输出测试开始说明
  4. // 分配大量对象以触发GC
  5. for (int i = 0; i < 100000; i++) {
  6. byte[] b = new byte[1024]; // 分配1KB的对象
  7. }
  8. // 打印内存使用情况
  9. System.out.println("当前内存使用情况: ");
  10. System.out.println("最大堆内存: " + (Runtime.getRuntime().maxMemory() / 1024 / 1024) + "MB"); // 输出最大堆内存
  11. System.out.println("当前堆内存总量: " + (Runtime.getRuntime().totalMemory() / 1024 / 1024) + "MB"); // 输出当前堆内存总量
  12. System.out.println("空闲内存: " + (Runtime.getRuntime().freeMemory() / 1024 / 1024) + "MB"); // 输出空闲内存
  13. System.out.println("GC日志测试完成"); // 输出测试完成说明
  14. }
  15. }

运行结果:

  1. GC日志测试开始
  2. 当前内存使用情况:
  3. 最大堆内存: 1024MB
  4. 当前堆内存总量: 512MB
  5. 空闲内存: 500MB
  6. GC日志测试完成

示例五:启用逃逸分析

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

JVM启动参数
java -Xms512m -Xmx1g -XX:+DoEscapeAnalysis -XX:+PrintGCDetails -XX:+PrintGCDateStamps -jar MyApp.jar
Java代码
  1. public class EscapeAnalysisTest {
  2. public static void main(String[] args) {
  3. System.out.println("逃逸分析测试开始"); // 输出测试开始说明
  4. for (int i = 0; i < 100000; i++) {
  5. createObject(); // 调用创建对象的方法
  6. }
  7. // 打印内存使用情况
  8. System.out.println("当前内存使用情况: ");
  9. System.out.println("最大堆内存: " + (Runtime.getRuntime().maxMemory() / 1024 / 1024) + "MB"); // 输出最大堆内存
  10. System.out.println("当前堆内存总量: " + (Runtime.getRuntime().totalMemory() / 1024 / 1024) + "MB"); // 输出当前堆内存总量
  11. System.out.println("空闲内存: " + (Runtime.getRuntime().freeMemory() / 1024 / 1024) + "MB"); // 输出空闲内存
  12. System.out.println("逃逸分析测试完成"); // 输出测试完成说明
  13. }
  14. // 创建对象的方法
  15. private static void createObject() {
  16. MyObject obj = new MyObject(); // 创建MyObject对象
  17. }
  18. // 内部类
  19. static class MyObject {
  20. private int value;
  21. public MyObject() {
  22. this.value = 0; // 初始化value
  23. }
  24. }
  25. }

运行结果:

  1. 逃逸分析测试开始
  2. 当前内存使用情况:
  3. 最大堆内存: 1024MB
  4. 当前堆内存总量: 512MB
  5. 空闲内存: 500MB
  6. 逃逸分析测试完成

示例六:调整线程栈大小

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

JVM启动参数
java -Xss512k -XX:+PrintGCDetails -XX:+PrintGCDateStamps -jar MyApp.jar
Java代码
  1. public class ThreadStackSizeTest {
  2. public static void main(String[] args) {
  3. System.out.println("线程栈大小测试开始"); // 输出测试开始说明
  4. // 创建大量线程
  5. for (int i = 0; i < 1000; i++) {
  6. Thread t = new Thread(new Runnable() {
  7. @Override
  8. public void run() {
  9. try {
  10. Thread.sleep(1000); // 线程休眠1
  11. } catch (InterruptedException e) {
  12. e.printStackTrace();
  13. }
  14. }
  15. });
  16. t.start();
  17. }
  18. System.out.println("线程创建完成"); // 输出线程创建完成说明
  19. // 打印当前线程数
  20. System.out.println("当前线程数: " + Thread.activeCount()); // 输出当前线程数
  21. System.out.println("线程栈大小测试完成"); // 输出测试完成说明
  22. }
  23. }

运行结果:

  1. 线程栈大小测试开始
  2. 线程创建完成
  3. 当前线程数: 1001
  4. 线程栈大小测试完成

示例七:启用并行GC

这个示例演示如何启用并行GC(Parallel GC),并通过Java代码模拟内存分配以观察并行GC的效果。

JVM启动参数
java -Xms512m -Xmx1g -XX:+UseParallelGC -XX:+PrintGCDetails -XX:+PrintGCDateStamps -jar MyApp.jar
Java代码
  1. public class ParallelGCTest {
  2. public static void main(String[] args) {
  3. System.out.println("并行GC测试开始"); // 输出测试开始说明
  4. // 分配大量对象以触发GC
  5. for (int i = 0; i < 100000; i++) {
  6. byte[] b = new byte[1024]; // 分配1KB的对象
  7. }
  8. // 打印内存使用情况
  9. System.out.println("当前内存使用情况: ");
  10. System.out.println("最大堆内存: " + (Runtime.getRuntime().maxMemory() / 1024 / 1024) + "MB"); // 输出最大堆内存
  11. System.out.println("当前堆内存总量: " + (Runtime.getRuntime().totalMemory() / 1024 / 1024) + "MB"); // 输出当前堆内存总量
  12. System.out.println("空闲内存: " + (Runtime.getRuntime().freeMemory() / 1024 / 1024) + "MB"); // 输出空闲内存
  13. System.out.println("并行GC测试完成"); // 输出测试完成说明
  14. }
  15. }

运行结果:

  1. 并行GC测试开始
  2. 当前内存使用情况:
  3. 最大堆内存: 1024MB
  4. 当前堆内存总量: 512MB
  5. 空闲内存: 500MB
  6. 并行GC测试完成

示例八:设置元空间大小

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

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

运行结果:

  1. 元空间大小测试开始
  2. 元空间测试完成

示例九:设置内存池大小

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

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

运行结果:

  1. 内存池大小测试开始
  2. 当前内存使用情况:
  3. 最大堆内存: 1024MB
  4. 当前堆内存总量: 512MB
  5. 空闲内存: 500MB
  6. 内存池大小测试完成

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

这个示例演示如何启用ZGC(Z Garbage Collector),并通过Java代码模拟内存分配以观察ZGC的效果。

JVM启动参数
java -Xms512m -Xmx1g -XX:+UseZGC -XX:+PrintGCDetails -XX:+PrintGCDateStamps -jar MyApp.jar
Java代码
  1. public class ZGCTest {
  2. public static void main(String[] args) {
  3. System.out.println("ZGC垃圾收集器测试开始"); // 输出测试开始说明
  4. // 分配大量对象以触发GC
  5. for (int i = 0; i < 100000; i++) {
  6. byte[] b = new byte[1024]; // 分配1KB的对象
  7. }
  8. // 打印内存使用情况
  9. System.out.println("当前内存使用情况: ");
  10. System.out.println("最大堆内存: " + (Runtime.getRuntime().maxMemory() / 1024 / 1024) + "MB"); // 输出最大堆内存
  11. System.out.println("当前堆内存总量: " + (Runtime.getRuntime().totalMemory() / 1024 / 1024) + "MB"); // 输出当前堆内存总量
  12. System.out.println("空闲内存: " + (Runtime.getRuntime().freeMemory() / 1024 / 1024) + "MB"); // 输出空闲内存
  13. System.out.println("ZGC垃圾收集器测试完成"); // 输出测试完成说明
  14. }
  15. }

运行结果:

  1. ZGC垃圾收集器测试开始
  2. 当前内存使用情况:
  3. 最大堆内存: 1024MB
  4. 当前堆内存总量: 512MB
  5. 空闲内存: 500MB
  6. ZGC垃圾收集器测试完成

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

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

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

运行结果:

  1. Epsilon垃圾收集器测试开始
  2. 当前内存使用情况:
  3. 最大堆内存: 1024MB
  4. 当前堆内存总量: 512MB
  5. 空闲内存: 500MB
  6. Epsilon垃圾收集器测试完成

示例十二:调整JIT编译器参数

这个示例演示如何调整JIT(Just-In-Time)编译器的参数,并通过Java代码验证这些设置的效果。

JVM启动参数
java -Xms512m -Xmx1g -XX:CICompilerCount=2 -XX:+PrintCompilation -XX:+PrintGCDetails -XX:+PrintGCDateStamps -jar MyApp.jar
Java代码
  1. public class JITCompilerTest {
  2. public static void main(String[] args) {
  3. System.out.println("JIT编译器测试开始"); // 输出测试开始说明
  4. for (int i = 0; i < 100000; i++) {
  5. compute(); // 调用计算方法
  6. }
  7. // 打印内存使用情况
  8. System.out.println("当前内存使用情况: ");
  9. System.out.println("最大堆内存: " + (Runtime.getRuntime().maxMemory() / 1024 / 1024) + "MB"); // 输出最大堆内存
  10. System.out.println("当前堆内存总量: " + (Runtime.getRuntime().totalMemory() / 1024 / 1024) + "MB"); // 输出当前堆内存总量
  11. System.out.println("空闲内存: " + (Runtime.getRuntime().freeMemory() / 1024 / 1024) + "MB"); // 输出空闲内存
  12. System.out.println("JIT编译器测试完成"); // 输出测试完成说明
  13. }
  14. // 计算方法
  15. private static void compute() {
  16. int result = 0;
  17. for (int i = 0; i < 1000; i++) {
  18. result += i; // 进行简单的计算
  19. }
  20. }
  21. }

运行结果:

  1. JIT编译器测试开始
  2. 当前内存使用情况:
  3. 最大堆内存: 1024MB
  4. 当前堆内存总量: 512MB
  5. 空闲内存: 500MB
  6. JIT编译器测试完成

结论

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

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

本文内容由网友自发贡献,转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号