| /* |
| * 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. |
| */ |
| package org.apache.lucene.util; |
| |
| |
| import java.lang.reflect.Array; |
| import java.lang.reflect.Field; |
| import java.lang.reflect.Method; |
| import java.lang.reflect.Modifier; |
| import java.security.AccessController; |
| import java.security.PrivilegedAction; |
| import java.text.DecimalFormat; |
| import java.text.DecimalFormatSymbols; |
| import java.util.Collection; |
| import java.util.Collections; |
| import java.util.IdentityHashMap; |
| import java.util.Locale; |
| import java.util.Map; |
| |
| import org.apache.lucene.index.Term; |
| import org.apache.lucene.search.BooleanClause; |
| import org.apache.lucene.search.Query; |
| import org.apache.lucene.search.QueryVisitor; |
| |
| /** |
| * Estimates the size (memory representation) of Java objects. |
| * <p> |
| * This class uses assumptions that were discovered for the Hotspot |
| * virtual machine. If you use a non-OpenJDK/Oracle-based JVM, |
| * the measurements may be slightly wrong. |
| * |
| * @see #shallowSizeOf(Object) |
| * @see #shallowSizeOfInstance(Class) |
| * |
| * @lucene.internal |
| */ |
| public final class RamUsageEstimator { |
| |
| /** One kilobyte bytes. */ |
| public static final long ONE_KB = 1024; |
| |
| /** One megabyte bytes. */ |
| public static final long ONE_MB = ONE_KB * ONE_KB; |
| |
| /** One gigabyte bytes.*/ |
| public static final long ONE_GB = ONE_KB * ONE_MB; |
| |
| /** No instantiation. */ |
| private RamUsageEstimator() {} |
| |
| /** |
| * True, iff compressed references (oops) are enabled by this JVM |
| */ |
| public final static boolean COMPRESSED_REFS_ENABLED; |
| |
| /** |
| * Number of bytes this JVM uses to represent an object reference. |
| */ |
| public final static int NUM_BYTES_OBJECT_REF; |
| |
| /** |
| * Number of bytes to represent an object header (no fields, no alignments). |
| */ |
| public final static int NUM_BYTES_OBJECT_HEADER; |
| |
| /** |
| * Number of bytes to represent an array header (no content, but with alignments). |
| */ |
| public final static int NUM_BYTES_ARRAY_HEADER; |
| |
| /** |
| * A constant specifying the object alignment boundary inside the JVM. Objects will |
| * always take a full multiple of this constant, possibly wasting some space. |
| */ |
| public final static int NUM_BYTES_OBJECT_ALIGNMENT; |
| |
| /** |
| * Approximate memory usage that we assign to all unknown queries - |
| * this maps roughly to a BooleanQuery with a couple term clauses. |
| */ |
| public static final int QUERY_DEFAULT_RAM_BYTES_USED = 1024; |
| |
| /** |
| * Approximate memory usage that we assign to all unknown objects - |
| * this maps roughly to a few primitive fields and a couple short String-s. |
| */ |
| public static final int UNKNOWN_DEFAULT_RAM_BYTES_USED = 256; |
| |
| /** |
| * Sizes of primitive classes. |
| */ |
| public static final Map<Class<?>,Integer> primitiveSizes; |
| |
| static { |
| Map<Class<?>, Integer> primitiveSizesMap = new IdentityHashMap<>(); |
| primitiveSizesMap.put(boolean.class, 1); |
| primitiveSizesMap.put(byte.class, 1); |
| primitiveSizesMap.put(char.class, Integer.valueOf(Character.BYTES)); |
| primitiveSizesMap.put(short.class, Integer.valueOf(Short.BYTES)); |
| primitiveSizesMap.put(int.class, Integer.valueOf(Integer.BYTES)); |
| primitiveSizesMap.put(float.class, Integer.valueOf(Float.BYTES)); |
| primitiveSizesMap.put(double.class, Integer.valueOf(Double.BYTES)); |
| primitiveSizesMap.put(long.class, Integer.valueOf(Long.BYTES)); |
| |
| primitiveSizes = Collections.unmodifiableMap(primitiveSizesMap); |
| } |
| |
| /** |
| * JVMs typically cache small longs. This tries to find out what the range is. |
| */ |
| static final long LONG_CACHE_MIN_VALUE, LONG_CACHE_MAX_VALUE; |
| static final int LONG_SIZE, STRING_SIZE; |
| |
| /** For testing only */ |
| static final boolean JVM_IS_HOTSPOT_64BIT; |
| |
| static final String MANAGEMENT_FACTORY_CLASS = "java.lang.management.ManagementFactory"; |
| static final String HOTSPOT_BEAN_CLASS = "com.sun.management.HotSpotDiagnosticMXBean"; |
| |
| /** |
| * Initialize constants and try to collect information about the JVM internals. |
| */ |
| static { |
| if (Constants.JRE_IS_64BIT) { |
| // Try to get compressed oops and object alignment (the default seems to be 8 on Hotspot); |
| // (this only works on 64 bit, on 32 bits the alignment and reference size is fixed): |
| boolean compressedOops = false; |
| int objectAlignment = 8; |
| boolean isHotspot = false; |
| try { |
| final Class<?> beanClazz = Class.forName(HOTSPOT_BEAN_CLASS); |
| // we use reflection for this, because the management factory is not part |
| // of Java 8's compact profile: |
| final Object hotSpotBean = Class.forName(MANAGEMENT_FACTORY_CLASS) |
| .getMethod("getPlatformMXBean", Class.class) |
| .invoke(null, beanClazz); |
| if (hotSpotBean != null) { |
| isHotspot = true; |
| final Method getVMOptionMethod = beanClazz.getMethod("getVMOption", String.class); |
| try { |
| final Object vmOption = getVMOptionMethod.invoke(hotSpotBean, "UseCompressedOops"); |
| compressedOops = Boolean.parseBoolean( |
| vmOption.getClass().getMethod("getValue").invoke(vmOption).toString() |
| ); |
| } catch (ReflectiveOperationException | RuntimeException e) { |
| isHotspot = false; |
| } |
| try { |
| final Object vmOption = getVMOptionMethod.invoke(hotSpotBean, "ObjectAlignmentInBytes"); |
| objectAlignment = Integer.parseInt( |
| vmOption.getClass().getMethod("getValue").invoke(vmOption).toString() |
| ); |
| } catch (ReflectiveOperationException | RuntimeException e) { |
| isHotspot = false; |
| } |
| } |
| } catch (ReflectiveOperationException | RuntimeException e) { |
| isHotspot = false; |
| } |
| JVM_IS_HOTSPOT_64BIT = isHotspot; |
| COMPRESSED_REFS_ENABLED = compressedOops; |
| NUM_BYTES_OBJECT_ALIGNMENT = objectAlignment; |
| // reference size is 4, if we have compressed oops: |
| NUM_BYTES_OBJECT_REF = COMPRESSED_REFS_ENABLED ? 4 : 8; |
| // "best guess" based on reference size: |
| NUM_BYTES_OBJECT_HEADER = 8 + NUM_BYTES_OBJECT_REF; |
| // array header is NUM_BYTES_OBJECT_HEADER + NUM_BYTES_INT, but aligned (object alignment): |
| NUM_BYTES_ARRAY_HEADER = (int) alignObjectSize(NUM_BYTES_OBJECT_HEADER + Integer.BYTES); |
| } else { |
| JVM_IS_HOTSPOT_64BIT = false; |
| COMPRESSED_REFS_ENABLED = false; |
| NUM_BYTES_OBJECT_ALIGNMENT = 8; |
| NUM_BYTES_OBJECT_REF = 4; |
| NUM_BYTES_OBJECT_HEADER = 8; |
| // For 32 bit JVMs, no extra alignment of array header: |
| NUM_BYTES_ARRAY_HEADER = NUM_BYTES_OBJECT_HEADER + Integer.BYTES; |
| } |
| |
| // get min/max value of cached Long class instances: |
| long longCacheMinValue = 0; |
| while (longCacheMinValue > Long.MIN_VALUE |
| && Long.valueOf(longCacheMinValue - 1) == Long.valueOf(longCacheMinValue - 1)) { |
| longCacheMinValue -= 1; |
| } |
| long longCacheMaxValue = -1; |
| while (longCacheMaxValue < Long.MAX_VALUE |
| && Long.valueOf(longCacheMaxValue + 1) == Long.valueOf(longCacheMaxValue + 1)) { |
| longCacheMaxValue += 1; |
| } |
| LONG_CACHE_MIN_VALUE = longCacheMinValue; |
| LONG_CACHE_MAX_VALUE = longCacheMaxValue; |
| LONG_SIZE = (int) shallowSizeOfInstance(Long.class); |
| STRING_SIZE = (int) shallowSizeOfInstance(String.class); |
| } |
| |
| /** Approximate memory usage that we assign to a Hashtable / HashMap entry. */ |
| public static final long HASHTABLE_RAM_BYTES_PER_ENTRY = |
| 2 * NUM_BYTES_OBJECT_REF // key + value |
| * 2; // hash tables need to be oversized to avoid collisions, assume 2x capacity |
| |
| /** Approximate memory usage that we assign to a LinkedHashMap entry. */ |
| public static final long LINKED_HASHTABLE_RAM_BYTES_PER_ENTRY = |
| HASHTABLE_RAM_BYTES_PER_ENTRY |
| + 2 * NUM_BYTES_OBJECT_REF; // previous & next references |
| |
| /** |
| * Aligns an object size to be the next multiple of {@link #NUM_BYTES_OBJECT_ALIGNMENT}. |
| */ |
| public static long alignObjectSize(long size) { |
| size += (long) NUM_BYTES_OBJECT_ALIGNMENT - 1L; |
| return size - (size % NUM_BYTES_OBJECT_ALIGNMENT); |
| } |
| |
| /** |
| * Return the size of the provided {@link Long} object, returning 0 if it is |
| * cached by the JVM and its shallow size otherwise. |
| */ |
| public static long sizeOf(Long value) { |
| if (value >= LONG_CACHE_MIN_VALUE && value <= LONG_CACHE_MAX_VALUE) { |
| return 0; |
| } |
| return LONG_SIZE; |
| } |
| |
| /** Returns the size in bytes of the byte[] object. */ |
| public static long sizeOf(byte[] arr) { |
| return alignObjectSize((long) NUM_BYTES_ARRAY_HEADER + arr.length); |
| } |
| |
| /** Returns the size in bytes of the boolean[] object. */ |
| public static long sizeOf(boolean[] arr) { |
| return alignObjectSize((long) NUM_BYTES_ARRAY_HEADER + arr.length); |
| } |
| |
| /** Returns the size in bytes of the char[] object. */ |
| public static long sizeOf(char[] arr) { |
| return alignObjectSize((long) NUM_BYTES_ARRAY_HEADER + (long) Character.BYTES * arr.length); |
| } |
| |
| /** Returns the size in bytes of the short[] object. */ |
| public static long sizeOf(short[] arr) { |
| return alignObjectSize((long) NUM_BYTES_ARRAY_HEADER + (long) Short.BYTES * arr.length); |
| } |
| |
| /** Returns the size in bytes of the int[] object. */ |
| public static long sizeOf(int[] arr) { |
| return alignObjectSize((long) NUM_BYTES_ARRAY_HEADER + (long) Integer.BYTES * arr.length); |
| } |
| |
| /** Returns the size in bytes of the float[] object. */ |
| public static long sizeOf(float[] arr) { |
| return alignObjectSize((long) NUM_BYTES_ARRAY_HEADER + (long) Float.BYTES * arr.length); |
| } |
| |
| /** Returns the size in bytes of the long[] object. */ |
| public static long sizeOf(long[] arr) { |
| return alignObjectSize((long) NUM_BYTES_ARRAY_HEADER + (long) Long.BYTES * arr.length); |
| } |
| |
| /** Returns the size in bytes of the double[] object. */ |
| public static long sizeOf(double[] arr) { |
| return alignObjectSize((long) NUM_BYTES_ARRAY_HEADER + (long) Double.BYTES * arr.length); |
| } |
| |
| /** Returns the size in bytes of the String[] object. */ |
| public static long sizeOf(String[] arr) { |
| long size = shallowSizeOf(arr); |
| for (String s : arr) { |
| if (s == null) { |
| continue; |
| } |
| size += sizeOf(s); |
| } |
| return size; |
| } |
| |
| /** Recurse only into immediate descendants. */ |
| public static final int MAX_DEPTH = 1; |
| |
| /** Returns the size in bytes of a Map object, including sizes of its keys and values, supplying |
| * {@link #UNKNOWN_DEFAULT_RAM_BYTES_USED} when object type is not well known. |
| * This method recurses up to {@link #MAX_DEPTH}. |
| */ |
| public static long sizeOfMap(Map<?, ?> map) { |
| return sizeOfMap(map, 0, UNKNOWN_DEFAULT_RAM_BYTES_USED); |
| } |
| |
| /** Returns the size in bytes of a Map object, including sizes of its keys and values, supplying |
| * default object size when object type is not well known. |
| * This method recurses up to {@link #MAX_DEPTH}. |
| */ |
| public static long sizeOfMap(Map<?, ?> map, long defSize) { |
| return sizeOfMap(map, 0, defSize); |
| } |
| |
| private static long sizeOfMap(Map<?, ?> map, int depth, long defSize) { |
| if (map == null) { |
| return 0; |
| } |
| long size = shallowSizeOf(map); |
| if (depth > MAX_DEPTH) { |
| return size; |
| } |
| long sizeOfEntry = -1; |
| for (Map.Entry<?, ?> entry : map.entrySet()) { |
| if (sizeOfEntry == -1) { |
| sizeOfEntry = shallowSizeOf(entry); |
| } |
| size += sizeOfEntry; |
| size += sizeOfObject(entry.getKey(), depth, defSize); |
| size += sizeOfObject(entry.getValue(), depth, defSize); |
| } |
| return alignObjectSize(size); |
| } |
| |
| /** Returns the size in bytes of a Collection object, including sizes of its values, supplying |
| * {@link #UNKNOWN_DEFAULT_RAM_BYTES_USED} when object type is not well known. |
| * This method recurses up to {@link #MAX_DEPTH}. |
| */ |
| public static long sizeOfCollection(Collection<?> collection) { |
| return sizeOfCollection(collection, 0, UNKNOWN_DEFAULT_RAM_BYTES_USED); |
| } |
| |
| /** Returns the size in bytes of a Collection object, including sizes of its values, supplying |
| * default object size when object type is not well known. |
| * This method recurses up to {@link #MAX_DEPTH}. |
| */ |
| public static long sizeOfCollection(Collection<?> collection, long defSize) { |
| return sizeOfCollection(collection, 0, defSize); |
| } |
| |
| private static long sizeOfCollection(Collection<?> collection, int depth, long defSize) { |
| if (collection == null) { |
| return 0; |
| } |
| long size = shallowSizeOf(collection); |
| if (depth > MAX_DEPTH) { |
| return size; |
| } |
| // assume array-backed collection and add per-object references |
| size += NUM_BYTES_ARRAY_HEADER + collection.size() * NUM_BYTES_OBJECT_REF; |
| for (Object o : collection) { |
| size += sizeOfObject(o, depth, defSize); |
| } |
| return alignObjectSize(size); |
| } |
| |
| private static final class RamUsageQueryVisitor extends QueryVisitor { |
| long total; |
| long defSize; |
| Query root; |
| |
| RamUsageQueryVisitor(Query root, long defSize) { |
| this.root = root; |
| this.defSize = defSize; |
| if (defSize > 0) { |
| total = defSize; |
| } else { |
| total = shallowSizeOf(root); |
| } |
| } |
| |
| @Override |
| public void consumeTerms(Query query, Term... terms) { |
| if (query != root) { |
| if (defSize > 0) { |
| total += defSize; |
| } else { |
| total += shallowSizeOf(query); |
| } |
| } |
| if (terms != null) { |
| total += sizeOf(terms); |
| } |
| } |
| |
| @Override |
| public void visitLeaf(Query query) { |
| if (query == root) { |
| return; |
| } |
| if (query instanceof Accountable) { |
| total += ((Accountable)query).ramBytesUsed(); |
| } else { |
| if (defSize > 0) { |
| total += defSize; |
| } else { |
| total += shallowSizeOf(query); |
| } |
| } |
| } |
| |
| @Override |
| public QueryVisitor getSubVisitor(BooleanClause.Occur occur, Query parent) { |
| return this; |
| } |
| } |
| |
| /** |
| * Returns the size in bytes of a Query object. Unknown query types will be estimated |
| * as {@link #QUERY_DEFAULT_RAM_BYTES_USED}. |
| */ |
| public static long sizeOf(Query q) { |
| return sizeOf(q, QUERY_DEFAULT_RAM_BYTES_USED); |
| } |
| |
| /** |
| * Returns the size in bytes of a Query object. Unknown query types will be estimated |
| * using {@link #shallowSizeOf(Object)}, or using the supplied <code>defSize</code> parameter |
| * if its value is greater than 0. |
| */ |
| public static long sizeOf(Query q, long defSize) { |
| if (q instanceof Accountable) { |
| return ((Accountable)q).ramBytesUsed(); |
| } else { |
| RamUsageQueryVisitor visitor = new RamUsageQueryVisitor(q, defSize); |
| q.visit(visitor); |
| return alignObjectSize(visitor.total); |
| } |
| } |
| |
| /** Best effort attempt to estimate the size in bytes of an undetermined object. Known types |
| * will be estimated according to their formulas, and all other object sizes will be estimated |
| * as {@link #UNKNOWN_DEFAULT_RAM_BYTES_USED}. |
| */ |
| public static long sizeOfObject(Object o) { |
| return sizeOfObject(o, 0, UNKNOWN_DEFAULT_RAM_BYTES_USED); |
| } |
| |
| /** Best effort attempt to estimate the size in bytes of an undetermined object. Known types |
| * will be estimated according to their formulas, and all other object sizes will be estimated |
| * using {@link #shallowSizeOf(Object)}, or using the supplied <code>defSize</code> parameter if |
| * its value is greater than 0. |
| */ |
| public static long sizeOfObject(Object o, long defSize) { |
| return sizeOfObject(o, 0, defSize); |
| } |
| |
| private static long sizeOfObject(Object o, int depth, long defSize) { |
| if (o == null) { |
| return 0; |
| } |
| long size; |
| if (o instanceof Accountable) { |
| size = ((Accountable)o).ramBytesUsed(); |
| } else if (o instanceof String) { |
| size = sizeOf((String)o); |
| } else if (o instanceof boolean[]) { |
| size = sizeOf((boolean[])o); |
| } else if (o instanceof byte[]) { |
| size = sizeOf((byte[])o); |
| } else if (o instanceof char[]) { |
| size = sizeOf((char[])o); |
| } else if (o instanceof double[]) { |
| size = sizeOf((double[])o); |
| } else if (o instanceof float[]) { |
| size = sizeOf((float[])o); |
| } else if (o instanceof int[]) { |
| size = sizeOf((int[])o); |
| } else if (o instanceof Long) { |
| size = sizeOf((Long)o); |
| } else if (o instanceof long[]) { |
| size = sizeOf((long[])o); |
| } else if (o instanceof short[]) { |
| size = sizeOf((short[])o); |
| } else if (o instanceof String[]) { |
| size = sizeOf((String[]) o); |
| } else if (o instanceof Query) { |
| size = sizeOf((Query)o, defSize); |
| } else if (o instanceof Map) { |
| size = sizeOfMap((Map) o, ++depth, defSize); |
| } else if (o instanceof Collection) { |
| size = sizeOfCollection((Collection)o, ++depth, defSize); |
| } else { |
| if (defSize > 0) { |
| size = defSize; |
| } else { |
| size = shallowSizeOf(o); |
| } |
| } |
| return size; |
| } |
| |
| /** Returns the size in bytes of the {@link Accountable} object, using its |
| * {@link Accountable#ramBytesUsed()} method. |
| */ |
| public static long sizeOf(Accountable accountable) { |
| return accountable.ramBytesUsed(); |
| } |
| |
| /** Returns the size in bytes of the String object. */ |
| public static long sizeOf(String s) { |
| if (s == null) { |
| return 0; |
| } |
| // may not be true in Java 9+ and CompactStrings - but we have no way to determine this |
| |
| // char[] + hashCode |
| long size = STRING_SIZE + (long)NUM_BYTES_ARRAY_HEADER + (long)Character.BYTES * s.length(); |
| return alignObjectSize(size); |
| } |
| |
| /** Returns the shallow size in bytes of the Object[] object. */ |
| // Use this method instead of #shallowSizeOf(Object) to avoid costly reflection |
| public static long shallowSizeOf(Object[] arr) { |
| return alignObjectSize((long) NUM_BYTES_ARRAY_HEADER + (long) NUM_BYTES_OBJECT_REF * arr.length); |
| } |
| |
| /** |
| * Estimates a "shallow" memory usage of the given object. For arrays, this will be the |
| * memory taken by array storage (no subreferences will be followed). For objects, this |
| * will be the memory taken by the fields. |
| * |
| * JVM object alignments are also applied. |
| */ |
| public static long shallowSizeOf(Object obj) { |
| if (obj == null) return 0; |
| final Class<?> clz = obj.getClass(); |
| if (clz.isArray()) { |
| return shallowSizeOfArray(obj); |
| } else { |
| return shallowSizeOfInstance(clz); |
| } |
| } |
| |
| /** |
| * Returns the shallow instance size in bytes an instance of the given class would occupy. |
| * This works with all conventional classes and primitive types, but not with arrays |
| * (the size then depends on the number of elements and varies from object to object). |
| * |
| * @see #shallowSizeOf(Object) |
| * @throws IllegalArgumentException if {@code clazz} is an array class. |
| */ |
| public static long shallowSizeOfInstance(Class<?> clazz) { |
| if (clazz.isArray()) |
| throw new IllegalArgumentException("This method does not work with array classes."); |
| if (clazz.isPrimitive()) |
| return primitiveSizes.get(clazz); |
| |
| long size = NUM_BYTES_OBJECT_HEADER; |
| |
| // Walk type hierarchy |
| for (;clazz != null; clazz = clazz.getSuperclass()) { |
| final Class<?> target = clazz; |
| final Field[] fields = AccessController.doPrivileged(new PrivilegedAction<Field[]>() { |
| @Override |
| public Field[] run() { |
| return target.getDeclaredFields(); |
| } |
| }); |
| for (Field f : fields) { |
| if (!Modifier.isStatic(f.getModifiers())) { |
| size = adjustForField(size, f); |
| } |
| } |
| } |
| return alignObjectSize(size); |
| } |
| |
| /** |
| * Return shallow size of any <code>array</code>. |
| */ |
| private static long shallowSizeOfArray(Object array) { |
| long size = NUM_BYTES_ARRAY_HEADER; |
| final int len = Array.getLength(array); |
| if (len > 0) { |
| Class<?> arrayElementClazz = array.getClass().getComponentType(); |
| if (arrayElementClazz.isPrimitive()) { |
| size += (long) len * primitiveSizes.get(arrayElementClazz); |
| } else { |
| size += (long) NUM_BYTES_OBJECT_REF * len; |
| } |
| } |
| return alignObjectSize(size); |
| } |
| |
| /** |
| * This method returns the maximum representation size of an object. <code>sizeSoFar</code> |
| * is the object's size measured so far. <code>f</code> is the field being probed. |
| * |
| * <p>The returned offset will be the maximum of whatever was measured so far and |
| * <code>f</code> field's offset and representation size (unaligned). |
| */ |
| static long adjustForField(long sizeSoFar, final Field f) { |
| final Class<?> type = f.getType(); |
| final int fsize = type.isPrimitive() ? primitiveSizes.get(type) : NUM_BYTES_OBJECT_REF; |
| // TODO: No alignments based on field type/ subclass fields alignments? |
| return sizeSoFar + fsize; |
| } |
| |
| /** |
| * Returns <code>size</code> in human-readable units (GB, MB, KB or bytes). |
| */ |
| public static String humanReadableUnits(long bytes) { |
| return humanReadableUnits(bytes, |
| new DecimalFormat("0.#", DecimalFormatSymbols.getInstance(Locale.ROOT))); |
| } |
| |
| /** |
| * Returns <code>size</code> in human-readable units (GB, MB, KB or bytes). |
| */ |
| public static String humanReadableUnits(long bytes, DecimalFormat df) { |
| if (bytes / ONE_GB > 0) { |
| return df.format((float) bytes / ONE_GB) + " GB"; |
| } else if (bytes / ONE_MB > 0) { |
| return df.format((float) bytes / ONE_MB) + " MB"; |
| } else if (bytes / ONE_KB > 0) { |
| return df.format((float) bytes / ONE_KB) + " KB"; |
| } else { |
| return bytes + " bytes"; |
| } |
| } |
| |
| /** |
| * Return the size of the provided array of {@link Accountable}s by summing |
| * up the shallow size of the array and the |
| * {@link Accountable#ramBytesUsed() memory usage} reported by each |
| * {@link Accountable}. |
| */ |
| public static long sizeOf(Accountable[] accountables) { |
| long size = shallowSizeOf(accountables); |
| for (Accountable accountable : accountables) { |
| if (accountable != null) { |
| size += accountable.ramBytesUsed(); |
| } |
| } |
| return size; |
| } |
| } |