blob: 17a644b69f2b751a7da9e33825fc4bf0389337ae [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2008, 2021 SAP AG, IBM Corporation and others
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* SAP AG - initial API and implementation
* IBM Corporation - enhancements and fixes
* James Livingston - expose collection utils as API
*******************************************************************************/
package org.eclipse.mat.inspections.collections;
import java.util.Arrays;
import org.eclipse.mat.SnapshotException;
import org.eclipse.mat.collect.HashMapIntLong;
import org.eclipse.mat.collect.HashMapIntObject;
import org.eclipse.mat.inspections.collectionextract.AbstractExtractedCollection;
import org.eclipse.mat.inspections.collectionextract.CollectionExtractionUtils;
import org.eclipse.mat.inspections.collectionextract.ICollectionExtractor;
import org.eclipse.mat.internal.Messages;
import org.eclipse.mat.internal.collectionextract.FieldSizeArrayCollectionExtractor;
import org.eclipse.mat.query.Column;
import org.eclipse.mat.query.Column.SortDirection;
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.HelpUrl;
import org.eclipse.mat.query.annotations.Icon;
import org.eclipse.mat.query.quantize.Quantize;
import org.eclipse.mat.snapshot.ISnapshot;
import org.eclipse.mat.snapshot.extension.Subjects;
import org.eclipse.mat.snapshot.model.IClass;
import org.eclipse.mat.snapshot.model.IObject;
import org.eclipse.mat.snapshot.query.IHeapObjectArgument;
import org.eclipse.mat.snapshot.query.RetainedSizeDerivedData;
import org.eclipse.mat.util.IProgressListener;
import org.eclipse.mat.util.MessageUtil;
@CommandName("collections_grouped_by_size")
@Icon("/META-INF/icons/collection_size.gif")
@HelpUrl("/org.eclipse.mat.ui.help/tasks/analyzingjavacollectionusage.html")
@Subjects({"java.util.AbstractCollection",
"java.util.jar.Attributes",
"java.util.Dictionary",
"java.lang.ThreadLocal$ThreadLocalMap",
"java.util.concurrent.ConcurrentHashMap$Segment",
"java.util.concurrent.ConcurrentHashMap$CollectionView",
"java.util.concurrent.CopyOnWriteArrayList",
"java.util.Collections$SynchronizedCollection",
"java.util.Collections$SynchronizedMap",
"java.util.Collections$UnmodifiableCollection",
"java.util.Collections$UnmodifiableMap",
"java.util.Collections$CheckedCollection",
"java.util.Collections$CheckedMap$CheckedEntrySet",
"java.util.Collections$SetFromMap",
"java.util.Collections$EmptyList",
"java.util.Collections$EmptySet",
"java.util.Properties$EntrySet",
"java.util.ResourceBundle",
"java.awt.RenderingHints",
"java.beans.beancontext.BeanContextSupport",
"sun.awt.WeakIdentityHashMap",
"javax.script.SimpleBindings",
"javax.management.openmbean.TabularDataSupport",
"com.ibm.jvm.util.HashMapRT",
"com.sap.engine.lib.util.AbstractDataStructure",
"org.eclipse.mat.collect.SetInt",
"org.eclipse.mat.collect.SetLong",
"org.eclipse.mat.collect.QueueInt",
"org.eclipse.mat.collect.ArrayInt",
"org.eclipse.mat.collect.ArrayLong",
"org.eclipse.mat.collect.ArrayIntBig",
"org.eclipse.mat.collect.ArrayLongBig",
"java.util.AbstractMap",
"java.util.jar.Attributes",
"java.util.Dictionary",
"java.lang.ThreadLocal$ThreadLocalMap",
"java.util.concurrent.ConcurrentHashMap$Segment",
"java.util.concurrent.ConcurrentHashMap$CollectionView",
"java.util.Collections$SynchronizedMap",
"java.util.Collections$UnmodifiableMap",
"java.util.Collections$CheckedMap",
"java.util.Collections$SetFromMap",
"java.util.Collections$EmptyMap",
"java.util.ResourceBundle",
"java.beans.beancontext.BeanContextSupport",
"sun.awt.WeakIdentityHashMap",
"javax.script.SimpleBindings",
"javax.management.openmbean.TabularDataSupport",
"com.ibm.jvm.util.HashMapRT",
"com.sap.engine.lib.util.AbstractDataStructure",
"org.eclipse.mat.collect.HashMapIntLong",
"org.eclipse.mat.collect.HashMapIntObject",
"org.eclipse.mat.collect.HashMapLongObject",
"org.eclipse.mat.collect.HashMapObjectLong",
})
public class CollectionsBySizeQuery implements IQuery
{
@Argument
public ISnapshot snapshot;
@Argument(flag = Argument.UNFLAGGED)
public IHeapObjectArgument objects;
@Argument(isMandatory = false)
public String collection;
@Argument(isMandatory = false)
public String size_attribute;
public IResult execute(IProgressListener listener) throws Exception
{
listener.subTask(Messages.CollectionsBySizeQuery_CollectingSizes);
ICollectionExtractor specificExtractor;
if (size_attribute != null && collection != null)
{
specificExtractor = new FieldSizeArrayCollectionExtractor(size_attribute, collection);
}
else if (size_attribute == null && collection == null)
{
specificExtractor = null;
}
else
{
throw new IllegalArgumentException(Messages.CollectionsBySizeQuery_NeedSizeAndArrayOrNone);
}
// group by length attribute
Quantize.Builder builder = Quantize.valueDistribution(new Column(Messages.CollectionsBySizeQuery_Column_Length,
int.class).noTotals());
builder.column(Messages.CollectionsBySizeQuery_Column_NumObjects, Quantize.COUNT);
builder.column(Messages.Column_ShallowHeap, Quantize.SUM_BYTES, SortDirection.DESC);
builder.addDerivedData(RetainedSizeDerivedData.APPROXIMATE);
Quantize quantize = builder.build();
runQuantizer(listener, quantize, specificExtractor, collection);
return quantize.getResult();
}
private static class Result
{
final int size;
final long used;
public Result(int size, long used)
{
this.size = size;
this.used = used;
}
}
private void runQuantizer(IProgressListener listener, Quantize quantize, ICollectionExtractor specificExtractor,
String specificClass) throws SnapshotException
{
final long LIMIT = 20;
HashMapIntLong exceptions = new HashMapIntLong();
int counter = 0;
IClass type = null;
for (int[] objectIds : objects)
{
HashMapIntObject<Result> resultMap = null;
int sortedObjs[] = objectIds;
int prev = Integer.MIN_VALUE;
for (int objectId : objectIds)
{
if (objectId < prev)
{
sortedObjs = objectIds.clone();
Arrays.sort(sortedObjs);
resultMap = new HashMapIntObject<Result>();
break;
}
prev = objectId;
}
for (int objectId : sortedObjs)
{
if (listener.isCanceled())
break;
IObject obj = snapshot.getObject(objectId);
if (counter++ % 1000 == 0 && !obj.getClazz().equals(type))
{
type = obj.getClazz();
listener.subTask(Messages.CollectionsBySizeQuery_CollectingSizes + "\n" + type.getName()); //$NON-NLS-1$
}
try
{
AbstractExtractedCollection<?, ?> coll = CollectionExtractionUtils.extractCollection(obj, specificClass,
specificExtractor);
if (coll != null && coll.hasSize())
{
Integer size = coll.size();
if (size != null)
{
if (resultMap != null)
resultMap.put(objectId, new Result(size, coll.getUsedHeapSize()));
else
quantize.addValue(objectId, size, null, coll.getUsedHeapSize());
}
}
}
catch (RuntimeException e)
{
int classId = obj.getClazz().getObjectId();
if (!exceptions.containsKey(classId))
{
exceptions.put(classId, 0);
}
long c = exceptions.get(classId);
exceptions.put(classId, c + 1);
if (c < LIMIT)
{
listener.sendUserMessage(
IProgressListener.Severity.INFO,
MessageUtil.format(Messages.CollectionsBySizeQuery_IgnoringCollection,
obj.getTechnicalName()), e);
}
}
}
if (resultMap != null)
{
for (int objectId : objectIds)
{
if (resultMap.containsKey(objectId))
{
Result r = resultMap.get(objectId);
quantize.addValue(objectId, r.size, null, r.used);
}
}
}
if (listener.isCanceled())
break;
}
}
}