--- old/src/os/bsd/vm/os_bsd.cpp 2013-05-14 13:35:29.700678178 +0200 +++ new/src/os/bsd/vm/os_bsd.cpp 2013-05-14 13:35:29.552678181 +0200 @@ -3403,8 +3403,6 @@ #endif } - os::large_page_init(); - // initialize suspend/resume support - must do this before signal_sets_init() if (SR_initialize() != 0) { perror("SR_initialize failed"); --- old/src/os/linux/vm/os_linux.cpp 2013-05-14 13:35:30.532678161 +0200 +++ new/src/os/linux/vm/os_linux.cpp 2013-05-14 13:35:30.388678164 +0200 @@ -4292,8 +4292,6 @@ #endif } - os::large_page_init(); - // initialize suspend/resume support - must do this before signal_sets_init() if (SR_initialize() != 0) { perror("SR_initialize failed"); --- old/src/os/solaris/vm/os_solaris.cpp 2013-05-14 13:35:31.488678142 +0200 +++ new/src/os/solaris/vm/os_solaris.cpp 2013-05-14 13:35:31.336678145 +0200 @@ -3330,6 +3330,7 @@ UseMPSS = false; _page_sizes[0] = _large_page_size; _page_sizes[1] = vm_page_size(); + _page_sizes[2] = 0; } UseMPSS = UseMPSS && @@ -5075,9 +5076,7 @@ if(Verbose && PrintMiscellaneous) tty->print("[Memory Serialize Page address: " INTPTR_FORMAT "]\n", (intptr_t)mem_serialize_page); #endif -} - - os::large_page_init(); + } // Check minimum allowable stack size for thread creation and to initialize // the java system classes, including StackOverflowError - depends on page --- old/src/os/windows/vm/os_windows.cpp 2013-05-14 13:35:32.576678121 +0200 +++ new/src/os/windows/vm/os_windows.cpp 2013-05-14 13:35:32.388678125 +0200 @@ -3811,8 +3811,6 @@ #endif } - os::large_page_init(); - // Setup Windows Exceptions // for debugging float code generation bugs --- old/src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp 2013-05-14 13:35:33.592678101 +0200 +++ new/src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp 2013-05-14 13:35:33.412678104 +0200 @@ -2182,6 +2182,10 @@ return JNI_OK; } +size_t G1CollectedHeap::max_heap_alignment() { + return HeapRegion::max_heap_alignment(); +} + void G1CollectedHeap::ref_processing_init() { // Reference processing in G1 currently works as follows: // --- old/src/share/vm/gc_implementation/g1/g1CollectedHeap.hpp 2013-05-14 13:35:34.544678082 +0200 +++ new/src/share/vm/gc_implementation/g1/g1CollectedHeap.hpp 2013-05-14 13:35:34.408678085 +0200 @@ -1076,6 +1076,9 @@ // specified by the policy object. jint initialize(); + // return the (conservative) maximum heap alignment for any G1 heap + static size_t max_heap_alignment(); + // Initialize weak reference processing. virtual void ref_processing_init(); --- old/src/share/vm/gc_implementation/g1/heapRegion.cpp 2013-05-14 13:35:35.380678066 +0200 +++ new/src/share/vm/gc_implementation/g1/heapRegion.cpp 2013-05-14 13:35:35.216678069 +0200 @@ -286,6 +286,10 @@ // many regions in the heap (based on the min heap size). #define TARGET_REGION_NUMBER 2048 +size_t HeapRegion::max_heap_alignment() { + return (size_t) MAX_REGION_SIZE; +} + void HeapRegion::setup_heap_region_size(uintx min_heap_size) { // region_size in bytes uintx region_size = G1HeapRegionSize; --- old/src/share/vm/gc_implementation/g1/heapRegion.hpp 2013-05-14 13:35:36.508678043 +0200 +++ new/src/share/vm/gc_implementation/g1/heapRegion.hpp 2013-05-14 13:35:36.316678047 +0200 @@ -354,6 +354,8 @@ ~((1 << (size_t) LogOfHRGrainBytes) - 1); } + // return the (conservative) maximum heap alignment for any heap region + static size_t max_heap_alignment(); // It sets up the heap region size (GrainBytes / GrainWords), as // well as other related fields that are based on the heap region // size (LogOfHRGrainBytes / LogOfHRGrainWords / --- old/src/share/vm/gc_implementation/parallelScavenge/parallelScavengeHeap.hpp 2013-05-14 13:35:37.648678021 +0200 +++ new/src/share/vm/gc_implementation/parallelScavenge/parallelScavengeHeap.hpp 2013-05-14 13:35:37.376678026 +0200 @@ -80,6 +80,11 @@ set_alignment(_old_gen_alignment, intra_heap_alignment()); } + // return the (conservative) maximum heap alignment + static size_t max_heap_alignment() { + return MAX2(os::max_page_size(), intra_heap_alignment()); + } + // For use by VM operations enum CollectionType { Scavenge, @@ -116,7 +121,7 @@ // The alignment used for eden and survivors within the young gen // and for boundary between young gen and old gen. - size_t intra_heap_alignment() const { return 64 * K; } + static size_t intra_heap_alignment() { return 64 * K; } size_t capacity() const; size_t used() const; --- old/src/share/vm/memory/collectorPolicy.cpp 2013-05-14 13:35:38.580678002 +0200 +++ new/src/share/vm/memory/collectorPolicy.cpp 2013-05-14 13:35:38.432678005 +0200 @@ -145,6 +145,17 @@ _all_soft_refs_clear = true; } +size_t CollectorPolicy::compute_max_alignment() { + // The card marking array and the offset arrays for old generations are + // committed in os pages as well. Make sure they are entirely full (to + // avoid partial page problems), e.g. if 512 bytes heap corresponds to 1 + // byte entry and the os page size is 4096, the maximum heap size should + // be 512*4096 = 2MB aligned. + + // there is only the GenRemSet in Hotspot and only the GenRemSet::CardTable + // is supported. + return GenRemSet::max_alignment_constraint(GenRemSet::CardTable); +} // GenCollectorPolicy methods. @@ -175,27 +186,6 @@ GCTimeRatio); } -size_t GenCollectorPolicy::compute_max_alignment() { - // The card marking array and the offset arrays for old generations are - // committed in os pages as well. Make sure they are entirely full (to - // avoid partial page problems), e.g. if 512 bytes heap corresponds to 1 - // byte entry and the os page size is 4096, the maximum heap size should - // be 512*4096 = 2MB aligned. - size_t alignment = GenRemSet::max_alignment_constraint(rem_set_name()); - - // Parallel GC does its own alignment of the generations to avoid requiring a - // large page (256M on some platforms) for the permanent generation. The - // other collectors should also be updated to do their own alignment and then - // this use of lcm() should be removed. - if (UseLargePages && !UseParallelGC) { - // in presence of large pages we have to make sure that our - // alignment is large page aware - alignment = lcm(os::large_page_size(), alignment); - } - - return alignment; -} - void GenCollectorPolicy::initialize_flags() { // All sizes must be multiples of the generation granularity. set_min_alignment((uintx) Generation::GenGrain); --- old/src/share/vm/memory/collectorPolicy.hpp 2013-05-14 13:35:39.404677986 +0200 +++ new/src/share/vm/memory/collectorPolicy.hpp 2013-05-14 13:35:39.244677989 +0200 @@ -98,6 +98,9 @@ {} public: + // compute (conservative) maximum heap alignment + static size_t compute_max_alignment(); + void set_min_alignment(size_t align) { _min_alignment = align; } size_t min_alignment() { return _min_alignment; } void set_max_alignment(size_t align) { _max_alignment = align; } @@ -234,9 +237,6 @@ // Try to allocate space by expanding the heap. virtual HeapWord* expand_heap_and_allocate(size_t size, bool is_tlab); - // compute max heap alignment - size_t compute_max_alignment(); - // Scale the base_size by NewRation according to // result = base_size / (NewRatio + 1) // and align by min_alignment() --- old/src/share/vm/memory/genCollectedHeap.cpp 2013-05-14 13:35:40.168677971 +0200 +++ new/src/share/vm/memory/genCollectedHeap.cpp 2013-05-14 13:35:40.008677974 +0200 @@ -95,7 +95,7 @@ guarantee(HeapWordSize == wordSize, "HeapWordSize must equal wordSize"); // The heap must be at least as aligned as generations. - size_t alignment = Generation::GenGrain; + size_t alignment = max_heap_alignment(); _gen_specs = gen_policy()->generations(); --- old/src/share/vm/memory/genCollectedHeap.hpp 2013-05-14 13:35:40.904677957 +0200 +++ new/src/share/vm/memory/genCollectedHeap.hpp 2013-05-14 13:35:40.736677960 +0200 @@ -148,6 +148,11 @@ return gen_policy()->size_policy(); } + // return the (conservative) maximum heap alignment + static size_t max_heap_alignment() { + return Generation::GenGrain; + } + size_t capacity() const; size_t used() const; --- old/src/share/vm/memory/universe.cpp 2013-05-14 13:35:41.624677942 +0200 +++ new/src/share/vm/memory/universe.cpp 2013-05-14 13:35:41.456677946 +0200 @@ -879,12 +879,13 @@ // Reserve the Java heap, which is now the same for all GCs. ReservedSpace Universe::reserve_heap(size_t heap_size, size_t alignment) { + assert(alignment <= Arguments::max_heap_alignment(), "actual alignment must be within maximum heap alignment"); // Add in the class metaspace area so the classes in the headers can // be compressed the same as instances. // Need to round class space size up because it's below the heap and // the actual alignment depends on its size. Universe::set_class_metaspace_size(align_size_up(ClassMetaspaceSize, alignment)); - size_t total_reserved = align_size_up(heap_size + Universe::class_metaspace_size(), alignment); + size_t total_reserved = align_size_up(heap_size, alignment) + Universe::class_metaspace_size(); assert(!UseCompressedOops || (total_reserved <= (OopEncodingHeapMax - os::vm_page_size())), "heap size is too big for compressed oops"); char* addr = Universe::preferred_heap_base(total_reserved, Universe::UnscaledNarrowOop); --- old/src/share/vm/prims/whitebox.cpp 2013-05-14 13:35:42.340677928 +0200 +++ new/src/share/vm/prims/whitebox.cpp 2013-05-14 13:35:42.192677931 +0200 @@ -33,6 +33,7 @@ #include "prims/whitebox.hpp" #include "prims/wbtestmethods/parserTests.hpp" +#include "runtime/arguments.hpp" #include "runtime/interfaceSupport.hpp" #include "runtime/os.hpp" #include "utilities/debug.hpp" @@ -93,6 +94,12 @@ return closure.found(); WB_END +WB_ENTRY(void, WB_PrintCompressedOopsHeapInfo(JNIEnv* env, jobject o)) { + gclog_or_tty->print_cr("Max heap for compressed oops "SIZE_FORMAT" ClassMetaspaceSize "SIZE_FORMAT, + Arguments::max_heap_for_compressed_oops(), ClassMetaspaceSize); +} +WB_END + WB_ENTRY(void, WB_PrintHeapSizes(JNIEnv* env, jobject o)) { CollectorPolicy * p = Universe::heap()->collector_policy(); gclog_or_tty->print_cr("Minimum heap "SIZE_FORMAT" Initial heap " @@ -395,6 +402,9 @@ CC"(Ljava/lang/String;[Lsun/hotspot/parser/DiagnosticCommand;)[Ljava/lang/Object;", (void*) &WB_ParseCommandLine }, + {CC"printCompressedOopsHeapInfo", + CC"()V", + (void*)&WB_PrintCompressedOopsHeapInfo}, {CC"printHeapSizes", CC"()V", (void*)&WB_PrintHeapSizes }, #if INCLUDE_ALL_GCS {CC"g1InConcurrentMark", CC"()Z", (void*)&WB_G1InConcurrentMark}, --- old/src/share/vm/runtime/arguments.cpp 2013-05-14 13:35:43.104677913 +0200 +++ new/src/share/vm/runtime/arguments.cpp 2013-05-14 13:35:42.920677917 +0200 @@ -52,7 +52,10 @@ #ifdef TARGET_OS_FAMILY_bsd # include "os_bsd.inline.hpp" #endif +#include "memory/genCollectedHeap.hpp" #if INCLUDE_ALL_GCS +#include "gc_implementation/parallelScavenge/parallelScavengeHeap.hpp" +#include "gc_implementation/g1/g1CollectedHeap.inline.hpp" #include "gc_implementation/concurrentMarkSweep/compactibleFreeListSpace.hpp" #endif // INCLUDE_ALL_GCS @@ -69,6 +72,7 @@ const char* Arguments::_gc_log_filename = NULL; bool Arguments::_has_profile = false; bool Arguments::_has_alloc_profile = false; +size_t Arguments::_max_heap_alignment = 0; uintx Arguments::_min_heap_size = 0; Arguments::Mode Arguments::_mode = _mixed; bool Arguments::_java_compiler = false; @@ -1354,12 +1358,32 @@ return true; } -inline uintx max_heap_for_compressed_oops() { +bool Arguments::apply_ergonomics_on_classmetaspacesize() { + return FLAG_IS_DEFAULT(ClassMetaspaceSize); +} + +size_t Arguments::max_heap_for_compressed_oops() { + // determine the maximum heap size that allows compressed oops. Use the theoretical + // maximum (OopEncodingHeapMax) and subtract the sizes of other areas that need + // to be below that maximum. Use a conservative estimate on the alignment returned + // by max_heap_alignment() to calculate these estimates for the other areas. + // We need to apply the alignment on all areas separately to make sure that later, + // when aligning the final heap size up, all sizes fit below the absolute maximum. + + // the absolute theoretical maximum + size_t max_total_heap = OopEncodingHeapMax; + + size_t max_class_metaspace_size = ClassMetaspaceSize; + if (apply_ergonomics_on_classmetaspacesize()) { + max_class_metaspace_size = MAX2(max_class_metaspace_size, ErgoClassMetaspaceSize); + } + size_t aligned_metaspace_size = align_size_up_(max_class_metaspace_size, max_heap_alignment()); + size_t aligned_page_size = align_size_up_(os::vm_page_size(), max_heap_alignment()); // Avoid sign flip. - if (OopEncodingHeapMax < ClassMetaspaceSize + os::vm_page_size()) { + if (max_total_heap < aligned_metaspace_size + aligned_page_size) { return 0; } - LP64_ONLY(return OopEncodingHeapMax - ClassMetaspaceSize - os::vm_page_size()); + LP64_ONLY(return max_total_heap - aligned_metaspace_size - aligned_page_size); NOT_LP64(ShouldNotReachHere(); return 0); } @@ -1411,6 +1435,23 @@ #endif // ZERO } +void Arguments::set_max_heap_alignment() { + size_t gc_alignment; +#if INCLUDE_ALL_GCS + if (UseParallelGC) { + gc_alignment = ParallelScavengeHeap::max_heap_alignment(); + } else if (UseG1GC) { + gc_alignment = G1CollectedHeap::max_heap_alignment(); + } else { +#endif // INCLUDE_ALL_GCS + gc_alignment = GenCollectedHeap::max_heap_alignment(); +#if INCLUDE_ALL_GCS + } +#endif // INCLUDE_ALL_GCS + _max_heap_alignment = MAX3(gc_alignment, os::max_page_size(), + CollectorPolicy::compute_max_alignment()); +} + void Arguments::set_ergonomics_flags() { if (os::is_server_class_machine()) { @@ -1438,6 +1479,8 @@ } } + set_max_heap_alignment(); + #ifndef ZERO #ifdef _LP64 set_use_compressed_oops(); @@ -1458,13 +1501,13 @@ if (ClassMetaspaceSize > KlassEncodingMetaspaceMax) { warning("Class metaspace size is too large for UseCompressedKlassPointers"); FLAG_SET_DEFAULT(UseCompressedKlassPointers, false); - } else if (FLAG_IS_DEFAULT(ClassMetaspaceSize)) { + } else if (apply_ergonomics_on_classmetaspacesize()) { // 100,000 classes seems like a good size, so 100M assumes around 1K // per klass. The vtable and oopMap is embedded so we don't have a fixed // size per klass. Eventually, this will be parameterized because it // would also be useful to determine the optimal size of the // systemDictionary. - FLAG_SET_ERGO(uintx, ClassMetaspaceSize, 100*M); + FLAG_SET_ERGO(uintx, ClassMetaspaceSize, ErgoClassMetaspaceSize); } } } @@ -3360,6 +3403,11 @@ no_shared_spaces(); #endif // INCLUDE_CDS + // We need to initialize large page support here because ergonomics takes some + // decisions depending on large page support and the calculated large page size. + // Ergonomics may turn off large page support off later again. + os::large_page_init(); + // Set flags based on ergonomics. set_ergonomics_flags(); --- old/src/share/vm/runtime/arguments.hpp 2013-05-14 13:35:43.948677896 +0200 +++ new/src/share/vm/runtime/arguments.hpp 2013-05-14 13:35:43.780677900 +0200 @@ -221,6 +221,11 @@ }; private: + // should ergenomics be applied to the ClassMetaspaceSize? + static bool apply_ergonomics_on_classmetaspacesize(); + + // value of the ergonomically determined ClassMetaspaceSize + static const size_t ErgoClassMetaspaceSize = 100 * M; // an array containing all flags specified in the .hotspotrc file static char** _jvm_flags_array; @@ -264,6 +269,9 @@ static bool _has_profile; static bool _has_alloc_profile; static const char* _gc_log_filename; + // computed value for the maximum heap alignment + static size_t _max_heap_alignment; + static uintx _min_heap_size; // -Xrun arguments @@ -309,6 +317,7 @@ // Garbage-First (UseG1GC) static void set_g1_gc_flags(); // GC ergonomics + static void set_max_heap_alignment(); static void set_use_compressed_oops(); static void set_ergonomics_flags(); static void set_shared_spaces_flags(); @@ -426,6 +435,10 @@ // Used by os_solaris static bool process_settings_file(const char* file_name, bool should_exist, jboolean ignore_unrecognized); + static size_t max_heap_alignment() { return _max_heap_alignment; } + // return the maximum size a heap with compressed oops can take + static size_t max_heap_for_compressed_oops(); + // return a char* array containing all options static char** jvm_flags_array() { return _jvm_flags_array; } static char** jvm_args_array() { return _jvm_args_array; } --- old/src/share/vm/runtime/os.hpp 2013-05-14 13:35:44.840677879 +0200 +++ new/src/share/vm/runtime/os.hpp 2013-05-14 13:35:44.528677885 +0200 @@ -238,6 +238,11 @@ static size_t page_size_for_region(size_t region_min_size, size_t region_max_size, uint min_pages); + // return the largest page size used + static size_t max_page_size() { + // the _page_sizes array is sorted in descending order. + return _page_sizes[0]; + } // Methods for tracing page sizes returned by the above method; enabled by // TracePageSizes. The region_{min,max}_size parameters should be the values --- old/test/testlibrary/whitebox/sun/hotspot/WhiteBox.java 2013-05-14 13:35:45.624677863 +0200 +++ new/test/testlibrary/whitebox/sun/hotspot/WhiteBox.java 2013-05-14 13:35:45.444677867 +0200 @@ -61,6 +61,7 @@ registerNatives(); } + public native void printCompressedOopsHeapInfo(); // Arguments public native void printHeapSizes(); --- /dev/null 2013-04-16 09:14:15.018414535 +0200 +++ new/test/gc/arguments/TestUseCompressedOopsErgo.java 2013-05-14 13:35:46.740677841 +0200 @@ -0,0 +1,47 @@ +/* +* Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. +* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +* +* This code is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License version 2 only, as +* published by the Free Software Foundation. +* +* This code is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +* version 2 for more details (a copy is included in the LICENSE file that +* accompanied this code). +* +* You should have received a copy of the GNU General Public License version +* 2 along with this work; if not, write to the Free Software Foundation, +* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +* +* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA +* or visit www.oracle.com if you need additional information or have any +* questions. +*/ + +/* + * @test TestUseCompressedOopsErgo + * @key gc + * @bug 8010722 + * @summary Tests ergonomics for UseCompressedOops. + * @library /testlibrary /testlibrary/whitebox + * @build TestUseCompressedOopsErgo TestUseCompressedOopsErgoTools + * @run main ClassFileInstaller sun.hotspot.WhiteBox + * @run main/othervm TestUseCompressedOopsErgo -XX:+UseG1GC + * @run main/othervm TestUseCompressedOopsErgo -XX:+UseParallelOldGC + * @run main/othervm TestUseCompressedOopsErgo -XX:+UseConcMarkSweepGC + * @run main/othervm TestUseCompressedOopsErgo -XX:+UseSerialGC + * @author thomas.schatzl@oracle.com +*/ + +public class TestUseCompressedOopsErgo { + + public static void main(String args[]) throws Exception { + final String gcName = args[0]; + + TestUseCompressedOopsErgoTools.checkCompressedOopsErgo(gcName); + } +} + --- /dev/null 2013-04-16 09:14:15.018414535 +0200 +++ new/test/gc/arguments/TestUseCompressedOopsErgoTools.java 2013-05-14 13:35:49.476677787 +0200 @@ -0,0 +1,158 @@ +/* +* Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. +* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +* +* This code is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License version 2 only, as +* published by the Free Software Foundation. +* +* This code is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +* version 2 for more details (a copy is included in the LICENSE file that +* accompanied this code). +* +* You should have received a copy of the GNU General Public License version +* 2 along with this work; if not, write to the Free Software Foundation, +* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +* +* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA +* or visit www.oracle.com if you need additional information or have any +* questions. +*/ + +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.ArrayList; +import java.util.Arrays; + +import com.oracle.java.testlibrary.*; +import sun.hotspot.WhiteBox; + +class CompressedOopsErgoPrinter { + public static void main(String[] args) throws Exception { + WhiteBox wb = WhiteBox.getWhiteBox(); + wb.printCompressedOopsHeapInfo(); + } +} + +final class HeapForCompressedOopsValues { + public long maxHeapForCompressedOops; +} + +class TestUseCompressedOopsErgoTools { + + public static void checkCompressedOopsErgo(String gcflag) throws Exception { + HeapForCompressedOopsValues v = new HeapForCompressedOopsValues(); + + getMaxHeapForCompressedOops(new String[] { gcflag }, v); + + checkUseCompressedOops(new String[] { gcflag }, v.maxHeapForCompressedOops, true); + checkUseCompressedOops(new String[] { gcflag }, v.maxHeapForCompressedOops - 1, true); + checkUseCompressedOops(new String[] { gcflag }, v.maxHeapForCompressedOops + 1, false); + + // the use of HeapBaseMinAddress should not change the outcome + checkUseCompressedOops(new String[] { gcflag, "-XX:HeapBaseMinAddress=32G" }, v.maxHeapForCompressedOops, true); + checkUseCompressedOops(new String[] { gcflag, "-XX:HeapBaseMinAddress=32G" }, v.maxHeapForCompressedOops - 1, true); + checkUseCompressedOops(new String[] { gcflag, "-XX:HeapBaseMinAddress=32G" }, v.maxHeapForCompressedOops + 1, false); + + // use a different object alignment + getMaxHeapForCompressedOops(new String[] { gcflag, "-XX:ObjectAlignmentInBytes=16" }, v); + + checkUseCompressedOops(new String[] { gcflag, "-XX:ObjectAlignmentInBytes=16" }, v.maxHeapForCompressedOops, true); + checkUseCompressedOops(new String[] { gcflag, "-XX:ObjectAlignmentInBytes=16" }, v.maxHeapForCompressedOops - 1, true); + checkUseCompressedOops(new String[] { gcflag, "-XX:ObjectAlignmentInBytes=16" }, v.maxHeapForCompressedOops + 1, false); + + // use a different ClassMetaspaceSize + getMaxHeapForCompressedOops(new String[] { gcflag, "-XX:ClassMetaspaceSize=1G" }, v); + + checkUseCompressedOops(new String[] { gcflag, "-XX:ClassMetaspaceSize=1G" }, v.maxHeapForCompressedOops, true); + checkUseCompressedOops(new String[] { gcflag, "-XX:ClassMetaspaceSize=1G" }, v.maxHeapForCompressedOops - 1, true); + checkUseCompressedOops(new String[] { gcflag, "-XX:ClassMetaspaceSize=1G" }, v.maxHeapForCompressedOops + 1, false); + } + + private static void checkUseCompressedOops(String[] args, long heapsize, boolean expectUseCompressedOops) throws Exception { + ArrayList finalargs = new ArrayList(); + finalargs.addAll(Arrays.asList(args)); + finalargs.add("-Xmx" + heapsize); + finalargs.add("-XX:+PrintFlagsFinal"); + finalargs.add("-version"); + + String output = expectValid(finalargs.toArray(new String[0])); + + boolean actualUseCompressedOops = getFlagBoolValue(" UseCompressedOops", output); + + if (expectUseCompressedOops != actualUseCompressedOops) { + throw new RuntimeException("Expected use of compressed oops: " + expectUseCompressedOops + " but was: " + actualUseCompressedOops); + } + } + + private static long valueAfter(String source, String match) { + int start = source.indexOf(match) + match.length(); + String tail = source.substring(start).split(" ")[0]; + return Long.parseLong(tail); + } + + private static void getMaxHeapForCompressedOops(String[] args, HeapForCompressedOopsValues val) throws Exception { + ArrayList finalargs = new ArrayList(); + + String[] testVMopts = new String[] { + "-XX:+UnlockDiagnosticVMOptions", "-XX:+WhiteBoxAPI", + "-Xbootclasspath/a:.", + "-cp", System.getProperty("java.class.path"), + }; + + finalargs.addAll(Arrays.asList(args)); + finalargs.addAll(Arrays.asList(testVMopts)); + finalargs.add(CompressedOopsErgoPrinter.class.getName()); + + ProcessBuilder pb = ProcessTools.createJavaProcessBuilder(finalargs.toArray(new String[0])); + OutputAnalyzer output = new OutputAnalyzer(pb.start()); + output.shouldHaveExitValue(0); + + // the output we watch for has the following format: + // + // "Max heap for compressed oops X Max heap alignment Y ClassMetaspaceSize Z Page size A" + // + // where X, Y, Z and A are sizes in bytes. + + Matcher m = Pattern.compile("Max heap for compressed oops \\d+ ClassMetaspaceSize \\d+"). + matcher(output.getStdout()); + if (!m.find()) { + throw new RuntimeException("Could not find heap size string."); + } + String match = m.group(); + + // parse actual values + val.maxHeapForCompressedOops = valueAfter(match, "Max heap for compressed oops "); + } + + private static boolean getFlagBoolValue(String flag, String where) { + Matcher m = Pattern.compile(flag + "\\s+:?= (true|false)").matcher(where); + if (!m.find()) { + throw new RuntimeException("Could not find value for flag " + flag + " in output string"); + } + String match = m.group(); + return match.substring(match.lastIndexOf(" ") + 1, match.length()).equals("true"); + } + + private static void shouldContainOrNot(OutputAnalyzer output, boolean contains, String message) throws Exception { + if (contains) { + output.shouldContain(message); + } else { + output.shouldNotContain(message); + } + } + + private static String expect(String[] flags, boolean hasWarning, boolean hasError, int errorcode) throws Exception { + ProcessBuilder pb = ProcessTools.createJavaProcessBuilder(flags); + OutputAnalyzer output = new OutputAnalyzer(pb.start()); + output.shouldHaveExitValue(errorcode); + return output.getStdout(); + } + + private static String expectValid(String[] flags) throws Exception { + return expect(flags, false, false, 0); + } +} +