private CollectionInstanceDescriptor handleCollection()

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;
	}