"[图片] JVM 调优有许多参数优化,下面整理了一些我自己能够理解的参数,后面慢慢补充 -XX:AutoBoxCacheMax -XX:+AlwaysPreTouch CMSInitiatingOccupancyFraction MaxTenuringThreshold ExplicitGCInvokesConcurr .."

一些容易理解的 JVM 参数调优

JVM 调优有许多参数优化,下面整理了一些我自己能够理解的参数,后面慢慢补充

-XX:AutoBoxCacheMax
-XX:+AlwaysPreTouch
CMSInitiatingOccupancyFraction
MaxTenuringThreshold
ExplicitGCInvokesConcurrent
-Xmx, -Xms
NewRatio

-XX:AutoBoxCacheMax

JAVA 进程启动的时候, 会加载 rt.jar 这个核心包的,rt.jar 包里的 Integer 自然也是被加载到 JVM 中,Integer 里面有一个 IntegerCache 缓存, 如下:

private static class IntegerCache {
        static final int low = -128;
        static final int high;
        static final Integer cache[];

        static {
            // high value may be configured by property
            int h = 127;
            String integerCacheHighPropValue =
                sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
            if (integerCacheHighPropValue != null) {
                int i = parseInt(integerCacheHighPropValue);
                i = Math.max(i, 127);
                // Maximum array size is Integer.MAX_VALUE
                h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
            }
            high = h;

            cache = new Integer[(high - low) + 1];
            int j = low;
            for(int k = 0; k < cache.length; k++)
                cache[k] = new Integer(j++);
        }

        private IntegerCache() {}
}

IntegerCache 有一个静态代码块,JVM 在加载 Integer 这个类时, 会优先加载静态的代码。当 JVM 进程启动完毕后, -128 ~ +127 范围的数字会被缓存起来, 调用 valueOf 方法的时候, 如果是这个范围内的数字, 则直接从缓存取出。 超过这个范围的, 就只能构造新的 Integer 对象了。

public static Integer valueOf(int i) {
        assert IntegerCache.high >= 127;
        if (i >= IntegerCache.low && i <= IntegerCache.high)
            return IntegerCache.cache[i + (-IntegerCache.low)];
        return new Integer(i);
}

因此可以根据实际情况把 AutoBoxCacheMax 的值设置的大写, 比如江南白衣推荐的

-XX:AutoBoxCacheMax=20000

-XX:+AlwaysPreTouch

JAVA 进程启动的时候, 虽然我们可以为 JVM 指定合适的内存大小, 但是这些内存操作系统并没有真正的分配给 JVM, 而是等 JVM 访问这些内存的时候, 才真正分配, 这样会造成以下问题。 1、GC 的时候, 新生代的对象要晋升到老年代的时候, 需要内存, 这个时候操作系统才真正分配内存, 这样就会加大 young gc 的停顿时间; 2、可能存在内存碎片的问题。

可以在 JVM 启动的时候, 配置

-XX:+AlwaysPreTouch

参数, 这样 JVM 就会先访问所有分配给它的内存, 让操作系统把内存真正的分配给 JVM. 后续 JVM 就可以顺畅的访问内存了。

JAVA 1.7 用的垃圾收集算法还是 CMS, 下文提到的参数都是针对 CMS 的。

CMSInitiatingOccupancyFraction

java 垃圾回收算法之 -CMS(并发标记清除), 垃圾收集线程会跟应用的线程一起并行的工作, 万一垃圾收集线程在工作的时候, 老年代内存不足怎么办? 因此最好还是提前启动 CMS 来收集垃圾 (CMS GC)。 可以通过设置

CMSInitiatingOccupancyFraction=75

那么当老年代堆空间的使用率达到 75% 的时候就开始执行垃圾回收,CMSInitiatingOccupancyFraction 默认值是 92%, 这个就太大了。 CMSInitiatingOccupancyFraction 参数必须跟下面两个参数一起使用才能生效的。

-XX:+UseConcMarkSweepGC
-XX:+UseCMSInitiatingOccupancyOnly

MaxTenuringThreshold

新生代是使用 copy 算法来进行垃圾回收的, 可以参看

java 垃圾回收算法之 -coping 复制

默认情况下, 当新生代执行了 15 次 young gc 后, 如果还有对象存活在 Survivor 区中, 那么就可以直接将这些对象晋升到老年代, 但是由于新生代使用 copy 算法, 如果 Survivor 区存活的对象太久的话,Survivor 区存活的对象就越多, 这个就会影响 copy 算法的性能, 使得 young gc 停顿的时间加长, 建议设置成 6。

-XX:MaxTenuringThreshold=6

ExplicitGCInvokesConcurrent

如果系统使用堆外内存, 比如用到了 Netty 的 DirectByteBuffer 类, 那么当想回收堆外内存的时候, 需要调用

System.gc()

而这个方法将进行 full gc, 整个应用将会停顿, 如果是使用 CMS 垃圾收集器, 那么可以设置

-XX:+ExplicitGCInvokesConcurrent

这个参数来改变System.gc()的行为, 让其从 full gc --> CMS GC,CMS GC 是并发收集的, 且中间执行的过程中, 只有部分阶段需要 stop the world。

注意: 设置了 ExplicitGCInvokesConcurrent, 那就不要设置 DisableExplicitGC 参数来禁掉System.gc()

-Xmx, -Xms

这两个一般都是设置 4 个 g

NewRatio

GC 最多的还是发生在新生代的 young gc, 所以可以提高一下新生代在整个堆的占用比例, 建议设置为对半分, 尽量避免 young gc

-XX:NewRatio=1

  • JVM

    JVM(Java Virtual Machine)Java 虚拟机是一个微型操作系统,有自己的硬件构架体系,还有相应的指令系统。能够识别 Java 独特的 .class 文件(字节码),能够将这些文件中的信息读取出来,使得 Java 程序只需要生成 Java 虚拟机上的字节码后就能在不同操作系统平台上进行运行。

    83 引用 • 105 回帖 • 1 关注
感谢    关注    收藏    赞同    反对    举报    分享
回帖    
请输入回帖内容...