in src/application/org.openjdk.jmc.joverflow/src/main/java/org/openjdk/jmc/joverflow/stats/DetailedStatsCalculator.java [207:372]
private CollectionInstanceDescriptor handleCollection(JavaObject col) {
CollectionInstanceDescriptor colDesc = colDescriptors.getDescriptor(col);
CollectionClassDescriptor classDesc = colDesc.getClassDescriptor();
// Check if this collection is in the implementation (via encapsulation) of
// another one. For example, an instance of java.util.HashMap is encapsulated
// by an instance of java.util.HashSet. In this case, the current collection
// should not be inspected for overhead on its own.
JavaObject potentialParentCol = refChain.getPointingJavaObject();
if (potentialParentCol != null) {
if (classDesc.isInImplementationOf(potentialParentCol.getClazz().getName())) {
return null;
}
}
numCols++;
// Get impl-inclusive size and mark collection implementation objects
int implSize = colDesc.getImplSize();
col.getClazz().updateInclusiveInstanceSize(implSize);
totalColImplSize += implSize;
// Check if this collection is empty. A collection with this problem cannot
// have other problems.
int nEls = colDesc.getNumElements();
if (nEls == 0) {
ProblemKind problemKind;
if (colDesc.getClassDescriptor().canDetermineModCount()) {
if (colDesc.getModCount() != 0) {
problemKind = ProblemKind.EMPTY_USED;
numEmptyUsedCols++;
emptyUsedColsOvhd += implSize;
} else {
problemKind = ProblemKind.EMPTY_UNUSED;
numEmptyUnusedCols++;
emptyUnusedColsOvhd += implSize;
}
} else {
problemKind = ProblemKind.EMPTY;
numEmptyCols++;
emptyColsOvhd += implSize;
}
classDesc.addProblematicCollection(problemKind, implSize);
refChain.recordCurrentRefChainForColCluster(col, colDesc, problemKind, implSize);
return colDesc;
}
int ovhd;
boolean goodCollection = true;
// Check if this collection is sparse
if (colDesc instanceof CollectionInstanceDescriptor.CapacityDifferentFromSize) {
CollectionInstanceDescriptor.CapacityDifferentFromSize arColDesc = (CollectionInstanceDescriptor.CapacityDifferentFromSize) colDesc;
ovhd = arColDesc.getSparsenessOverhead(ptrSize);
if (ovhd > 0) {
goodCollection = false;
ProblemKind problemKind;
if (arColDesc.getCapacity() <= arColDesc.getDefaultCapacity()) {
problemKind = ProblemKind.SPARSE_SMALL;
numSparseSmallCols++;
sparseSmallColsOvhd += ovhd;
} else {
problemKind = ProblemKind.SPARSE_LARGE;
numSparseLargeCols++;
sparseLargeColsOvhd += ovhd;
}
classDesc.addProblematicCollection(problemKind, ovhd);
refChain.recordCurrentRefChainForColCluster(col, colDesc, problemKind, ovhd);
}
}
if (nEls <= SMALL_COL_MAX_SIZE) {
goodCollection = false;
// Calculate overhead as a number of bytes we save if we replace this data
// structure with an array of objects (or two, for maps). The array's own
// overhead is its header. The formula below is still not ideal, because the
// user likely won't be able to keep the exact-size array for each small
// collection - instead, they would probably have to use arrays of the same
// (highest) fixed size for all collections created at the same place in the code.
int multiplier = colDesc.getClassDescriptor().isMap() ? 2 : 1;
ovhd = colDesc.getImplSize() - multiplier * (nEls * ptrSize + arrayHeaderSize);
numSmallCols++;
smallColsOvhd += ovhd;
classDesc.addProblematicCollection(ProblemKind.SMALL, ovhd);
refChain.recordCurrentRefChainForColCluster(col, colDesc, ProblemKind.SMALL, ovhd);
}
// Check if this collection contains boxed numbers.
/*
* TODO: Our calculations for boxed arrays are much more precise, since they take into
* account possible multiple pointers to the same boxed object. To implement the same for a
* collection, we need to iterate all its elements, which may be time-consuming...
*/
ovhd = 0;
if (classDesc.isMap()) {
JavaHeapObject[] entryObjs = colDesc.getSampleKeyAndValue();
int totalObjSize = 0, totalBoxedSize = 0, numPtrs = 0;
for (JavaHeapObject keyOrValue : entryObjs) {
if (keyOrValue == null) {
continue;
}
int boxedNumSize = keyOrValue.getClazz().getBoxedNumberSize();
if (boxedNumSize > 0) {
totalBoxedSize += boxedNumSize;
totalObjSize += keyOrValue.getSize();
numPtrs++;
}
}
if (totalBoxedSize > 0) {
// Take into account what happens if we replace this with an array of numbers,
// with a normal array header size.
ovhd = colDesc.getImplSize() + (totalObjSize + ptrSize * numPtrs - totalBoxedSize) * nEls
- arrayHeaderSize * numPtrs;
}
} else {
JavaHeapObject obj = colDesc.getSampleElement();
if (obj != null) {
int boxedNumSize = obj.getClazz().getBoxedNumberSize();
if (boxedNumSize > 0) {
ovhd = colDesc.getImplSize() + (obj.getSize() + ptrSize - boxedNumSize) * nEls - arrayHeaderSize;
}
}
}
if (ovhd > 0) {
goodCollection = false;
numBoxedNumberCols++;
boxedNumberColsOvhd += ovhd;
classDesc.addProblematicCollection(ProblemKind.BOXED, ovhd);
refChain.recordCurrentRefChainForColCluster(col, colDesc, ProblemKind.BOXED, ovhd);
}
// Check if this is a WeakHashMap or its subclass, in which elements have hard
// references back to keys.
WeakMapHandler wmHandler = WeakMapHandler.createInstance(colDesc);
if (wmHandler != null) {
WeakMapHandler.Result result = wmHandler.calculateOverhead();
if (result != null) {
// numBadWeakCols++;
// badWeakColsOverhead += ovhd;
goodCollection = false;
classDesc.addProblematicCollection(ProblemKind.WEAK_MAP_WITH_BACK_REFS, result.overhead);
refChain.recordCurrentRefChainForWeakHashMapWithBackRefs(col, colDesc, result.overhead,
result.valueTypeAndFieldSample);
}
}
BarArrayHandler barHandler = BarArrayHandler.createInstance(colDesc, colDescriptors);
if (barHandler != null) {
ovhd = barHandler.calculateOverhead();
if (ovhd > 0) {
goodCollection = false;
numBarCols++;
barColsOvhd += ovhd;
classDesc.addProblematicCollection(ProblemKind.BAR, ovhd);
refChain.recordCurrentRefChainForColCluster(col, colDesc, ProblemKind.BAR, ovhd);
}
}
if (goodCollection) { // No defects found for this collection
refChain.recordCurrentRefChainForGoodCollection(col, colDesc);
}
return colDesc;
}