Class LeakPresenceDetector<T>
- Type Parameters:
T- The resource type to detect
- Direct Known Subclasses:
LeakPresenceExtension.WithTransferableScope
Background
The standard ResourceLeakDetector produces no "false positives", but this comes with tradeoffs. You either
get many false negatives because only a small sample of buffers is instrumented, or you turn on paranoid detection
which carries a somewhat heavy performance cost with each allocation. Additionally, paranoid detection enables
detailed recording of buffer access operations with heavy performance impact. Avoiding false negatives is necessary
for (unit, fuzz...) testing if bugs should lead to reliable test failures, but the performance impact can be
prohibitive for some tests.
The presence detector
The leak presence detector takes a different approach. It foregoes detailed tracking of allocation and modification stack traces. In return every resource is counted, so there are no false negatives where a leak would not be detected.
The presence detector also does not wait for an unclosed resource to be garbage collected before it's reported as leaked. This ensures that leaks are detected promptly and can be directly associated with a particular test, but it can lead to false positives. Tests that use the presence detector must shut down completely before checking for resource leaks. There are also complications with static fields, described below.
Resource Scopes
A resource scope manages all resources of a set of threads over time. On allocation, a resource is assigned to a
scope through the currentScope() method. When check() is called, or the scope is
closed, all resources in that scope must have been released.
By default, there is only a single "global" scope, and when check() is called, all resources in the entire
JVM must have been released. To enable parallel test execution, it may be necessary to use separate scopes for
separate tests instead, so that one test can check for its own leaks while another test is still in progress. You
can override currentScope() to implement this for your test framework.
Static Fields
While the presence detector requires that all resources be closed after a test, some resources kept in static
fields cannot be released, or there would be false positives. To avoid this, resources created inside static
initializers, specifically when the allocation stack trace contains a <clinit> method, are not
tracked.
Because the presence detector does not normally capture or introspect allocation stack traces, additional
cooperation is required. Any static initializer must be wrapped in a staticInitializer(Supplier) call,
which will temporarily enable stack trace introspection. For example:
private static final ByteBuf CRLF_BUF = LeakPresenceDetector.staticInitializer(() -> unreleasableBuffer(
directBuffer(2).writeByte(CR).writeByte(LF)).asReadOnly());
Since stack traces are not captured by default, it can be difficult to tell apart a real leak from a missed static
initializer. You can temporarily turn on allocation stack trace capture using the
-Dio.netty.util.LeakPresenceDetector.trackCreationStack=true system property.
-
Nested Class Summary
Nested ClassesModifier and TypeClassDescriptionstatic final classSpecial exception type to show that an allocation is prohibited at the moment, for example because theLeakPresenceDetector.ResourceScopeis closed, or because the current thread cannot be associated with a particular scope.static final classA resource scope keeps track of the resources for a particular set of threads.Nested classes/interfaces inherited from class ResourceLeakDetector
ResourceLeakDetector.LeakListener, ResourceLeakDetector.Level -
Constructor Summary
ConstructorsConstructorDescriptionLeakPresenceDetector(Class<?> resourceType) Create a new detector for the given resource type.LeakPresenceDetector(Class<?> resourceType, int samplingInterval) Deprecated.LeakPresenceDetector(Class<?> resourceType, int samplingInterval, long maxActive) This constructor should not be used directly, it is called reflectively byResourceLeakDetectorFactory. -
Method Summary
Modifier and TypeMethodDescriptionstatic voidcheck()Check the current leak presence detector scope for open resources.protected LeakPresenceDetector.ResourceScopeGet the resource scope for the current thread.final booleanCheck whetherResourceLeakTracker.record()does anything for this detector.static <R> RstaticInitializer(Supplier<R> supplier) Wrap a static initializer so that any resources created inside the block will not be tracked.final ResourceLeakTracker<T> Creates a newResourceLeakTrackerwhich is expected to be closed viaResourceLeakTracker.close(Object)when the related resource is deallocated.final ResourceLeakTracker<T> trackForcibly(T obj) Creates a newResourceLeakTrackerwhich is expected to be closed viaResourceLeakTracker.close(Object)when the related resource is deallocated.Methods inherited from class ResourceLeakDetector
addExclusions, getInitialHint, getLevel, isEnabled, needReport, open, reportInstancesLeak, reportTracedLeak, reportUntracedLeak, setEnabled, setLeakListener, setLevel
-
Constructor Details
-
LeakPresenceDetector
Create a new detector for the given resource type.- Parameters:
resourceType- The resource type
-
LeakPresenceDetector
Deprecated.This constructor should not be used directly, it is called reflectively byResourceLeakDetectorFactory.- Parameters:
resourceType- The resource typesamplingInterval- Ignored
-
LeakPresenceDetector
This constructor should not be used directly, it is called reflectively byResourceLeakDetectorFactory.- Parameters:
resourceType- The resource typesamplingInterval- IgnoredmaxActive- Ignored
-
-
Method Details
-
staticInitializer
Wrap a static initializer so that any resources created inside the block will not be tracked. Example:private static final ByteBuf CRLF_BUF = LeakPresenceDetector.staticInitializer(() -> unreleasableBuffer( directBuffer(2).writeByte(CR).writeByte(LF)).asReadOnly());Note that technically, this method does not actually care what happens inside the block. Instead, it turns on stack trace introspection at the start of the block, and turns it back off at the end. Any allocation in that interval will be checked to see whether it is part of a static initializer, and if it is, it will not be tracked.
- Type Parameters:
R- The supplier return type- Parameters:
supplier- A code block to run- Returns:
- The value returned by the
supplier
-
currentScope
protected LeakPresenceDetector.ResourceScope currentScope() throws LeakPresenceDetector.AllocationProhibitedExceptionGet the resource scope for the current thread. This is used to assign resources to scopes, and it is used bycheck()to tell which scope to check for open resources. By default, the global scope is returned.- Returns:
- The resource scope to use
- Throws:
LeakPresenceDetector.AllocationProhibitedException
-
track
Description copied from class:ResourceLeakDetectorCreates a newResourceLeakTrackerwhich is expected to be closed viaResourceLeakTracker.close(Object)when the related resource is deallocated.- Overrides:
trackin classResourceLeakDetector<T>- Returns:
- the
ResourceLeakTrackerornull
-
trackForcibly
Description copied from class:ResourceLeakDetectorCreates a newResourceLeakTrackerwhich is expected to be closed viaResourceLeakTracker.close(Object)when the related resource is deallocated.Unlike
ResourceLeakDetector.track(Object), this method always returns a tracker, regardless of the detection settings.- Overrides:
trackForciblyin classResourceLeakDetector<T>- Returns:
- the
ResourceLeakTracker
-
isRecordEnabled
public final boolean isRecordEnabled()Description copied from class:ResourceLeakDetectorCheck whetherResourceLeakTracker.record()does anything for this detector.- Overrides:
isRecordEnabledin classResourceLeakDetector<T>- Returns:
trueifResourceLeakTracker.record()should be called
-
check
public static void check()Check the current leak presence detector scope for open resources. If any resources remain unclosed, an exception is thrown.- Throws:
IllegalStateException- If there is a leak, or if the leak detector is not aLeakPresenceDetector.
-