blob: de993b9c21adfc31819eacfb62d3ced62069297e [file] [log] [blame]
/*
* 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.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
import org.apache.lucene.index.Term;
import org.apache.lucene.search.BooleanClause;
import org.apache.lucene.search.BooleanQuery;
import org.apache.lucene.search.DisjunctionMaxQuery;
import org.apache.lucene.search.PhraseQuery;
import org.apache.lucene.search.TermQuery;
import static org.apache.lucene.util.RamUsageEstimator.COMPRESSED_REFS_ENABLED;
import static org.apache.lucene.util.RamUsageEstimator.HOTSPOT_BEAN_CLASS;
import static org.apache.lucene.util.RamUsageEstimator.JVM_IS_HOTSPOT_64BIT;
import static org.apache.lucene.util.RamUsageEstimator.LONG_CACHE_MAX_VALUE;
import static org.apache.lucene.util.RamUsageEstimator.LONG_CACHE_MIN_VALUE;
import static org.apache.lucene.util.RamUsageEstimator.LONG_SIZE;
import static org.apache.lucene.util.RamUsageEstimator.MANAGEMENT_FACTORY_CLASS;
import static org.apache.lucene.util.RamUsageEstimator.NUM_BYTES_ARRAY_HEADER;
import static org.apache.lucene.util.RamUsageEstimator.NUM_BYTES_OBJECT_ALIGNMENT;
import static org.apache.lucene.util.RamUsageEstimator.NUM_BYTES_OBJECT_HEADER;
import static org.apache.lucene.util.RamUsageEstimator.NUM_BYTES_OBJECT_REF;
import static org.apache.lucene.util.RamUsageEstimator.shallowSizeOf;
import static org.apache.lucene.util.RamUsageEstimator.shallowSizeOfInstance;
import static org.apache.lucene.util.RamUsageTester.sizeOf;
public class TestRamUsageEstimator extends LuceneTestCase {
static final String[] strings = new String[] {
"test string",
"hollow",
"catchmaster"
};
public void testSanity() {
assertTrue(sizeOf("test string") > shallowSizeOfInstance(String.class));
Holder holder = new Holder();
holder.holder = new Holder("string2", 5000L);
assertTrue(sizeOf(holder) > shallowSizeOfInstance(Holder.class));
assertTrue(sizeOf(holder) > sizeOf(holder.holder));
assertTrue(
shallowSizeOfInstance(HolderSubclass.class) >= shallowSizeOfInstance(Holder.class));
assertTrue(
shallowSizeOfInstance(Holder.class) == shallowSizeOfInstance(HolderSubclass2.class));
assertTrue(sizeOf(strings) > shallowSizeOf(strings));
}
public void testStaticOverloads() {
Random rnd = random();
{
byte[] array = new byte[rnd.nextInt(1024)];
assertEquals(sizeOf(array), sizeOf((Object) array));
}
{
boolean[] array = new boolean[rnd.nextInt(1024)];
assertEquals(sizeOf(array), sizeOf((Object) array));
}
{
char[] array = new char[rnd.nextInt(1024)];
assertEquals(sizeOf(array), sizeOf((Object) array));
}
{
short[] array = new short[rnd.nextInt(1024)];
assertEquals(sizeOf(array), sizeOf((Object) array));
}
{
int[] array = new int[rnd.nextInt(1024)];
assertEquals(sizeOf(array), sizeOf((Object) array));
}
{
float[] array = new float[rnd.nextInt(1024)];
assertEquals(sizeOf(array), sizeOf((Object) array));
}
{
long[] array = new long[rnd.nextInt(1024)];
assertEquals(sizeOf(array), sizeOf((Object) array));
}
{
double[] array = new double[rnd.nextInt(1024)];
assertEquals(sizeOf(array), sizeOf((Object) array));
}
}
public void testStrings() {
long actual = sizeOf(strings);
long estimated = RamUsageEstimator.sizeOf(strings);
assertEquals(actual, estimated);
}
public void testBytesRefHash() {
BytesRefHash bytes = new BytesRefHash();
for (int i = 0; i < 100; i++) {
bytes.add(new BytesRef("foo bar " + i));
bytes.add(new BytesRef("baz bam " + i));
}
long actual = sizeOf(bytes);
long estimated = RamUsageEstimator.sizeOf(bytes);
assertEquals((double)actual, (double)estimated, (double)actual * 0.1);
}
//@AwaitsFix(bugUrl="https://issues.apache.org/jira/browse/LUCENE-8898")
public void testMap() {
Map<String, Object> map = new HashMap<>();
map.put("primitive", 1234L);
map.put("string", "string");
for (int i = 0; i < 100; i++) {
map.put("complex " + i, new Term("foo " + i, "bar " + i));
}
double errorFactor = COMPRESSED_REFS_ENABLED ? 0.2 : 0.3;
long actual = sizeOf(map);
long estimated = RamUsageEstimator.sizeOfObject(map);
assertEquals((double)actual, (double)estimated, (double)actual * errorFactor);
// test recursion
map.put("self", map);
actual = sizeOf(map);
estimated = RamUsageEstimator.sizeOfObject(map);
assertEquals((double)actual, (double)estimated, (double)actual * errorFactor);
}
public void testCollection() {
List<Object> list = new ArrayList<>();
list.add(1234L);
list.add("string");
for (int i = 0; i < 100; i++) {
list.add(new Term("foo " + i, "term " + i));
}
long actual = sizeOf(list);
long estimated = RamUsageEstimator.sizeOfObject(list);
assertEquals((double)actual, (double)estimated, (double)actual * 0.1);
// test recursion
list.add(list);
actual = sizeOf(list);
estimated = RamUsageEstimator.sizeOfObject(list);
assertEquals((double)actual, (double)estimated, (double)actual * 0.1);
}
public void testQuery() {
DisjunctionMaxQuery dismax = new DisjunctionMaxQuery(
Arrays.asList(new TermQuery(new Term("foo1", "bar1")), new TermQuery(new Term("baz1", "bam1"))), 1.0f);
BooleanQuery bq = new BooleanQuery.Builder()
.add(new TermQuery(new Term("foo2", "bar2")), BooleanClause.Occur.SHOULD)
.add(new PhraseQuery.Builder().add(new Term("foo3", "baz3")).build(), BooleanClause.Occur.MUST_NOT)
.add(dismax, BooleanClause.Occur.MUST)
.build();
long actual = sizeOf(bq);
long estimated = RamUsageEstimator.sizeOfObject(bq);
// sizeOfObject uses much lower default size estimate than we normally use
// but the query-specific default is so large that the comparison becomes meaningless.
assertEquals((double)actual, (double)estimated, (double)actual * 0.5);
}
public void testReferenceSize() {
assertTrue(NUM_BYTES_OBJECT_REF == 4 || NUM_BYTES_OBJECT_REF == 8);
if (Constants.JRE_IS_64BIT) {
assertEquals("For 64 bit JVMs, reference size must be 8, unless compressed references are enabled",
COMPRESSED_REFS_ENABLED ? 4 : 8, NUM_BYTES_OBJECT_REF);
} else {
assertEquals("For 32bit JVMs, reference size must always be 4", 4, NUM_BYTES_OBJECT_REF);
assertFalse("For 32bit JVMs, compressed references can never be enabled", COMPRESSED_REFS_ENABLED);
}
}
public void testHotspotBean() {
assumeTrue("testHotspotBean only works on 64bit JVMs.", Constants.JRE_IS_64BIT);
try {
Class.forName(MANAGEMENT_FACTORY_CLASS);
} catch (ClassNotFoundException e) {
assumeNoException("testHotspotBean does not work on Java 8+ compact profile.", e);
}
try {
Class.forName(HOTSPOT_BEAN_CLASS);
} catch (ClassNotFoundException e) {
assumeNoException("testHotspotBean only works on Hotspot (OpenJDK, Oracle) virtual machines.", e);
}
assertTrue("We should have been able to detect Hotspot's internal settings from the management bean.", JVM_IS_HOTSPOT_64BIT);
}
/** Helper to print out current settings for debugging {@code -Dtests.verbose=true} */
public void testPrintValues() {
assumeTrue("Specify -Dtests.verbose=true to print constants of RamUsageEstimator.", VERBOSE);
System.out.println("JVM_IS_HOTSPOT_64BIT = " + JVM_IS_HOTSPOT_64BIT);
System.out.println("COMPRESSED_REFS_ENABLED = " + COMPRESSED_REFS_ENABLED);
System.out.println("NUM_BYTES_OBJECT_ALIGNMENT = " + NUM_BYTES_OBJECT_ALIGNMENT);
System.out.println("NUM_BYTES_OBJECT_REF = " + NUM_BYTES_OBJECT_REF);
System.out.println("NUM_BYTES_OBJECT_HEADER = " + NUM_BYTES_OBJECT_HEADER);
System.out.println("NUM_BYTES_ARRAY_HEADER = " + NUM_BYTES_ARRAY_HEADER);
System.out.println("LONG_SIZE = " + LONG_SIZE);
System.out.println("LONG_CACHE_MIN_VALUE = " + LONG_CACHE_MIN_VALUE);
System.out.println("LONG_CACHE_MAX_VALUE = " + LONG_CACHE_MAX_VALUE);
}
@SuppressWarnings("unused")
private static class Holder {
long field1 = 5000L;
String name = "name";
Holder holder;
long field2, field3, field4;
Holder() {}
Holder(String name, long field1) {
this.name = name;
this.field1 = field1;
}
}
@SuppressWarnings("unused")
private static class HolderSubclass extends Holder {
byte foo;
int bar;
}
private static class HolderSubclass2 extends Holder {
// empty, only inherits all fields -> size should be identical to superclass
}
}