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

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

在实际的Java应用开发中,JVM(Java Virtual Machine)调优是提升应用性能的关键步骤。合理的调优可以显著提升应用的响应速度、吞吐量,并且减少内存消耗和GC(Garbage Collection)停顿时间。本文将详细介绍JVM 8的优化指南,包含如何进行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 -Xms256m -Xmx512m -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"); // 输出当前堆内存总量

}

}

运行结果:

最大堆内存: 512MB

当前堆内存总量: 256MB

示例二:使用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 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

示例四:调整元空间大小

这个示例演示如何调整元空间(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) {

// 打印当前Metaspace大小设置

System.out.println("当前Metaspace大小设置: 64MB 初始, 128MB 最大"); // 输出Metaspace大小设置说明

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("元空间测试完成"); // 输出测试完成说明

}

}

运行结果:

当前Metaspace大小设置: 64MB 初始, 128MB 最大

元空间测试完成

示例五:调整GC日志输出

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

JVM启动参数

java -Xms512m -Xmx1g -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintGCTimeStamps -Xloggc:gc.log -jar MyApp.jar

Java代码

public class GCLoggingTest {

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日志测试完成

示例六:调整线程栈大小

这个示例演示如何调整线程栈大小,并通过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:ParallelGCThreads=4 -XX:+PrintGCDetails -XX:+PrintGCDateStamps -jar MyApp.jar

Java代码

public class GCThreadsTest {

public static void main(String[] args) {

System.out.println("垃圾收集器线程数测试开始"); // 输出测试开始说明

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

}

}

运行结果:

垃圾收集器线程数测试开始

当前内存使用情况:

最大堆内存: 1024MB

当前堆内存总量: 512MB

空闲内存: 500MB

垃圾收集器线程数测试完成

示例八:调整逃逸分析参数

这个示例演示如何启用逃逸分析,并通过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

逃逸分析测试完成

示例九:调整JIT编译器参数

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

JVM启动参数

java -Xms512m -Xmx1g -XX:CICompilerCount=2 -XX:+PrintCompilation -XX:+PrintGCDetails -XX:+PrintGCDateStamps -jar MyApp.jar

Java代码

public class JITCompilerTest {

public static void main(String[] args) {

System.out.println("JIT编译器测试开始"); // 输出测试开始说明

for (int i = 0; i < 100000; i++) {

compute(); // 调用计算方法

}

// 打印内存使用情况

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("JIT编译器测试完成"); // 输出测试完成说明

}

// 计算方法

private static void compute() {

int result = 0;

for (int i = 0; i < 1000; i++) {

result += i; // 进行简单的计算

}

}

}

运行结果:

JIT编译器测试开始

当前内存使用情况:

最大堆内存: 1024MB

当前堆内存总量: 512MB

空闲内存: 500MB

JIT编译器测试完成

示例十:设置CMS垃圾收集器

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

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垃圾收集器的相关参数,并通过Java代码模拟内存分配以观察效果。

JVM启动参数

java -Xms512m -Xmx1g -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垃圾收集器测试开始"); // 输出测试开始说明

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

}

}

运行结果:

G1垃圾收集器测试开始

当前内存使用情况:

最大堆内存: 1024MB

当前堆内存总量: 512MB

空闲内存: 500MB

G1垃圾收集器测试完成

示例十二:设置内存池大小

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

JVM启动参数

java -Xms512m -Xmx1g -XX:NewSize=256m -XX:MaxNewSize=256m -XX:SurvivorRatio=6 -XX:+PrintGCDetails -XX:+PrintGCDateStamps -jar MyApp.jar

Java代码

public class MemoryPoolExample {

public static void main(String[] args) {

System.out.println("内存池大小测试开始"); // 输出测试开始说明

// 分配大量对象以触发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("内存池大小测试完成"); // 输出测试完成说明

}

}

运行结果:

内存池大小测试开始

当前内存使用情况:

最大堆内存: 1024MB

当前堆内存总量: 512MB

空闲内存: 500MB

内存池大小测试完成

结语

通过这些示例,我们可以深入了解JVM 8的调优策略和方法。合理使用JVM调优参数,可以帮助您的Java应用实现更高效、稳定的性能。希望这些示例能帮助您在实际工作中更有效地进行JVM调优。

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