jvm之你需要知道的配置

426 阅读7分钟

前言

所有的配置都是笔者所了解的,欢迎读者进行补充。

环境:jdk1.8

常用配置

推荐配置

  • -XX:-HeapDumpOnOutOfMemoryError 默认关闭,建议开启,在java.lang.OutOfMemoryError异常出现时,输出一个dump文件,记录当时的堆内存。
  • -XX:HeapDumpPath=./java_pid<pid>.hprof用来设置堆快照的存储文件路径,默认是java进程启动位置。
  • -Xmx用来控制最多堆空间。

调试

  • -XX:+PrintGC 打印简单的GC信息。默认关闭。
  • -XX:+PrintGCDetails 打印详细的GC信息。默认关闭。
  • -XX:+PrintGCDateStamps 打印GC发生的时间。
  • -Xloggc:<filename> 指定gc日志重定向到指定文件

-XX:+PrintGCDetails

我们使用在《JVM之内存结构》的逃逸分析的例子,进行分析下(使用的配置java -XX:-DoEscapeAnalysis -XX:+PrintGCDetails -XX:+PrintGCDateStamps -Xmx500m -Xms500m,设置的Xmx和Xms的目的是输出的信息比较好理解 ):

/**
 * 逃逸分析-栈上分配
 * -XX:-DoEscapeAnalysis
 */
public class EscapeAnalysisTest {
    public static void main(String[] args) throws Exception {
        long start = System.currentTimeMillis();
        for (int i = 0; i < 50000000; i++) {//5000万次---5000万个对象
            allocate();
        }
        System.out.println((System.currentTimeMillis() - start) + " ms");
        Thread.sleep(3000);
    }

    static void allocate() {//逃逸分析(不会逃逸出方法)
        //这个myObject引用没有出去,也没有其他方法使用
        MyObject myObject = new MyObject(2020, 2020.6);
    }

    static class MyObject {
        int a;
        double b;

        MyObject(int a, double b) {
            this.a = a;
            this.b = b;
        }
    }
}

日志输出:

