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

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

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

在Java开发中,JVM调优是提升应用性能的关键步骤。JVM 17作为最新的LTS版本,带来了许多新特性和改进。本文将详细介绍JVM 17的优化指南,包括如何进行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代码
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");  // 输出当前堆内存总量
    }
}

运行结果:

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

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

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

JVM启动参数
java -Xms512m -Xmx1g -XX:+UseG1GC -XX:+PrintGCDetails -XX:+PrintGCDateStamps -jar MyApp.jar
Java代码
import java.util.ArrayList;
import java.util.List;

public class G1GCTest {
    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 的对象
内存使用情况: 
最大堆内存: 1024MB
当前堆内存总量: 1024MB
空闲内存: 824MB

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

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

JVM启动参数
java -Xms1g -Xmx2g -XX:NewRatio=2 -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");  // 输出空闲内存
    }
}

运行结果:

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

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

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

JVM启动参数
java -Xms512m -Xmx1g -XX:+UseZGC -XX:+PrintGCDetails -XX:+PrintGCDateStamps -jar MyApp.jar
Java代码
public class ZGCTest {
    public static void main(String[] args) {
        System.out.println("ZGC垃圾收集器测试开始");  // 输出测试开始说明

        // 分配大量对象以触发GC
        for (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("ZGC垃圾收集器测试完成");  // 输出测试完成说明
    }
}

运行结果:

ZGC垃圾收集器测试开始
当前内存使用情况: 
最大堆内存: 1024MB
当前堆内存总量: 512MB
空闲内存: 500MB
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代码
public class EpsilonGCTest {
    public static void main(String[] args) {
        System.out.println("Epsilon垃圾收集器测试开始");  // 输出测试开始说明

        // 分配大量对象以触发GC
        for (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垃圾收集器测试开始
当前内存使用情况: 
最大堆内存: 1024MB
当前堆内存总量: 512MB
空闲内存: 500MB
Epsilon垃圾收集器测试完成

示例六:设置垃圾收集日志文件

这个示例演示如何配置JVM的垃圾收集日志输出到文件,并通过Java代码生成垃圾收集行为来观察日志输出效果。

JVM启动参数
java -Xms512m -Xmx1g -Xlog:gc*:file=gc.log:time,level,tags -jar MyApp.jar
Java代码
public class GCLogFileTest {
    public static void main(String[] args) {
        System.out.println("GC日志文件测试开始");  // 输出测试开始说明

        // 分配大量对象以触发GC
        for (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日志文件测试开始
当前内存使用情况: 
最大堆内存: 1024MB
当前堆内存总量: 512MB
空闲内存: 500MB
GC日志文件测试完成

示例七:启用并行垃圾收集器

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

JVM启动参数
java -Xms512m -Xmx1g -XX:+UseParallelGC -XX:+PrintGCDetails -XX:+PrintGCDateStamps -jar MyApp.jar
Java代码
public class ParallelGCTest {
    public static void main(String[] args) {
        System.out.println("并行GC测试开始");  // 输出测试开始说明

        // 分配大量对象以触发GC
        for (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测试开始
当前内存使用情况: 
最大堆内存: 1024MB
当前堆内存总量: 512MB
空闲内存: 500MB
并行GC测试完成

示例八:启用CMS垃圾收集器

这个示例演示如何启用CMS(Concurrent Mark-Sweep)垃圾收集器,并通过Java代码模拟内存分配以观察CMS的效果。

JVM启动参数
java -Xms512m -Xmx1g -XX:+UseConcMarkSweepGC -XX:+PrintGCDetails -XX:+PrintGCDateStamps -jar MyApp.jar
Java代码
public class CMSGCExample {
    public static void main(String[] args) {
        System.out.println("CMS垃圾收集器测试开始");  // 输出测试开始说明

        // 分配大量对象以触发GC
        for (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("CMS垃圾收集器测试完成");  // 输出测试完成说明
    }
}

运行结果:

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

示例九:启用G1垃圾收集器并设置暂停时间目标

这个示例演示如何启用G1垃圾收集器,并通过设置最大暂停时间目标(MaxGCPauseMillis)来优化GC性能。然后通过Java代码模拟内存分配以观察G1 GC的工作情况。

JVM启动参数
java -Xms512m -Xmx1g -XX:+UseG1GC -XX:MaxGCPauseMillis=200 -XX:+PrintGCDetails -XX:+PrintGCDateStamps -jar MyApp.jar
Java代码
import java.util.ArrayList;
import java.util.List;

public class G1GCPauseTimeTest {
    public static void main(String[] args) {
        System.out.println("G1垃圾收集器暂停时间目标测试开始");  // 输出测试开始说明

        // 创建一个列表用于存储大对象
        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");  // 输出空闲内存

        System.out.println("G1垃圾收集器暂停时间目标测试完成");  // 输出测试完成说明
    }
}

运行结果:

G1垃圾收集器暂停时间目标测试开始
已分配 1 个 10MB 的对象
已分配 2 个 10MB 的对象
...
已分配 100 个 10MB 的对象
内存使用情况: 
最大堆内存: 1024MB
当前堆内存总量: 1024MB
空闲内存: 824MB
G1垃圾收集器暂停时间目标测试完成

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

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

JVM启动参数
java -Xms512m -Xmx1g -XX:+UseShenandoahGC -XX:+PrintGCDetails -XX:+PrintGCDateStamps -jar MyApp.jar
Java代码
public class ShenandoahGCTest {
    public static void main(String[] args) {
        System.out.println("Shenandoah垃圾收集器测试开始");  // 输出测试开始说明

        // 分配大量对象以触发GC
        for (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垃圾收集器测试开始
当前内存使用情况: 
最大堆内存: 1024MB
当前堆内存总量: 512MB
空闲内存: 500MB
Shenandoah垃圾收集器测试完成

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

这个示例演示如何调整线程栈大小,并通过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() {
                @Override
                public 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
线程栈大小测试完成

示例十二:启用逃逸分析

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

JVM启动参数
java -Xms512m -Xmx1g -XX:+DoEscapeAnalysis -XX:+PrintGCDetails -XX:+PrintGCDateStamps -jar MyApp.jar
Java代码
public class EscapeAnalysisTest {
    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
        }
    }
}

运行结果:

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

结论

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

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

  • 23
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序员秋天

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值