Package io.netty.util

Class LeakPresenceDetector<T>

  • Type Parameters:
    T - The resource type to detect
    Direct Known Subclasses:
    LeakPresenceExtension.WithTransferableScope

    public class LeakPresenceDetector<T>
    extends ResourceLeakDetector<T>
    Alternative leak detector implementation for reliable and performant detection in tests.

    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.

    • Constructor Detail

      • LeakPresenceDetector

        public LeakPresenceDetector​(java.lang.Class<?> resourceType)
        Create a new detector for the given resource type.
        Parameters:
        resourceType - The resource type
      • LeakPresenceDetector

        @Deprecated
        public LeakPresenceDetector​(java.lang.Class<?> resourceType,
                                    int samplingInterval)
        Deprecated.
        This constructor should not be used directly, it is called reflectively by ResourceLeakDetectorFactory.
        Parameters:
        resourceType - The resource type
        samplingInterval - Ignored
      • LeakPresenceDetector

        public LeakPresenceDetector​(java.lang.Class<?> resourceType,
                                    int samplingInterval,
                                    long maxActive)
        This constructor should not be used directly, it is called reflectively by ResourceLeakDetectorFactory.
        Parameters:
        resourceType - The resource type
        samplingInterval - Ignored
        maxActive - Ignored
    • Method Detail

      • staticInitializer

        public static <R> R staticInitializer​(java.util.function.Supplier<R> supplier)
        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()
        Get the resource scope for the current thread. This is used to assign resources to scopes, and it is used by check() to tell which scope to check for open resources. By default, the global scope is returned.
        Returns:
        The resource scope to use
      • 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:
        java.lang.IllegalStateException - If there is a leak, or if the leak detector is not a LeakPresenceDetector.