blob: b861b2c01aebaa7bdae179d21f4ab795dc3b751e [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2008, 2018 SAP AG and IBM Corporation.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* SAP AG - initial API and implementation
*******************************************************************************/
package org.eclipse.mat.inspections;
import java.util.Arrays;
import java.util.Collection;
import java.util.regex.Pattern;
import org.eclipse.mat.SnapshotException;
import org.eclipse.mat.collect.ArrayInt;
import org.eclipse.mat.collect.SetInt;
import org.eclipse.mat.internal.Messages;
import org.eclipse.mat.query.IQuery;
import org.eclipse.mat.query.IResult;
import org.eclipse.mat.query.annotations.Argument;
import org.eclipse.mat.query.annotations.CommandName;
import org.eclipse.mat.query.annotations.Icon;
import org.eclipse.mat.query.results.CompositeResult;
import org.eclipse.mat.snapshot.Histogram;
import org.eclipse.mat.snapshot.ISnapshot;
import org.eclipse.mat.snapshot.model.Field;
import org.eclipse.mat.snapshot.model.IClass;
import org.eclipse.mat.snapshot.model.IInstance;
import org.eclipse.mat.snapshot.model.IObject;
import org.eclipse.mat.snapshot.model.ObjectReference;
import org.eclipse.mat.snapshot.query.IHeapObjectArgument;
import org.eclipse.mat.util.IProgressListener;
import org.eclipse.mat.util.MessageUtil;
/**
* Extract information about objects extending java.lang.ref.Reference, e.g.
* weak and soft references, and Finalizer.
*/
@CommandName("references_statistics")
@Icon("/META-INF/icons/reference.gif")
public class ReferenceQuery implements IQuery
{
@Argument
public ISnapshot snapshot;
@Argument(flag = Argument.UNFLAGGED)
public IHeapObjectArgument objects;
static final String DEFAULT_REFERENT = "referent"; //$NON-NLS-1$
@Argument(isMandatory = false)
public String referent_attribute = DEFAULT_REFERENT;
public IResult execute(IProgressListener listener) throws Exception
{
listener.subTask(Messages.ReferenceQuery_Msg_ComputingReferentSet);
ArrayInt instanceSet = new ArrayInt();
SetInt referentSet = new SetInt();
for (int[] objs : objects)
{
instanceSet.addAll(objs);
for (int ii = 0; ii < objs.length; ii++)
{
IObject o = snapshot.getObject(objs[ii]);
if (o instanceof IInstance)
{
IInstance obj = (IInstance) o;
ObjectReference ref = getReferent(obj, referent_attribute);
if (ref != null)
{
try
{
referentSet.add(ref.getObjectId());
}
catch (SnapshotException e)
{
listener.sendUserMessage(IProgressListener.Severity.WARNING, MessageUtil.format(Messages.ReferenceQuery_MissingReferentObject, Long.toHexString(ref.getObjectAddress()), Long.toHexString(o.getObjectAddress()), referent_attribute), e);
}
}
}
}
if (listener.isCanceled())
throw new IProgressListener.OperationCanceledException();
}
return execute(instanceSet, referentSet, snapshot, Messages.ReferenceQuery_HistogramOfReferentObjects,
Messages.ReferenceQuery_OnlyRetainedByReferents,
Messages.ReferenceQuery_StronglyRetainedByReferences, referent_attribute, listener);
}
/**
* Important: the <strong>className</strong> must point to
* java.lang.ref.Reference or one of its subclasses. It is not possible to
* check this, as some heap dumps lack class hierarchy information.
*/
public static IResult execute(String className, ISnapshot snapshot, String labelHistogramReferenced,
String labelHistogramRetained, String labelHistogramStronglyRetainedReferents, IProgressListener listener) throws SnapshotException
{
listener.subTask(Messages.ReferenceQuery_Msg_ComputingReferentSet);
ArrayInt instanceSet = new ArrayInt();
SetInt referentSet = new SetInt();
Collection<IClass> classes = snapshot.getClassesByName(Pattern.compile(className), true);
if (classes == null)
throw new SnapshotException(MessageUtil.format(Messages.ReferenceQuery_ErrorMsg_NoMatchingClassesFound,
className));
for (IClass clazz : classes)
{
int[] objs = clazz.getObjectIds();
instanceSet.addAll(objs);
for (int ii = 0; ii < objs.length; ii++)
{
IInstance obj = (IInstance) snapshot.getObject(objs[ii]);
ObjectReference ref = getReferent(obj);
if (ref != null)
{
try
{
referentSet.add(ref.getObjectId());
}
catch (SnapshotException e)
{
listener.sendUserMessage(IProgressListener.Severity.WARNING, MessageUtil.format(Messages.ReferenceQuery_MissingReferentObject, Long.toHexString(ref.getObjectAddress()), Long.toHexString(obj.getObjectAddress()), DEFAULT_REFERENT), e);
}
}
}
if (listener.isCanceled())
throw new IProgressListener.OperationCanceledException();
}
return execute(instanceSet, referentSet, snapshot, labelHistogramReferenced, labelHistogramRetained, labelHistogramStronglyRetainedReferents, DEFAULT_REFERENT, listener);
}
public static CompositeResult execute(ArrayInt instanceSet, SetInt referentSet, ISnapshot snapshot,
String labelHistogramReferenced, String labelHistogramRetained, String labelHistogramStronglyRetainedReferents, IProgressListener listener)
throws SnapshotException
{
return execute(instanceSet, referentSet, snapshot, labelHistogramReferenced, labelHistogramRetained,
labelHistogramStronglyRetainedReferents, DEFAULT_REFERENT, listener);
}
public static CompositeResult execute(ArrayInt instanceSet, SetInt referentSet, ISnapshot snapshot,
String labelHistogramReferenced, String labelHistogramRetained, String labelHistogramStronglyRetainedReferents, String referentField, IProgressListener listener)
throws SnapshotException
{
CompositeResult result = new CompositeResult();
Histogram histogram = snapshot.getHistogram(referentSet.toArray(), listener);
if (listener.isCanceled())
throw new IProgressListener.OperationCanceledException();
histogram.setLabel(labelHistogramReferenced);
result.addResult(labelHistogramReferenced, histogram);
listener.subTask(Messages.ReferenceQuery_Msg_ComputingRetainedSet);
int[] retainedSet = snapshot.getRetainedSet(instanceSet.toArray(), new String[] { referentField }, listener);
if (listener.isCanceled())
throw new IProgressListener.OperationCanceledException();
histogram = snapshot.getHistogram(retainedSet, listener);
if (listener.isCanceled())
throw new IProgressListener.OperationCanceledException();
histogram.setLabel(labelHistogramRetained);
result.addResult(labelHistogramRetained, histogram);
listener.subTask(Messages.ReferenceQuery_Msg_ComputingStronglyRetainedSet);
int[] allRetainedSet = snapshot.getRetainedSet(instanceSet.toArray(), listener);
if (listener.isCanceled())
throw new IProgressListener.OperationCanceledException();
// Exclude referent retained
Arrays.sort(allRetainedSet);
int newWeakRetained[] = new int[retainedSet.length];
System.arraycopy(retainedSet, 0, newWeakRetained, 0, retainedSet.length);
Arrays.sort(newWeakRetained);
int[] r = new int[allRetainedSet.length - newWeakRetained.length];
int t2 = -1;
for (int s = 0, d = 0, w = 0; s < allRetainedSet.length; ++s)
{
int t1 = allRetainedSet[s];
while (w < newWeakRetained.length && (t2 < t1))
{
t2 = newWeakRetained[w++];
}
if (t1 != t2)
r[d++] = t1;
}
// Find which of the referent set are strongly retained elsewhere
int referents[] = referentSet.toArray();
Arrays.sort(referents);
t2 = -1;
int d = 0;
for (int s = 0, w = 0; s < referents.length; ++s)
{
int t1 = referents[s];
while (w < r.length && (t2 < t1))
{
t2 = r[w++];
}
if (t1 == t2)
referents[d++] = t1;
}
int leakingReferents[] = new int[d];
System.arraycopy(referents, 0, leakingReferents, 0, d);
histogram = snapshot.getHistogram(leakingReferents, listener);
if (listener.isCanceled())
throw new IProgressListener.OperationCanceledException();
histogram.setLabel(labelHistogramStronglyRetainedReferents);
result.addResult(labelHistogramStronglyRetainedReferents, histogram);
return result;
}
public static ObjectReference getReferent(IInstance instance) throws SnapshotException
{
return getReferent(instance, DEFAULT_REFERENT);
}
static ObjectReference getReferent(IInstance instance, String referentName) throws SnapshotException
{
Field field = instance.getField(referentName);
if (field != null)
{
return (ObjectReference) field.getValue();
}
else if (REFERENCE_PATTERN.matcher(instance.getClazz().getName()).matches()
|| "java.lang.ref.Finalizer".equals(instance.getClazz().getName())) //$NON-NLS-1$
{
// see [266231] guess the referent field for references objects from
// java
final ISnapshot snapshot = instance.getSnapshot();
int[] outbounds = snapshot.getOutboundReferentIds(instance.getObjectId());
for (int outboundId : outbounds)
{
IClass outboundType = snapshot.getClassOf(outboundId);
// pick outbound reference only if it is not the <class>
// reference, a reference type (next) or reference queue (queue)
if (instance.getClazz().getObjectId() != outboundId // <class>
&& !isReferenceType(outboundType) // 'next'
&& !isReferenceQueueType(outboundType)) // 'queue'
{
long address = snapshot.mapIdToAddress(outboundId);
return new ObjectReference(snapshot, address);
}
}
return null;
}
else
{
return null;
}
}
private static final Pattern REFERENCE_PATTERN = Pattern.compile("java\\.lang\\.ref\\..*Reference"); //$NON-NLS-1$
private static final Pattern QUEUE_PATTERN = Pattern.compile("java\\.lang\\.ref\\..*ReferenceQueue.*"); //$NON-NLS-1$
private static boolean isReferenceType(IClass clazz) throws SnapshotException
{
return REFERENCE_PATTERN.matcher(clazz.getName()).matches()
|| "java.lang.ref.Finalizer".equals(clazz.getName()) //$NON-NLS-1$
|| clazz.doesExtend("java.lang.ref.Reference"); //$NON-NLS-1$
}
private static boolean isReferenceQueueType(IClass clazz) throws SnapshotException
{
return QUEUE_PATTERN.matcher(clazz.getName()).matches() //
|| clazz.doesExtend("java.lang.ref.ReferenceQueue"); //$NON-NLS-1$
}
}