[ISSUE# 9841] Improve Resource Management in TimerWheel to Prevent File Handle Leaks (#9842)

* fix: fix file handle leak in TimerWheel

- Move RandomAccessFile and FileChannel to instance variables
- Close FileChannel in shutdown() method instead of constructor finally block
- This ensures proper resource cleanup and prevents file handle leaks

Change-Id: If6f0bdd555e5f6fdd5c44ca8404f7918b45cf67b
Co-developed-by: Cursor <noreply@cursor.com>

* fix

Change-Id: Icf8f6c316208f8e38421013f2d7b7028f56d5259

* fix

Change-Id: I9ed022b94fca31f005e71afe92a0374265f6ea11

* fix

Change-Id: Ibfd499652378710ccc7b5632370a77ab5dc4b8f3

---------

Co-authored-by: guyinyou <guyinyou.gyy@alibaba-inc.com>
This commit is contained in:
guyinyou
2025-11-12 15:22:59 +08:00
committed by GitHub
parent e2be0f259d
commit 7a1a9501ca
@@ -43,7 +43,9 @@ public class TimerWheel {
public final int slotsTotal;
public final int precisionMs;
private final String fileName;
private MappedByteBuffer mappedByteBuffer;
private final MappedByteBuffer mappedByteBuffer;
private final RandomAccessFile randomAccessFile;
private final FileChannel fileChannel;
private final ByteBuffer byteBuffer;
private final ThreadLocal<ByteBuffer> localBuffer = new ThreadLocal<ByteBuffer>() {
@Override
@@ -69,7 +71,6 @@ public class TimerWheel {
File file = new File(finalFileName);
UtilAll.ensureDirOK(file.getParent());
RandomAccessFile randomAccessFile = null;
try {
randomAccessFile = new RandomAccessFile(finalFileName, "rw");
if (file.exists() && randomAccessFile.length() != 0 &&
@@ -79,8 +80,13 @@ public class TimerWheel {
}
randomAccessFile.setLength(wheelLength);
if (snapOffset < 0) {
mappedByteBuffer = randomAccessFile.getChannel().map(FileChannel.MapMode.READ_WRITE, 0, wheelLength);
fileChannel = randomAccessFile.getChannel();
mappedByteBuffer = fileChannel.map(FileChannel.MapMode.READ_WRITE, 0, wheelLength);
assert wheelLength == mappedByteBuffer.remaining();
} else {
fileChannel = null;
mappedByteBuffer = null;
randomAccessFile.close();
}
this.byteBuffer = ByteBuffer.allocateDirect(wheelLength);
this.byteBuffer.put(Files.readAllBytes(file.toPath()));
@@ -90,10 +96,6 @@ public class TimerWheel {
} catch (IOException e) {
log.error("map file " + finalFileName + " Failed. ", e);
throw e;
} finally {
if (randomAccessFile != null) {
randomAccessFile.close();
}
}
}
@@ -114,6 +116,12 @@ public class TimerWheel {
UtilAll.cleanBuffer(this.mappedByteBuffer);
UtilAll.cleanBuffer(this.byteBuffer);
localBuffer.remove();
try {
this.fileChannel.close();
} catch (Throwable t) {
log.error("Shutdown error in timer wheel", t);
}
}
public void flush() {