X-Git-Url: https://git.mdrn.pl/pylucene.git/blobdiff_plain/a2e61f0c04805cfcb8706176758d1283c7e3a55c..aaeed5504b982cf3545252ab528713250aa33eed:/lucene-java-3.5.0/lucene/src/java/org/apache/lucene/util/RamUsageEstimator.java diff --git a/lucene-java-3.5.0/lucene/src/java/org/apache/lucene/util/RamUsageEstimator.java b/lucene-java-3.5.0/lucene/src/java/org/apache/lucene/util/RamUsageEstimator.java new file mode 100644 index 0000000..fd76948 --- /dev/null +++ b/lucene-java-3.5.0/lucene/src/java/org/apache/lucene/util/RamUsageEstimator.java @@ -0,0 +1,210 @@ +package org.apache.lucene.util; + +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import java.lang.reflect.*; +import java.text.DecimalFormat; +import java.util.*; + +/** + * Estimates the size of a given Object using a given MemoryModel for primitive + * size information. + * + * Resource Usage: + * + * Internally uses a Map to temporally hold a reference to every + * object seen. + * + * If checkInterned, all Strings checked will be interned, but those + * that were not already interned will be released for GC when the + * estimate is complete. + * + * @lucene.internal + */ +public final class RamUsageEstimator { + + public final static int NUM_BYTES_SHORT = 2; + public final static int NUM_BYTES_INT = 4; + public final static int NUM_BYTES_LONG = 8; + public final static int NUM_BYTES_FLOAT = 4; + public final static int NUM_BYTES_DOUBLE = 8; + public final static int NUM_BYTES_CHAR = 2; + public final static int NUM_BYTES_OBJECT_HEADER = 8; + public final static int NUM_BYTES_OBJECT_REF = Constants.JRE_IS_64BIT ? 8 : 4; + public final static int NUM_BYTES_ARRAY_HEADER = NUM_BYTES_OBJECT_HEADER + NUM_BYTES_INT + NUM_BYTES_OBJECT_REF; + + private MemoryModel memoryModel; + + private final Map seen; + + private int refSize; + private int arraySize; + private int classSize; + + private boolean checkInterned; + + /** + * Constructs this object with an AverageGuessMemoryModel and + * checkInterned = true. + */ + public RamUsageEstimator() { + this(new AverageGuessMemoryModel()); + } + + /** + * @param checkInterned check if Strings are interned and don't add to size + * if they are. Defaults to true but if you know the objects you are checking + * won't likely contain many interned Strings, it will be faster to turn off + * intern checking. + */ + public RamUsageEstimator(boolean checkInterned) { + this(new AverageGuessMemoryModel(), checkInterned); + } + + /** + * @param memoryModel MemoryModel to use for primitive object sizes. + */ + public RamUsageEstimator(MemoryModel memoryModel) { + this(memoryModel, true); + } + + /** + * @param memoryModel MemoryModel to use for primitive object sizes. + * @param checkInterned check if Strings are interned and don't add to size + * if they are. Defaults to true but if you know the objects you are checking + * won't likely contain many interned Strings, it will be faster to turn off + * intern checking. + */ + public RamUsageEstimator(MemoryModel memoryModel, boolean checkInterned) { + this.memoryModel = memoryModel; + this.checkInterned = checkInterned; + // Use Map rather than Set so that we can use an IdentityHashMap - not + // seeing an IdentityHashSet + seen = new IdentityHashMap(64); + this.refSize = memoryModel.getReferenceSize(); + this.arraySize = memoryModel.getArraySize(); + this.classSize = memoryModel.getClassSize(); + } + + public long estimateRamUsage(Object obj) { + long size = size(obj); + seen.clear(); + return size; + } + + private long size(Object obj) { + if (obj == null) { + return 0; + } + // interned not part of this object + if (checkInterned && obj instanceof String + && obj == ((String) obj).intern()) { // interned string will be eligible + // for GC on + // estimateRamUsage(Object) return + return 0; + } + + // skip if we have seen before + if (seen.containsKey(obj)) { + return 0; + } + + // add to seen + seen.put(obj, null); + + Class clazz = obj.getClass(); + if (clazz.isArray()) { + return sizeOfArray(obj); + } + + long size = 0; + + // walk type hierarchy + while (clazz != null) { + Field[] fields = clazz.getDeclaredFields(); + for (int i = 0; i < fields.length; i++) { + if (Modifier.isStatic(fields[i].getModifiers())) { + continue; + } + + if (fields[i].getType().isPrimitive()) { + size += memoryModel.getPrimitiveSize(fields[i].getType()); + } else { + size += refSize; + fields[i].setAccessible(true); + try { + Object value = fields[i].get(obj); + if (value != null) { + size += size(value); + } + } catch (IllegalAccessException ex) { + // ignore for now? + } + } + + } + clazz = clazz.getSuperclass(); + } + size += classSize; + return size; + } + + private long sizeOfArray(Object obj) { + int len = Array.getLength(obj); + if (len == 0) { + return 0; + } + long size = arraySize; + Class arrayElementClazz = obj.getClass().getComponentType(); + if (arrayElementClazz.isPrimitive()) { + size += len * memoryModel.getPrimitiveSize(arrayElementClazz); + } else { + for (int i = 0; i < len; i++) { + size += refSize + size(Array.get(obj, i)); + } + } + + return size; + } + + private static final long ONE_KB = 1024; + private static final long ONE_MB = ONE_KB * ONE_KB; + private static final long ONE_GB = ONE_KB * ONE_MB; + + /** + * Return good default units based on byte size. + */ + public static String humanReadableUnits(long bytes, DecimalFormat df) { + String newSizeAndUnits; + + if (bytes / ONE_GB > 0) { + newSizeAndUnits = String.valueOf(df.format((float) bytes / ONE_GB)) + + " GB"; + } else if (bytes / ONE_MB > 0) { + newSizeAndUnits = String.valueOf(df.format((float) bytes / ONE_MB)) + + " MB"; + } else if (bytes / ONE_KB > 0) { + newSizeAndUnits = String.valueOf(df.format((float) bytes / ONE_KB)) + + " KB"; + } else { + newSizeAndUnits = String.valueOf(bytes) + " bytes"; + } + + return newSizeAndUnits; + } +}