in src/core/org.openjdk.jmc.flightrecorder.rules.jdk/src/main/java/org/openjdk/jmc/flightrecorder/rules/jdk/memory/GcFreedRatioRule.java [171:287]
private GcInfoHolder getMaxFreedWindow(final IItemCollection allItems, IQuantity windowSize, IQuantity slideSize) {
final GcInfoHolder maxFreedGcInfo = new GcInfoHolder();
maxFreedGcInfo.freedPerSecondToLivesetRatio = UnitLookup.PERCENT.quantity(0);
maxFreedGcInfo.freedPerSecond = UnitLookup.BYTE.quantity(0);
maxFreedGcInfo.averageLiveset = UnitLookup.BYTE.quantity(0);
maxFreedGcInfo.range = QuantityRange.createWithEnd(UnitLookup.EPOCH_MS.quantity(0),
UnitLookup.EPOCH_MS.quantity(0));
// FIXME: Check which of heapSummarySlide and normal sliding window that seems to give the best result
SlidingWindowToolkit.slidingWindowUnordered(new IUnorderedWindowVisitor() {
@Override
public void visitWindow(IItemCollection windowItems, IQuantity startTime, IQuantity endTime) {
Pair<IItemCollection, IRange<IQuantity>> windowRangePair = getWindowWithPairedHeapSummaryEvents(
windowItems, startTime, endTime);
windowItems = windowRangePair.left;
IQuantity beforeGc = windowItems.getAggregate(JdkAggregators.SUM_HEAP_USED_BEFORE_GC);
IQuantity afterGc = windowItems.getAggregate(JdkAggregators.SUM_HEAP_USED_AFTER_GC);
IQuantity averageLiveset = windowItems.getAggregate(JdkAggregators.AVG_HEAP_USED_AFTER_GC);
if (beforeGc == null || afterGc == null || averageLiveset == null) {
return;
}
IQuantity totalFreed = beforeGc.subtract(afterGc);
IRange<IQuantity> range = windowRangePair.right;
double recordingLengthInSeconds = range.getExtent().in(UnitLookup.SECOND).doubleValue();
IQuantity freedPerSecond = totalFreed.multiply(1 / recordingLengthInSeconds);
IQuantity freedPerSecondToLivesetRatio = UnitLookup.PERCENT_UNITY
.quantity(freedPerSecond.ratioTo(averageLiveset));
if (freedPerSecondToLivesetRatio.compareTo(maxFreedGcInfo.freedPerSecondToLivesetRatio) > 0) {
maxFreedGcInfo.freedPerSecondToLivesetRatio = freedPerSecondToLivesetRatio;
maxFreedGcInfo.freedPerSecond = freedPerSecond;
maxFreedGcInfo.averageLiveset = averageLiveset;
maxFreedGcInfo.range = range;
}
}
/**
* Fixes the item collection by including the potential orphan 'before' event in the
* beginning and 'after' event in the end, and after that removing any non-paired events
* in the whole item collection.
*/
private Pair<IItemCollection, IRange<IQuantity>> getWindowWithPairedHeapSummaryEvents(
IItemCollection windowItems, IQuantity startTime, IQuantity endTime) {
IQuantity newStartTime = null;
IQuantity newEndTime = null;
IItemCollection heapSummaryWindowItems = windowItems.apply(JdkFilters.HEAP_SUMMARY);
IItemCollection heapSummaryAllItems = allItems.apply(JdkFilters.HEAP_SUMMARY);
IQuantity lowestGcId = heapSummaryWindowItems
.getAggregate((IAggregator<IQuantity, ?>) Aggregators.min(JdkAttributes.GC_ID));
IItemCollection lowestGcIdWindowItems = heapSummaryWindowItems
.apply(ItemFilters.equals(JdkAttributes.GC_ID, lowestGcId));
IItemCollection lowestGcIdAllItems = heapSummaryAllItems
.apply(ItemFilters.equals(JdkAttributes.GC_ID, lowestGcId));
IItemCollection lowestGcIdBeforeWindowItems = lowestGcIdWindowItems
.apply(JdkFilters.HEAP_SUMMARY_BEFORE_GC);
IItemCollection lowestGcIdAfterWindowItems = lowestGcIdWindowItems
.apply(JdkFilters.HEAP_SUMMARY_AFTER_GC);
IItemCollection lowestGcIdBeforeAllItems = lowestGcIdAllItems.apply(JdkFilters.HEAP_SUMMARY_BEFORE_GC);
// If the beginning of the window is between a 'before' and an 'after' event.
if (lowestGcIdAfterWindowItems.hasItems() && !lowestGcIdBeforeWindowItems.hasItems()) {
if (lowestGcIdBeforeAllItems.hasItems()) {
newStartTime = RulesToolkit.getEarliestEndTime(lowestGcIdBeforeAllItems);
}
}
IQuantity highestGcId = heapSummaryWindowItems
.getAggregate((IAggregator<IQuantity, ?>) Aggregators.max(JdkAttributes.GC_ID));
IItemCollection highestGcIdWindowItems = heapSummaryWindowItems
.apply(ItemFilters.equals(JdkAttributes.GC_ID, highestGcId));
IItemCollection highestGcIdAllItems = heapSummaryAllItems
.apply(ItemFilters.equals(JdkAttributes.GC_ID, highestGcId));
IItemCollection highestGcIdBeforeWindowItems = highestGcIdWindowItems
.apply(JdkFilters.HEAP_SUMMARY_BEFORE_GC);
IItemCollection highestGcIdAfterWindowItems = lowestGcIdWindowItems
.apply(JdkFilters.HEAP_SUMMARY_AFTER_GC);
IItemCollection highestGcIdAfterAllItems = highestGcIdAllItems.apply(JdkFilters.HEAP_SUMMARY_BEFORE_GC);
if (highestGcIdBeforeWindowItems.hasItems() && !highestGcIdAfterWindowItems.hasItems()) {
if (highestGcIdAfterAllItems.hasItems()) {
newEndTime = RulesToolkit.getEarliestStartTime(highestGcIdAfterAllItems);
}
}
if (newStartTime != null || newEndTime != null) {
if (newStartTime != null) {
startTime = newStartTime;
}
if (newEndTime != null) {
endTime = newEndTime;
}
windowItems = allItems
.apply(ItemFilters.interval(JfrAttributes.END_TIME, startTime, false, endTime, false));
}
// Filter out those that don't have matching before/after pairs
Set<IQuantity> gcIds = windowItems.apply(JdkFilters.HEAP_SUMMARY)
.getAggregate((IAggregator<Set<IQuantity>, ?>) Aggregators.distinct(JdkAttributes.GC_ID));
for (Iterator<IQuantity> iterator = gcIds.iterator(); iterator.hasNext();) {
IQuantity gcId = iterator.next();
IItemCollection gcItems = windowItems.apply(ItemFilters.equals(JdkAttributes.GC_ID, gcId));
if (!(gcItems.apply(JdkFilters.AFTER_GC).hasItems()
&& gcItems.apply(JdkFilters.BEFORE_GC).hasItems())) {
iterator.remove();
}
}
return new Pair<>(windowItems.apply(ItemFilters.memberOf(JdkAttributes.GC_ID, gcIds)),
QuantityRange.createWithEnd(startTime, endTime));
}
@Override
public boolean shouldContinue() {
return !evaluationTask.isCancelled();
}
}, allItems, windowSize, slideSize);
return maxFreedGcInfo;
}