diff --git a/lucene/CHANGES.txt b/lucene/CHANGES.txt
index 255867d68c3..7a118f1d8c2 100644
--- a/lucene/CHANGES.txt
+++ b/lucene/CHANGES.txt
@@ -208,6 +208,10 @@ Other
* LUCENE-7599: Simplify TestRandomChains using Java's built-in Predicate and
Function interfaces. (Ahmet Arslan via Adrien Grand)
+* LUCENE-7595: Improve RAMUsageTester in test-framework to estimate memory usage of
+ runtime classes and work with Java 9 EA (b148+). Disable static field heap usage
+ checker in LuceneTestCase. (Uwe Schindler, Dawid Weiss)
+
Build
* LUCENE-7387: fix defaultCodec in build.xml to account for the line ending (hossman)
diff --git a/lucene/core/src/test/org/apache/lucene/search/TestLRUQueryCache.java b/lucene/core/src/test/org/apache/lucene/search/TestLRUQueryCache.java
index 87382f98387..9ebacf7292a 100644
--- a/lucene/core/src/test/org/apache/lucene/search/TestLRUQueryCache.java
+++ b/lucene/core/src/test/org/apache/lucene/search/TestLRUQueryCache.java
@@ -265,6 +265,8 @@ public class TestLRUQueryCache extends LuceneTestCase {
// This test makes sure that by making the same assumptions as LRUQueryCache, RAMUsageTester
// computes the same memory usage.
public void testRamBytesUsedAgreesWithRamUsageTester() throws IOException {
+ assumeFalse("LUCENE-7595: RamUsageTester does not work exact in Java 9 (estimations for maps and lists)", Constants.JRE_IS_MINIMUM_JAVA9);
+
final LRUQueryCache queryCache = new LRUQueryCache(1 + random().nextInt(5), 1 + random().nextInt(10000), context -> random().nextBoolean());
// an accumulator that only sums up memory usage of referenced filters and doc id sets
final RamUsageTester.Accumulator acc = new RamUsageTester.Accumulator() {
@@ -379,7 +381,6 @@ public class TestLRUQueryCache extends LuceneTestCase {
// by the cache itself, not cache entries, and we want to make sure that
// memory usage is not grossly underestimated.
public void testRamBytesUsedConstantEntryOverhead() throws IOException {
- LuceneTestCase.assumeFalse("RamUsageTester does not fully work on Java 9", Constants.JRE_IS_MINIMUM_JAVA9);
final LRUQueryCache queryCache = new LRUQueryCache(1000000, 10000000, context -> true);
final RamUsageTester.Accumulator acc = new RamUsageTester.Accumulator() {
diff --git a/lucene/test-framework/src/java/org/apache/lucene/util/LuceneTestCase.java b/lucene/test-framework/src/java/org/apache/lucene/util/LuceneTestCase.java
index 1848c4e7964..50139a099de 100644
--- a/lucene/test-framework/src/java/org/apache/lucene/util/LuceneTestCase.java
+++ b/lucene/test-framework/src/java/org/apache/lucene/util/LuceneTestCase.java
@@ -598,51 +598,55 @@ public abstract class LuceneTestCase extends Assert {
* other.
*/
@ClassRule
- public static TestRule classRules = RuleChain
- .outerRule(new TestRuleIgnoreTestSuites())
- .around(ignoreAfterMaxFailures)
- .around(suiteFailureMarker = new TestRuleMarkFailure())
- .around(new TestRuleAssertionsRequired())
- .around(new TestRuleLimitSysouts(suiteFailureMarker))
- .around(tempFilesCleanupRule = new TestRuleTemporaryFilesCleanup(suiteFailureMarker))
- .around(new StaticFieldsInvariantRule(STATIC_LEAK_THRESHOLD, true) {
- @Override
- protected boolean accept(java.lang.reflect.Field field) {
- // Don't count known classes that consume memory once.
- if (STATIC_LEAK_IGNORED_TYPES.contains(field.getType().getName())) {
- return false;
+ public static TestRule classRules;
+ static {
+ RuleChain r = RuleChain.outerRule(new TestRuleIgnoreTestSuites())
+ .around(ignoreAfterMaxFailures)
+ .around(suiteFailureMarker = new TestRuleMarkFailure())
+ .around(new TestRuleAssertionsRequired())
+ .around(new TestRuleLimitSysouts(suiteFailureMarker))
+ .around(tempFilesCleanupRule = new TestRuleTemporaryFilesCleanup(suiteFailureMarker));
+ // TODO LUCENE-7595: Java 9 does not allow to look into runtime classes, so we have to fix the RAM usage checker!
+ if (!Constants.JRE_IS_MINIMUM_JAVA9) {
+ r = r.around(new StaticFieldsInvariantRule(STATIC_LEAK_THRESHOLD, true) {
+ @Override
+ protected boolean accept(java.lang.reflect.Field field) {
+ // Don't count known classes that consume memory once.
+ if (STATIC_LEAK_IGNORED_TYPES.contains(field.getType().getName())) {
+ return false;
+ }
+ // Don't count references from ourselves, we're top-level.
+ if (field.getDeclaringClass() == LuceneTestCase.class) {
+ return false;
+ }
+ return super.accept(field);
}
- // Don't count references from ourselves, we're top-level.
- if (field.getDeclaringClass() == LuceneTestCase.class) {
- return false;
+ });
+ }
+ classRules = r.around(new NoClassHooksShadowingRule())
+ .around(new NoInstanceHooksOverridesRule() {
+ @Override
+ protected boolean verify(Method key) {
+ String name = key.getName();
+ return !(name.equals("setUp") || name.equals("tearDown"));
}
- return super.accept(field);
- }
- })
- .around(new NoClassHooksShadowingRule())
- .around(new NoInstanceHooksOverridesRule() {
- @Override
- protected boolean verify(Method key) {
- String name = key.getName();
- return !(name.equals("setUp") || name.equals("tearDown"));
- }
- })
- .around(classNameRule = new TestRuleStoreClassName())
- .around(new TestRuleRestoreSystemProperties(
- // Enlist all properties to which we have write access (security manager);
- // these should be restored to previous state, no matter what the outcome of the test.
-
- // We reset the default locale and timezone; these properties change as a side-effect
- "user.language",
- "user.timezone",
-
- // TODO: these should, ideally, be moved to Solr's base class.
- "solr.directoryFactory",
- "solr.solr.home",
- "solr.data.dir"
- ))
- .around(classEnvRule = new TestRuleSetupAndRestoreClassEnv());
-
+ })
+ .around(classNameRule = new TestRuleStoreClassName())
+ .around(new TestRuleRestoreSystemProperties(
+ // Enlist all properties to which we have write access (security manager);
+ // these should be restored to previous state, no matter what the outcome of the test.
+
+ // We reset the default locale and timezone; these properties change as a side-effect
+ "user.language",
+ "user.timezone",
+
+ // TODO: these should, ideally, be moved to Solr's base class.
+ "solr.directoryFactory",
+ "solr.solr.home",
+ "solr.data.dir"
+ ))
+ .around(classEnvRule = new TestRuleSetupAndRestoreClassEnv());
+ }
// -----------------------------------------------------------------
// Test level rules.
diff --git a/lucene/test-framework/src/java/org/apache/lucene/util/RamUsageTester.java b/lucene/test-framework/src/java/org/apache/lucene/util/RamUsageTester.java
index 985052654c1..daf81a96b35 100644
--- a/lucene/test-framework/src/java/org/apache/lucene/util/RamUsageTester.java
+++ b/lucene/test-framework/src/java/org/apache/lucene/util/RamUsageTester.java
@@ -16,9 +16,12 @@
*/
package org.apache.lucene.util;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
+import java.nio.file.Path;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.AbstractList;
@@ -30,6 +33,10 @@ import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.function.ToLongFunction;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+import java.util.stream.StreamSupport;
/** Crawls object graph to collect RAM usage for testing */
public final class RamUsageTester {
@@ -40,9 +47,7 @@ public final class RamUsageTester {
/** Accumulate transitive references for the provided fields of the given
* object into queue and return the shallow size of this object. */
public long accumulateObject(Object o, long shallowSize, Map fieldValues, Collection