MK
摩柯社区 - 一个极简的技术知识社区
AI 面试

Java虚拟机参数及其配置

2021-12-057.5k 阅读

Java虚拟机参数基础

Java虚拟机(JVM)参数是一系列用于控制JVM运行时行为的配置选项。这些参数可以影响内存管理、垃圾回收策略、性能调优以及调试等多个方面。在深入探讨各种参数之前,我们先来了解一些基本概念。

JVM参数分类

  1. 标准参数:这些参数是标准化的,在不同的JVM实现中基本保持一致,并且通常具有稳定的功能。它们以 - 开头,例如 -version 用于查看JVM的版本信息。示例:
java -version

这将输出当前运行的Java版本号,比如:

java version "11.0.12" 2021-07-20 LTS
Java(TM) SE Runtime Environment 18.9 (build 11.0.12+7-LTS-267)
Java HotSpot(TM) 64-Bit Server VM 18.9 (build 11.0.12+7-LTS-267, mixed mode)
  1. 非标准参数:这类参数并非标准化的,不同的JVM实现可能会有不同的含义和行为。它们以 -X 开头,例如 -Xmx 用于设置JVM堆的最大内存大小。虽然非标准,但在大多数HotSpot JVM中被广泛支持。示例:
java -Xmx512m YourMainClass

这里将JVM堆的最大内存设置为512MB。 3. 不稳定参数:这些参数是实验性的,可能会在未来的JVM版本中发生变化甚至被移除。它们以 -XX 开头,例如 -XX:+UseG1GC 用于启用G1垃圾回收器。示例:

java -XX:+UseG1GC YourMainClass

这里启用了G1垃圾回收器,注意这类参数的使用需要谨慎,因为其行为可能不稳定。

内存相关参数

堆内存参数

  1. -Xms:设置JVM堆的初始内存大小。例如,如果设置 -Xms256m,则JVM启动时堆的大小为256MB。如果应用程序在启动时就需要大量内存,适当增大 -Xms 可以避免频繁的堆扩展。示例代码如下:
public class MemoryExample {
    public static void main(String[] args) {
        byte[] data = new byte[1024 * 1024 * 100]; // 分配100MB内存
        System.out.println("Allocated 100MB memory.");
    }
}

在运行这段代码时,可以使用 -Xms256m 参数,确保一开始就有足够的堆内存:

java -Xms256m MemoryExample
  1. -Xmx:设置JVM堆的最大内存大小。如果应用程序在运行过程中需要动态分配大量内存,如处理大数据集或进行复杂的计算, -Xmx 的合理设置就至关重要。若设置过小,可能导致OutOfMemoryError。例如 -Xmx1024m 将堆的最大内存设置为1GB。假设我们有一个简单的内存占用程序:
import java.util.ArrayList;
import java.util.List;

public class LargeMemoryUsage {
    public static void main(String[] args) {
        List<byte[]> list = new ArrayList<>();
        while (true) {
            try {
                list.add(new byte[1024 * 1024]); // 每次添加1MB数据
            } catch (OutOfMemoryError e) {
                System.out.println("OutOfMemoryError caught. Maximum memory reached.");
                break;
            }
        }
    }
}

运行时可以设置 -Xmx512m

java -Xmx512m LargeMemoryUsage

程序最终会因为达到 -Xmx 设置的最大内存而抛出 OutOfMemoryError

  1. -Xmn:设置新生代的大小。新生代是对象创建和短期存活的区域。合理设置 -Xmn 可以优化垃圾回收效率,因为新生代的垃圾回收通常比老年代更频繁且速度更快。例如 -Xmn128m 将新生代大小设置为128MB。下面是一个简单的示例来演示新生代对对象生命周期的影响:
public class YoungGenerationExample {
    public static void main(String[] args) {
        for (int i = 0; i < 1000000; i++) {
            byte[] smallData = new byte[1024]; // 创建小对象
        }
        System.out.println("Created a large number of small objects.");
    }
}

