Java 11中强制释放MappedByteBuffer内存映射:解决sun.misc.Cleaner找不到问题
解决Java 11中sun.misc.Cleaner找不到的MappedByteBuffer内存释放问题
问题根源
Java 9起sun.misc.Cleaner被标记为废弃,Java 11中完全从JDK中移除,导致你通过反射加载该类时抛出ClassNotFoundException。而java.lang.ref.Cleaner是Java 9引入的公开标准API,属于java.base核心模块,默认即可使用,无需额外JVM参数启用内部模块,是官方推荐的替代方案。
修改后的实现代码
替换原来的反射逻辑,改为获取java.lang.ref.Cleaner实例并调用其clean()方法:
/** * Clean a {@code MappedByteBuffer}. Method to call to delete memory of the file and free the resources * @param pByteBuffer Buffer to clean */ private void freeByteBuffer(ByteBuffer pByteBuffer) { try { // 获取MappedByteBuffer的私有cleaner方法,返回java.lang.ref.Cleaner实例 Method cleanerMethod = pByteBuffer.getClass().getMethod("cleaner"); cleanerMethod.setAccessible(true); Cleaner cleaner = (Cleaner) cleanerMethod.invoke(pByteBuffer); if (cleaner != null) { cleaner.clean(); } } catch (Exception ex) { logger.error(KernelError.FREE_MEM, idefixComponentName, ex); } }
关键注意事项
- 无需额外JVM参数(Java 11-15):
java.lang.ref.Cleaner属于java.base模块的公开API,默认对所有应用可见,不需要添加--add-modules或其他模块相关参数。 - Java 16+的特殊处理:从Java 16开始,JDK加强了模块访问控制,反射访问模块内私有成员会被限制。如果你的应用需要运行在Java 16及以上版本,需要添加JVM启动参数:
这条参数允许你的应用(未命名模块)访问--add-opens java.base/java.nio=ALL-UNNAMEDjava.nio模块中的私有成员,保证反射调用cleaner()方法正常工作。
额外说明
虽然反射调用私有方法不是最理想的实践,但这是目前跨Java 8到Java 17+版本主动释放MappedByteBuffer资源的通用方案。如果你的应用不再需要兼容Java 8,也可以考虑使用FileChannel的force()方法配合GC自动回收,但主动调用Cleaner的方式能更及时地释放内存资源。
内容的提问来源于stack exchange,提问作者Etienne
相关产品推荐
相关产品推荐

