HeapDumpInstanceObject implements InstanceObject { @VisibleForTesting @NotNull public List<ReferenceObject> extractReferences() { List<Instance> sortedReferences = new ArrayList<>(myInstance.getHardReverseReferences()); sortedReferences.sort(DEPTH_COMPARATOR); if (myInstance.getSoftReverseReferences() != null) { List<Instance> sortedSoftReferences = new ArrayList<>(myInstance.getSoftReverseReferences()); sortedSoftReferences.sort(DEPTH_COMPARATOR); sortedReferences.addAll(sortedSoftReferences); } List<ReferenceObject> referrers = new ArrayList<>(sortedReferences.size()); for (Instance reference : sortedReferences) { List<String> referencingFieldNames = new ArrayList<>(3); if (reference instanceof ClassInstance) { ClassInstance classInstance = (ClassInstance)reference; for (ClassInstance.FieldValue entry : classInstance.getValues()) { if (entry.getField().getType() == Type.OBJECT && entry.getValue() == myInstance) { referencingFieldNames.add(entry.getField().getName()); } } } else if (reference instanceof ArrayInstance) { ArrayInstance arrayInstance = (ArrayInstance)reference; assert arrayInstance.getArrayType() == Type.OBJECT; Object[] values = arrayInstance.getValues(); for (int i = 0; i < values.length; ++i) { if (values[i] == myInstance) { referencingFieldNames.add(String.valueOf(i)); } } } else if (reference instanceof ClassObj) { ClassObj classObj = (ClassObj)reference; Map<Field, Object> staticValues = classObj.getStaticFieldValues(); for (Map.Entry<Field, Object> entry : staticValues.entrySet()) { if (entry.getKey().getType() == Type.OBJECT && entry.getValue() == myInstance) { referencingFieldNames.add(entry.getKey().getName()); } } } InstanceObject referencingInstance = myCaptureObject.findInstanceObject(reference); assert referencingInstance != null; referrers.add(new ReferenceObject(referencingFieldNames, referencingInstance)); } return referrers; } HeapDumpInstanceObject(@NotNull HeapDumpCaptureObject captureObject,
                         @NotNull Instance instance,
                         @NotNull ClassDb.ClassEntry classEntry,
                         @Nullable ValueType precomputedValueType); @Override boolean equals(Object obj); @Override int hashCode(); @NotNull @Override String getName(); @NotNull @Override String getValueText(); @NotNull @Override String getToStringText(); @Override int getHeapId(); @NotNull @Override ClassDb.ClassEntry getClassEntry(); @Override int getDepth(); @Override long getNativeSize(); @Override int getShallowSize(); @Override long getRetainedSize(); @Override int getFieldCount(); @NotNull @Override List<FieldObject> getFields(); @Nullable @Override ArrayObject getArrayObject(); @Override @NotNull ValueType getValueType(); @Nullable @Override AllocationStack getAllocationCallStack(); @Override boolean getIsRoot(); @NotNull @Override List<ReferenceObject> getReferences(); @VisibleForTesting @NotNull List<ReferenceObject> extractReferences();  }
@Test public void testExtractReferences() throws Exception { MockClassInstance mockInstance = new MockClassInstance(-1, 0, MOCK_CLASS); MockClassInstance hardInstanceRef = new MockClassInstance(-1, 3, MOCK_CLASS); hardInstanceRef.addFieldValue(Type.OBJECT, "hardInstanceRef", mockInstance); hardInstanceRef.addFieldValue(Type.OBJECT, "invalidRef", new Object()); MockArrayInstance hardArrayRef = new MockArrayInstance(-1, Type.OBJECT, 3, 2); hardArrayRef.setValue(0, new Object()); hardArrayRef.setValue(1, mockInstance); hardArrayRef.setValue(2, mockInstance); MockClassObj hardClassRef = new MockClassObj(-1, "hardClassRef", 1); hardClassRef.addStaticField(Type.OBJECT, "staticClassRef", mockInstance); hardClassRef.addStaticField(Type.BOOLEAN, "invalidBoolRef", false); MockClassInstance softInstanceRef = new MockClassInstance(-1, 0, MOCK_CLASS); softInstanceRef.addFieldValue(Type.OBJECT, "softInstanceRef", mockInstance); softInstanceRef.addFieldValue(Type.OBJECT, "invalidRef", new Object()); mockInstance.addHardReference(hardInstanceRef); mockInstance.addHardReference(hardArrayRef); mockInstance.addHardReference(hardClassRef); mockInstance.addSoftReferences(softInstanceRef); ClassDb.ClassEntry mockClassEntry = myCaptureObject.getClassDb().registerClass(0, MOCK_CLASS); myCaptureObject.addInstance(hardInstanceRef, new HeapDumpInstanceObject( myCaptureObject, hardInstanceRef, mockClassEntry, OBJECT)); myCaptureObject.addInstance(hardArrayRef, new HeapDumpInstanceObject( myCaptureObject, hardArrayRef, mockClassEntry, ARRAY)); myCaptureObject.addInstance(hardClassRef, new HeapDumpInstanceObject( myCaptureObject, hardClassRef, mockClassEntry, CLASS)); myCaptureObject.addInstance(softInstanceRef, new HeapDumpInstanceObject( myCaptureObject, softInstanceRef, mockClassEntry, OBJECT)); myCaptureObject.addInstance(mockInstance, new HeapDumpInstanceObject( myCaptureObject, mockInstance, mockClassEntry, OBJECT)); List<ReferenceObject> referrers = myCaptureObject.getInstance(mockInstance).extractReferences(); assertEquals(4, referrers.size()); List<String> refs = referrers.get(0).getReferenceFieldNames(); assertEquals(1, refs.size()); assertEquals("staticClassRef", refs.get(0)); refs = referrers.get(1).getReferenceFieldNames(); assertEquals(2, refs.size()); assertEquals("1", refs.get(0)); assertEquals("2", refs.get(1)); refs = referrers.get(2).getReferenceFieldNames(); assertEquals(1, refs.size()); assertEquals("hardInstanceRef", refs.get(0)); refs = referrers.get(3).getReferenceFieldNames(); assertEquals(1, refs.size()); assertEquals("softInstanceRef", refs.get(0)); }