运行时使用 -Xmn128m 参数:

java -Xmn128m YoungGenerationExample

通过调整 -Xmn 大小,可以观察到垃圾回收行为的变化。

非堆内存参数

  1. -XX:MetaspaceSize:在Java 8及以后的版本中,元空间(Metaspace)取代了永久代。-XX:MetaspaceSize 设置元空间的初始大小。元空间主要存储类的元数据,如类的结构、方法、常量池等。如果应用程序加载大量的类,适当调整此参数很重要。例如,设置 -XX:MetaspaceSize=64m,初始元空间大小为64MB。示例代码:
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

public class MetaspaceExample {
    public static void main(String[] args) throws Exception {
        List<Class<?>> classes = new ArrayList<>();
        while (true) {
            Class<?> clazz = Class.forName("java.lang.String");
            Method method = clazz.getMethod("length");
            classes.add(clazz);
        }
    }
}

运行时设置 -XX:MetaspaceSize=64m

java -XX:MetaspaceSize=64m MetaspaceExample

随着类的不断加载,当元空间达到初始大小后,会根据需要进行扩展。 2. -XX:MaxMetaspaceSize:设置元空间的最大大小。若不设置此参数,元空间理论上可以使用系统可用的所有内存,但在实际应用中,为了避免耗尽系统资源,通常会设置一个合理的上限。例如 -XX:MaxMetaspaceSize=256m 将元空间最大大小设置为256MB。

垃圾回收相关参数

垃圾回收器选择参数

  1. -XX:+UseSerialGC:启用串行垃圾回收器。串行垃圾回收器是单线程的,适用于单核处理器环境或对停顿时间要求不高的小型应用程序。在这种模式下,垃圾回收时会暂停所有应用线程。示例:
java -XX:+UseSerialGC YourMainClass

假设我们有一个简单的计算任务程序:

public class SerialGCTest {
    public static void main(String[] args) {
        long startTime = System.currentTimeMillis();
        for (int i = 0; i < 10000000; i++) {
            double result = Math.sqrt(i);
        }
        long endTime = System.currentTimeMillis();
        System.out.println("Execution time: " + (endTime - startTime) + " ms");
    }
}

运行时使用 -XX:+UseSerialGC

java -XX:+UseSerialGC SerialGCTest

观察其执行时间和垃圾回收的停顿情况。 2. -XX:+UseParallelGC:启用并行垃圾回收器,也称为吞吐量优先垃圾回收器。它使用多线程进行垃圾回收,适用于多核处理器环境,目标是最大化应用程序的吞吐量(即应用程序运行时间与总运行时间的比率)。示例:

java -XX:+UseParallelGC YourMainClass

同样对上述 SerialGCTest 程序,使用并行垃圾回收器运行:

java -XX:+UseParallelGC SerialGCTest

比较与串行垃圾回收器下的执行时间和吞吐量。 3. -XX:+UseConcMarkSweepGC:启用CMS(Concurrent Mark Sweep)垃圾回收器,它是一种以获取最短停顿时间为目标的垃圾回收器,适用于对响应时间要求较高的应用程序,如Web应用。CMS垃圾回收器在进行垃圾回收时,尽量与应用程序并发执行,减少停顿时间。示例:

java -XX:+UseConcMarkSweepGC YourMainClass
  1. -XX:+UseG1GC:启用G1(Garbage - First)垃圾回收器。G1是一种面向服务器的垃圾回收器,适用于多核处理器和大内存的应用程序。它将堆内存划分为多个大小相等的Region,在回收时可以根据Region中垃圾的多少,优先回收垃圾最多的Region,从而提高回收效率。示例:
java -XX:+UseG1GC YourMainClass

