in core/org.openjdk.jmc.flightrecorder.rules.jdk/src/main/java/org/openjdk/jmc/flightrecorder/rules/jdk/memory/IncreasingLiveSetRule.java [100:236]
private Result getResult(IItemCollection items, IPreferenceValueProvider valueProvider) {
EventAvailability eventAvailability = RulesToolkit.getEventAvailability(items, JdkTypeIDs.HEAP_SUMMARY);
if (eventAvailability == EventAvailability.UNKNOWN || eventAvailability == EventAvailability.DISABLED) {
return RulesToolkit.getEventAvailabilityResult(this, items, eventAvailability, JdkTypeIDs.HEAP_SUMMARY);
}
IQuantity postWarmupTime = getPostWarmupTime(items, valueProvider.getPreferenceValue(CLASSES_LOADED_PERCENT));
Iterator<? extends IItemIterable> allAfterItems = items.apply(JdkFilters.HEAP_SUMMARY_AFTER_GC).iterator();
double score = 0;
IQuantity liveSetIncreasePerSecond = UnitLookup.MEMORY.getUnit(BinaryPrefix.MEBI).quantity(0);
if (allAfterItems.hasNext()) {
// FIXME: Handle multiple IItemIterable
IItemIterable afterItems = allAfterItems.next();
IMemberAccessor<IQuantity, IItem> timeAccessor = JfrAttributes.END_TIME.getAccessor(afterItems.getType());
IMemberAccessor<IQuantity, IItem> memAccessor = JdkAttributes.HEAP_USED.getAccessor(afterItems.getType());
liveSetIncreasePerSecond = UnitLookup.MEMORY.getUnit(BinaryPrefix.MEBI)
.quantity(RulesToolkit.leastSquareMemory(afterItems.iterator(), timeAccessor, memAccessor));
if (postWarmupTime == null) {
return RulesToolkit.getTooFewEventsResult(this);
}
IQuantity postWarmupHeapSize = items
.apply(ItemFilters.and(JdkFilters.HEAP_SUMMARY_AFTER_GC,
ItemFilters.moreOrEqual(JfrAttributes.START_TIME, postWarmupTime)))
.getAggregate(JdkAggregators.first(JdkAttributes.HEAP_USED));
if (postWarmupHeapSize == null) {
return RulesToolkit.getTooFewEventsResult(this);
}
double relativeIncreasePerSecond = liveSetIncreasePerSecond.ratioTo(postWarmupHeapSize);
score = RulesToolkit.mapExp100(relativeIncreasePerSecond, PERCENT_OF_HEAP_INCREASE_PER_SECOND);
}
// If we have Old Object Sample events we can attempt to find suitable memory leak class candidates
// otherwise we just return the basic increasing live set score
EventAvailability ea = RulesToolkit.getEventAvailability(items, JdkTypeIDs.OLD_OBJECT_SAMPLE);
// FIXME: Should construct an message using memoryIncrease, not use a hard limit
if (score >= 25 && (ea == EventAvailability.DISABLED || ea == EventAvailability.UNKNOWN)) {
IQuantity timeAfterJVMStart = items.getAggregate(JdkAggregators.FIRST_ITEM_START).subtract(items.getAggregate(JdkAggregators.JVM_START_TIME));
String shortMessage = MessageFormat.format(
Messages.getString(Messages.IncreasingLiveSetRuleFactory_TEXT_INFO),
liveSetIncreasePerSecond.displayUsing(IDisplayable.AUTO));
String longMessage = shortMessage + "<p>" //$NON-NLS-1$
+ MessageFormat.format(Messages.getString(Messages.IncreasingLiveSetRuleFactory_TEXT_INFO_LONG),
timeAfterJVMStart.displayUsing(IDisplayable.AUTO));
return new Result(this, score, shortMessage, longMessage, JdkQueries.HEAP_SUMMARY_AFTER_GC);
} else if (score < 25) {
return new Result(this, score, Messages.getString(Messages.IncreasingLiveSetRule_TEXT_OK));
}
// step 1. extract events from after the estimated warmup period
IItemCollection oldObjectItems = items.apply(ItemFilters.and(ItemFilters.type(JdkTypeIDs.OLD_OBJECT_SAMPLE),
ItemFilters.more(JfrAttributes.START_TIME, postWarmupTime)));
ReferenceTreeModel tree = ReferenceTreeModel.buildReferenceTree(oldObjectItems);
// step 2. perform a balance calculation on the old object sample events aggregated by class count
boolean anyReferrerChains = false;
for (ReferenceTreeObject referenceTreeObject : tree.getLeakObjects()) {
if (referenceTreeObject.getParent() != null) {
anyReferrerChains = true;
break;
}
}
if (!anyReferrerChains) {
List<IntEntry<IMCType>> calculateGroupingScore = RulesToolkit.calculateGroupingScore(oldObjectItems,
JdkAttributes.OLD_OBJECT_CLASS);
double calculateBalanceScore = RulesToolkit.calculateBalanceScore(calculateGroupingScore);
String shortDescription = MessageFormat.format(Messages.IncreasingLiveSetRuleFactory_TEXT_INFO,
liveSetIncreasePerSecond.displayUsing(IDisplayable.AUTO))
+ (calculateBalanceScore >= 25
? Messages.getString(Messages.IncreasingLiveSetRule_TEXT_INFO_UNBALANCED)
: Messages.getString(Messages.IncreasingLiveSetRule_TEXT_INFO_BALANCED));
return new Result(this, Math.min(calculateBalanceScore, 25), // because we already know that there is a leak.
shortDescription, Messages.getString(Messages.IncreasingLiveSetRule_TEXT_INFO_LONG));
}
List<ReferenceTreeObject> leakCandidates = tree.getLeakCandidates(
valueProvider.getPreferenceValue(RELEVANCE_THRESHOLD).doubleValueIn(UnitLookup.NUMBER_UNITY));
if (leakCandidates.size() > 0) {
StringBuilder descriptionBuilder = new StringBuilder();
descriptionBuilder
.append(MessageFormat.format(Messages.getString(Messages.IncreasingLiveSetRuleFactory_TEXT_INFO),
liveSetIncreasePerSecond.displayUsing(IDisplayable.AUTO)));
descriptionBuilder.append("<br/>"); //$NON-NLS-1$
descriptionBuilder.append(MessageFormat
.format(Messages.getString(Messages.IncreasingLiveSetRule_LEAK_CANDIDATES), leakCandidates.size()));
descriptionBuilder.append("<ul>"); //$NON-NLS-1$
int objectFormat = ReferenceTreeObject.FORMAT_PACKAGE | ReferenceTreeObject.FORMAT_FIELD
| ReferenceTreeObject.FORMAT_ARRAY_INFO;
for (ReferenceTreeObject candidate : leakCandidates) {
descriptionBuilder.append("<li>"); //$NON-NLS-1$
descriptionBuilder.append(candidate.toString(objectFormat));
descriptionBuilder.append("<br/>"); //$NON-NLS-1$
descriptionBuilder.append(Messages.getString(Messages.IncreasingLiveSetRule_CANDIDATE_REFERRED_BY));
descriptionBuilder.append("<ul>"); //$NON-NLS-1$
ReferenceTreeObject chainObject = candidate.getParent();
for (int i = 0; i < 10 && chainObject != null; i++) {
descriptionBuilder.append("<li>"); //$NON-NLS-1$
descriptionBuilder.append(chainObject.toString(objectFormat));
if (chainObject.getParent() == null) { // aborting the loop because we have found the root
descriptionBuilder.append(" ("); //$NON-NLS-1$
descriptionBuilder.append(chainObject.getRootDescription());
descriptionBuilder.append(")</li>"); //$NON-NLS-1$
break;
}
descriptionBuilder.append("</li>"); //$NON-NLS-1$
chainObject = chainObject.getParent();
}
if (chainObject != null && chainObject.getParent() != null) { // we never iterated to the object
while (chainObject.getParent() != null) {
chainObject = chainObject.getParent();
}
descriptionBuilder.append("<li>"); //$NON-NLS-1$
descriptionBuilder.append(Messages.getString(Messages.IncreasingLiveSetRule_ELLIPSIS));
descriptionBuilder.append("</li><li>"); //$NON-NLS-1$
descriptionBuilder.append(chainObject.toString(objectFormat));
descriptionBuilder.append(" ("); //$NON-NLS-1$
descriptionBuilder.append(chainObject.getRootDescription());
descriptionBuilder.append(")</li>"); //$NON-NLS-1$
}
descriptionBuilder.append("</ul>"); //$NON-NLS-1$
descriptionBuilder.append("</li>"); //$NON-NLS-1$
}
descriptionBuilder.append("</ul>"); //$NON-NLS-1$
return new Result(this, score, descriptionBuilder.toString());
}
String description = ""; //$NON-NLS-1$
if (score >= 25) {
description = MessageFormat.format(Messages.getString(Messages.IncreasingLiveSetRuleFactory_TEXT_INFO),
liveSetIncreasePerSecond.displayUsing(IDisplayable.AUTO)) + "</br>"; //$NON-NLS-1$
}
return new Result(this, score,
description + MessageFormat.format(
Messages.getString(Messages.IncreasingLiveSetRule_TEXT_INFO_NO_CANDIDATES),
postWarmupTime.displayUsing(IDisplayable.AUTO)),
null, JdkQueries.HEAP_SUMMARY_AFTER_GC);
}