Skip to content

Commit d28a0fc

Browse files
yawkatnormanmaurer
andauthored
Make ResourceLeakDetector.track not final (#15620)
Motivation: For fuzzing, paranoid leak detection is necessary, but running GC for each test is too slow. Instead, I check the `allLeaks` field after each run for any open trackers (with some pruning to avoid static buffers). This means that a lot of the tracked data, and in particular the whole reference queue, is unnecessary. For better performance, it would be nice to instead only track the number of open leaks, and compare that number before and after a test runs. This can be achieved using a custom ResourceLeakTracker implementation, but for that ResourceLeakDetector.track must become non-final. Modification: Make the method non-final. Also fix a bunch of warnings. Result: ResourceLeakDetector.track can be overridden for more efficient leak detection. --------- Co-authored-by: Norman Maurer <[email protected]>
1 parent 7134f60 commit d28a0fc

1 file changed

Lines changed: 18 additions & 18 deletions

File tree

common/src/main/java/io/netty/util/ResourceLeakDetector.java

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -205,7 +205,8 @@ public ResourceLeakDetector(String resourceType) {
205205
* @param maxActive This is deprecated and will be ignored.
206206
*/
207207
@Deprecated
208-
public ResourceLeakDetector(Class<?> resourceType, int samplingInterval, long maxActive) {
208+
public ResourceLeakDetector(
209+
Class<?> resourceType, int samplingInterval, @SuppressWarnings("unused") long maxActive) {
209210
this(resourceType, samplingInterval);
210211
}
211212

@@ -225,7 +226,8 @@ public ResourceLeakDetector(Class<?> resourceType, int samplingInterval) {
225226
* @param maxActive This is deprecated and will be ignored.
226227
*/
227228
@Deprecated
228-
public ResourceLeakDetector(String resourceType, int samplingInterval, long maxActive) {
229+
public ResourceLeakDetector(
230+
String resourceType, int samplingInterval, @SuppressWarnings("unused") long maxActive) {
229231
this.resourceType = ObjectUtil.checkNotNull(resourceType, "resourceType");
230232
this.samplingInterval = samplingInterval;
231233
}
@@ -248,40 +250,37 @@ public final ResourceLeak open(T obj) {
248250
*
249251
* @return the {@link ResourceLeakTracker} or {@code null}
250252
*/
251-
@SuppressWarnings("unchecked")
252-
public final ResourceLeakTracker<T> track(T obj) {
253+
public ResourceLeakTracker<T> track(T obj) {
253254
return track0(obj, false);
254255
}
255256

256257
/**
257258
* Creates a new {@link ResourceLeakTracker} which is expected to be closed via
258259
* {@link ResourceLeakTracker#close(Object)} when the related resource is deallocated.
259-
*
260+
* <p>
260261
* Unlike {@link #track(Object)}, this method always returns a tracker, regardless
261262
* of the detection settings.
262263
*
263264
* @return the {@link ResourceLeakTracker}
264265
*/
265-
@SuppressWarnings("unchecked")
266266
public ResourceLeakTracker<T> trackForcibly(T obj) {
267267
return track0(obj, true);
268268
}
269269

270-
@SuppressWarnings("unchecked")
271-
private DefaultResourceLeak track0(T obj, boolean force) {
270+
private DefaultResourceLeak<T> track0(T obj, boolean force) {
272271
Level level = ResourceLeakDetector.level;
273272
if (force ||
274273
level == Level.PARANOID ||
275274
(level != Level.DISABLED && ThreadLocalRandom.current().nextInt(samplingInterval) == 0)) {
276275
reportLeak();
277-
return new DefaultResourceLeak(obj, refQueue, allLeaks, getInitialHint(resourceType));
276+
return new DefaultResourceLeak<>(obj, refQueue, allLeaks, getInitialHint(resourceType));
278277
}
279278
return null;
280279
}
281280

282281
private void clearRefQueue() {
283282
for (;;) {
284-
DefaultResourceLeak ref = (DefaultResourceLeak) refQueue.poll();
283+
DefaultResourceLeak<?> ref = (DefaultResourceLeak<?>) refQueue.poll();
285284
if (ref == null) {
286285
break;
287286
}
@@ -307,7 +306,7 @@ private void reportLeak() {
307306

308307
// Detect and report previous leaks.
309308
for (;;) {
310-
DefaultResourceLeak ref = (DefaultResourceLeak) refQueue.poll();
309+
DefaultResourceLeak<?> ref = (DefaultResourceLeak<?>) refQueue.poll();
311310
if (ref == null) {
312311
break;
313312
}
@@ -391,12 +390,12 @@ public interface LeakListener {
391390
private static final class DefaultResourceLeak<T>
392391
extends WeakReference<Object> implements ResourceLeakTracker<T>, ResourceLeak {
393392

394-
@SuppressWarnings("unchecked") // generics and updaters do not mix.
393+
@SuppressWarnings({"unchecked", "rawtypes"}) // generics and updaters do not mix.
395394
private static final AtomicReferenceFieldUpdater<DefaultResourceLeak<?>, TraceRecord> headUpdater =
396395
(AtomicReferenceFieldUpdater)
397396
AtomicReferenceFieldUpdater.newUpdater(DefaultResourceLeak.class, TraceRecord.class, "head");
398397

399-
@SuppressWarnings("unchecked") // generics and updaters do not mix.
398+
@SuppressWarnings({"unchecked", "rawtypes"}) // generics and updaters do not mix.
400399
private static final AtomicIntegerFieldUpdater<DefaultResourceLeak<?>> droppedRecordsUpdater =
401400
(AtomicIntegerFieldUpdater)
402401
AtomicIntegerFieldUpdater.newUpdater(DefaultResourceLeak.class, "droppedRecords");
@@ -460,7 +459,7 @@ public void record(Object hint) {
460459
* {@link #TARGET_RECORDS} accesses, backoff occurs. This matches typical access patterns,
461460
* where there are either a high number of accesses (i.e. a cached buffer), or low (an ephemeral buffer), but
462461
* not many in between.
463-
*
462+
* <p>
464463
* The use of atomics avoids serializing a high number of accesses, when most of the records will be thrown
465464
* away. High contention only happens when there are very few existing records, which is only likely when the
466465
* object isn't shared! If this is a problem, the loop can be aborted and the record dropped, because another
@@ -548,6 +547,7 @@ public boolean close(T trackedObject) {
548547
* @param ref the reference. If {@code null}, this method has no effect.
549548
* @see java.lang.ref.Reference#reachabilityFence
550549
*/
550+
@SuppressWarnings({"SynchronizationOnLocalVariableOrMethodParameter", "EmptySynchronizedStatement"})
551551
private static void reachabilityFence0(Object ref) {
552552
if (ref != null) {
553553
synchronized (ref) {
@@ -591,7 +591,7 @@ private String generateReport(TraceRecord oldHead) {
591591
buf.append("Recent access records: ").append(NEWLINE);
592592

593593
int i = 1;
594-
Set<String> seen = new HashSet<String>(present);
594+
Set<String> seen = new HashSet<>(present);
595595
for (; oldHead != TraceRecord.BOTTOM; oldHead = oldHead.next) {
596596
String s = oldHead.toString();
597597
if (seen.add(s)) {
@@ -629,10 +629,10 @@ private String generateReport(TraceRecord oldHead) {
629629
}
630630

631631
private static final AtomicReference<String[]> excludedMethods =
632-
new AtomicReference<String[]>(EmptyArrays.EMPTY_STRINGS);
632+
new AtomicReference<>(EmptyArrays.EMPTY_STRINGS);
633633

634-
public static void addExclusions(Class clz, String ... methodNames) {
635-
Set<String> nameSet = new HashSet<String>(Arrays.asList(methodNames));
634+
public static void addExclusions(@SuppressWarnings("rawtypes") Class clz, String ... methodNames) {
635+
Set<String> nameSet = new HashSet<>(Arrays.asList(methodNames));
636636
// Use loop rather than lookup. This avoids knowing the parameters, and doesn't have to handle
637637
// NoSuchMethodException.
638638
for (Method method : clz.getDeclaredMethods()) {

0 commit comments

Comments
 (0)