From 95a96a8974f74b84159a58266545f438c5811aba Mon Sep 17 00:00:00 2001 From: tschatzl Date: Wed, 11 Sep 2013 16:25:02 +0200 Subject: [PATCH] 8010722: assert: failed: heap size is too big for compressed oops Summary: Use conservative assumptions of required alignment for the various garbage collector components into account when determining the maximum heap size that supports compressed oops. Using this conservative value avoids several circular dependencies in the calculation. Reviewed-by: stefank, dholmes --- src/os/bsd/vm/os_bsd.cpp | 2 - src/os/linux/vm/os_linux.cpp | 2 - src/os/solaris/vm/os_solaris.cpp | 4 +- src/os/windows/vm/os_windows.cpp | 2 - .../gc_implementation/g1/g1CollectedHeap.cpp | 4 + .../gc_implementation/g1/g1CollectedHeap.hpp | 3 + .../vm/gc_implementation/g1/heapRegion.cpp | 6 +- .../vm/gc_implementation/g1/heapRegion.hpp | 4 +- .../parallelScavenge/parallelScavengeHeap.hpp | 7 +- src/share/vm/memory/collectorPolicy.cpp | 47 ++--- src/share/vm/memory/collectorPolicy.hpp | 6 +- src/share/vm/memory/genCollectedHeap.hpp | 7 +- src/share/vm/memory/universe.cpp | 3 + src/share/vm/prims/whitebox.cpp | 8 + src/share/vm/runtime/arguments.cpp | 39 +++- src/share/vm/runtime/arguments.hpp | 12 +- src/share/vm/runtime/os.cpp | 5 + src/share/vm/runtime/os.hpp | 8 + src/share/vm/runtime/thread.cpp | 5 + .../arguments/TestUseCompressedOopsErgo.java | 50 +++++ .../TestUseCompressedOopsErgoTools.java | 179 ++++++++++++++++++ .../whitebox/sun/hotspot/WhiteBox.java | 2 + 22 files changed, 363 insertions(+), 42 deletions(-) create mode 100644 test/gc/arguments/TestUseCompressedOopsErgo.java create mode 100644 test/gc/arguments/TestUseCompressedOopsErgoTools.java diff --git a/src/os/bsd/vm/os_bsd.cpp b/src/os/bsd/vm/os_bsd.cpp index 6b636d379..58c961b43 100644 --- a/src/os/bsd/vm/os_bsd.cpp +++ b/src/os/bsd/vm/os_bsd.cpp @@ -3589,8 +3589,6 @@ jint os::init_2(void) #endif } - os::large_page_init(); - // initialize suspend/resume support - must do this before signal_sets_init() if (SR_initialize() != 0) { perror("SR_initialize failed"); diff --git a/src/os/linux/vm/os_linux.cpp b/src/os/linux/vm/os_linux.cpp index 70837a850..5b3d6b311 100644 --- a/src/os/linux/vm/os_linux.cpp +++ b/src/os/linux/vm/os_linux.cpp @@ -4755,8 +4755,6 @@ jint os::init_2(void) #endif } - os::large_page_init(); - // initialize suspend/resume support - must do this before signal_sets_init() if (SR_initialize() != 0) { perror("SR_initialize failed"); diff --git a/src/os/solaris/vm/os_solaris.cpp b/src/os/solaris/vm/os_solaris.cpp index c3e6e8790..8e5984ffa 100644 --- a/src/os/solaris/vm/os_solaris.cpp +++ b/src/os/solaris/vm/os_solaris.cpp @@ -5178,9 +5178,7 @@ jint os::init_2(void) { 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 diff --git a/src/os/windows/vm/os_windows.cpp b/src/os/windows/vm/os_windows.cpp index 985cbd623..e55281af4 100644 --- a/src/os/windows/vm/os_windows.cpp +++ b/src/os/windows/vm/os_windows.cpp @@ -3920,8 +3920,6 @@ jint os::init_2(void) { #endif } - os::large_page_init(); - // Setup Windows Exceptions // for debugging float code generation bugs diff --git a/src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp b/src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp index bd01ec3b6..1d8ff8a26 100644 --- a/src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp +++ b/src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp @@ -2191,6 +2191,10 @@ jint G1CollectedHeap::initialize() { return JNI_OK; } +size_t G1CollectedHeap::conservative_max_heap_alignment() { + return HeapRegion::max_region_size(); +} + void G1CollectedHeap::ref_processing_init() { // Reference processing in G1 currently works as follows: // diff --git a/src/share/vm/gc_implementation/g1/g1CollectedHeap.hpp b/src/share/vm/gc_implementation/g1/g1CollectedHeap.hpp index aecaa5e97..747b23262 100644 --- a/src/share/vm/gc_implementation/g1/g1CollectedHeap.hpp +++ b/src/share/vm/gc_implementation/g1/g1CollectedHeap.hpp @@ -1092,6 +1092,9 @@ public: // specified by the policy object. jint initialize(); + // Return the (conservative) maximum heap alignment for any G1 heap + static size_t conservative_max_heap_alignment(); + // Initialize weak reference processing. virtual void ref_processing_init(); diff --git a/src/share/vm/gc_implementation/g1/heapRegion.cpp b/src/share/vm/gc_implementation/g1/heapRegion.cpp index e66268885..726acbfcd 100644 --- a/src/share/vm/gc_implementation/g1/heapRegion.cpp +++ b/src/share/vm/gc_implementation/g1/heapRegion.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 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 @@ -149,6 +149,10 @@ void HeapRegionDCTOC::walk_mem_region_with_cl(MemRegion mr, // many regions in the heap (based on the min heap size). #define TARGET_REGION_NUMBER 2048 +size_t HeapRegion::max_region_size() { + return (size_t)MAX_REGION_SIZE; +} + void HeapRegion::setup_heap_region_size(size_t initial_heap_size, size_t max_heap_size) { uintx region_size = G1HeapRegionSize; if (FLAG_IS_DEFAULT(G1HeapRegionSize)) { diff --git a/src/share/vm/gc_implementation/g1/heapRegion.hpp b/src/share/vm/gc_implementation/g1/heapRegion.hpp index 097c0b543..ad2b06497 100644 --- a/src/share/vm/gc_implementation/g1/heapRegion.hpp +++ b/src/share/vm/gc_implementation/g1/heapRegion.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 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 @@ -355,6 +355,8 @@ class HeapRegion: public G1OffsetTableContigSpace { ~((1 << (size_t) LogOfHRGrainBytes) - 1); } + static size_t max_region_size(); + // 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 / diff --git a/src/share/vm/gc_implementation/parallelScavenge/parallelScavengeHeap.hpp b/src/share/vm/gc_implementation/parallelScavenge/parallelScavengeHeap.hpp index 11ef53251..4e458efa9 100644 --- a/src/share/vm/gc_implementation/parallelScavenge/parallelScavengeHeap.hpp +++ b/src/share/vm/gc_implementation/parallelScavenge/parallelScavengeHeap.hpp @@ -86,6 +86,11 @@ class ParallelScavengeHeap : public CollectedHeap { set_alignment(_old_gen_alignment, intra_heap_alignment()); } + // Return the (conservative) maximum heap alignment + static size_t conservative_max_heap_alignment() { + return intra_heap_alignment(); + } + // For use by VM operations enum CollectionType { Scavenge, @@ -122,7 +127,7 @@ class ParallelScavengeHeap : public CollectedHeap { // 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 * HeapWordSize; } + static size_t intra_heap_alignment() { return 64 * K * HeapWordSize; } size_t capacity() const; size_t used() const; diff --git a/src/share/vm/memory/collectorPolicy.cpp b/src/share/vm/memory/collectorPolicy.cpp index 6a1967dae..a5b4aa618 100644 --- a/src/share/vm/memory/collectorPolicy.cpp +++ b/src/share/vm/memory/collectorPolicy.cpp @@ -145,6 +145,30 @@ void CollectorPolicy::cleared_all_soft_refs() { _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. + // Requirements of any new remembered set implementations must be added here. + size_t alignment = GenRemSet::max_alignment_constraint(GenRemSet::CardTable); + + // 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; +} // GenCollectorPolicy methods. @@ -175,29 +199,6 @@ void GenCollectorPolicy::initialize_size_policy(size_t init_eden_size, 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); - } - - assert(alignment >= min_alignment(), "Must be"); - - return alignment; -} - void GenCollectorPolicy::initialize_flags() { // All sizes must be multiples of the generation granularity. set_min_alignment((uintx) Generation::GenGrain); diff --git a/src/share/vm/memory/collectorPolicy.hpp b/src/share/vm/memory/collectorPolicy.hpp index 4acf7ba78..73fd177d7 100644 --- a/src/share/vm/memory/collectorPolicy.hpp +++ b/src/share/vm/memory/collectorPolicy.hpp @@ -98,6 +98,9 @@ class CollectorPolicy : public CHeapObj { {} public: + // Return maximum heap alignment that may be imposed by the policy + 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 @@ class GenCollectorPolicy : public CollectorPolicy { // 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() diff --git a/src/share/vm/memory/genCollectedHeap.hpp b/src/share/vm/memory/genCollectedHeap.hpp index 9ab9d1991..8f814132a 100644 --- a/src/share/vm/memory/genCollectedHeap.hpp +++ b/src/share/vm/memory/genCollectedHeap.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 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 @@ -148,6 +148,11 @@ public: return gen_policy()->size_policy(); } + // Return the (conservative) maximum heap alignment + static size_t conservative_max_heap_alignment() { + return Generation::GenGrain; + } + size_t capacity() const; size_t used() const; diff --git a/src/share/vm/memory/universe.cpp b/src/share/vm/memory/universe.cpp index d0f34b33a..54d6851d1 100644 --- a/src/share/vm/memory/universe.cpp +++ b/src/share/vm/memory/universe.cpp @@ -872,6 +872,9 @@ jint Universe::initialize_heap() { // 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::conservative_max_heap_alignment(), + err_msg("actual alignment "SIZE_FORMAT" must be within maximum heap alignment "SIZE_FORMAT, + alignment, Arguments::conservative_max_heap_alignment())); size_t total_reserved = align_size_up(heap_size, alignment); assert(!UseCompressedOops || (total_reserved <= (OopEncodingHeapMax - os::vm_page_size())), "heap size is too big for compressed oops"); diff --git a/src/share/vm/prims/whitebox.cpp b/src/share/vm/prims/whitebox.cpp index 0f5607e22..6f6a2000a 100644 --- a/src/share/vm/prims/whitebox.cpp +++ b/src/share/vm/prims/whitebox.cpp @@ -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" @@ -94,6 +95,11 @@ WB_ENTRY(jboolean, WB_IsClassAlive(JNIEnv* env, jobject target, jstring name)) return closure.found(); WB_END +WB_ENTRY(jlong, WB_GetCompressedOopsMaxHeapSize(JNIEnv* env, jobject o)) { + return (jlong)Arguments::max_heap_for_compressed_oops(); +} +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 " @@ -436,6 +442,8 @@ static JNINativeMethod methods[] = { CC"(Ljava/lang/String;[Lsun/hotspot/parser/DiagnosticCommand;)[Ljava/lang/Object;", (void*) &WB_ParseCommandLine }, + {CC"getCompressedOopsMaxHeapSize", CC"()J", + (void*)&WB_GetCompressedOopsMaxHeapSize}, {CC"printHeapSizes", CC"()V", (void*)&WB_PrintHeapSizes }, #if INCLUDE_ALL_GCS {CC"g1InConcurrentMark", CC"()Z", (void*)&WB_G1InConcurrentMark}, diff --git a/src/share/vm/runtime/arguments.cpp b/src/share/vm/runtime/arguments.cpp index fbce8b6b6..9751c5522 100644 --- a/src/share/vm/runtime/arguments.cpp +++ b/src/share/vm/runtime/arguments.cpp @@ -28,6 +28,7 @@ #include "compiler/compilerOracle.hpp" #include "memory/allocation.inline.hpp" #include "memory/cardTableRS.hpp" +#include "memory/genCollectedHeap.hpp" #include "memory/referenceProcessor.hpp" #include "memory/universe.inline.hpp" #include "oops/oop.inline.hpp" @@ -54,6 +55,8 @@ #endif #if INCLUDE_ALL_GCS #include "gc_implementation/concurrentMarkSweep/compactibleFreeListSpace.hpp" +#include "gc_implementation/g1/g1CollectedHeap.inline.hpp" +#include "gc_implementation/parallelScavenge/parallelScavengeHeap.hpp" #endif // INCLUDE_ALL_GCS // Note: This is a special bug reporting site for the JVM @@ -90,6 +93,7 @@ char* Arguments::_java_command = NULL; SystemProperty* Arguments::_system_properties = NULL; const char* Arguments::_gc_log_filename = NULL; bool Arguments::_has_profile = false; +size_t Arguments::_conservative_max_heap_alignment = 0; uintx Arguments::_min_heap_size = 0; Arguments::Mode Arguments::_mode = _mixed; bool Arguments::_java_compiler = false; @@ -1391,10 +1395,17 @@ bool verify_object_alignment() { return true; } -inline uintx max_heap_for_compressed_oops() { +uintx Arguments::max_heap_for_compressed_oops() { // Avoid sign flip. assert(OopEncodingHeapMax > (uint64_t)os::vm_page_size(), "Unusual page size"); - LP64_ONLY(return OopEncodingHeapMax - os::vm_page_size()); + // We need to fit both the NULL page and the heap into the memory budget, while + // keeping alignment constraints of the heap. To guarantee the latter, as the + // NULL page is located before the heap, we pad the NULL page to the conservative + // maximum alignment that the GC may ever impose upon the heap. + size_t displacement_due_to_null_page = align_size_up_(os::vm_page_size(), + Arguments::conservative_max_heap_alignment()); + + LP64_ONLY(return OopEncodingHeapMax - displacement_due_to_null_page); NOT_LP64(ShouldNotReachHere(); return 0); } @@ -1475,6 +1486,23 @@ void Arguments::set_use_compressed_klass_ptrs() { #endif // !ZERO } +void Arguments::set_conservative_max_heap_alignment() { + // The conservative maximum required alignment for the heap is the maximum of + // the alignments imposed by several sources: any requirements from the heap + // itself, the collector policy and the maximum page size we may run the VM + // with. + size_t heap_alignment = GenCollectedHeap::conservative_max_heap_alignment(); +#if INCLUDE_ALL_GCS + if (UseParallelGC) { + heap_alignment = ParallelScavengeHeap::conservative_max_heap_alignment(); + } else if (UseG1GC) { + heap_alignment = G1CollectedHeap::conservative_max_heap_alignment(); + } +#endif // INCLUDE_ALL_GCS + _conservative_max_heap_alignment = MAX3(heap_alignment, os::max_page_size(), + CollectorPolicy::compute_max_alignment()); +} + void Arguments::set_ergonomics_flags() { if (os::is_server_class_machine()) { @@ -1503,6 +1531,8 @@ void Arguments::set_ergonomics_flags() { } } + set_conservative_max_heap_alignment(); + #ifndef ZERO #ifdef _LP64 set_use_compressed_oops(); @@ -3506,6 +3536,11 @@ jint Arguments::parse(const JavaVMInitArgs* args) { no_shared_spaces(); #endif // INCLUDE_CDS + return JNI_OK; +} + +jint Arguments::apply_ergo() { + // Set flags based on ergonomics. set_ergonomics_flags(); diff --git a/src/share/vm/runtime/arguments.hpp b/src/share/vm/runtime/arguments.hpp index 43256b750..c2f4c9883 100644 --- a/src/share/vm/runtime/arguments.hpp +++ b/src/share/vm/runtime/arguments.hpp @@ -280,6 +280,9 @@ class Arguments : AllStatic { // Option flags static bool _has_profile; static const char* _gc_log_filename; + // Value of the conservative maximum heap alignment needed + static size_t _conservative_max_heap_alignment; + static uintx _min_heap_size; // -Xrun arguments @@ -327,6 +330,7 @@ class Arguments : AllStatic { // Garbage-First (UseG1GC) static void set_g1_gc_flags(); // GC ergonomics + static void set_conservative_max_heap_alignment(); static void set_use_compressed_oops(); static void set_use_compressed_klass_ptrs(); static void set_ergonomics_flags(); @@ -430,8 +434,10 @@ class Arguments : AllStatic { static char* SharedArchivePath; public: - // Parses the arguments + // Parses the arguments, first phase static jint parse(const JavaVMInitArgs* args); + // Apply ergonomics + static jint apply_ergo(); // Adjusts the arguments after the OS have adjusted the arguments static jint adjust_after_os(); // Check for consistency in the selection of the garbage collector. @@ -445,6 +451,10 @@ class Arguments : AllStatic { // Used by os_solaris static bool process_settings_file(const char* file_name, bool should_exist, jboolean ignore_unrecognized); + static size_t conservative_max_heap_alignment() { return _conservative_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; } diff --git a/src/share/vm/runtime/os.cpp b/src/share/vm/runtime/os.cpp index 0c8356519..8f49f4a2e 100644 --- a/src/share/vm/runtime/os.cpp +++ b/src/share/vm/runtime/os.cpp @@ -314,6 +314,11 @@ static void signal_thread_entry(JavaThread* thread, TRAPS) { } } +void os::init_before_ergo() { + // We need to initialize large page support here because ergonomics takes some + // decisions depending on large page support and the calculated large page size. + large_page_init(); +} void os::signal_init() { if (!ReduceSignalUsage) { diff --git a/src/share/vm/runtime/os.hpp b/src/share/vm/runtime/os.hpp index e43d68981..3438badb0 100644 --- a/src/share/vm/runtime/os.hpp +++ b/src/share/vm/runtime/os.hpp @@ -139,7 +139,10 @@ class os: AllStatic { public: static void init(void); // Called before command line parsing + static void init_before_ergo(void); // Called after command line parsing + // before VM ergonomics processing. static jint init_2(void); // Called after command line parsing + // and VM ergonomics processing static void init_globals(void) { // Called from init_globals() in init.cpp init_globals_ext(); } @@ -254,6 +257,11 @@ class os: AllStatic { 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 that can be 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 diff --git a/src/share/vm/runtime/thread.cpp b/src/share/vm/runtime/thread.cpp index d5bc3a000..02bd4b38f 100644 --- a/src/share/vm/runtime/thread.cpp +++ b/src/share/vm/runtime/thread.cpp @@ -3329,6 +3329,11 @@ jint Threads::create_vm(JavaVMInitArgs* args, bool* canTryAgain) { jint parse_result = Arguments::parse(args); if (parse_result != JNI_OK) return parse_result; + os::init_before_ergo(); + + jint ergo_result = Arguments::apply_ergo(); + if (ergo_result != JNI_OK) return ergo_result; + if (PauseAtStartup) { os::pause(); } diff --git a/test/gc/arguments/TestUseCompressedOopsErgo.java b/test/gc/arguments/TestUseCompressedOopsErgo.java new file mode 100644 index 000000000..852076e76 --- /dev/null +++ b/test/gc/arguments/TestUseCompressedOopsErgo.java @@ -0,0 +1,50 @@ +/* +* 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:+UseParallelGC + * @run main/othervm TestUseCompressedOopsErgo -XX:+UseParallelGC -XX:-UseParallelOldGC + * @run main/othervm TestUseCompressedOopsErgo -XX:+UseConcMarkSweepGC + * @run main/othervm TestUseCompressedOopsErgo -XX:+UseSerialGC + */ + +public class TestUseCompressedOopsErgo { + + public static void main(String args[]) throws Exception { + if (!TestUseCompressedOopsErgoTools.is64bitVM()) { + // this test is relevant for 64 bit VMs only + return; + } + final String[] gcFlags = args; + TestUseCompressedOopsErgoTools.checkCompressedOopsErgo(gcFlags); + } +} + diff --git a/test/gc/arguments/TestUseCompressedOopsErgoTools.java b/test/gc/arguments/TestUseCompressedOopsErgoTools.java new file mode 100644 index 000000000..49d0a8a1d --- /dev/null +++ b/test/gc/arguments/TestUseCompressedOopsErgoTools.java @@ -0,0 +1,179 @@ +/* +* 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 sun.management.ManagementFactoryHelper; +import com.sun.management.HotSpotDiagnosticMXBean; +import com.sun.management.VMOption; + +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 DetermineMaxHeapForCompressedOops { + public static void main(String[] args) throws Exception { + WhiteBox wb = WhiteBox.getWhiteBox(); + System.out.print(wb.getCompressedOopsMaxHeapSize()); + } +} + +class TestUseCompressedOopsErgoTools { + + private static long getClassMetaspaceSize() { + HotSpotDiagnosticMXBean diagnostic = ManagementFactoryHelper.getDiagnosticMXBean(); + + VMOption option = diagnostic.getVMOption("ClassMetaspaceSize"); + return Long.parseLong(option.getValue()); + } + + + public static long getMaxHeapForCompressedOops(String[] vmargs) throws Exception { + OutputAnalyzer output = runWhiteBoxTest(vmargs, DetermineMaxHeapForCompressedOops.class.getName(), new String[] {}, false); + return Long.parseLong(output.getStdout()); + } + + public static boolean is64bitVM() { + String val = System.getProperty("sun.arch.data.model"); + if (val == null) { + throw new RuntimeException("Could not read sun.arch.data.model"); + } + if (val.equals("64")) { + return true; + } else if (val.equals("32")) { + return false; + } + throw new RuntimeException("Unexpected value " + val + " of sun.arch.data.model"); + } + + /** + * Executes a new VM process with the given class and parameters. + * @param vmargs Arguments to the VM to run + * @param classname Name of the class to run + * @param arguments Arguments to the class + * @param useTestDotJavaDotOpts Use test.java.opts as part of the VM argument string + * @return The OutputAnalyzer with the results for the invocation. + */ + public static OutputAnalyzer runWhiteBoxTest(String[] vmargs, String classname, String[] arguments, boolean useTestDotJavaDotOpts) throws Exception { + ArrayList finalargs = new ArrayList(); + + String[] whiteboxOpts = new String[] { + "-Xbootclasspath/a:.", + "-XX:+UnlockDiagnosticVMOptions", "-XX:+WhiteBoxAPI", + "-cp", System.getProperty("java.class.path"), + }; + + if (useTestDotJavaDotOpts) { + // System.getProperty("test.java.opts") is '' if no options is set, + // we need to skip such a result + String[] externalVMOpts = new String[0]; + if (System.getProperty("test.java.opts") != null && System.getProperty("test.java.opts").length() != 0) { + externalVMOpts = System.getProperty("test.java.opts").split(" "); + } + finalargs.addAll(Arrays.asList(externalVMOpts)); + } + + finalargs.addAll(Arrays.asList(vmargs)); + finalargs.addAll(Arrays.asList(whiteboxOpts)); + finalargs.add(classname); + finalargs.addAll(Arrays.asList(arguments)); + + ProcessBuilder pb = ProcessTools.createJavaProcessBuilder(finalargs.toArray(new String[0])); + OutputAnalyzer output = new OutputAnalyzer(pb.start()); + output.shouldHaveExitValue(0); + return output; + } + + private static String[] join(String[] part1, String part2) { + ArrayList result = new ArrayList(); + result.addAll(Arrays.asList(part1)); + result.add(part2); + return result.toArray(new String[0]); + } + + public static void checkCompressedOopsErgo(String[] gcflags) throws Exception { + long maxHeapForCompressedOops = getMaxHeapForCompressedOops(gcflags); + + checkUseCompressedOops(gcflags, maxHeapForCompressedOops, true); + checkUseCompressedOops(gcflags, maxHeapForCompressedOops - 1, true); + checkUseCompressedOops(gcflags, maxHeapForCompressedOops + 1, false); + + // the use of HeapBaseMinAddress should not change the outcome + checkUseCompressedOops(join(gcflags, "-XX:HeapBaseMinAddress=32G"), maxHeapForCompressedOops, true); + checkUseCompressedOops(join(gcflags, "-XX:HeapBaseMinAddress=32G"), maxHeapForCompressedOops - 1, true); + checkUseCompressedOops(join(gcflags, "-XX:HeapBaseMinAddress=32G"), maxHeapForCompressedOops + 1, false); + + // use a different object alignment + maxHeapForCompressedOops = getMaxHeapForCompressedOops(join(gcflags, "-XX:ObjectAlignmentInBytes=16")); + + checkUseCompressedOops(join(gcflags, "-XX:ObjectAlignmentInBytes=16"), maxHeapForCompressedOops, true); + checkUseCompressedOops(join(gcflags, "-XX:ObjectAlignmentInBytes=16"), maxHeapForCompressedOops - 1, true); + checkUseCompressedOops(join(gcflags, "-XX:ObjectAlignmentInBytes=16"), maxHeapForCompressedOops + 1, false); + + // use a different ClassMetaspaceSize + String classMetaspaceSizeArg = "-XX:ClassMetaspaceSize=" + 2 * getClassMetaspaceSize(); + maxHeapForCompressedOops = getMaxHeapForCompressedOops(join(gcflags, classMetaspaceSizeArg)); + + checkUseCompressedOops(join(gcflags, classMetaspaceSizeArg), maxHeapForCompressedOops, true); + checkUseCompressedOops(join(gcflags, classMetaspaceSizeArg), maxHeapForCompressedOops - 1, true); + checkUseCompressedOops(join(gcflags, classMetaspaceSizeArg), 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 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(1).equals("true"); + } + + 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); + } +} + diff --git a/test/testlibrary/whitebox/sun/hotspot/WhiteBox.java b/test/testlibrary/whitebox/sun/hotspot/WhiteBox.java index c508b8f12..091b5f754 100644 --- a/test/testlibrary/whitebox/sun/hotspot/WhiteBox.java +++ b/test/testlibrary/whitebox/sun/hotspot/WhiteBox.java @@ -61,6 +61,8 @@ public class WhiteBox { registerNatives(); } + // Get the maximum heap size supporting COOPs + public native long getCompressedOopsMaxHeapSize(); // Arguments public native void printHeapSizes(); -- GitLab