/* * $Id$ * * Copyright (C) 2003-2015 JNode.org * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library 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 Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library; If not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ package org.jnode.vm.memmgr.def; import java.io.IOException; import java.util.TreeMap; import org.jnode.util.NumberUtils; import org.jnode.vm.facade.HeapStatistics; import org.jnode.vm.facade.NoObjectFilter; import org.jnode.vm.facade.ObjectFilter; import org.jnode.vm.objects.VmSystemObject; /** * @author Martin Husted Hartvig (hagar@jnode.org) */ final class DefHeapStatistics extends VmSystemObject implements HeapStatistics { private int minInstanceCount = 0; private long minTotalSize = 0; private ObjectFilter objectFilter = NoObjectFilter.INSTANCE; private final TreeMap<String, HeapCounter> countData = new TreeMap<String, HeapCounter>(); private static final char NEWLINE = '\n'; private static final String USAGE = " memory usage="; private static final String NO_MATCHING_OBJECT = "No object is matching criteria"; private static final String SUMMARY = "Summary : "; private static final String CLASSES = " classe(s) "; private static final String INSTANCES = " instances(s) "; public boolean contains(String classname) { // If we don't accept this class, we pretend to have it already to (maybe) avoid unnecessary work // and memory allocation (we also hope to avoid a call to add(String, int)). return !objectFilter.accept(classname) || countData.containsKey(classname); } public void add(String className, int size) { if (objectFilter.accept(className)) { HeapCounter count = (HeapCounter) countData.get(className); if (count == null) { count = new HeapCounter(className, size); countData.put(className, count); } count.inc(); } } /** * Sets the minimum number of instances a class must have before * it is listed in toString. * * @param count */ public void setMinimumInstanceCount(int count) { this.minInstanceCount = count; } /** * Sets the minimum bytes of occupied memory by all instances of a class * before it is listed in toString. * * @param bytes */ public void setMinimumTotalSize(long bytes) { this.minTotalSize = bytes; } /** * {@inheritDoc} */ @Override public void setObjectFilter(ObjectFilter objectFilter) { this.objectFilter = (objectFilter == null) ? NoObjectFilter.INSTANCE : objectFilter; } /** * {@inheritDoc} * * @throws IOException */ public void writeTo(Appendable a) throws IOException { boolean first = true; if (countData.isEmpty()) { a.append(NO_MATCHING_OBJECT); } else { int nbClasses = 0; int nbInstances = 0; int totalSize = 0; for (HeapCounter c : countData.values()) { if ((c.getInstanceCount() >= minInstanceCount) && (c.getTotalSize() >= minTotalSize)) { if (first) { first = false; } else { a.append(NEWLINE); } c.append(a); nbClasses++; nbInstances += c.getInstanceCount(); totalSize += c.getTotalSize(); } } if (nbClasses == 0) { a.append(NO_MATCHING_OBJECT); } else { a.append(NEWLINE); a.append(SUMMARY).append(Integer.toString(nbClasses)).append(CLASSES); a.append(Integer.toString(nbInstances)).append(INSTANCES); appendUsage(a, totalSize); } } a.append(NEWLINE); } private static void appendUsage(Appendable a, long size) throws IOException { a.append(USAGE); if (size >= 1024) { a.append(NumberUtils.toBinaryByte(size)).append(" ("); a.append(Long.toString(size)).append("b)"); } else { a.append(Long.toString(size)).append('b'); } } /** * {@inheritDoc} */ public String toString() { final StringBuilder sb = new StringBuilder(); try { writeTo(sb); } catch (IOException e) { // normally, it will never happen throw new RuntimeException(e); } return sb.toString(); } static final class HeapCounter { private final String name; private int instanceCount; private int objectSize = 0; public HeapCounter(String objectName, int objectSize) { this.name = objectName; this.objectSize = objectSize; this.instanceCount = 0; } public void inc() { instanceCount++; } public int getInstanceCount() { return instanceCount; } public int getObjectSize() { return objectSize; } public long getTotalSize() { return objectSize * (long) instanceCount; } public void append(Appendable a) throws IOException { a.append(name); a.append(" #"); a.append(Integer.toString(instanceCount)); if (objectSize != 0) { appendUsage(a, getTotalSize()); } } } }