Skip to content

Commit c96363d

Browse files
authored
Merge pull request #1518 from TomasHofman/UNDERTOW-2309
UNDERTOW-2309 Prevent memory leak in DefaultByteBufferPool
2 parents d24e1f7 + 205bf2d commit c96363d

File tree

1 file changed

+29
-1
lines changed

1 file changed

+29
-1
lines changed

core/src/main/java/io/undertow/server/DefaultByteBufferPool.java

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,9 @@
2626
import java.nio.ByteBuffer;
2727
import java.util.ArrayDeque;
2828
import java.util.ArrayList;
29+
import java.util.HashMap;
2930
import java.util.List;
31+
import java.util.Map;
3032
import java.util.concurrent.ConcurrentLinkedQueue;
3133
import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
3234

@@ -39,7 +41,7 @@
3941
*/
4042
public class DefaultByteBufferPool implements ByteBufferPool {
4143

42-
private final ThreadLocal<ThreadLocalData> threadLocalCache = new ThreadLocal<>();
44+
private final ThreadLocalCache threadLocalCache = new ThreadLocalCache();
4345
// Access requires synchronization on the threadLocalDataList instance
4446
private final List<WeakReference<ThreadLocalData>> threadLocalDataList = new ArrayList<>();
4547
private final ConcurrentLinkedQueue<ByteBuffer> queue = new ConcurrentLinkedQueue<>();
@@ -229,6 +231,7 @@ public void close() {
229231
local.buffers.clear();
230232
}
231233
ref.clear();
234+
threadLocalCache.remove(local);
232235
}
233236
threadLocalDataList.clear();
234237
}
@@ -336,4 +339,29 @@ protected void finalize() throws Throwable {
336339
}
337340
}
338341

342+
// This is used instead of Java ThreadLocal class. Unlike in the ThreadLocal class, the remove() method in this
343+
// class can be called by a different thread than the one that initialized the data.
344+
private static class ThreadLocalCache {
345+
346+
Map<Thread, ThreadLocalData> localsByThread = new HashMap<>();
347+
348+
ThreadLocalData get() {
349+
return localsByThread.get(Thread.currentThread());
350+
}
351+
352+
void set(ThreadLocalData threadLocalData) {
353+
localsByThread.put(Thread.currentThread(), threadLocalData);
354+
}
355+
356+
void remove(ThreadLocalData threadLocalData) {
357+
// Find the entry containing given data instance and remove it from the map.
358+
for (Map.Entry<Thread, ThreadLocalData> entry: localsByThread.entrySet()) {
359+
if (threadLocalData.equals(entry.getValue())) {
360+
localsByThread.remove(entry.getKey(), entry.getValue());
361+
break;
362+
}
363+
}
364+
}
365+
}
366+
339367
}

0 commit comments

Comments
 (0)