You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Chronicle Queue分钟滚动删除文件后仍在lsof列表未释放内存问题

问题根因

Chronicle Queue 5.x版本的onReleased回调仅代表队列内核逻辑不再主动持有该周期文件的引用,并不代表所有进程侧的内存映射句柄已经释放:

  1. Linux系统下内存映射类型的文件,只要还有任意进程持有映射引用,执行file.delete()仅会将文件标记为待删除,不会真正释放磁盘空间和文件句柄,lsof中会显示为(deleted)状态
  2. 手动调用storeForCycle获取旧周期存储实例的操作,反而会主动创建对旧周期文件的新引用,导致资源无法释放
  3. OpenJDK 11的MappedByteBuffer回收依赖GC触发,若JVM缓存了旧周期的映射 buffer,也会导致文件句柄迟迟不释放
修复方案
  • 第一,删除手动调用storeForCycle关闭旧存储的逻辑,改为通过tailer消费进度自动触发资源释放
  • 第二,调整初始化配置,增加空闲存储自动释放逻辑,示例代码如下:
eventStore = SingleChronicleQueueBuilder.binary(GlobalConstants.CURRENT_DIR
        + GlobalConstants.PATH_SEPARATOR + EventBusConstants.EVENT_DIR
        + GlobalConstants.PATH_SEPARATOR + eventType)
        .rollCycle(RollCycles.MINUTELY)
        .storeFileListener(storeFileListener)
        // 新增:配置周期文件空闲1分钟后自动释放所有映射引用
        .storeFileIdleTimeout(Duration.ofMinutes(1))
        .build();
  • 第三,调整周期资源回收逻辑,每次确认tailer滚动到新周期后,主动释放所有旧周期资源:
// 每次消费完消息后执行
int currentCycle = tailer.cycle();
if (currentCycle > previousCycle) {
    // 释放所有早于当前周期的存储资源、内存映射
    ((SingleChronicleQueue) eventStore).releaseResourcesForCyclesLessThan(currentCycle);
    previousCycle = currentCycle;
}
  • 第四,优化StoreFileListener删除逻辑,确认没有活跃tailer引用旧周期后再删除文件:
storeFileListener = new StoreFileListener() {
    @Override
    public void onReleased(int cycle, File file) {
        // 确保当前tailer已经消费到更新的周期,没有引用待删除文件
        if (tailer.cycle() > cycle) {
            file.delete();
        }
    }
};
  • 第五,调整JVM启动参数,加速内存映射回收:
    新增以下参数,避免JDK缓存大量映射buffer导致资源不释放:
    -Djdk.nio.maxCachedBufferSize=262144
    -XX:MaxDirectMemorySize=4G # 根据实际内存配置,建议不小于物理内存的1/4
    # 不要添加-XX:+DisableExplicitGC参数,Chronicle Queue依赖显式GC触发旧映射回收
    
验证方式

修改后运行服务,每隔5分钟执行lsof -p <进程ID> | grep deleted查看待删除文件列表,若旧周期的队列文件在被标记删除后1~2分钟内从列表中消失,即修复成功。

内容的提问来源于stack exchange,提问作者chaitas shah

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.25 16:45:00