/*
* Copyright (c) 2011, 2011, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.max.vm.heap.gcx;
import com.sun.max.unsafe.*;
import com.sun.max.vm.*;
import com.sun.max.vm.runtime.*;
/**
* Statistics on heap regions free space and fragmentation.
*/
public final class HeapRegionStatistics {
/**
* Log2 of the smallest fragment size (smallest space reclaimable by the GC).
*/
final int log2MinFragmentSize;
/**
* Log2 of the largest free chunk size (i.e., heap region size).
*/
final int log2LargestChunkSize;
/**
* Histogram of fragment sizes. Each entries correspond to a power of 2 of fragment size, starting at the minimum size for a fragment.
* Entry at index i records the number of fragments of size between 2^i and 2^(i+1) -1.
*/
final int [] fragmentSizes;
/**
* Histogram of free space within regions. Entry at index i records the number of regions with free space amount comprises between 2^i and 2^(i+1) -1.
*/
final int [] freeSpaceSizes;
/**
* Histogram of number of free chunks per region.
*/
final int [] regionsFragmentation;
/**
* Private region info iterator.
*/
private final HeapRegionInfoIterable regionInfoIterable;
private int sizeBin(int size) {
// bin index for a given size is the log 2 of its highest bit.
return 31 - Integer.numberOfLeadingZeros(size);
}
private int sizeBin(Size size) {
return size.mostSignificantBitSet();
}
public HeapRegionStatistics(Size minFragmentSize) {
log2MinFragmentSize = sizeBin(minFragmentSize);
log2LargestChunkSize = HeapRegionConstants.log2RegionSizeInBytes;
// Maximum fragmentation for a region of size R when the smallest fragment size is F is (R / F) / 2.
final int maxFragmentation = 1 << (log2LargestChunkSize - (log2MinFragmentSize + 1));
regionInfoIterable = new HeapRegionInfoIterable();
// We over allocate to allow fast indexing.
fragmentSizes = new int[log2LargestChunkSize + 1];
freeSpaceSizes = new int[log2LargestChunkSize + 1];
regionsFragmentation = new int[maxFragmentation + 1];
}
public void clear() {
for (int i = log2MinFragmentSize; i <= log2MinFragmentSize; i++) {
fragmentSizes[i] = 0;
freeSpaceSizes[i] = 0;
}
for (int i = 0; i < regionsFragmentation.length; i++) {
regionsFragmentation[i] = 0;
}
}
/**
* Add statistics for the specified region.
* @param rinfo a heap region info
*/
public void add(HeapRegionInfo rinfo) {
if (MaxineVM.isDebug()) {
FatalError.check(rinfo.hasFreeChunks() || (rinfo.isEmpty() && rinfo.freeBytesInChunks() == 0) || (rinfo.isFull() && rinfo.freeBytesInChunks() == 0), "Invalid RegionInfo");
}
regionsFragmentation[rinfo.numFreeChunks()]++;
if (rinfo.hasFreeChunks()) {
freeSpaceSizes[sizeBin(rinfo.freeBytesInChunks())]++;
} else if (rinfo.isEmpty()) {
freeSpaceSizes[log2LargestChunkSize]++;
} else {
freeSpaceSizes[0]++;
}
}
public void addFull(HeapRegionInfo rinfo) {
add(rinfo);
if (rinfo.hasFreeChunks()) {
HeapFreeChunk c = HeapFreeChunk.toHeapFreeChunk(rinfo.firstFreeBytes());
while (c != null) {
fragmentSizes[sizeBin(c.size)]++;
c = c.next;
}
}
}
public void doStats(HeapRegionList regionList) {
regionInfoIterable.initialize(regionList);
regionInfoIterable.reset();
while (regionInfoIterable.hasNext()) {
add(regionInfoIterable.next());
}
}
public void doStats(HeapAccount<? extends HeapAccountOwner>heapAccount) {
doStats(heapAccount.committedRegions());
}
public void doFullStats(HeapRegionList regionList) {
regionInfoIterable.initialize(regionList);
regionInfoIterable.reset();
while (regionInfoIterable.hasNext()) {
addFull(regionInfoIterable.next());
}
}
public void doFullStats(HeapAccount<? extends HeapAccountOwner>heapAccount) {
doFullStats(heapAccount.committedRegions());
}
public void dump() {
Log.println("[ min, max ] : # fragments # regions");
for (int i = log2MinFragmentSize; i < log2LargestChunkSize; i++) {
Log.print(" ["); Log.print(1 << i); Log.print(", "); Log.print((1 << i + 1) - 1); Log.print(" ] : ");
Log.print(fragmentSizes[i]);
Log.print(" ");
Log.println(freeSpaceSizes[i]);
}
Log.print("empty regions ("); Log.print(1 << log2LargestChunkSize); Log.print(") : "); Log.println(freeSpaceSizes[log2LargestChunkSize]);
Log.print("full regions : "); Log.println(freeSpaceSizes[0]);
Log.println(" # fragments : # regions");
for (int i = 0; i < regionsFragmentation.length; i++) {
int numRegions = regionsFragmentation[i];
if (numRegions > 0) {
Log.print(i); Log.print(" : "); Log.println(numRegions);
}
}
}
public void reportStats(HeapAccount<? extends HeapAccountOwner>heapAccount) {
clear();
doFullStats(heapAccount);
dump();
}
}