//发生minor gc的时候年轻代从125.5M回收到还剩余0.45M 总的堆空间从125.5M回收到还剩余0.45M 实际消耗了0.01秒
2022-03-12T22:41:28.457-0800: [GC (Allocation Failure) [PSYoungGen: 128512K->464K(149504K)] 128512K->464K(491008K), 0.0051801 secs] [Times: user=0.01 sys=0.00, real=0.01 secs] 
2022-03-12T22:41:28.493-0800: [GC (Allocation Failure) [PSYoungGen: 128976K->432K(149504K)] 128976K->440K(491008K), 0.0006600 secs] [Times: user=0.01 sys=0.00, real=0.00 secs] 
2022-03-12T22:41:28.502-0800: [GC (Allocation Failure) [PSYoungGen: 128944K->448K(149504K)] 128952K->456K(491008K), 0.0006161 secs] [Times: user=0.01 sys=0.00, real=0.00 secs] 
2022-03-12T22:41:28.510-0800: [GC (Allocation Failure) [PSYoungGen: 128960K->368K(149504K)] 128968K->376K(491008K), 0.0005145 secs] [Times: user=0.01 sys=0.00, real=0.00 secs] 
2022-03-12T22:41:28.520-0800: [GC (Allocation Failure) [PSYoungGen: 128880K->432K(149504K)] 128888K->440K(491008K), 0.0006140 secs] [Times: user=0.01 sys=0.00, real=0.00 secs] 
2022-03-12T22:41:28.528-0800: [GC (Allocation Failure) [PSYoungGen: 128944K->416K(169984K)] 128952K->424K(511488K), 0.0005380 secs] [Times: user=0.01 sys=0.00, real=0.00 secs] 
2022-03-12T22:41:28.555-0800: [GC (Allocation Failure) [PSYoungGen: 169888K->0K(169984K)] 169896K->308K(511488K), 0.0006665 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
2022-03-12T22:41:28.576-0800: [GC (Allocation Failure) [PSYoungGen: 169472K->0K(168960K)] 169780K->308K(510464K), 0.0003318 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
//程序打印的日志
191 ms
//最后一次GC的时候堆的情况
Heap
//年轻代占用总空间165M 使用了69M  
 PSYoungGen      total 168960K, used 70715K [0x00000007b5980000, 0x00000007c0000000, 0x00000007c0000000)
  //eden区占用164.5M 使用了41%
  eden space 168448K, 41% used [0x00000007b5980000,0x00000007b9e8edc0,0x00000007bfe00000)
  //from区 占用了0.5M 使用了0%
  from space 512K, 0% used [0x00000007bff80000,0x00000007bff80000,0x00000007c0000000)
  //to区 占用了1M 使用l0%
  to   space 1024K, 0% used [0x00000007bfe00000,0x00000007bfe00000,0x00000007bff00000)
 //老年代占用了 333.5M 使用了308k
 ParOldGen       total 341504K, used 308K [0x00000007a0c00000, 0x00000007b5980000, 0x00000007b5980000)
  //对象空间333.5M 使用了0%
  object space 341504K, 0% used [0x00000007a0c00000,0x00000007a0c4d060,0x00000007b5980000)
 // 元空间(包括类空间和非类空间(常量池、JIT产生的一些东西等)) 使用了2.47M 实际分配的chunk大小4.38M 分配的chunk大小4.75M 空间总和1032M
 Metaspace       used 2531K, capacity 4490K, committed 4864K, reserved 1056768K
  //类空间 使用了271k 实际分配的chunk大小0.37M 分配的chunk大小0.5M 空间总和1024M
  class space    used 271K, capacity 386K, committed 512K, reserved 1048576K

老年代和年轻代总的空间约等于指定的500M。发生了8次minor gc,最后一次显示年轻代已经全部回收完,老年代使用了308k,但是不是对象使用的空间,应该还有一些字符串常量池(jdk1.7及之后移动到堆内)等。eden survivor的比例不是8,因为默认开启动态分配。

元空间用来存放类元信息和常量池、JIT产生的一些东西等,used、capacity、committed、reserved具体参考

image.png

需要知道的

  • -XX:+PrintFlagsFinal –version 参看jvm参数配置信息命令
  • -Xss 线程栈默认分配1k。
  • -Xms 最小堆空间,默认128M。
  • -Xmx 最大堆空间,默认2G。
  • -XX:MaxDirectMemorySize 默认为0
  • -XX:SurvivorRatio 对年轻代进行划分比例,默认为8,Eden区占8/10。
  • -XX:+UsePSAdaptiveSurvivorSizePolicy 默认开启,根据情况动态划分比例。
  • -XX:+MaxTenuringThreshold 默认15,控制年轻代的对象在survivor区中来回被复制多少次后进入老年代
  • -XX:TargetSurvivorRatio 默认50%。如果survivor区中的对象占用了多少空间后,较高复制次数的对象会进入老年区。
  • -XX:+PrintCommandLineFlags -version 打印出现在命令行上的标志。
-XX:InitialHeapSize=134217728 -XX:MaxHeapSize=2147483648 -XX:+PrintCommandLineFlags -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:+UseParallelGC
java version "1.8.0_211"
Java(TM) SE Runtime Environment (build 1.8.0_211-b12)
Java HotSpot(TM) 64-Bit Server VM (build 25.211-b12, mixed mode)

默认使用ParallelGC垃圾收集器。

可以用于优化的

  • -Xms:默认128M,可以设置大点,避免根据不足再动态调整产生gc。

  • -Xmx:默认为2G,服务器允许可以设置大点,避免不足产生gc。

  • -Xss:默认为1k,能放128个long变量,一般业务够了,但是jvm做了对象逃逸分析,不逃逸的可以直接在栈上分配,可以根据情况设置大点。

  • -XX:-UseBiasedLocking:用于禁用偏向锁,直接使用轻量锁。

    因为生产环境如果使用了synchronized锁,大部分场景不存在只有一个线程竞争。如果有多个线程先后尝试进入临界区,第一个线程先持有偏向锁,第二个线程尝试进入的时候需要等待全局安全点,再暂停拥有偏向锁的线程,判断它是否还存活,死亡了就释放占用,还活着就升级为“轻量锁”,整个步骤其实很繁琐效率也不高。

  • -XX:MaxDirectMemorySize:默认为0,如果需要用到堆外内存需要设置。比如nio中使用ByteBuffer.allocateDirect避免堆内堆外来回拷贝。

  • -XX:newSize:默认为40M,虽然会扩容可以根据情况分配大点,相当于预热避免前期的ygc。

  • -Dsun.reflect.inflationThreshold= 15 :默认达到15次时动态生成字节码对反射进行优化。可以设置小点或者使用-Dsun.reflect.noInflation=true来关闭,直接生产字节码,一步优化。

  • -XX:MetaspaceSize=:默认为20M,可以根据情况设置大点,比如项目比较大,动态生成字节码的地方比较多,避免扩容耗时。

  • -XX:TypeProfileWidth:默认为2,虚方法内联记录类型数目,能使虚方法调用变快,可以根据情况设置大点。

  • 垃圾收集器:默认-XX:+UseParallelGC,可以使用-XX:+UseG1GC化整为零整体上不STW。