JVM 调优

一、StringTable调优

1、修改虚拟机参数(StringTable桶个数)

对于读取大量string到内存的情况(例如读取一个存放弱密码的字典),通过StringTable调优,可以显著提升性能。

# 设置Bucket大小(StringTable底层是哈希表,
# 当出现哈希冲突时,用链表解决。Bucket是指用到的数组长度)
-XX:StringTableSize=10240
# 值越大,哈希冲突几率越小,形成链表概率更低,速度更快

# 若想查看配置是否生效,可以使用:
-XX:+PrintStringTableStatistics
# 打印信息范例:
StringTable statistics:
Number of buckets: xxxx
Number of entries: xxxx    # key的数量

2、大量重复的string入池

Twitter的故事:他们需要存储大量的用户信息,其中一个是地址信息。然而地址信息拆分以后,会有大量的重复字符串。通过字符串入池,极大的降低了内存的占用。

使用jvisualvm中的抽样器,可以查看JVM中实时的对象大小占用情况

如果有大量重复的字符串,可以使用String对象的intern()函数,把它放入StringTable

二、使用直接内存

对于读写本地大文件的情况,可以使用NIO的ByteBuffer来提供极致的读写性能

import java.nio.ByteBuffer;

public class DirectMemoryExample {
    public static void main(String[] args) {
        // 1. 分配直接内存(1024字节)
        int size = 1024;
        ByteBuffer directBuffer = ByteBuffer.allocateDirect(size);

        System.out.println("Direct Memory Allocated: " + size + " bytes");

        // 2. 向直接内存写入数据
        byte[] data = "Hello, Direct Memory!".getBytes();
        directBuffer.put(data);
        System.out.println("Data written to direct memory.");

        // 3. 从直接内存读取数据
        directBuffer.flip(); // 切换到读模式
        byte[] readData = new byte[data.length];
        directBuffer.get(readData);
        String message = new String(readData);
        System.out.println("Data read from direct memory: " + message);

        // 4. 手动释放直接内存(可选,依赖 Cleaner 机制)
        // 注意:直接内存的释放由 JVM 自动管理,但可以通过显式调用 cleaner 优化
        try {
            // 获取 unsafe 实例(需禁用安全管理器)
            sun.misc.Unsafe unsafe = getUnsafe();
            long baseAddress = ((sun.nio.ch.DirectBuffer) directBuffer).address();
            unsafe.freeMemory(baseAddress);
            System.out.println("Direct memory manually freed.");
        } catch (Exception e) {
            System.err.println("Failed to free direct memory: " + e.getMessage());
        }
    }

    // 通过反射获取 Unsafe 实例
    private static sun.misc.Unsafe getUnsafe() {
        try {
            java.lang.reflect.Field field = sun.misc.Unsafe.class.getDeclaredField("theUnsafe");
            field.setAccessible(true);
            return (sun.misc.Unsafe) field.get(null);
        } catch (Exception e) {
            throw new RuntimeException("Unable to access Unsafe", e);
        }
    }
}

在做JVM调优时,经常会加-XX:DisableExplicitGC禁用显式GC,即让程序员代码中的System.gc()失效,因为它是一种FullGC,比较影响性能(例如STW)。但是对于以下代码,ByteBuffer的手动回收将不再起效,直到下一次的GC开始。

public class Demo1_26 {

    static int _1Gb = 1024 * 1024 * 1024;

    public static void main(String[] args) throws IOException {
        ByteBuffer byteBuffer = ByteBuffer.allocateDirect(_1Gb);
        System.out.println("分配完毕...");
        System.in.read();
        System.out.println("开始释放...");
        byteBuffer = null;
        System.gc(); // 显式的垃圾回收, Full GC
        System.in.read();
    }
}

因此建议在启用-XX:DisableExplicitGC的情况下,手动释放直接内存(用Unsafe)。

This article was updated on