
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)。
