Index: lucene/core/src/java/org/apache/lucene/util/Constants.java =================================================================== --- lucene/core/src/java/org/apache/lucene/util/Constants.java (revision 1300512) +++ lucene/core/src/java/org/apache/lucene/util/Constants.java (working copy) @@ -17,6 +17,8 @@ * limitations under the License. */ +import java.lang.reflect.Field; + import org.apache.lucene.LucenePackage; /** @@ -51,20 +53,63 @@ public static final boolean JRE_IS_64BIT; public static final boolean JRE_IS_MINIMUM_JAVA7; + public static final int JRE_REFERENCE_SIZE; static { - // NOTE: this logic may not be correct; if you know of a - // more reliable approach please raise it on java-dev! - final String x = System.getProperty("sun.arch.data.model"); - if (x != null) { - JRE_IS_64BIT = x.indexOf("64") != -1; - } else { - if (OS_ARCH != null && OS_ARCH.indexOf("64") != -1) { - JRE_IS_64BIT = true; + Object unsafe = null; + try { + final Class unsafeClass = Class.forName("sun.misc.Unsafe"); + final Field unsafeField = unsafeClass.getDeclaredField("theUnsafe"); + unsafeField.setAccessible(true); + unsafe = unsafeField.get(null); + } catch (Exception e) { + unsafe = null; + } + + Boolean is64Bit = null; + if (unsafe != null) { + try { + final int addressSize = ((Number) unsafe.getClass().getMethod("addressSize") + .invoke(unsafe)).intValue(); + //System.out.println("Address size: " + addressSize); + switch (addressSize) { + case 4: + is64Bit = Boolean.FALSE; + break; + case 8: + is64Bit = Boolean.TRUE; + break; + } + } catch (Exception e) { + // ignore + } + } + // fallback, if Unsafe cannot be accessed: + if (is64Bit == null) { + final String x = System.getProperty("sun.arch.data.model"); + if (x != null) { + is64Bit = Boolean.valueOf(x.indexOf("64") != -1); } else { - JRE_IS_64BIT = false; + if (OS_ARCH != null && OS_ARCH.indexOf("64") != -1) { + is64Bit = Boolean.TRUE; + } else { + is64Bit = Boolean.FALSE; + } } } + assert is64Bit != null; + JRE_IS_64BIT = is64Bit.booleanValue(); + int referenceSize = JRE_IS_64BIT ? 8 : 4; + if (unsafe != null) { + try { + referenceSize = ((Number) unsafe.getClass().getMethod("arrayIndexScale", Class.class) + .invoke(unsafe, Object[].class)).intValue(); + } catch (Exception e) { + // ignore + } + } + JRE_REFERENCE_SIZE = referenceSize; + // this method only exists in Java 7: boolean v7 = true; try { Index: lucene/core/src/java/org/apache/lucene/util/RamUsageEstimator.java =================================================================== --- lucene/core/src/java/org/apache/lucene/util/RamUsageEstimator.java (revision 1300512) +++ lucene/core/src/java/org/apache/lucene/util/RamUsageEstimator.java (working copy) @@ -45,9 +45,71 @@ 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; + public final static int NUM_BYTES_OBJECT_REF = Constants.JRE_REFERENCE_SIZE; + public final static int NUM_BYTES_ARRAY_HEADER = NUM_BYTES_OBJECT_HEADER + NUM_BYTES_INT /* array length */; + + /** Aligns an object size to be the next multiple of 8. */ + public static int alignObjectSize(int size) { + return (size + 7) & 0x7FFFFFF8; + } + + /** Returns the size in bytes of the byte[] object. */ + public static int sizeOf(byte[] arr) { + return alignObjectSize(NUM_BYTES_ARRAY_HEADER + arr.length); + } + + /** Returns the size in bytes of the char[] object. */ + public static int sizeOf(char[] arr) { + return alignObjectSize(NUM_BYTES_ARRAY_HEADER + NUM_BYTES_CHAR * arr.length); + } + /** Returns the size in bytes of the short[] object. */ + public static int sizeOf(short[] arr) { + return alignObjectSize(NUM_BYTES_ARRAY_HEADER + NUM_BYTES_SHORT * arr.length); + } + + /** Returns the size in bytes of the int[] object. */ + public static int sizeOf(int[] arr) { + return alignObjectSize(NUM_BYTES_ARRAY_HEADER + NUM_BYTES_INT * arr.length); + } + + /** Returns the size in bytes of the float[] object. */ + public static int sizeOf(float[] arr) { + return alignObjectSize(NUM_BYTES_ARRAY_HEADER + NUM_BYTES_FLOAT * arr.length); + } + + /** Returns the size in bytes of the long[] object. */ + public static int sizeOf(long[] arr) { + return alignObjectSize(NUM_BYTES_ARRAY_HEADER + NUM_BYTES_LONG * arr.length); + } + + /** Returns the size in bytes of the double[] object. */ + public static int sizeOf(double[] arr) { + return alignObjectSize(NUM_BYTES_ARRAY_HEADER + NUM_BYTES_DOUBLE * arr.length); + } + + /** Returns the size in bytes of the String[] object. */ + public static int sizeOf(String[] arr) { + int size = alignObjectSize(NUM_BYTES_ARRAY_HEADER + NUM_BYTES_OBJECT_REF * arr.length); + for (String s : arr) { + size += sizeOf(s); + } + return size; + } + + /** Returns the approximate size of a String object. */ + public static int sizeOf(String str) { + // String's char[] size + int arraySize = alignObjectSize(NUM_BYTES_ARRAY_HEADER + NUM_BYTES_CHAR * str.length()); + + // String's row object size + int objectSize = alignObjectSize(NUM_BYTES_OBJECT_REF /* array reference */ + + 3 * NUM_BYTES_INT /* String holds 3 integers */ + + NUM_BYTES_OBJECT_HEADER /* String object header */); + + return objectSize + arraySize; + } + private MemoryModel memoryModel; private final Map seen; Index: lucene/core/src/test/org/apache/lucene/util/TestRamUsageEstimator.java =================================================================== --- lucene/core/src/test/org/apache/lucene/util/TestRamUsageEstimator.java (revision 1300512) +++ lucene/core/src/test/org/apache/lucene/util/TestRamUsageEstimator.java (working copy) @@ -35,6 +35,17 @@ rue.estimateRamUsage(strings); } + public void testCompressedOops() { + if (VERBOSE) { + System.out.println("NOTE: This JVM is 64bit: " + Constants.JRE_IS_64BIT); + System.out.println("NOTE: Reference size in this JVM: " + Constants.JRE_REFERENCE_SIZE); + } + assertTrue(Constants.JRE_REFERENCE_SIZE == 4 || Constants.JRE_REFERENCE_SIZE == 8); + if (!Constants.JRE_IS_64BIT) { + assertEquals("For 32bit JVMs, reference size must always be 4", 4, Constants.JRE_REFERENCE_SIZE); + } + } + private static final class Holder { long field1 = 5000L; String name = "name";