垃圾回收器调优参数

  1. -XX:ParallelGCThreads:对于并行垃圾回收器,此参数设置垃圾回收的并行线程数。在多核处理器环境下,适当增加此参数可以提高垃圾回收效率,但也会增加系统资源的竞争。例如 -XX:ParallelGCThreads=8 设置并行线程数为8。示例代码:
public class ParallelGCThreadsExample {
    public static void main(String[] args) {
        byte[] data = new byte[1024 * 1024 * 500]; // 分配500MB内存
        System.out.println("Allocated 500MB memory.");
    }
}

运行时使用 -XX:ParallelGCThreads=8

java -XX:ParallelGCThreads=8 ParallelGCThreadsExample

观察垃圾回收的速度和应用程序的响应。 2. -XX:CMSInitiatingOccupancyFraction:对于CMS垃圾回收器,此参数设置在老年代达到多少占用率时开始执行CMS垃圾回收周期。默认值是68%,即当老年代占用率达到68%时,启动CMS垃圾回收。如果应用程序的对象晋升到老年代的速度较快,可以适当降低此值,以避免老年代满而导致Full GC。例如 -XX:CMSInitiatingOccupancyFraction=75 将此比例设置为75%。 3. -XX:G1HeapRegionSize:对于G1垃圾回收器,此参数设置G1堆中每个Region的大小。G1会根据堆大小自动调整Region的大小,但也可以手动设置。例如 -XX:G1HeapRegionSize=16m 将每个Region大小设置为16MB。

性能调优相关参数

编译相关参数

  1. -XX:CompileThreshold:设置方法被调用多少次后会被即时编译器(JIT)编译成本地代码。默认值是10000次,对于一些短时间内频繁调用的方法,可以适当降低此值,让JIT更早地编译这些方法,提高执行效率。例如 -XX:CompileThreshold=5000 将编译阈值设置为5000次。示例代码:
public class CompileThresholdExample {
    public static void main(String[] args) {
        for (int i = 0; i < 100000; i++) {
            calculate(i);
        }
    }

    private static double calculate(int num) {
        return Math.sqrt(num);
    }
}

运行时使用 -XX:CompileThreshold=5000

java -XX:CompileThreshold=5000 CompileThresholdExample

观察执行时间的变化。 2. -XX:+TieredCompilation:启用分层编译。分层编译将编译过程分为多个层次,不同层次的编译优化程度不同。这可以在启动速度和运行效率之间取得较好的平衡。默认情况下,Java 7及以后的版本是启用分层编译的。示例:

java -XX:+TieredCompilation YourMainClass

其他性能参数

  1. -XX:MaxDirectMemorySize:设置直接内存的最大大小。直接内存是Java堆外的一块内存区域,常用于NIO操作等场景。如果应用程序大量使用直接内存,如进行文件I/O或网络通信,合理设置此参数可以避免OutOfMemoryError。例如 -XX:MaxDirectMemorySize=256m 将直接内存最大大小设置为256MB。示例代码:
import java.nio.ByteBuffer;

public class DirectMemoryExample {
    public static void main(String[] args) {
        ByteBuffer buffer = ByteBuffer.allocateDirect(1024 * 1024 * 100); // 分配100MB直接内存
        System.out.println("Allocated 100MB direct memory.");
    }
}

运行时使用 -XX:MaxDirectMemorySize=256m

java -XX:MaxDirectMemorySize=256m DirectMemoryExample
  1. -Dsun.nio.ch.bugLevel:此参数用于调整NIO(New I/O)相关的性能和行为。例如,设置 -Dsun.nio.ch.bugLevel=release 可以优化NIO的性能,适用于生产环境。示例:
java -Dsun.nio.ch.bugLevel=release YourMainClass

调试相关参数

日志参数

  1. -Xloggc:filename:将垃圾回收日志输出到指定文件。例如 -Xloggc:gc.log 会将垃圾回收的详细信息记录到 gc.log 文件中。通过分析这些日志,可以了解垃圾回收的频率、停顿时间、内存使用情况等,从而进行垃圾回收调优。示例代码:
