{"record":{"id":"75856040be475b88","repo":"Tencent/matrix","slug":"unabled-to-find-destroy-activity-info-class-with-n","errorCode":null,"errorMessage":"Unabled to find destroy activity info class with name: ","messagePattern":"Unabled to find destroy activity info class with name: ","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"matrix/matrix-android/matrix-resource-canary/matrix-resource-canary-analyzer/src/main/java/com/tencent/matrix/resource/analyzer/ActivityLeakAnalyzer.java","lineNumber":89,"sourceCode":"            final Snapshot snapshot = heapSnapshot.getSnapshot();\n            final Instance leakingRef = findLeakingReference(refKey, snapshot);\n\n            // False alarm, weak reference was cleared in between key check and heap dump.\n            if (leakingRef == null) {\n                return ActivityLeakResult.noLeak(AnalyzeUtil.since(analysisStartNanoTime));\n            }\n\n            return findLeakTrace(analysisStartNanoTime, snapshot, leakingRef);\n        } catch (Throwable e) {\n            e.printStackTrace();\n            return ActivityLeakResult.failure(e, AnalyzeUtil.since(analysisStartNanoTime));\n        }\n    }\n\n    private Instance findLeakingReference(String key, Snapshot snapshot) {\n        final ClassObj infoClass = snapshot.findClass(DESTROYED_ACTIVITY_INFO_CLASSNAME);\n        if (infoClass == null) {\n            throw new IllegalStateException(\"Unabled to find destroy activity info class with name: \"\n                    + DESTROYED_ACTIVITY_INFO_CLASSNAME);\n        }\n        List<String> keysFound = new ArrayList<>();\n        for (Instance infoInstance : infoClass.getInstancesList()) {\n            final List<ClassInstance.FieldValue> values = classInstanceValues(infoInstance);\n            final String keyCandidate = asString(fieldValue(values, ACTIVITY_REFERENCE_KEY_FIELDNAME));\n            if (keyCandidate.equals(key)) {\n                final Instance weakRefObj = fieldValue(values, ACTIVITY_REFERENCE_FIELDNAME);\n                if (weakRefObj == null) {\n                    continue;\n                }\n                final List<ClassInstance.FieldValue> activityRefs = classInstanceValues(weakRefObj);\n                return fieldValue(activityRefs, \"referent\");\n            }\n            keysFound.add(keyCandidate);\n        }\n        throw new IllegalStateException(\n                \"Could not find weak reference with key \" + key + \" in \" + keysFound);","sourceCodeStart":71,"sourceCodeEnd":107,"githubUrl":"https://github.com/Tencent/matrix/blob/3b8293bd65d47eeea7caf1f32a3a5d4d5eab60e7/matrix/matrix-android/matrix-resource-canary/matrix-resource-canary-analyzer/src/main/java/com/tencent/matrix/resource/analyzer/ActivityLeakAnalyzer.java#L71-L107","documentation":"ActivityLeakAnalyzer looks up the DestroyedActivityInfo class in the heap dump snapshot to find keyed references to destroyed activities. If that class is absent from the dump, the analyzer cannot locate leak candidates and throws IllegalStateException. This usually means the Matrix resource monitoring was not active at capture time, so no destroyed-activity info instances were ever created.","triggerScenarios":"findLeakingReference runs snapshot.findClass(DESTROYED_ACTIVITY_INFO_CLASSNAME) and it returns null — the class was never loaded in the dumped process because the leak-monitor hook never recorded a destroyed activity, or the dump comes from a process where the class was stripped/renamed.","commonSituations":"Heap dumps captured before Matrix's ResourcePlugin initialized; dumps from builds without the resource-canary instrumentation; obfuscation (ProGuard/R8) renaming or removing DestroyedActivityInfo; analyzing a dump from the wrong process.","solutions":["Ensure the ResourcePlugin is installed and has detected at least one destroyed activity before the hprof is taken.","Check ProGuard/R8 keep rules so DestroyedActivityInfo is not renamed/stripped.","Verify you are analyzing the hprof from the app process where the leak was suspected.","Confirm DESTROYED_ACTIVITY_INFO_CLASSNAME matches the class actually present (no package relocation between versions)."],"exampleFix":"// before\nfinal ClassObj infoClass = snapshot.findClass(DESTROYED_ACTIVITY_INFO_CLASSNAME);\nif (infoClass == null) {\n    throw new IllegalStateException(\"Unabled to find destroy activity info class with name: \" + DESTROYED_ACTIVITY_INFO_CLASSNAME);\n}\n// after\nfinal ClassObj infoClass = snapshot.findClass(DESTROYED_ACTIVITY_INFO_CLASSNAME);\nif (infoClass == null) {\n    return null; // no destroyed-activity info recorded; report 'no leak found'\n}","handlingStrategy":"validation","validationCode":"if (snapshot.findClass(DESTROYED_ACTIVITY_INFO_CLASSNAME) == null) { reportNoLeak(\"class absent from dump\"); return; }","typeGuard":null,"tryCatchPattern":"try { ref = findLeakingReference(key, snapshot); } catch (IllegalStateException e) { log.warn(\"no destroyed-activity info in dump\", e); return ActivityLeakResult.noLeak(); }","preventionTips":["Install ResourcePlugin before captures; confirm destroyed activities were recorded","Add keep rules for DestroyedActivityInfo under obfuscation","Analyze the hprof from the correct process/session"],"tags":["android","memory-leak","hprof","activity-leak"],"backgroundTag":"class-not-found","analyzedSha":"3b8293bd65d47eeea7caf1f32a3a5d4d5eab60e7","analyzedAt":"2026-09-08T08:01:39.722Z","contentChangedAt":"2026-09-08T08:01:39.722Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}