Skip to content

Commit 205bf2d

Browse files
committed
UNDERTOW-2309 Prevent memory leak in DefaultByteBufferPool
1 parent 510bb19 commit 205bf2d

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<>();
@@ -228,6 +230,7 @@ public void close() {
228230
local.buffers.clear();
229231
}
230232
ref.clear();
233+
threadLocalCache.remove(local);
231234
}
232235
threadLocalDataList.clear();
233236
}
@@ -332,4 +335,29 @@ protected void finalize() throws Throwable {
332335
}
333336
}
334337

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

0 commit comments

Comments
 (0)