This is an automated email from the ASF dual-hosted git repository.

wu-sheng pushed a commit to branch feat/otlp-log-json-body
in repository https://gitbox.apache.org/repos/asf/skywalking.git

commit f3b4892bde60405b569e8c514212904adb842858
Author: Wu Sheng <[email protected]>
AuthorDate: Thu Jul 2 21:21:11 2026 +0800

    Fix delayed DSL classloader leak detection: record probe-drain time as 
unload evidence
    
    The unload probe recorded its mint time as evidence, but the first probe is
    minted when the loader retires, so its evidence could never satisfy
    evidenceUpToMs >= retiredAt + settleMs — a real leak needed two 
class-unloading
    GC cycles before the WARN fired (potentially hours apart on a quiet heap).
    Record the drain (observation) time instead; the sweeper polls every 30s, 
so it
    is a tight upper bound on cycle completion and a single post-settle cycle 
the
    loader survives now fires the WARN.
    
    Co-Authored-By: Claude Fable 5 <[email protected]>
---
 .../oap/server/core/classloader/ClassLoaderGc.java | 27 +++++++++++-----------
 1 file changed, 13 insertions(+), 14 deletions(-)

diff --git 
a/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/classloader/ClassLoaderGc.java
 
b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/classloader/ClassLoaderGc.java
index cd2286215d..11aea56a41 100644
--- 
a/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/classloader/ClassLoaderGc.java
+++ 
b/oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/classloader/ClassLoaderGc.java
@@ -77,9 +77,9 @@ final class ClassLoaderGc {
     /** Live probe generation; at most one at a time. Guarded by {@link 
#probeLock}. The
      *  phantom ref must stay strongly held here or it would be GC'd before 
enqueuing. */
     private PhantomReference<ClassLoader> liveProbe;
-    private long liveProbeMintedAtMs;
-    /** Latest probe mint-time for which a class-unloading GC cycle is 
confirmed complete.
-     *  {@code 0} until the first probe collection is observed. */
+    /** Observed (probe-drain) time a class-unloading GC cycle was last 
confirmed complete;
+     *  {@code 0} until the first is observed. Drain time, not the probe's 
mint time, so a
+     *  single post-settle cycle a loader survives advances evidence past its 
settle window. */
     private volatile long unloadEvidenceUpToMs;
 
     @Getter
@@ -171,9 +171,9 @@ final class ClassLoaderGc {
         return out;
     }
 
-    /** Latest probe mint-time confirmed survived-past by a class-unloading GC 
cycle;
-     *  {@code 0} while no cycle has been observed. Exposed for the manager's 
diagnostics
-     *  and for deterministic tests. */
+    /** Latest observed time a class-unloading GC cycle was confirmed complete 
(via probe
+     *  collection); {@code 0} while no cycle has been observed. Exposed for 
the manager's
+     *  diagnostics and for deterministic tests. */
     long unloadEvidenceUpToMs() {
         return unloadEvidenceUpToMs;
     }
@@ -186,14 +186,14 @@ final class ClassLoaderGc {
     }
 
     /**
-     * Record that a class-unloading GC cycle completed after {@code 
probeMintedAtMs}.
-     * Normally driven by {@link #drainUnloadProbe()}; package-private so 
tests can exercise
-     * {@link #leakSuspects} without depending on real GC timing.
+     * Record that a class-unloading GC cycle was confirmed complete as of 
{@code observedAtMs}.
+     * Normally driven by {@link #drainUnloadProbe()} with the drain time; 
package-private so
+     * tests can exercise {@link #leakSuspects} without depending on real GC 
timing.
      */
-    void recordUnloadEvidence(final long probeMintedAtMs) {
+    void recordUnloadEvidence(final long observedAtMs) {
         synchronized (probeLock) {
-            if (probeMintedAtMs > unloadEvidenceUpToMs) {
-                unloadEvidenceUpToMs = probeMintedAtMs;
+            if (observedAtMs > unloadEvidenceUpToMs) {
+                unloadEvidenceUpToMs = observedAtMs;
             }
         }
     }
@@ -212,7 +212,6 @@ final class ClassLoaderGc {
             final ProbeClassLoader probe = new ProbeClassLoader();
             probe.definePayload();
             liveProbe = new PhantomReference<>(probe, probeQueue);
-            liveProbeMintedAtMs = System.currentTimeMillis();
         }
     }
 
@@ -223,7 +222,7 @@ final class ClassLoaderGc {
         }
         if (collected) {
             synchronized (probeLock) {
-                recordUnloadEvidence(liveProbeMintedAtMs);
+                recordUnloadEvidence(System.currentTimeMillis());
                 liveProbe = null;
             }
         }

Reply via email to