public class GCLogExample {
    public static void main(String[] args) {
        byte[] data = new byte[1024 * 1024 * 100]; // 分配100MB内存
        System.out.println("Allocated 100MB memory.");
    }
}

运行时使用 -Xloggc:gc.log

java -Xloggc:gc.log GCLogExample

查看 gc.log 文件内容,例如:

2023-09-15T10:00:00.123+0800: 0.123: [GC (Allocation Failure) [PSYoungGen: 30720K->5120K(32768K)] 30720K->15360K(126976K), 0.001234 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]

这里记录了一次新生代垃圾回收的相关信息,包括回收前后新生代和堆的内存使用情况,以及回收耗时。 2. -XX:+PrintGCDetails:在控制台输出详细的垃圾回收信息。与 -Xloggc 结合使用,可以在开发和调试过程中更方便地查看垃圾回收的细节。示例:

java -XX:+PrintGCDetails -Xloggc:gc.log YourMainClass

输出内容类似:

[GC (Allocation Failure) [PSYoungGen: 30720K->5120K(32768K)] 30720K->15360K(126976K), 0.001234 secs]
Heap
 PSYoungGen      total 32768K, used 21024K [0x00000007bfd00000, 0x00000007c1d00000, 0x00000007c1d00000)
  eden space 28672K, 73% used [0x00000007bfd00000,0x00000007c15340a0,0x00000007c1500000)
  from space 4096K, 125% used [0x00000007c1500000,0x00000007c1a00010,0x00000007c1900000)
  to   space 4096K, 0% used [0x00000007c1900000,0x00000007c1900000,0x00000007c1d00000)
 ParOldGen       total 94208K, used 10240K [0x00000007b9800000, 0x00000007bfd00000, 0x00000007bfd00000)
  object space 94208K, 10% used [0x00000007b9800000,0x00000007b9d00010,0x00000007bfd00000)
 Metaspace       used 3283K, capacity 4496K, committed 4864K, reserved 1056768K
  class space    used 358K, capacity 388K, committed 512K, reserved 1048576K

详细展示了堆内存各个区域的使用情况。

调试工具参数

  1. -agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=5005:启用远程调试功能。通过此参数,JVM会启动一个调试服务器,允许外部调试工具(如Eclipse、Intellij IDEA)连接到该JVM进行调试。transport=dt_socket 表示使用套接字传输,server=y 表示作为服务器端,suspend=y 表示JVM启动后暂停,等待调试器连接,address=5005 表示监听在5005端口。示例:
java -agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=5005 YourMainClass

在IDE中配置远程调试,连接到 localhost:5005 即可对程序进行调试。 2. -XX:+HeapDumpOnOutOfMemoryError:当发生OutOfMemoryError时,自动生成堆转储文件(heap dump)。堆转储文件包含了JVM堆内存的快照,通过分析此文件可以找出导致内存溢出的原因。例如 -XX:HeapDumpPath=/tmp/heapdump.hprof 设置堆转储文件的路径为 /tmp/heapdump.hprof。示例代码:

public class HeapDumpExample {
    public static void main(String[] args) {
        List<byte[]> list = new ArrayList<>();
        while (true) {
            list.add(new byte[1024 * 1024]); // 不断分配内存
        }
    }
}

运行时使用 -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/tmp/heapdump.hprof

java -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/tmp/heapdump.hprof HeapDumpExample

当程序抛出 OutOfMemoryError 时,会在 /tmp 目录下生成 heapdump.hprof 文件,可以使用工具如MAT(Memory Analyzer Tool)进行分析。

在实际应用中,合理配置JVM参数对于提高应用程序的性能、稳定性和可维护性至关重要。需要根据应用程序的特点、硬件环境以及业务需求,不断试验和调整参数,以达到最佳的运行效果。同时,随着JVM技术的不断发展,新的参数和功能也会不断涌现,开发者需要持续关注并学习相关知识。