OpenJDK / amber / amber
changeset 26382:b764fbee45e2
Merge
author | duke |
---|---|
date | Wed, 05 Jul 2017 19:59:54 +0200 |
parents | bbce32388a2d 436d37b20f8d |
children | 3b419bf4f176 |
files | hotspot/agent/src/share/classes/sun/jvm/hotspot/gc_implementation/g1/HeapRegionSeq.java hotspot/src/share/vm/gc_implementation/g1/heapRegionSeq.cpp hotspot/src/share/vm/gc_implementation/g1/heapRegionSeq.hpp hotspot/src/share/vm/gc_implementation/g1/heapRegionSeq.inline.hpp jdk/src/java.desktop/share/conf/cmm/lcms/CIEXYZ.pf jdk/src/java.desktop/share/conf/cmm/lcms/GRAY.pf jdk/src/java.desktop/share/conf/cmm/lcms/LINEAR_RGB.pf jdk/src/java.desktop/share/conf/cmm/lcms/PYCC.pf jdk/src/java.desktop/share/conf/cmm/lcms/sRGB.pf jdk/test/java/awt/Mixing/AWT_Mixing/Util.java nashorn/bin/dump_octane_code.sh nashorn/bin/fixorphantests.sh nashorn/bin/fixwhitespace.sh nashorn/bin/jjsdebug.sh nashorn/bin/rm-non-tracked.sh nashorn/bin/run_octane.sh nashorn/bin/rundiff.sh nashorn/bin/runopt.sh nashorn/bin/runopt_noassert.sh nashorn/bin/runopt_nojfr.sh |
diffstat | 309 files changed, 12474 insertions(+), 5861 deletions(-) [+] |
line wrap: on
line diff
--- a/.hgtags-top-repo Thu Sep 04 13:26:26 2014 -0700 +++ b/.hgtags-top-repo Wed Jul 05 19:59:54 2017 +0200 @@ -271,3 +271,4 @@ d3ec8d048e6c3c46b6e0ee011cc551ad386dfba5 jdk9-b26 ba5645f2735b41ed085d07ba20fa7b322afff318 jdk9-b27 ea2f7981236f3812436958748ab3d26e80a35130 jdk9-b28 +9e6581aeda388a23fbee021fc33e6aa152a60657 jdk9-b29
--- a/corba/.hgtags Thu Sep 04 13:26:26 2014 -0700 +++ b/corba/.hgtags Wed Jul 05 19:59:54 2017 +0200 @@ -271,3 +271,4 @@ 6c777df597bbf5abba3488d44c401edfe73c74af jdk9-b26 7e06bf1dcb0907b80ddf59315426ce9ce775e56d jdk9-b27 a00b04ef067e39f50b9a0fea6f1904e35d632a73 jdk9-b28 +163a9cd806fd09970baf1f5f42b92a3cfe7ee945 jdk9-b29
--- a/hotspot/.hgtags Thu Sep 04 13:26:26 2014 -0700 +++ b/hotspot/.hgtags Wed Jul 05 19:59:54 2017 +0200 @@ -431,3 +431,4 @@ 48b95a073d752d6891cc0d1d2836b321ecf3ce0c jdk9-b26 f95347244306affc32ce3056f27ceff7b2100810 jdk9-b27 657294869d7ff063e055f5492cab7ce5612ca851 jdk9-b28 +deb29e92f68ace2808a36ecfa18c7d61dcb645bb jdk9-b29
--- a/hotspot/agent/src/share/classes/sun/jvm/hotspot/gc_implementation/g1/G1CollectedHeap.java Thu Sep 04 13:26:26 2014 -0700 +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/gc_implementation/g1/G1CollectedHeap.java Wed Jul 05 19:59:54 2017 +0200 @@ -43,8 +43,8 @@ // Mirror class for G1CollectedHeap. public class G1CollectedHeap extends SharedHeap { - // HeapRegionSeq _seq; - static private long hrsFieldOffset; + // HeapRegionManager _hrm; + static private long hrmFieldOffset; // MemRegion _g1_reserved; static private long g1ReservedFieldOffset; // size_t _summary_bytes_used; @@ -67,7 +67,7 @@ static private synchronized void initialize(TypeDataBase db) { Type type = db.lookupType("G1CollectedHeap"); - hrsFieldOffset = type.getField("_hrs").getOffset(); + hrmFieldOffset = type.getField("_hrm").getOffset(); summaryBytesUsedField = type.getCIntegerField("_summary_bytes_used"); g1mmField = type.getAddressField("_g1mm"); oldSetFieldOffset = type.getField("_old_set").getOffset(); @@ -75,7 +75,7 @@ } public long capacity() { - return hrs().capacity(); + return hrm().capacity(); } public long used() { @@ -83,13 +83,13 @@ } public long n_regions() { - return hrs().length(); + return hrm().length(); } - private HeapRegionSeq hrs() { - Address hrsAddr = addr.addOffsetTo(hrsFieldOffset); - return (HeapRegionSeq) VMObjectFactory.newObject(HeapRegionSeq.class, - hrsAddr); + private HeapRegionManager hrm() { + Address hrmAddr = addr.addOffsetTo(hrmFieldOffset); + return (HeapRegionManager) VMObjectFactory.newObject(HeapRegionManager.class, + hrmAddr); } public G1MonitoringSupport g1mm() { @@ -110,7 +110,7 @@ } private Iterator<HeapRegion> heapRegionIterator() { - return hrs().heapRegionIterator(); + return hrm().heapRegionIterator(); } public void heapRegionIterate(SpaceClosure scl) {
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/gc_implementation/g1/HeapRegionManager.java Wed Jul 05 19:59:54 2017 +0200 @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2011, 2014, 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. + * + */ + +package sun.jvm.hotspot.gc_implementation.g1; + +import java.util.Iterator; +import java.util.Observable; +import java.util.Observer; + +import sun.jvm.hotspot.debugger.Address; +import sun.jvm.hotspot.runtime.VM; +import sun.jvm.hotspot.runtime.VMObject; +import sun.jvm.hotspot.runtime.VMObjectFactory; +import sun.jvm.hotspot.types.AddressField; +import sun.jvm.hotspot.types.CIntegerField; +import sun.jvm.hotspot.types.Type; +import sun.jvm.hotspot.types.TypeDataBase; + +// Mirror class for HeapRegionManager. + +public class HeapRegionManager extends VMObject { + // G1HeapRegionTable _regions + static private long regionsFieldOffset; + // uint _committed_length + static private CIntegerField numCommittedField; + + static { + VM.registerVMInitializedObserver(new Observer() { + public void update(Observable o, Object data) { + initialize(VM.getVM().getTypeDataBase()); + } + }); + } + + static private synchronized void initialize(TypeDataBase db) { + Type type = db.lookupType("HeapRegionManager"); + + regionsFieldOffset = type.getField("_regions").getOffset(); + numCommittedField = type.getCIntegerField("_num_committed"); + } + + private G1HeapRegionTable regions() { + Address regionsAddr = addr.addOffsetTo(regionsFieldOffset); + return (G1HeapRegionTable) VMObjectFactory.newObject(G1HeapRegionTable.class, + regionsAddr); + } + + public long capacity() { + return length() * HeapRegion.grainBytes(); + } + + public long length() { + return regions().length(); + } + + public long committedLength() { + return numCommittedField.getValue(addr); + } + + public Iterator<HeapRegion> heapRegionIterator() { + return regions().heapRegionIterator(length()); + } + + public HeapRegionManager(Address addr) { + super(addr); + } +}
--- a/hotspot/agent/src/share/classes/sun/jvm/hotspot/gc_implementation/g1/HeapRegionSeq.java Thu Sep 04 13:26:26 2014 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,88 +0,0 @@ -/* - * Copyright (c) 2011, 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. - * - */ - -package sun.jvm.hotspot.gc_implementation.g1; - -import java.util.Iterator; -import java.util.Observable; -import java.util.Observer; - -import sun.jvm.hotspot.debugger.Address; -import sun.jvm.hotspot.runtime.VM; -import sun.jvm.hotspot.runtime.VMObject; -import sun.jvm.hotspot.runtime.VMObjectFactory; -import sun.jvm.hotspot.types.AddressField; -import sun.jvm.hotspot.types.CIntegerField; -import sun.jvm.hotspot.types.Type; -import sun.jvm.hotspot.types.TypeDataBase; - -// Mirror class for HeapRegionSeq. It essentially encapsulates the G1HeapRegionTable. - -public class HeapRegionSeq extends VMObject { - // G1HeapRegionTable _regions - static private long regionsFieldOffset; - // uint _committed_length - static private CIntegerField numCommittedField; - - static { - VM.registerVMInitializedObserver(new Observer() { - public void update(Observable o, Object data) { - initialize(VM.getVM().getTypeDataBase()); - } - }); - } - - static private synchronized void initialize(TypeDataBase db) { - Type type = db.lookupType("HeapRegionSeq"); - - regionsFieldOffset = type.getField("_regions").getOffset(); - numCommittedField = type.getCIntegerField("_num_committed"); - } - - private G1HeapRegionTable regions() { - Address regionsAddr = addr.addOffsetTo(regionsFieldOffset); - return (G1HeapRegionTable) VMObjectFactory.newObject(G1HeapRegionTable.class, - regionsAddr); - } - - public long capacity() { - return length() * HeapRegion.grainBytes(); - } - - public long length() { - return regions().length(); - } - - public long committedLength() { - return numCommittedField.getValue(addr); - } - - public Iterator<HeapRegion> heapRegionIterator() { - return regions().heapRegionIterator(length()); - } - - public HeapRegionSeq(Address addr) { - super(addr); - } -}
--- a/hotspot/make/Makefile Thu Sep 04 13:26:26 2014 -0700 +++ b/hotspot/make/Makefile Wed Jul 05 19:59:54 2017 +0200 @@ -721,6 +721,19 @@ ($(CD) $(JDK_IMAGE_DIR)/debug && $(TAR) -xf -) ; \ fi +copy_optimized_jdk:: + $(RM) -r $(JDK_IMAGE_DIR)/optimized + $(MKDIR) -p $(JDK_IMAGE_DIR)/optimized + if [ -d $(JDK_IMPORT_PATH)/optimized ] ; then \ + ($(CD) $(JDK_IMPORT_PATH)/optimized && \ + $(TAR) -cf - $(JDK_DIRS)) | \ + ($(CD) $(JDK_IMAGE_DIR)/optimized && $(TAR) -xf -) ; \ + else \ + ($(CD) $(JDK_IMPORT_PATH) && \ + $(TAR) -cf - $(JDK_DIRS)) | \ + ($(CD) $(JDK_IMAGE_DIR)/optimized && $(TAR) -xf -) ; \ + fi + # # Check target #
--- a/hotspot/make/jprt.gmk Thu Sep 04 13:26:26 2014 -0700 +++ b/hotspot/make/jprt.gmk Wed Jul 05 19:59:54 2017 +0200 @@ -42,6 +42,9 @@ jprt_build_fastdebugEmb: $(MAKE) JAVASE_EMBEDDED=true MINIMIZE_RAM_USAGE=true jprt_build_fastdebug +jprt_build_optimizedEmb: + $(MAKE) JAVASE_EMBEDDED=true MINIMIZE_RAM_USAGE=true jprt_build_optimized + jprt_build_productOpen: $(MAKE) OPENJDK=true jprt_build_product @@ -51,6 +54,9 @@ jprt_build_fastdebugOpen: $(MAKE) OPENJDK=true jprt_build_fastdebug +jprt_build_optimizedOpen: + $(MAKE) OPENJDK=true jprt_build_optimized + jprt_build_product: all_product copy_product_jdk export_product_jdk ( $(CD) $(JDK_IMAGE_DIR) && \ $(ZIPEXE) $(ZIPFLAGS) -r $(JPRT_ARCHIVE_BUNDLE) . ) @@ -63,5 +69,9 @@ ( $(CD) $(JDK_IMAGE_DIR)/debug && \ $(ZIPEXE) $(ZIPFLAGS) -r $(JPRT_ARCHIVE_BUNDLE) . ) -.PHONY: jprt_build_product jprt_build_fastdebug jprt_build_debug +jprt_build_optimized: all_optimized copy_optimized_jdk export_optimized_jdk + ( $(CD) $(JDK_IMAGE_DIR)/optimized && \ + $(ZIPEXE) $(ZIPFLAGS) -r $(JPRT_ARCHIVE_BUNDLE) . ) +.PHONY: jprt_build_product jprt_build_fastdebug jprt_build_debug jprt_build_optimized +
--- a/hotspot/make/jprt.properties Thu Sep 04 13:26:26 2014 -0700 +++ b/hotspot/make/jprt.properties Wed Jul 05 19:59:54 2017 +0200 @@ -93,13 +93,13 @@ # Standard list of jprt build targets for this source tree jprt.build.targets.standard= \ - ${jprt.my.solaris.sparcv9}-{product|fastdebug|optimized}, \ + ${jprt.my.solaris.sparcv9}-{product|fastdebug}, \ ${jprt.my.solaris.x64}-{product|fastdebug}, \ ${jprt.my.linux.i586}-{product|fastdebug}, \ - ${jprt.my.linux.x64}-{product|fastdebug|optimized}, \ + ${jprt.my.linux.x64}-{product|fastdebug}, \ ${jprt.my.macosx.x64}-{product|fastdebug}, \ ${jprt.my.windows.i586}-{product|fastdebug}, \ - ${jprt.my.windows.x64}-{product|fastdebug|optimized}, \ + ${jprt.my.windows.x64}-{product|fastdebug}, \ ${jprt.my.linux.armvh}-{product|fastdebug} jprt.build.targets.open= \
--- a/hotspot/make/windows/makefiles/vm.make Thu Sep 04 13:26:26 2014 -0700 +++ b/hotspot/make/windows/makefiles/vm.make Wed Jul 05 19:59:54 2017 +0200 @@ -34,6 +34,9 @@ CXX_FLAGS=$(CXX_FLAGS) /D "PRODUCT" !else CXX_FLAGS=$(CXX_FLAGS) /D "ASSERT" +!if "$(BUILDARCH)" == "amd64" +CXX_FLAGS=$(CXX_FLAGS) /homeparams +!endif !endif !if "$(Variant)" == "compiler1"
--- a/hotspot/src/cpu/x86/vm/vm_version_x86.cpp Thu Sep 04 13:26:26 2014 -0700 +++ b/hotspot/src/cpu/x86/vm/vm_version_x86.cpp Wed Jul 05 19:59:54 2017 +0200 @@ -604,6 +604,17 @@ #if INCLUDE_RTM_OPT if (UseRTMLocking) { + if (is_intel_family_core()) { + if ((_model == CPU_MODEL_HASWELL_E3) || + (_model == CPU_MODEL_HASWELL_E7 && _stepping < 3) || + (_model == CPU_MODEL_BROADWELL && _stepping < 4)) { + if (!UnlockExperimentalVMOptions) { + vm_exit_during_initialization("UseRTMLocking is only available as experimental option on this platform. It must be enabled via -XX:+UnlockExperimentalVMOptions flag."); + } else { + warning("UseRTMLocking is only available as experimental option on this platform."); + } + } + } if (!FLAG_IS_CMDLINE(UseRTMLocking)) { // RTM locking should be used only for applications with // high lock contention. For now we do not use it by default.
--- a/hotspot/src/cpu/x86/vm/vm_version_x86.hpp Thu Sep 04 13:26:26 2014 -0700 +++ b/hotspot/src/cpu/x86/vm/vm_version_x86.hpp Wed Jul 05 19:59:54 2017 +0200 @@ -276,7 +276,10 @@ CPU_MODEL_WESTMERE_EX = 0x2f, CPU_MODEL_SANDYBRIDGE = 0x2a, CPU_MODEL_SANDYBRIDGE_EP = 0x2d, - CPU_MODEL_IVYBRIDGE_EP = 0x3a + CPU_MODEL_IVYBRIDGE_EP = 0x3a, + CPU_MODEL_HASWELL_E3 = 0x3c, + CPU_MODEL_HASWELL_E7 = 0x3f, + CPU_MODEL_BROADWELL = 0x3d } cpuExtendedFamily; // cpuid information block. All info derived from executing cpuid with
--- a/hotspot/src/os/windows/vm/os_windows.cpp Thu Sep 04 13:26:26 2014 -0700 +++ b/hotspot/src/os/windows/vm/os_windows.cpp Wed Jul 05 19:59:54 2017 +0200 @@ -135,11 +135,6 @@ if (ForceTimeHighResolution) timeEndPeriod(1L); - // Workaround for issue when a custom launcher doesn't call - // DestroyJavaVM and NMT is trying to track memory when free is - // called from a static destructor - MemTracker::shutdown(); - break; default: break; @@ -414,6 +409,8 @@ LONG WINAPI topLevelExceptionFilter(struct _EXCEPTION_POINTERS* exceptionInfo); +extern jint volatile vm_getting_terminated; + // Thread start routine for all new Java threads static unsigned __stdcall java_start(Thread* thread) { // Try to randomize the cache line index of hot stack frames. @@ -435,9 +432,17 @@ } } + // Diagnostic code to investigate JDK-6573254 (Part I) + unsigned res = 90115; // non-java thread + if (thread->is_Java_thread()) { + JavaThread* java_thread = (JavaThread*)thread; + res = java_lang_Thread::is_daemon(java_thread->threadObj()) + ? 70115 // java daemon thread + : 80115; // java non-daemon thread + } // Install a win32 structured exception handler around every thread created - // by VM, so VM can genrate error dump when an exception occurred in non- + // by VM, so VM can generate error dump when an exception occurred in non- // Java thread (e.g. VM thread). __try { thread->run(); @@ -453,6 +458,11 @@ Atomic::dec_ptr((intptr_t*)&os::win32::_os_thread_count); } + // Diagnostic code to investigate JDK-6573254 (Part II) + if (OrderAccess::load_acquire(&vm_getting_terminated)) { + return res; + } + return 0; }
--- a/hotspot/src/share/tools/ProjectCreator/BuildConfig.java Thu Sep 04 13:26:26 2014 -0700 +++ b/hotspot/src/share/tools/ProjectCreator/BuildConfig.java Wed Jul 05 19:59:54 2017 +0200 @@ -504,7 +504,7 @@ super.init(includes, defines); - getV("CompilerFlags").addAll(getCI().getDebugCompilerFlags(getOptFlag())); + getV("CompilerFlags").addAll(getCI().getDebugCompilerFlags(getOptFlag(), get("PlatformName"))); getV("LinkerFlags").addAll(getCI().getDebugLinkerFlags()); } } @@ -619,7 +619,7 @@ abstract class CompilerInterface { abstract Vector getBaseCompilerFlags(Vector defines, Vector includes, String outDir); abstract Vector getBaseLinkerFlags(String outDir, String outDll, String platformName); - abstract Vector getDebugCompilerFlags(String opt); + abstract Vector getDebugCompilerFlags(String opt, String platformName); abstract Vector getDebugLinkerFlags(); abstract void getAdditionalNonKernelLinkerFlags(Vector rv); abstract Vector getProductCompilerFlags();
--- a/hotspot/src/share/tools/ProjectCreator/WinGammaPlatformVC10.java Thu Sep 04 13:26:26 2014 -0700 +++ b/hotspot/src/share/tools/ProjectCreator/WinGammaPlatformVC10.java Wed Jul 05 19:59:54 2017 +0200 @@ -357,7 +357,7 @@ } @Override - Vector getDebugCompilerFlags(String opt) { + Vector getDebugCompilerFlags(String opt, String platformName) { Vector rv = new Vector(); // Set /On option @@ -369,6 +369,10 @@ addAttr(rv, "RuntimeLibrary", "MultiThreadedDLL"); // Set /Oy- option addAttr(rv, "OmitFramePointers", "false"); + // Set /homeparams for x64 debug builds + if(platformName.equals("x64")) { + addAttr(rv, "AdditionalOptions", "/homeparams"); + } return rv; }
--- a/hotspot/src/share/tools/ProjectCreator/WinGammaPlatformVC7.java Thu Sep 04 13:26:26 2014 -0700 +++ b/hotspot/src/share/tools/ProjectCreator/WinGammaPlatformVC7.java Wed Jul 05 19:59:54 2017 +0200 @@ -284,7 +284,7 @@ } - Vector getDebugCompilerFlags(String opt) { + Vector getDebugCompilerFlags(String opt, String platformName) { Vector rv = new Vector(); getDebugCompilerFlags_common(opt, rv);
--- a/hotspot/src/share/tools/ProjectCreator/WinGammaPlatformVC8.java Thu Sep 04 13:26:26 2014 -0700 +++ b/hotspot/src/share/tools/ProjectCreator/WinGammaPlatformVC8.java Wed Jul 05 19:59:54 2017 +0200 @@ -48,7 +48,7 @@ } - Vector getDebugCompilerFlags(String opt) { + Vector getDebugCompilerFlags(String opt, String platformName) { Vector rv = new Vector(); getDebugCompilerFlags_common(opt,rv);
--- a/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepGeneration.cpp Thu Sep 04 13:26:26 2014 -0700 +++ b/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepGeneration.cpp Wed Jul 05 19:59:54 2017 +0200 @@ -328,9 +328,11 @@ void ConcurrentMarkSweepGeneration::initialize_performance_counters() { const char* gen_name = "old"; + GenCollectorPolicy* gcp = (GenCollectorPolicy*) GenCollectedHeap::heap()->collector_policy(); // Generation Counters - generation 1, 1 subspace - _gen_counters = new GenerationCounters(gen_name, 1, 1, &_virtual_space); + _gen_counters = new GenerationCounters(gen_name, 1, 1, + gcp->min_old_size(), gcp->max_old_size(), &_virtual_space); _space_counters = new GSpaceCounters(gen_name, 0, _virtual_space.reserved_size(),
--- a/hotspot/src/share/vm/gc_implementation/g1/concurrentMark.cpp Thu Sep 04 13:26:26 2014 -0700 +++ b/hotspot/src/share/vm/gc_implementation/g1/concurrentMark.cpp Wed Jul 05 19:59:54 2017 +0200 @@ -34,8 +34,8 @@ #include "gc_implementation/g1/g1OopClosures.inline.hpp" #include "gc_implementation/g1/g1RemSet.hpp" #include "gc_implementation/g1/heapRegion.inline.hpp" +#include "gc_implementation/g1/heapRegionManager.inline.hpp" #include "gc_implementation/g1/heapRegionRemSet.hpp" -#include "gc_implementation/g1/heapRegionSeq.inline.hpp" #include "gc_implementation/g1/heapRegionSet.inline.hpp" #include "gc_implementation/shared/vmGCOperations.hpp" #include "gc_implementation/shared/gcTimer.hpp" @@ -434,10 +434,6 @@ } } -bool ConcurrentMark::not_yet_marked(oop obj) const { - return _g1h->is_obj_ill(obj); -} - CMRootRegions::CMRootRegions() : _young_list(NULL), _cm(NULL), _scan_in_progress(false), _should_abort(false), _next_survivor(NULL) { } @@ -892,7 +888,16 @@ } virtual bool doHeapRegion(HeapRegion* r) { - return _bitmap->getNextMarkedWordAddress(r->bottom(), r->end()) != r->end(); + // This closure can be called concurrently to the mutator, so we must make sure + // that the result of the getNextMarkedWordAddress() call is compared to the + // value passed to it as limit to detect any found bits. + // We can use the region's orig_end() for the limit and the comparison value + // as it always contains the "real" end of the region that never changes and + // has no side effects. + // Due to the latter, there can also be no problem with the compiler generating + // reloads of the orig_end() call. + HeapWord* end = r->orig_end(); + return _bitmap->getNextMarkedWordAddress(r->bottom(), end) != end; } }; @@ -1117,20 +1122,17 @@ if (!_cm->has_aborted()) { do { double start_vtime_sec = os::elapsedVTime(); - double start_time_sec = os::elapsedTime(); double mark_step_duration_ms = G1ConcMarkStepDurationMillis; the_task->do_marking_step(mark_step_duration_ms, true /* do_termination */, false /* is_serial*/); - double end_time_sec = os::elapsedTime(); double end_vtime_sec = os::elapsedVTime(); double elapsed_vtime_sec = end_vtime_sec - start_vtime_sec; - double elapsed_time_sec = end_time_sec - start_time_sec; _cm->clear_has_overflown(); - bool ret = _cm->do_yield_check(worker_id); + _cm->do_yield_check(worker_id); jlong sleep_time_ms; if (!_cm->has_aborted() && the_task->has_aborted()) { @@ -1140,17 +1142,6 @@ os::sleep(Thread::current(), sleep_time_ms, false); SuspendibleThreadSet::join(); } - double end_time2_sec = os::elapsedTime(); - double elapsed_time2_sec = end_time2_sec - start_time_sec; - -#if 0 - gclog_or_tty->print_cr("CM: elapsed %1.4lf ms, sleep %1.4lf ms, " - "overhead %1.4lf", - elapsed_vtime_sec * 1000.0, (double) sleep_time_ms, - the_task->conc_overhead(os::elapsedTime()) * 8.0); - gclog_or_tty->print_cr("elapsed time %1.4lf ms, time 2: %1.4lf ms", - elapsed_time_sec * 1000.0, elapsed_time2_sec * 1000.0); -#endif } while (!_cm->has_aborted() && the_task->has_aborted()); } the_task->record_end_time(); @@ -1409,7 +1400,7 @@ void set_bit_for_region(HeapRegion* hr) { assert(!hr->continuesHumongous(), "should have filtered those out"); - BitMap::idx_t index = (BitMap::idx_t) hr->hrs_index(); + BitMap::idx_t index = (BitMap::idx_t) hr->hrm_index(); if (!hr->startsHumongous()) { // Normal (non-humongous) case: just set the bit. _region_bm->par_at_put(index, true); @@ -1597,7 +1588,7 @@ if (_verbose) { gclog_or_tty->print_cr("Region %u: marked bytes mismatch: " "expected: " SIZE_FORMAT ", actual: " SIZE_FORMAT, - hr->hrs_index(), exp_marked_bytes, act_marked_bytes); + hr->hrm_index(), exp_marked_bytes, act_marked_bytes); } failures += 1; } @@ -1606,7 +1597,7 @@ // (which was just calculated) region bit maps. // We're not OK if the bit in the calculated expected region // bitmap is set and the bit in the actual region bitmap is not. - BitMap::idx_t index = (BitMap::idx_t) hr->hrs_index(); + BitMap::idx_t index = (BitMap::idx_t) hr->hrm_index(); bool expected = _exp_region_bm->at(index); bool actual = _region_bm->at(index); @@ -1614,7 +1605,7 @@ if (_verbose) { gclog_or_tty->print_cr("Region %u: region bitmap mismatch: " "expected: %s, actual: %s", - hr->hrs_index(), + hr->hrm_index(), BOOL_TO_STR(expected), BOOL_TO_STR(actual)); } failures += 1; @@ -1635,7 +1626,7 @@ if (_verbose) { gclog_or_tty->print_cr("Region %u: card bitmap mismatch at " SIZE_FORMAT ": " "expected: %s, actual: %s", - hr->hrs_index(), i, + hr->hrm_index(), i, BOOL_TO_STR(expected), BOOL_TO_STR(actual)); } failures += 1; @@ -2949,11 +2940,6 @@ _nextMarkBitMap->clearRange(mr); } -void ConcurrentMark::clearRangeBothBitmaps(MemRegion mr) { - clearRangePrevBitmap(mr); - clearRangeNextBitmap(mr); -} - HeapRegion* ConcurrentMark::claim_region(uint worker_id) { // "checkpoint" the finger @@ -3256,7 +3242,7 @@ assert(limit_idx <= end_idx, "or else use atomics"); // Aggregate the "stripe" in the count data associated with hr. - uint hrs_index = hr->hrs_index(); + uint hrm_index = hr->hrm_index(); size_t marked_bytes = 0; for (uint i = 0; i < _max_worker_id; i += 1) { @@ -3265,7 +3251,7 @@ // Fetch the marked_bytes in this region for task i and // add it to the running total for this region. - marked_bytes += marked_bytes_array[hrs_index]; + marked_bytes += marked_bytes_array[hrm_index]; // Now union the bitmaps[0,max_worker_id)[start_idx..limit_idx) // into the global card bitmap. @@ -3499,17 +3485,6 @@ } } -bool ConcurrentMark::containing_card_is_marked(void* p) { - size_t offset = pointer_delta(p, _g1h->reserved_region().start(), 1); - return _card_bm.at(offset >> CardTableModRefBS::card_shift); -} - -bool ConcurrentMark::containing_cards_are_marked(void* start, - void* last) { - return containing_card_is_marked(start) && - containing_card_is_marked(last); -} - #ifndef PRODUCT // for debugging purposes void ConcurrentMark::print_finger() { @@ -3762,7 +3737,7 @@ if (_cm->verbose_medium()) { gclog_or_tty->print_cr("[%u] regular clock, interval = %1.2lfms, " - "scanned = %d%s, refs reached = %d%s", + "scanned = "SIZE_FORMAT"%s, refs reached = "SIZE_FORMAT"%s", _worker_id, last_interval_ms, _words_scanned, (_words_scanned >= _words_scanned_limit) ? " (*)" : "",
--- a/hotspot/src/share/vm/gc_implementation/g1/concurrentMark.hpp Thu Sep 04 13:26:26 2014 -0700 +++ b/hotspot/src/share/vm/gc_implementation/g1/concurrentMark.hpp Wed Jul 05 19:59:54 2017 +0200 @@ -683,7 +683,9 @@ return _task_queues->steal(worker_id, hash_seed, obj); } - ConcurrentMark(G1CollectedHeap* g1h, G1RegionToSpaceMapper* prev_bitmap_storage, G1RegionToSpaceMapper* next_bitmap_storage); + ConcurrentMark(G1CollectedHeap* g1h, + G1RegionToSpaceMapper* prev_bitmap_storage, + G1RegionToSpaceMapper* next_bitmap_storage); ~ConcurrentMark(); ConcurrentMarkThread* cmThread() { return _cmThread; } @@ -712,8 +714,10 @@ // inconsistent) and always passing the size. hr is the region that // contains the object and it's passed optionally from callers who // might already have it (no point in recalculating it). - inline void grayRoot(oop obj, size_t word_size, - uint worker_id, HeapRegion* hr = NULL); + inline void grayRoot(oop obj, + size_t word_size, + uint worker_id, + HeapRegion* hr = NULL); // It iterates over the heap and for each object it comes across it // will dump the contents of its reference fields, as well as @@ -734,7 +738,8 @@ // AND MARKED : indicates that an object is both explicitly and // implicitly live (it should be one or the other, not both) void print_reachable(const char* str, - VerifyOption vo, bool all) PRODUCT_RETURN; + VerifyOption vo, + bool all) PRODUCT_RETURN; // Clear the next marking bitmap (will be called concurrently). void clearNextBitmap(); @@ -771,12 +776,11 @@ // this carefully! inline void markPrev(oop p); - // Clears marks for all objects in the given range, for the prev, - // next, or both bitmaps. NB: the previous bitmap is usually + // Clears marks for all objects in the given range, for the prev or + // next bitmaps. NB: the previous bitmap is usually // read-only, so use this carefully! void clearRangePrevBitmap(MemRegion mr); void clearRangeNextBitmap(MemRegion mr); - void clearRangeBothBitmaps(MemRegion mr); // Notify data structures that a GC has started. void note_start_of_gc() { @@ -798,21 +802,6 @@ bool verify_thread_buffers, bool verify_fingers) PRODUCT_RETURN; - bool isMarked(oop p) const { - assert(p != NULL && p->is_oop(), "expected an oop"); - HeapWord* addr = (HeapWord*)p; - assert(addr >= _nextMarkBitMap->startWord() || - addr < _nextMarkBitMap->endWord(), "in a region"); - - return _nextMarkBitMap->isMarked(addr); - } - - inline bool not_yet_marked(oop p) const; - - // XXX Debug code - bool containing_card_is_marked(void* p); - bool containing_cards_are_marked(void* start, void* last); - bool isPrevMarked(oop p) const { assert(p != NULL && p->is_oop(), "expected an oop"); HeapWord* addr = (HeapWord*)p; @@ -898,7 +887,8 @@ // marked_bytes array slot for the given HeapRegion. // Sets the bits in the given card bitmap that are associated with the // cards that are spanned by the memory region. - inline void count_region(MemRegion mr, HeapRegion* hr, + inline void count_region(MemRegion mr, + HeapRegion* hr, size_t* marked_bytes_array, BitMap* task_card_bm); @@ -906,56 +896,27 @@ // data structures for the given worker id. inline void count_region(MemRegion mr, HeapRegion* hr, uint worker_id); - // Counts the given memory region in the task/worker counting - // data structures for the given worker id. - inline void count_region(MemRegion mr, uint worker_id); - // Counts the given object in the given task/worker counting // data structures. - inline void count_object(oop obj, HeapRegion* hr, + inline void count_object(oop obj, + HeapRegion* hr, size_t* marked_bytes_array, BitMap* task_card_bm); - // Counts the given object in the task/worker counting data - // structures for the given worker id. - inline void count_object(oop obj, HeapRegion* hr, uint worker_id); - // Attempts to mark the given object and, if successful, counts // the object in the given task/worker counting structures. - inline bool par_mark_and_count(oop obj, HeapRegion* hr, + inline bool par_mark_and_count(oop obj, + HeapRegion* hr, size_t* marked_bytes_array, BitMap* task_card_bm); // Attempts to mark the given object and, if successful, counts // the object in the task/worker counting structures for the // given worker id. - inline bool par_mark_and_count(oop obj, size_t word_size, - HeapRegion* hr, uint worker_id); - - // Attempts to mark the given object and, if successful, counts - // the object in the task/worker counting structures for the - // given worker id. - inline bool par_mark_and_count(oop obj, HeapRegion* hr, uint worker_id); - - // Similar to the above routine but we don't know the heap region that - // contains the object to be marked/counted, which this routine looks up. - inline bool par_mark_and_count(oop obj, uint worker_id); - - // Similar to the above routine but there are times when we cannot - // safely calculate the size of obj due to races and we, therefore, - // pass the size in as a parameter. It is the caller's responsibility - // to ensure that the size passed in for obj is valid. - inline bool par_mark_and_count(oop obj, size_t word_size, uint worker_id); - - // Unconditionally mark the given object, and unconditionally count - // the object in the counting structures for worker id 0. - // Should *not* be called from parallel code. - inline bool mark_and_count(oop obj, HeapRegion* hr); - - // Similar to the above routine but we don't know the heap region that - // contains the object to be marked/counted, which this routine looks up. - // Should *not* be called from parallel code. - inline bool mark_and_count(oop obj); + inline bool par_mark_and_count(oop obj, + size_t word_size, + HeapRegion* hr, + uint worker_id); // Returns true if initialization was successfully completed. bool completed_initialization() const { @@ -1227,9 +1188,12 @@ _finger = new_finger; } - CMTask(uint worker_id, ConcurrentMark *cm, - size_t* marked_bytes, BitMap* card_bm, - CMTaskQueue* task_queue, CMTaskQueueSet* task_queues); + CMTask(uint worker_id, + ConcurrentMark *cm, + size_t* marked_bytes, + BitMap* card_bm, + CMTaskQueue* task_queue, + CMTaskQueueSet* task_queues); // it prints statistics associated with this task void print_stats();
--- a/hotspot/src/share/vm/gc_implementation/g1/concurrentMark.inline.hpp Thu Sep 04 13:26:26 2014 -0700 +++ b/hotspot/src/share/vm/gc_implementation/g1/concurrentMark.inline.hpp Wed Jul 05 19:59:54 2017 +0200 @@ -86,7 +86,7 @@ HeapWord* start = mr.start(); HeapWord* end = mr.end(); size_t region_size_bytes = mr.byte_size(); - uint index = hr->hrs_index(); + uint index = hr->hrm_index(); assert(!hr->continuesHumongous(), "should not be HC region"); assert(hr == g1h->heap_region_containing(start), "sanity"); @@ -125,14 +125,6 @@ count_region(mr, hr, marked_bytes_array, task_card_bm); } -// Counts the given memory region, which may be a single object, in the -// task/worker counting data structures for the given worker id. -inline void ConcurrentMark::count_region(MemRegion mr, uint worker_id) { - HeapWord* addr = mr.start(); - HeapRegion* hr = _g1h->heap_region_containing_raw(addr); - count_region(mr, hr, worker_id); -} - // Counts the given object in the given task/worker counting data structures. inline void ConcurrentMark::count_object(oop obj, HeapRegion* hr, @@ -142,17 +134,6 @@ count_region(mr, hr, marked_bytes_array, task_card_bm); } -// Counts the given object in the task/worker counting data -// structures for the given worker id. -inline void ConcurrentMark::count_object(oop obj, - HeapRegion* hr, - uint worker_id) { - size_t* marked_bytes_array = count_marked_bytes_array_for(worker_id); - BitMap* task_card_bm = count_card_bitmap_for(worker_id); - HeapWord* addr = (HeapWord*) obj; - count_object(obj, hr, marked_bytes_array, task_card_bm); -} - // Attempts to mark the given object and, if successful, counts // the object in the given task/worker counting structures. inline bool ConcurrentMark::par_mark_and_count(oop obj, @@ -184,63 +165,6 @@ return false; } -// Attempts to mark the given object and, if successful, counts -// the object in the task/worker counting structures for the -// given worker id. -inline bool ConcurrentMark::par_mark_and_count(oop obj, - HeapRegion* hr, - uint worker_id) { - HeapWord* addr = (HeapWord*)obj; - if (_nextMarkBitMap->parMark(addr)) { - // Update the task specific count data for the object. - count_object(obj, hr, worker_id); - return true; - } - return false; -} - -// As above - but we don't know the heap region containing the -// object and so have to supply it. -inline bool ConcurrentMark::par_mark_and_count(oop obj, uint worker_id) { - HeapWord* addr = (HeapWord*)obj; - HeapRegion* hr = _g1h->heap_region_containing_raw(addr); - return par_mark_and_count(obj, hr, worker_id); -} - -// Similar to the above routine but we already know the size, in words, of -// the object that we wish to mark/count -inline bool ConcurrentMark::par_mark_and_count(oop obj, - size_t word_size, - uint worker_id) { - HeapWord* addr = (HeapWord*)obj; - if (_nextMarkBitMap->parMark(addr)) { - // Update the task specific count data for the object. - MemRegion mr(addr, word_size); - count_region(mr, worker_id); - return true; - } - return false; -} - -// Unconditionally mark the given object, and unconditionally count -// the object in the counting structures for worker id 0. -// Should *not* be called from parallel code. -inline bool ConcurrentMark::mark_and_count(oop obj, HeapRegion* hr) { - HeapWord* addr = (HeapWord*)obj; - _nextMarkBitMap->mark(addr); - // Update the task specific count data for the object. - count_object(obj, hr, 0 /* worker_id */); - return true; -} - -// As above - but we don't have the heap region containing the -// object, so we have to supply it. -inline bool ConcurrentMark::mark_and_count(oop obj) { - HeapWord* addr = (HeapWord*)obj; - HeapRegion* hr = _g1h->heap_region_containing_raw(addr); - return mark_and_count(obj, hr); -} - inline bool CMBitMapRO::iterate(BitMapClosure* cl, MemRegion mr) { HeapWord* start_addr = MAX2(startWord(), mr.start()); HeapWord* end_addr = MIN2(endWord(), mr.end());
--- a/hotspot/src/share/vm/gc_implementation/g1/g1BlockOffsetTable.cpp Thu Sep 04 13:26:26 2014 -0700 +++ b/hotspot/src/share/vm/gc_implementation/g1/g1BlockOffsetTable.cpp Wed Jul 05 19:59:54 2017 +0200 @@ -30,14 +30,7 @@ #include "runtime/java.hpp" #include "services/memTracker.hpp" -PRAGMA_FORMAT_MUTE_WARNINGS_FOR_GCC -void G1BlockOffsetSharedArrayMappingChangedListener::on_commit(uint start_idx, size_t num_regions) { - // Nothing to do. The BOT is hard-wired to be part of the HeapRegion, and we cannot - // retrieve it here since this would cause firing of several asserts. The code - // executed after commit of a region already needs to do some re-initialization of - // the HeapRegion, so we combine that. -} ////////////////////////////////////////////////////////////////////// // G1BlockOffsetSharedArray @@ -59,10 +52,10 @@ if (TraceBlockOffsetTable) { gclog_or_tty->print_cr("G1BlockOffsetSharedArray::G1BlockOffsetSharedArray: "); gclog_or_tty->print_cr(" " - " rs.base(): " INTPTR_FORMAT - " rs.size(): " INTPTR_FORMAT - " rs end(): " INTPTR_FORMAT, - bot_reserved.start(), bot_reserved.byte_size(), bot_reserved.end()); + " rs.base(): " PTR_FORMAT + " rs.size(): " SIZE_FORMAT + " rs end(): " PTR_FORMAT, + p2i(bot_reserved.start()), bot_reserved.byte_size(), p2i(bot_reserved.end())); } } @@ -72,26 +65,16 @@ return (delta & right_n_bits(LogN_words)) == (size_t)NoBits; } -void G1BlockOffsetSharedArray::set_offset_array(HeapWord* left, HeapWord* right, u_char offset) { - set_offset_array(index_for(left), index_for(right -1), offset); -} - ////////////////////////////////////////////////////////////////////// // G1BlockOffsetArray ////////////////////////////////////////////////////////////////////// G1BlockOffsetArray::G1BlockOffsetArray(G1BlockOffsetSharedArray* array, - MemRegion mr, bool init_to_zero) : + MemRegion mr) : G1BlockOffsetTable(mr.start(), mr.end()), _unallocated_block(_bottom), - _array(array), _gsp(NULL), - _init_to_zero(init_to_zero) { + _array(array), _gsp(NULL) { assert(_bottom <= _end, "arguments out of order"); - if (!_init_to_zero) { - // initialize cards to point back to mr.start() - set_remainder_to_point_to_start(mr.start() + N_words, mr.end()); - _array->set_offset_array(0, 0); // set first card to 0 - } } void G1BlockOffsetArray::set_space(G1OffsetTableContigSpace* sp) { @@ -181,93 +164,6 @@ DEBUG_ONLY(check_all_cards(start_card, end_card);) } -// The block [blk_start, blk_end) has been allocated; -// adjust the block offset table to represent this information; -// right-open interval: [blk_start, blk_end) -void -G1BlockOffsetArray::alloc_block(HeapWord* blk_start, HeapWord* blk_end) { - mark_block(blk_start, blk_end); - allocated(blk_start, blk_end); -} - -// Adjust BOT to show that a previously whole block has been split -// into two. -void G1BlockOffsetArray::split_block(HeapWord* blk, size_t blk_size, - size_t left_blk_size) { - // Verify that the BOT shows [blk, blk + blk_size) to be one block. - verify_single_block(blk, blk_size); - // Update the BOT to indicate that [blk + left_blk_size, blk + blk_size) - // is one single block. - mark_block(blk + left_blk_size, blk + blk_size); -} - - -// Action_mark - update the BOT for the block [blk_start, blk_end). -// Current typical use is for splitting a block. -// Action_single - update the BOT for an allocation. -// Action_verify - BOT verification. -void G1BlockOffsetArray::do_block_internal(HeapWord* blk_start, - HeapWord* blk_end, - Action action) { - assert(Universe::heap()->is_in_reserved(blk_start), - "reference must be into the heap"); - assert(Universe::heap()->is_in_reserved(blk_end-1), - "limit must be within the heap"); - // This is optimized to make the test fast, assuming we only rarely - // cross boundaries. - uintptr_t end_ui = (uintptr_t)(blk_end - 1); - uintptr_t start_ui = (uintptr_t)blk_start; - // Calculate the last card boundary preceding end of blk - intptr_t boundary_before_end = (intptr_t)end_ui; - clear_bits(boundary_before_end, right_n_bits(LogN)); - if (start_ui <= (uintptr_t)boundary_before_end) { - // blk starts at or crosses a boundary - // Calculate index of card on which blk begins - size_t start_index = _array->index_for(blk_start); - // Index of card on which blk ends - size_t end_index = _array->index_for(blk_end - 1); - // Start address of card on which blk begins - HeapWord* boundary = _array->address_for_index(start_index); - assert(boundary <= blk_start, "blk should start at or after boundary"); - if (blk_start != boundary) { - // blk starts strictly after boundary - // adjust card boundary and start_index forward to next card - boundary += N_words; - start_index++; - } - assert(start_index <= end_index, "monotonicity of index_for()"); - assert(boundary <= (HeapWord*)boundary_before_end, "tautology"); - switch (action) { - case Action_mark: { - if (init_to_zero()) { - _array->set_offset_array(start_index, boundary, blk_start); - break; - } // Else fall through to the next case - } - case Action_single: { - _array->set_offset_array(start_index, boundary, blk_start); - // We have finished marking the "offset card". We need to now - // mark the subsequent cards that this blk spans. - if (start_index < end_index) { - HeapWord* rem_st = _array->address_for_index(start_index) + N_words; - HeapWord* rem_end = _array->address_for_index(end_index) + N_words; - set_remainder_to_point_to_start(rem_st, rem_end); - } - break; - } - case Action_check: { - _array->check_offset_array(start_index, boundary, blk_start); - // We have finished checking the "offset card". We need to now - // check the subsequent cards that this blk spans. - check_all_cards(start_index + 1, end_index); - break; - } - default: - ShouldNotReachHere(); - } - } -} - // The card-interval [start_card, end_card] is a closed interval; this // is an expensive check -- use with care and only under protection of // suitable flag. @@ -306,25 +202,6 @@ } } -// The range [blk_start, blk_end) represents a single contiguous block -// of storage; modify the block offset table to represent this -// information; Right-open interval: [blk_start, blk_end) -// NOTE: this method does _not_ adjust _unallocated_block. -void -G1BlockOffsetArray::single_block(HeapWord* blk_start, HeapWord* blk_end) { - do_block_internal(blk_start, blk_end, Action_single); -} - -// Mark the BOT such that if [blk_start, blk_end) straddles a card -// boundary, the card following the first such boundary is marked -// with the appropriate offset. -// NOTE: this method does _not_ adjust _unallocated_block or -// any cards subsequent to the first one. -void -G1BlockOffsetArray::mark_block(HeapWord* blk_start, HeapWord* blk_end) { - do_block_internal(blk_start, blk_end, Action_mark); -} - HeapWord* G1BlockOffsetArray::block_start_unsafe(const void* addr) { assert(_bottom <= addr && addr < _end, "addr must be covered by this Array"); @@ -381,7 +258,7 @@ assert(next_boundary <= _array->_end, err_msg("next_boundary is beyond the end of the covered region " " next_boundary " PTR_FORMAT " _array->_end " PTR_FORMAT, - next_boundary, _array->_end)); + p2i(next_boundary), p2i(_array->_end))); if (addr >= gsp()->top()) return gsp()->top(); while (next_boundary < addr) { while (n <= next_boundary) { @@ -397,57 +274,13 @@ return forward_to_block_containing_addr_const(q, n, addr); } -HeapWord* G1BlockOffsetArray::block_start_careful(const void* addr) const { - assert(_array->offset_array(0) == 0, "objects can't cross covered areas"); - - assert(_bottom <= addr && addr < _end, - "addr must be covered by this Array"); - // Must read this exactly once because it can be modified by parallel - // allocation. - HeapWord* ub = _unallocated_block; - if (BlockOffsetArrayUseUnallocatedBlock && addr >= ub) { - assert(ub < _end, "tautology (see above)"); - return ub; - } - - // Otherwise, find the block start using the table, but taking - // care (cf block_start_unsafe() above) not to parse any objects/blocks - // on the cards themselves. - size_t index = _array->index_for(addr); - assert(_array->address_for_index(index) == addr, - "arg should be start of card"); - - HeapWord* q = (HeapWord*)addr; - uint offset; - do { - offset = _array->offset_array(index--); - q -= offset; - } while (offset == N_words); - assert(q <= addr, "block start should be to left of arg"); - return q; -} - // Note that the committed size of the covered space may have changed, // so the table size might also wish to change. void G1BlockOffsetArray::resize(size_t new_word_size) { HeapWord* new_end = _bottom + new_word_size; - if (_end < new_end && !init_to_zero()) { - // verify that the old and new boundaries are also card boundaries - assert(_array->is_card_boundary(_end), - "_end not a card boundary"); - assert(_array->is_card_boundary(new_end), - "new _end would not be a card boundary"); - // set all the newly added cards - _array->set_offset_array(_end, new_end, N_words); - } _end = new_end; // update _end } -void G1BlockOffsetArray::set_region(MemRegion mr) { - _bottom = mr.start(); - _end = mr.end(); -} - // // threshold_ // | _index_ @@ -522,7 +355,7 @@ "blk_start: " PTR_FORMAT ", " "boundary: " PTR_FORMAT, (uint)_array->offset_array(orig_index), - blk_start, boundary)); + p2i(blk_start), p2i(boundary))); for (size_t j = orig_index + 1; j <= end_index; j++) { assert(_array->offset_array(j) > 0 && _array->offset_array(j) <= @@ -556,9 +389,9 @@ "card addr: "PTR_FORMAT" BOT entry: %u " "obj: "PTR_FORMAT" word size: "SIZE_FORMAT" " "cards: ["SIZE_FORMAT","SIZE_FORMAT"]", - block_start, card, card_addr, + p2i(block_start), card, p2i(card_addr), _array->offset_array(card), - obj_start, word_size, first_card, last_card); + p2i(obj_start), word_size, first_card, last_card); return false; } } @@ -572,10 +405,10 @@ size_t to_index = _array->index_for(_end); out->print_cr(">> BOT for area ["PTR_FORMAT","PTR_FORMAT") " "cards ["SIZE_FORMAT","SIZE_FORMAT")", - _bottom, _end, from_index, to_index); + p2i(_bottom), p2i(_end), from_index, to_index); for (size_t i = from_index; i < to_index; ++i) { out->print_cr(" entry "SIZE_FORMAT_W(8)" | "PTR_FORMAT" : %3u", - i, _array->address_for_index(i), + i, p2i(_array->address_for_index(i)), (uint) _array->offset_array(i)); } } @@ -606,7 +439,7 @@ G1BlockOffsetArrayContigSpace:: G1BlockOffsetArrayContigSpace(G1BlockOffsetSharedArray* array, MemRegion mr) : - G1BlockOffsetArray(array, mr, true) + G1BlockOffsetArray(array, mr) { _next_offset_threshold = NULL; _next_offset_index = 0; @@ -641,15 +474,6 @@ return _next_offset_threshold; } -void G1BlockOffsetArrayContigSpace::zero_bottom_entry() { - assert(!Universe::heap()->is_in_reserved(_array->_offset_array), - "just checking"); - size_t bottom_index = _array->index_for(_bottom); - assert(_array->address_for_index(bottom_index) == _bottom, - "Precondition of call"); - _array->set_offset_array(bottom_index, 0); -} - void G1BlockOffsetArrayContigSpace::set_for_starts_humongous(HeapWord* new_top) { assert(new_top <= _end, "_end should have already been updated"); @@ -663,7 +487,7 @@ void G1BlockOffsetArrayContigSpace::print_on(outputStream* out) { G1BlockOffsetArray::print_on(out); - out->print_cr(" next offset threshold: "PTR_FORMAT, _next_offset_threshold); + out->print_cr(" next offset threshold: "PTR_FORMAT, p2i(_next_offset_threshold)); out->print_cr(" next offset index: "SIZE_FORMAT, _next_offset_index); } #endif // !PRODUCT
--- a/hotspot/src/share/vm/gc_implementation/g1/g1BlockOffsetTable.hpp Thu Sep 04 13:26:26 2014 -0700 +++ b/hotspot/src/share/vm/gc_implementation/g1/g1BlockOffsetTable.hpp Wed Jul 05 19:59:54 2017 +0200 @@ -109,7 +109,12 @@ class G1BlockOffsetSharedArrayMappingChangedListener : public G1MappingChangedListener { public: - virtual void on_commit(uint start_idx, size_t num_regions); + virtual void on_commit(uint start_idx, size_t num_regions) { + // Nothing to do. The BOT is hard-wired to be part of the HeapRegion, and we cannot + // retrieve it here since this would cause firing of several asserts. The code + // executed after commit of a region already needs to do some re-initialization of + // the HeapRegion, so we combine that. + } }; // This implementation of "G1BlockOffsetTable" divides the covered region @@ -153,8 +158,6 @@ // For performance these have to devolve to array accesses in product builds. inline u_char offset_array(size_t index) const; - void set_offset_array(HeapWord* left, HeapWord* right, u_char offset); - void set_offset_array_raw(size_t index, u_char offset) { _offset_array[index] = offset; } @@ -165,8 +168,6 @@ inline void set_offset_array(size_t left, size_t right, u_char offset); - inline void check_offset_array(size_t index, HeapWord* high, HeapWord* low) const; - bool is_card_boundary(HeapWord* p) const; public: @@ -193,8 +194,6 @@ // G1BlockOffsetTable(s) to initialize cards. G1BlockOffsetSharedArray(MemRegion heap, G1RegionToSpaceMapper* storage); - void set_bottom(HeapWord* new_bottom); - // Return the appropriate index into "_offset_array" for "p". inline size_t index_for(const void* p) const; inline size_t index_for_raw(const void* p) const; @@ -220,14 +219,6 @@ LogN = G1BlockOffsetSharedArray::LogN }; - // The following enums are used by do_block_helper - enum Action { - Action_single, // BOT records a single block (see single_block()) - Action_mark, // BOT marks the start of a block (see mark_block()) - Action_check // Check that BOT records block correctly - // (see verify_single_block()). - }; - // This is the array, which can be shared by several BlockOffsetArray's // servicing different G1BlockOffsetSharedArray* _array; @@ -235,10 +226,6 @@ // The space that owns this subregion. G1OffsetTableContigSpace* _gsp; - // If true, array entries are initialized to 0; otherwise, they are - // initialized to point backwards to the beginning of the covered region. - bool _init_to_zero; - // The portion [_unallocated_block, _sp.end()) of the space that // is a single block known not to contain any objects. // NOTE: See BlockOffsetArrayUseUnallocatedBlock flag. @@ -253,9 +240,6 @@ // that is closed: [start_index, end_index] void set_remainder_to_point_to_start_incl(size_t start, size_t end); - // A helper function for BOT adjustment/verification work - void do_block_internal(HeapWord* blk_start, HeapWord* blk_end, Action action); - protected: G1OffsetTableContigSpace* gsp() const { return _gsp; } @@ -303,11 +287,9 @@ public: // The space may not have it's bottom and top set yet, which is why the - // region is passed as a parameter. If "init_to_zero" is true, the - // elements of the array are initialized to zero. Otherwise, they are - // initialized to point backwards to the beginning. - G1BlockOffsetArray(G1BlockOffsetSharedArray* array, MemRegion mr, - bool init_to_zero); + // region is passed as a parameter. The elements of the array are + // initialized to zero. + G1BlockOffsetArray(G1BlockOffsetSharedArray* array, MemRegion mr); // Note: this ought to be part of the constructor, but that would require // "this" to be passed as a parameter to a member constructor for @@ -315,114 +297,19 @@ // This would be legal C++, but MS VC++ doesn't allow it. void set_space(G1OffsetTableContigSpace* sp); - // Resets the covered region to the given "mr". - void set_region(MemRegion mr); - // Resets the covered region to one with the same _bottom as before but // the "new_word_size". void resize(size_t new_word_size); - // These must be guaranteed to work properly (i.e., do nothing) - // when "blk_start" ("blk" for second version) is "NULL". - virtual void alloc_block(HeapWord* blk_start, HeapWord* blk_end); - virtual void alloc_block(HeapWord* blk, size_t size) { - alloc_block(blk, blk + size); - } - - // The following methods are useful and optimized for a - // general, non-contiguous space. - - // Given a block [blk_start, blk_start + full_blk_size), and - // a left_blk_size < full_blk_size, adjust the BOT to show two - // blocks [blk_start, blk_start + left_blk_size) and - // [blk_start + left_blk_size, blk_start + full_blk_size). - // It is assumed (and verified in the non-product VM) that the - // BOT was correct for the original block. - void split_block(HeapWord* blk_start, size_t full_blk_size, - size_t left_blk_size); - - // Adjust the BOT to show that it has a single block in the - // range [blk_start, blk_start + size). All necessary BOT - // cards are adjusted, but _unallocated_block isn't. - void single_block(HeapWord* blk_start, HeapWord* blk_end); - void single_block(HeapWord* blk, size_t size) { - single_block(blk, blk + size); - } - - // Adjust BOT to show that it has a block in the range - // [blk_start, blk_start + size). Only the first card - // of BOT is touched. It is assumed (and verified in the - // non-product VM) that the remaining cards of the block - // are correct. - void mark_block(HeapWord* blk_start, HeapWord* blk_end); - void mark_block(HeapWord* blk, size_t size) { - mark_block(blk, blk + size); - } - - // Adjust _unallocated_block to indicate that a particular - // block has been newly allocated or freed. It is assumed (and - // verified in the non-product VM) that the BOT is correct for - // the given block. - inline void allocated(HeapWord* blk_start, HeapWord* blk_end) { - // Verify that the BOT shows [blk, blk + blk_size) to be one block. - verify_single_block(blk_start, blk_end); - if (BlockOffsetArrayUseUnallocatedBlock) { - _unallocated_block = MAX2(_unallocated_block, blk_end); - } - } - - inline void allocated(HeapWord* blk, size_t size) { - allocated(blk, blk + size); - } - - inline void freed(HeapWord* blk_start, HeapWord* blk_end); - - inline void freed(HeapWord* blk, size_t size); - virtual HeapWord* block_start_unsafe(const void* addr); virtual HeapWord* block_start_unsafe_const(const void* addr) const; - // Requires "addr" to be the start of a card and returns the - // start of the block that contains the given address. - HeapWord* block_start_careful(const void* addr) const; - - // If true, initialize array slots with no allocated blocks to zero. - // Otherwise, make them point back to the front. - bool init_to_zero() { return _init_to_zero; } - - // Verification & debugging - ensure that the offset table reflects the fact - // that the block [blk_start, blk_end) or [blk, blk + size) is a - // single block of storage. NOTE: can;t const this because of - // call to non-const do_block_internal() below. - inline void verify_single_block(HeapWord* blk_start, HeapWord* blk_end) { - if (VerifyBlockOffsetArray) { - do_block_internal(blk_start, blk_end, Action_check); - } - } - - inline void verify_single_block(HeapWord* blk, size_t size) { - verify_single_block(blk, blk + size); - } - // Used by region verification. Checks that the contents of the // BOT reflect that there's a single object that spans the address // range [obj_start, obj_start + word_size); returns true if this is // the case, returns false if it's not. bool verify_for_object(HeapWord* obj_start, size_t word_size) const; - // Verify that the given block is before _unallocated_block - inline void verify_not_unallocated(HeapWord* blk_start, - HeapWord* blk_end) const { - if (BlockOffsetArrayUseUnallocatedBlock) { - assert(blk_start < blk_end, "Block inconsistency?"); - assert(blk_end <= _unallocated_block, "_unallocated_block problem"); - } - } - - inline void verify_not_unallocated(HeapWord* blk, size_t size) const { - verify_not_unallocated(blk, blk + size); - } - void check_all_cards(size_t left_card, size_t right_card) const; virtual void print_on(outputStream* out) PRODUCT_RETURN; @@ -445,14 +332,12 @@ blk_start, blk_end); } - // Variant of zero_bottom_entry that does not check for availability of the + // Zero out the entry for _bottom (offset will be zero). Does not check for availability of the // memory first. void zero_bottom_entry_raw(); // Variant of initialize_threshold that does not check for availability of the // memory first. HeapWord* initialize_threshold_raw(); - // Zero out the entry for _bottom (offset will be zero). - void zero_bottom_entry(); public: G1BlockOffsetArrayContigSpace(G1BlockOffsetSharedArray* array, MemRegion mr);
--- a/hotspot/src/share/vm/gc_implementation/g1/g1BlockOffsetTable.inline.hpp Thu Sep 04 13:26:26 2014 -0700 +++ b/hotspot/src/share/vm/gc_implementation/g1/g1BlockOffsetTable.inline.hpp Wed Jul 05 19:59:54 2017 +0200 @@ -91,13 +91,6 @@ } } -void G1BlockOffsetSharedArray::check_offset_array(size_t index, HeapWord* high, HeapWord* low) const { - check_index(index, "index out of range"); - assert(high >= low, "addresses out of order"); - check_offset(pointer_delta(high, low), "offset too large"); - assert(_offset_array[index] == pointer_delta(high, low), "Wrong offset"); -} - // Variant of index_for that does not check the index for validity. inline size_t G1BlockOffsetSharedArray::index_for_raw(const void* p) const { return pointer_delta((char*)p, _reserved.start(), sizeof(char)) >> LogN; @@ -193,28 +186,4 @@ return q; } -////////////////////////////////////////////////////////////////////////// -// BlockOffsetArrayNonContigSpace inlines -////////////////////////////////////////////////////////////////////////// -inline void G1BlockOffsetArray::freed(HeapWord* blk_start, HeapWord* blk_end) { - // Verify that the BOT shows [blk_start, blk_end) to be one block. - verify_single_block(blk_start, blk_end); - // adjust _unallocated_block upward or downward - // as appropriate - if (BlockOffsetArrayUseUnallocatedBlock) { - assert(_unallocated_block <= _end, - "Inconsistent value for _unallocated_block"); - if (blk_end >= _unallocated_block && blk_start <= _unallocated_block) { - // CMS-specific note: a block abutting _unallocated_block to - // its left is being freed, a new block is being added or - // we are resetting following a compaction - _unallocated_block = blk_start; - } - } -} - -inline void G1BlockOffsetArray::freed(HeapWord* blk, size_t size) { - freed(blk, blk + size); -} - #endif // SHARE_VM_GC_IMPLEMENTATION_G1_G1BLOCKOFFSETTABLE_INLINE_HPP
--- a/hotspot/src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp Thu Sep 04 13:26:26 2014 -0700 +++ b/hotspot/src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp Wed Jul 05 19:59:54 2017 +0200 @@ -532,9 +532,9 @@ // again to allocate from it. append_secondary_free_list(); - assert(_hrs.num_free_regions() > 0, "if the secondary_free_list was not " + assert(_hrm.num_free_regions() > 0, "if the secondary_free_list was not " "empty we should have moved at least one entry to the free_list"); - HeapRegion* res = _hrs.allocate_free_region(is_old); + HeapRegion* res = _hrm.allocate_free_region(is_old); if (G1ConcRegionFreeingVerbose) { gclog_or_tty->print_cr("G1ConcRegionFreeing [region alloc] : " "allocated "HR_FORMAT" from secondary_free_list", @@ -575,7 +575,7 @@ } } - res = _hrs.allocate_free_region(is_old); + res = _hrm.allocate_free_region(is_old); if (res == NULL) { if (G1ConcRegionFreeingVerbose) { @@ -601,7 +601,7 @@ // always expand the heap by an amount aligned to the heap // region size, the free list should in theory not be empty. // In either case allocate_free_region() will check for NULL. - res = _hrs.allocate_free_region(is_old); + res = _hrm.allocate_free_region(is_old); } else { _expand_heap_after_alloc_failure = false; } @@ -613,7 +613,7 @@ G1CollectedHeap::humongous_obj_allocate_initialize_regions(uint first, uint num_regions, size_t word_size) { - assert(first != G1_NO_HRS_INDEX, "pre-condition"); + assert(first != G1_NO_HRM_INDEX, "pre-condition"); assert(isHumongous(word_size), "word_size should be humongous"); assert(num_regions * HeapRegion::GrainWords >= word_size, "pre-condition"); @@ -751,7 +751,7 @@ verify_region_sets_optional(); - uint first = G1_NO_HRS_INDEX; + uint first = G1_NO_HRM_INDEX; uint obj_regions = (uint)(align_size_up_(word_size, HeapRegion::GrainWords) / HeapRegion::GrainWords); if (obj_regions == 1) { @@ -760,7 +760,7 @@ // later. HeapRegion* hr = new_region(word_size, true /* is_old */, false /* do_expand */); if (hr != NULL) { - first = hr->hrs_index(); + first = hr->hrm_index(); } } else { // We can't allocate humongous regions spanning more than one region while @@ -776,18 +776,18 @@ // Policy: Try only empty regions (i.e. already committed first). Maybe we // are lucky enough to find some. - first = _hrs.find_contiguous_only_empty(obj_regions); - if (first != G1_NO_HRS_INDEX) { - _hrs.allocate_free_regions_starting_at(first, obj_regions); - } - } - - if (first == G1_NO_HRS_INDEX) { + first = _hrm.find_contiguous_only_empty(obj_regions); + if (first != G1_NO_HRM_INDEX) { + _hrm.allocate_free_regions_starting_at(first, obj_regions); + } + } + + if (first == G1_NO_HRM_INDEX) { // Policy: We could not find enough regions for the humongous object in the // free list. Look through the heap to find a mix of free and uncommitted regions. // If so, try expansion. - first = _hrs.find_contiguous_empty_or_unavailable(obj_regions); - if (first != G1_NO_HRS_INDEX) { + first = _hrm.find_contiguous_empty_or_unavailable(obj_regions); + if (first != G1_NO_HRM_INDEX) { // We found something. Make sure these regions are committed, i.e. expand // the heap. Alternatively we could do a defragmentation GC. ergo_verbose1(ErgoHeapSizing, @@ -796,7 +796,7 @@ ergo_format_byte("allocation request"), word_size * HeapWordSize); - _hrs.expand_at(first, obj_regions); + _hrm.expand_at(first, obj_regions); g1_policy()->record_new_heap_size(num_regions()); #ifdef ASSERT @@ -806,14 +806,14 @@ assert(is_on_master_free_list(hr), "sanity"); } #endif - _hrs.allocate_free_regions_starting_at(first, obj_regions); + _hrm.allocate_free_regions_starting_at(first, obj_regions); } else { // Policy: Potentially trigger a defragmentation GC. } } HeapWord* result = NULL; - if (first != G1_NO_HRS_INDEX) { + if (first != G1_NO_HRM_INDEX) { result = humongous_obj_allocate_initialize_regions(first, obj_regions, word_size); assert(result != NULL, "it should always return a valid result"); @@ -1248,7 +1248,7 @@ : _hr_printer(hr_printer) { } }; -void G1CollectedHeap::print_hrs_post_compaction() { +void G1CollectedHeap::print_hrm_post_compaction() { PostCompactionPrinterClosure cl(hr_printer()); heap_region_iterate(&cl); } @@ -1417,7 +1417,7 @@ // that all the COMMIT / UNCOMMIT events are generated before // the end GC event. - print_hrs_post_compaction(); + print_hrm_post_compaction(); _hr_printer.end_gc(true /* full */, (size_t) total_collections()); } @@ -1490,7 +1490,7 @@ // Update the number of full collections that have been completed. increment_old_marking_cycles_completed(false /* concurrent */); - _hrs.verify_optional(); + _hrm.verify_optional(); verify_region_sets_optional(); verify_after_gc(); @@ -1734,7 +1734,7 @@ ergo_format_byte("allocation request"), word_size * HeapWordSize); if (expand(expand_bytes)) { - _hrs.verify_optional(); + _hrm.verify_optional(); verify_region_sets_optional(); return attempt_allocation_at_safepoint(word_size, false /* expect_null_mutator_alloc_region */); @@ -1762,7 +1762,7 @@ uint regions_to_expand = (uint)(aligned_expand_bytes / HeapRegion::GrainBytes); assert(regions_to_expand > 0, "Must expand by at least one region"); - uint expanded_by = _hrs.expand_by(regions_to_expand); + uint expanded_by = _hrm.expand_by(regions_to_expand); if (expanded_by > 0) { size_t actual_expand_bytes = expanded_by * HeapRegion::GrainBytes; @@ -1775,7 +1775,7 @@ // The expansion of the virtual storage space was unsuccessful. // Let's see if it was because we ran out of swap. if (G1ExitOnExpansionFailure && - _hrs.available() >= regions_to_expand) { + _hrm.available() >= regions_to_expand) { // We had head room... vm_exit_out_of_memory(aligned_expand_bytes, OOM_MMAP_ERROR, "G1 heap expansion"); } @@ -1790,7 +1790,7 @@ HeapRegion::GrainBytes); uint num_regions_to_remove = (uint)(shrink_bytes / HeapRegion::GrainBytes); - uint num_regions_removed = _hrs.shrink_by(num_regions_to_remove); + uint num_regions_removed = _hrm.shrink_by(num_regions_to_remove); size_t shrunk_bytes = num_regions_removed * HeapRegion::GrainBytes; ergo_verbose3(ErgoHeapSizing, @@ -1823,7 +1823,7 @@ shrink_helper(shrink_bytes); rebuild_region_sets(true /* free_list_only */); - _hrs.verify_optional(); + _hrm.verify_optional(); verify_region_sets_optional(); } @@ -1867,6 +1867,7 @@ _old_marking_cycles_started(0), _old_marking_cycles_completed(0), _concurrent_cycle_started(false), + _heap_summary_sent(false), _in_cset_fast_test(), _dirty_cards_region_list(NULL), _worker_cset_start_region(NULL), @@ -2032,7 +2033,7 @@ CMBitMap::mark_distance(), mtGC); - _hrs.initialize(heap_storage, prev_bitmap_storage, next_bitmap_storage, bot_storage, cardtable_storage, card_counts_storage); + _hrm.initialize(heap_storage, prev_bitmap_storage, next_bitmap_storage, bot_storage, cardtable_storage, card_counts_storage); g1_barrier_set()->initialize(cardtable_storage); // Do later initialization work for concurrent refinement. _cg1r->init(card_counts_storage); @@ -2053,8 +2054,8 @@ _g1h = this; - _in_cset_fast_test.initialize(_hrs.reserved().start(), _hrs.reserved().end(), HeapRegion::GrainBytes); - _humongous_is_live.initialize(_hrs.reserved().start(), _hrs.reserved().end(), HeapRegion::GrainBytes); + _in_cset_fast_test.initialize(_hrm.reserved().start(), _hrm.reserved().end(), HeapRegion::GrainBytes); + _humongous_is_live.initialize(_hrm.reserved().start(), _hrm.reserved().end(), HeapRegion::GrainBytes); // Create the ConcurrentMark data structure and thread. // (Must do this late, so that "max_regions" is defined.) @@ -2115,7 +2116,7 @@ // Here we allocate the dummy HeapRegion that is required by the // G1AllocRegion class. - HeapRegion* dummy_region = _hrs.get_dummy_region(); + HeapRegion* dummy_region = _hrm.get_dummy_region(); // We'll re-use the same region whether the alloc region will // require BOT updates or not and, if it doesn't, then a non-young @@ -2232,14 +2233,14 @@ } size_t G1CollectedHeap::capacity() const { - return _hrs.length() * HeapRegion::GrainBytes; + return _hrm.length() * HeapRegion::GrainBytes; } void G1CollectedHeap::reset_gc_time_stamps(HeapRegion* hr) { assert(!hr->continuesHumongous(), "pre-condition"); hr->reset_gc_time_stamp(); if (hr->startsHumongous()) { - uint first_index = hr->hrs_index() + 1; + uint first_index = hr->hrm_index() + 1; uint last_index = hr->last_hc_index(); for (uint i = first_index; i < last_index; i += 1) { HeapRegion* chr = region_at(i); @@ -2445,13 +2446,24 @@ _gc_timer_cm->register_gc_end(); _gc_tracer_cm->report_gc_end(_gc_timer_cm->gc_end(), _gc_timer_cm->time_partitions()); + // Clear state variables to prepare for the next concurrent cycle. _concurrent_cycle_started = false; + _heap_summary_sent = false; } } void G1CollectedHeap::trace_heap_after_concurrent_cycle() { if (_concurrent_cycle_started) { - trace_heap_after_gc(_gc_tracer_cm); + // This function can be called when: + // the cleanup pause is run + // the concurrent cycle is aborted before the cleanup pause. + // the concurrent cycle is aborted after the cleanup pause, + // but before the concurrent cycle end has been registered. + // Make sure that we only send the heap information once. + if (!_heap_summary_sent) { + trace_heap_after_gc(_gc_tracer_cm); + _heap_summary_sent = true; + } } } @@ -2537,7 +2549,7 @@ } bool G1CollectedHeap::is_in(const void* p) const { - if (_hrs.reserved().contains(p)) { + if (_hrm.reserved().contains(p)) { // Given that we know that p is in the reserved space, // heap_region_containing_raw() should successfully // return the containing region. @@ -2551,7 +2563,7 @@ #ifdef ASSERT bool G1CollectedHeap::is_in_exact(const void* p) const { bool contains = reserved_region().contains(p); - bool available = _hrs.is_available(addr_to_region((HeapWord*)p)); + bool available = _hrm.is_available(addr_to_region((HeapWord*)p)); if (contains && available) { return true; } else { @@ -2618,7 +2630,7 @@ } void G1CollectedHeap::heap_region_iterate(HeapRegionClosure* cl) const { - _hrs.iterate(cl); + _hrm.iterate(cl); } void @@ -2626,7 +2638,7 @@ uint worker_id, uint num_workers, jint claim_value) const { - _hrs.par_iterate(cl, worker_id, num_workers, claim_value); + _hrm.par_iterate(cl, worker_id, num_workers, claim_value); } class ResetClaimValuesClosure: public HeapRegionClosure { @@ -2846,9 +2858,9 @@ } HeapRegion* G1CollectedHeap::next_compaction_region(const HeapRegion* from) const { - HeapRegion* result = _hrs.next_region_in_heap(from); + HeapRegion* result = _hrm.next_region_in_heap(from); while (result != NULL && result->isHumongous()) { - result = _hrs.next_region_in_heap(result); + result = _hrm.next_region_in_heap(result); } return result; } @@ -2908,7 +2920,7 @@ } size_t G1CollectedHeap::max_capacity() const { - return _hrs.reserved().byte_size(); + return _hrm.reserved().byte_size(); } jlong G1CollectedHeap::millis_since_last_gc() { @@ -3437,9 +3449,9 @@ st->print(" total " SIZE_FORMAT "K, used " SIZE_FORMAT "K", capacity()/K, used_unlocked()/K); st->print(" [" INTPTR_FORMAT ", " INTPTR_FORMAT ", " INTPTR_FORMAT ")", - _hrs.reserved().start(), - _hrs.reserved().start() + _hrs.length() + HeapRegion::GrainWords, - _hrs.reserved().end()); + _hrm.reserved().start(), + _hrm.reserved().start() + _hrm.length() + HeapRegion::GrainWords, + _hrm.reserved().end()); st->cr(); st->print(" region size " SIZE_FORMAT "K, ", HeapRegion::GrainBytes / K); uint young_regions = _young_list->length(); @@ -3682,7 +3694,7 @@ } G1CollectedHeap* g1h = G1CollectedHeap::heap(); - uint region_idx = r->hrs_index(); + uint region_idx = r->hrm_index(); bool is_candidate = !g1h->humongous_region_is_always_live(region_idx); // Is_candidate already filters out humongous regions with some remembered set. // This will not lead to humongous object that we mistakenly keep alive because @@ -4205,7 +4217,7 @@ // output from the concurrent mark thread interfering with this // logging output either. - _hrs.verify_optional(); + _hrm.verify_optional(); verify_region_sets_optional(); TASKQUEUE_STATS_ONLY(if (ParallelGCVerbose) print_taskqueue_stats()); @@ -6024,7 +6036,7 @@ bool locked) { assert(!hr->isHumongous(), "this is only for non-humongous regions"); assert(!hr->is_empty(), "the region should not be empty"); - assert(_hrs.is_available(hr->hrs_index()), "region should be committed"); + assert(_hrm.is_available(hr->hrm_index()), "region should be committed"); assert(free_list != NULL, "pre-condition"); if (G1VerifyBitmaps) { @@ -6055,7 +6067,7 @@ hr->set_notHumongous(); free_region(hr, free_list, par); - uint i = hr->hrs_index() + 1; + uint i = hr->hrm_index() + 1; while (i < last_index) { HeapRegion* curr_hr = region_at(i); assert(curr_hr->continuesHumongous(), "invariant"); @@ -6079,7 +6091,7 @@ assert(list != NULL, "list can't be null"); if (!list->is_empty()) { MutexLockerEx x(FreeList_lock, Mutex::_no_safepoint_check_flag); - _hrs.insert_list_into_free_list(list); + _hrm.insert_list_into_free_list(list); } } @@ -6448,7 +6460,7 @@ // While this cleanup is not strictly necessary to be done (or done instantly), // given that their occurrence is very low, this saves us this additional // complexity. - uint region_idx = r->hrs_index(); + uint region_idx = r->hrm_index(); if (g1h->humongous_is_live(region_idx) || g1h->humongous_region_is_always_live(region_idx)) { @@ -6687,22 +6699,22 @@ // this is that during a full GC string deduplication needs to know if // a collected region was young or old when the full GC was initiated. } - _hrs.remove_all_free_regions(); + _hrm.remove_all_free_regions(); } class RebuildRegionSetsClosure : public HeapRegionClosure { private: bool _free_list_only; HeapRegionSet* _old_set; - HeapRegionSeq* _hrs; + HeapRegionManager* _hrm; size_t _total_used; public: RebuildRegionSetsClosure(bool free_list_only, - HeapRegionSet* old_set, HeapRegionSeq* hrs) : + HeapRegionSet* old_set, HeapRegionManager* hrm) : _free_list_only(free_list_only), - _old_set(old_set), _hrs(hrs), _total_used(0) { - assert(_hrs->num_free_regions() == 0, "pre-condition"); + _old_set(old_set), _hrm(hrm), _total_used(0) { + assert(_hrm->num_free_regions() == 0, "pre-condition"); if (!free_list_only) { assert(_old_set->is_empty(), "pre-condition"); } @@ -6715,7 +6727,7 @@ if (r->is_empty()) { // Add free regions to the free list - _hrs->insert_into_free_list(r); + _hrm->insert_into_free_list(r); } else if (!_free_list_only) { assert(!r->is_young(), "we should not come across young regions"); @@ -6743,7 +6755,7 @@ _young_list->empty_list(); } - RebuildRegionSetsClosure cl(free_list_only, &_old_set, &_hrs); + RebuildRegionSetsClosure cl(free_list_only, &_old_set, &_hrm); heap_region_iterate(&cl); if (!free_list_only) { @@ -6933,7 +6945,7 @@ private: HeapRegionSet* _old_set; HeapRegionSet* _humongous_set; - HeapRegionSeq* _hrs; + HeapRegionManager* _hrm; public: HeapRegionSetCount _old_count; @@ -6942,8 +6954,8 @@ VerifyRegionListsClosure(HeapRegionSet* old_set, HeapRegionSet* humongous_set, - HeapRegionSeq* hrs) : - _old_set(old_set), _humongous_set(humongous_set), _hrs(hrs), + HeapRegionManager* hrm) : + _old_set(old_set), _humongous_set(humongous_set), _hrm(hrm), _old_count(), _humongous_count(), _free_count(){ } bool doHeapRegion(HeapRegion* hr) { @@ -6954,19 +6966,19 @@ if (hr->is_young()) { // TODO } else if (hr->startsHumongous()) { - assert(hr->containing_set() == _humongous_set, err_msg("Heap region %u is starts humongous but not in humongous set.", hr->hrs_index())); + assert(hr->containing_set() == _humongous_set, err_msg("Heap region %u is starts humongous but not in humongous set.", hr->hrm_index())); _humongous_count.increment(1u, hr->capacity()); } else if (hr->is_empty()) { - assert(_hrs->is_free(hr), err_msg("Heap region %u is empty but not on the free list.", hr->hrs_index())); + assert(_hrm->is_free(hr), err_msg("Heap region %u is empty but not on the free list.", hr->hrm_index())); _free_count.increment(1u, hr->capacity()); } else { - assert(hr->containing_set() == _old_set, err_msg("Heap region %u is old but not in the old set.", hr->hrs_index())); + assert(hr->containing_set() == _old_set, err_msg("Heap region %u is old but not in the old set.", hr->hrm_index())); _old_count.increment(1u, hr->capacity()); } return false; } - void verify_counts(HeapRegionSet* old_set, HeapRegionSet* humongous_set, HeapRegionSeq* free_list) { + void verify_counts(HeapRegionSet* old_set, HeapRegionSet* humongous_set, HeapRegionManager* free_list) { guarantee(old_set->length() == _old_count.length(), err_msg("Old set count mismatch. Expected %u, actual %u.", old_set->length(), _old_count.length())); guarantee(old_set->total_capacity_bytes() == _old_count.capacity(), err_msg("Old set capacity mismatch. Expected " SIZE_FORMAT ", actual " SIZE_FORMAT, old_set->total_capacity_bytes(), _old_count.capacity())); @@ -6985,7 +6997,7 @@ assert_heap_locked_or_at_safepoint(true /* should_be_vm_thread */); // First, check the explicit lists. - _hrs.verify(); + _hrm.verify(); { // Given that a concurrent operation might be adding regions to // the secondary free list we have to take the lock before @@ -7016,9 +7028,9 @@ // Finally, make sure that the region accounting in the lists is // consistent with what we see in the heap. - VerifyRegionListsClosure cl(&_old_set, &_humongous_set, &_hrs); + VerifyRegionListsClosure cl(&_old_set, &_humongous_set, &_hrm); heap_region_iterate(&cl); - cl.verify_counts(&_old_set, &_humongous_set, &_hrs); + cl.verify_counts(&_old_set, &_humongous_set, &_hrm); } // Optimized nmethod scanning
--- a/hotspot/src/share/vm/gc_implementation/g1/g1CollectedHeap.hpp Thu Sep 04 13:26:26 2014 -0700 +++ b/hotspot/src/share/vm/gc_implementation/g1/g1CollectedHeap.hpp Wed Jul 05 19:59:54 2017 +0200 @@ -33,7 +33,7 @@ #include "gc_implementation/g1/g1MonitoringSupport.hpp" #include "gc_implementation/g1/g1SATBCardTableModRefBS.hpp" #include "gc_implementation/g1/g1YCTypes.hpp" -#include "gc_implementation/g1/heapRegionSeq.hpp" +#include "gc_implementation/g1/heapRegionManager.hpp" #include "gc_implementation/g1/heapRegionSet.hpp" #include "gc_implementation/shared/hSpaceCounters.hpp" #include "gc_implementation/shared/parGCAllocBuffer.hpp" @@ -291,7 +291,7 @@ G1RegionMappingChangedListener _listener; // The sequence of all heap regions in the heap. - HeapRegionSeq _hrs; + HeapRegionManager _hrm; // Alloc region used to satisfy mutator allocation requests. MutatorAllocRegion _mutator_alloc_region; @@ -415,6 +415,7 @@ volatile unsigned int _old_marking_cycles_completed; bool _concurrent_cycle_started; + bool _heap_summary_sent; // This is a non-product method that is helpful for testing. It is // called at the end of a GC and artificially expands the heap by @@ -429,7 +430,7 @@ // If the HR printer is active, dump the state of the regions in the // heap after a compaction. - void print_hrs_post_compaction(); + void print_hrm_post_compaction(); double verify(bool guard, const char* msg); void verify_before_gc(); @@ -715,7 +716,7 @@ // We register a region with the fast "in collection set" test. We // simply set to true the array slot corresponding to this region. void register_region_with_in_cset_fast_test(HeapRegion* r) { - _in_cset_fast_test.set_in_cset(r->hrs_index()); + _in_cset_fast_test.set_in_cset(r->hrm_index()); } // This is a fast test on whether a reference points into the @@ -1171,17 +1172,17 @@ // But G1CollectedHeap doesn't yet support this. virtual bool is_maximal_no_gc() const { - return _hrs.available() == 0; + return _hrm.available() == 0; } // The current number of regions in the heap. - uint num_regions() const { return _hrs.length(); } + uint num_regions() const { return _hrm.length(); } // The max number of regions in the heap. - uint max_regions() const { return _hrs.max_length(); } + uint max_regions() const { return _hrm.max_length(); } // The number of regions that are completely free. - uint num_free_regions() const { return _hrs.num_free_regions(); } + uint num_free_regions() const { return _hrm.num_free_regions(); } // The number of regions that are not completely free. uint num_used_regions() const { return num_regions() - num_free_regions(); } @@ -1233,7 +1234,7 @@ #ifdef ASSERT bool is_on_master_free_list(HeapRegion* hr) { - return _hrs.is_free(hr); + return _hrm.is_free(hr); } #endif // ASSERT @@ -1245,7 +1246,7 @@ } void append_secondary_free_list() { - _hrs.insert_list_into_free_list(&_secondary_free_list); + _hrm.insert_list_into_free_list(&_secondary_free_list); } void append_secondary_free_list_if_not_empty_with_lock() { @@ -1356,13 +1357,13 @@ // Return "TRUE" iff the given object address is in the reserved // region of g1. bool is_in_g1_reserved(const void* p) const { - return _hrs.reserved().contains(p); + return _hrm.reserved().contains(p); } // Returns a MemRegion that corresponds to the space that has been // reserved for the heap MemRegion g1_reserved() const { - return _hrs.reserved(); + return _hrm.reserved(); } virtual bool is_in_closed_subset(const void* p) const;
--- a/hotspot/src/share/vm/gc_implementation/g1/g1CollectedHeap.inline.hpp Thu Sep 04 13:26:26 2014 -0700 +++ b/hotspot/src/share/vm/gc_implementation/g1/g1CollectedHeap.inline.hpp Wed Jul 05 19:59:54 2017 +0200 @@ -30,15 +30,15 @@ #include "gc_implementation/g1/g1AllocRegion.inline.hpp" #include "gc_implementation/g1/g1CollectorPolicy.hpp" #include "gc_implementation/g1/g1SATBCardTableModRefBS.hpp" +#include "gc_implementation/g1/heapRegionManager.inline.hpp" #include "gc_implementation/g1/heapRegionSet.inline.hpp" -#include "gc_implementation/g1/heapRegionSeq.inline.hpp" #include "runtime/orderAccess.inline.hpp" #include "utilities/taskqueue.hpp" // Inline functions for G1CollectedHeap // Return the region with the given index. It assumes the index is valid. -inline HeapRegion* G1CollectedHeap::region_at(uint index) const { return _hrs.at(index); } +inline HeapRegion* G1CollectedHeap::region_at(uint index) const { return _hrm.at(index); } inline uint G1CollectedHeap::addr_to_region(HeapWord* addr) const { assert(is_in_reserved(addr), @@ -48,7 +48,7 @@ } inline HeapWord* G1CollectedHeap::bottom_addr_for_region(uint index) const { - return _hrs.reserved().start() + index * HeapRegion::GrainWords; + return _hrm.reserved().start() + index * HeapRegion::GrainWords; } template <class T> @@ -57,7 +57,7 @@ assert(is_in_g1_reserved((const void*) addr), err_msg("Address "PTR_FORMAT" is outside of the heap ranging from ["PTR_FORMAT" to "PTR_FORMAT")", p2i((void*)addr), p2i(g1_reserved().start()), p2i(g1_reserved().end()))); - return _hrs.addr_to_region((HeapWord*) addr); + return _hrm.addr_to_region((HeapWord*) addr); } template <class T> @@ -87,7 +87,7 @@ } inline bool G1CollectedHeap::obj_in_cs(oop obj) { - HeapRegion* r = _hrs.addr_to_region((HeapWord*) obj); + HeapRegion* r = _hrm.addr_to_region((HeapWord*) obj); return r != NULL && r->in_collection_set(); }
--- a/hotspot/src/share/vm/gc_implementation/g1/g1RemSet.cpp Thu Sep 04 13:26:26 2014 -0700 +++ b/hotspot/src/share/vm/gc_implementation/g1/g1RemSet.cpp Wed Jul 05 19:59:54 2017 +0200 @@ -32,7 +32,7 @@ #include "gc_implementation/g1/g1GCPhaseTimes.hpp" #include "gc_implementation/g1/g1OopClosures.inline.hpp" #include "gc_implementation/g1/g1RemSet.inline.hpp" -#include "gc_implementation/g1/heapRegionSeq.inline.hpp" +#include "gc_implementation/g1/heapRegionManager.inline.hpp" #include "gc_implementation/g1/heapRegionRemSet.hpp" #include "memory/iterator.hpp" #include "oops/oop.inline.hpp"
--- a/hotspot/src/share/vm/gc_implementation/g1/heapRegion.cpp Thu Sep 04 13:26:26 2014 -0700 +++ b/hotspot/src/share/vm/gc_implementation/g1/heapRegion.cpp Wed Jul 05 19:59:54 2017 +0200 @@ -29,7 +29,7 @@ #include "gc_implementation/g1/g1OopClosures.inline.hpp" #include "gc_implementation/g1/heapRegion.inline.hpp" #include "gc_implementation/g1/heapRegionRemSet.hpp" -#include "gc_implementation/g1/heapRegionSeq.inline.hpp" +#include "gc_implementation/g1/heapRegionManager.inline.hpp" #include "gc_implementation/shared/liveRange.hpp" #include "memory/genOopClosures.inline.hpp" #include "memory/iterator.hpp" @@ -322,34 +322,11 @@ return false; } -HeapWord* HeapRegion::next_block_start_careful(HeapWord* addr) { - HeapWord* low = addr; - HeapWord* high = end(); - while (low < high) { - size_t diff = pointer_delta(high, low); - // Must add one below to bias toward the high amount. Otherwise, if - // "high" were at the desired value, and "low" were one less, we - // would not converge on "high". This is not symmetric, because - // we set "high" to a block start, which might be the right one, - // which we don't do for "low". - HeapWord* middle = low + (diff+1)/2; - if (middle == high) return high; - HeapWord* mid_bs = block_start_careful(middle); - if (mid_bs < addr) { - low = middle; - } else { - high = mid_bs; - } - } - assert(low == high && low >= addr, "Didn't work."); - return low; -} - -HeapRegion::HeapRegion(uint hrs_index, +HeapRegion::HeapRegion(uint hrm_index, G1BlockOffsetSharedArray* sharedOffsetArray, MemRegion mr) : G1OffsetTableContigSpace(sharedOffsetArray, mr), - _hrs_index(hrs_index), + _hrm_index(hrm_index), _humongous_type(NotHumongous), _humongous_start_region(NULL), _in_collection_set(false), _next_in_special_set(NULL), _orig_end(NULL),
--- a/hotspot/src/share/vm/gc_implementation/g1/heapRegion.hpp Thu Sep 04 13:26:26 2014 -0700 +++ b/hotspot/src/share/vm/gc_implementation/g1/heapRegion.hpp Wed Jul 05 19:59:54 2017 +0200 @@ -54,15 +54,15 @@ #define HR_FORMAT "%u:(%s)["PTR_FORMAT","PTR_FORMAT","PTR_FORMAT"]" #define HR_FORMAT_PARAMS(_hr_) \ - (_hr_)->hrs_index(), \ + (_hr_)->hrm_index(), \ (_hr_)->is_survivor() ? "S" : (_hr_)->is_young() ? "E" : \ (_hr_)->startsHumongous() ? "HS" : \ (_hr_)->continuesHumongous() ? "HC" : \ !(_hr_)->is_empty() ? "O" : "F", \ p2i((_hr_)->bottom()), p2i((_hr_)->top()), p2i((_hr_)->end()) -// sentinel value for hrs_index -#define G1_NO_HRS_INDEX ((uint) -1) +// sentinel value for hrm_index +#define G1_NO_HRM_INDEX ((uint) -1) // A dirty card to oop closure for heap regions. It // knows how to get the G1 heap and how to use the bitmap @@ -206,10 +206,6 @@ _offsets.reset_bot(); } - void update_bot_for_object(HeapWord* start, size_t word_size) { - _offsets.alloc_block(start, word_size); - } - void print_bot_on(outputStream* out) { _offsets.print_on(out); } @@ -234,7 +230,7 @@ protected: // The index of this region in the heap region sequence. - uint _hrs_index; + uint _hrm_index; HumongousType _humongous_type; // For a humongous region, region in which it starts. @@ -330,7 +326,7 @@ size_t _predicted_bytes_to_copy; public: - HeapRegion(uint hrs_index, + HeapRegion(uint hrm_index, G1BlockOffsetSharedArray* sharedOffsetArray, MemRegion mr); @@ -385,9 +381,9 @@ inline HeapWord* par_allocate_no_bot_updates(size_t word_size); inline HeapWord* allocate_no_bot_updates(size_t word_size); - // If this region is a member of a HeapRegionSeq, the index in that + // If this region is a member of a HeapRegionManager, the index in that // sequence, otherwise -1. - uint hrs_index() const { return _hrs_index; } + uint hrm_index() const { return _hrm_index; } // The number of bytes marked live in the region in the last marking phase. size_t marked_bytes() { return _prev_marked_bytes; } @@ -458,7 +454,7 @@ // with this HS region. uint last_hc_index() const { assert(startsHumongous(), "don't call this otherwise"); - return hrs_index() + region_num(); + return hrm_index() + region_num(); } // Same as Space::is_in_reserved, but will use the original size of the region. @@ -570,7 +566,7 @@ void set_next_dirty_cards_region(HeapRegion* hr) { _next_dirty_cards_region = hr; } bool is_on_dirty_cards_region_list() const { return get_next_dirty_cards_region() != NULL; } - HeapWord* orig_end() { return _orig_end; } + HeapWord* orig_end() const { return _orig_end; } // Reset HR stuff to default values. void hr_clear(bool par, bool clear_space, bool locked = false); @@ -737,18 +733,6 @@ bool filter_young, jbyte* card_ptr); - // A version of block start that is guaranteed to find *some* block - // boundary at or before "p", but does not object iteration, and may - // therefore be used safely when the heap is unparseable. - HeapWord* block_start_careful(const void* p) const { - return _offsets.block_start_careful(p); - } - - // Requires that "addr" is within the region. Returns the start of the - // first ("careful") block that starts at or after "addr", or else the - // "end" of the region if there is no such block. - HeapWord* next_block_start_careful(HeapWord* addr); - size_t recorded_rs_length() const { return _recorded_rs_length; } double predicted_elapsed_time_ms() const { return _predicted_elapsed_time_ms; } size_t predicted_bytes_to_copy() const { return _predicted_bytes_to_copy; } @@ -813,7 +797,7 @@ // HeapRegionClosure is used for iterating over regions. // Terminates the iteration when the "doHeapRegion" method returns "true". class HeapRegionClosure : public StackObj { - friend class HeapRegionSeq; + friend class HeapRegionManager; friend class G1CollectedHeap; bool _complete;
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hotspot/src/share/vm/gc_implementation/g1/heapRegionManager.cpp Wed Jul 05 19:59:54 2017 +0200 @@ -0,0 +1,446 @@ +/* + * Copyright (c) 2001, 2014, 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. + * + */ + +#include "precompiled.hpp" +#include "gc_implementation/g1/heapRegion.hpp" +#include "gc_implementation/g1/heapRegionManager.inline.hpp" +#include "gc_implementation/g1/heapRegionSet.inline.hpp" +#include "gc_implementation/g1/g1CollectedHeap.inline.hpp" +#include "gc_implementation/g1/concurrentG1Refine.hpp" +#include "memory/allocation.hpp" + +void HeapRegionManager::initialize(G1RegionToSpaceMapper* heap_storage, + G1RegionToSpaceMapper* prev_bitmap, + G1RegionToSpaceMapper* next_bitmap, + G1RegionToSpaceMapper* bot, + G1RegionToSpaceMapper* cardtable, + G1RegionToSpaceMapper* card_counts) { + _allocated_heapregions_length = 0; + + _heap_mapper = heap_storage; + + _prev_bitmap_mapper = prev_bitmap; + _next_bitmap_mapper = next_bitmap; + + _bot_mapper = bot; + _cardtable_mapper = cardtable; + + _card_counts_mapper = card_counts; + + MemRegion reserved = heap_storage->reserved(); + _regions.initialize(reserved.start(), reserved.end(), HeapRegion::GrainBytes); + + _available_map.resize(_regions.length(), false); + _available_map.clear(); +} + +bool HeapRegionManager::is_available(uint region) const { + return _available_map.at(region); +} + +#ifdef ASSERT +bool HeapRegionManager::is_free(HeapRegion* hr) const { + return _free_list.contains(hr); +} +#endif + +HeapRegion* HeapRegionManager::new_heap_region(uint hrm_index) { + HeapWord* bottom = G1CollectedHeap::heap()->bottom_addr_for_region(hrm_index); + MemRegion mr(bottom, bottom + HeapRegion::GrainWords); + assert(reserved().contains(mr), "invariant"); + return new HeapRegion(hrm_index, G1CollectedHeap::heap()->bot_shared(), mr); +} + +void HeapRegionManager::commit_regions(uint index, size_t num_regions) { + guarantee(num_regions > 0, "Must commit more than zero regions"); + guarantee(_num_committed + num_regions <= max_length(), "Cannot commit more than the maximum amount of regions"); + + _num_committed += (uint)num_regions; + + _heap_mapper->commit_regions(index, num_regions); + + // Also commit auxiliary data + _prev_bitmap_mapper->commit_regions(index, num_regions); + _next_bitmap_mapper->commit_regions(index, num_regions); + + _bot_mapper->commit_regions(index, num_regions); + _cardtable_mapper->commit_regions(index, num_regions); + + _card_counts_mapper->commit_regions(index, num_regions); +} + +void HeapRegionManager::uncommit_regions(uint start, size_t num_regions) { + guarantee(num_regions >= 1, err_msg("Need to specify at least one region to uncommit, tried to uncommit zero regions at %u", start)); + guarantee(_num_committed >= num_regions, "pre-condition"); + + // Print before uncommitting. + if (G1CollectedHeap::heap()->hr_printer()->is_active()) { + for (uint i = start; i < start + num_regions; i++) { + HeapRegion* hr = at(i); + G1CollectedHeap::heap()->hr_printer()->uncommit(hr->bottom(), hr->end()); + } + } + + _num_committed -= (uint)num_regions; + + _available_map.par_clear_range(start, start + num_regions, BitMap::unknown_range); + _heap_mapper->uncommit_regions(start, num_regions); + + // Also uncommit auxiliary data + _prev_bitmap_mapper->uncommit_regions(start, num_regions); + _next_bitmap_mapper->uncommit_regions(start, num_regions); + + _bot_mapper->uncommit_regions(start, num_regions); + _cardtable_mapper->uncommit_regions(start, num_regions); + + _card_counts_mapper->uncommit_regions(start, num_regions); +} + +void HeapRegionManager::make_regions_available(uint start, uint num_regions) { + guarantee(num_regions > 0, "No point in calling this for zero regions"); + commit_regions(start, num_regions); + for (uint i = start; i < start + num_regions; i++) { + if (_regions.get_by_index(i) == NULL) { + HeapRegion* new_hr = new_heap_region(i); + _regions.set_by_index(i, new_hr); + _allocated_heapregions_length = MAX2(_allocated_heapregions_length, i + 1); + } + } + + _available_map.par_set_range(start, start + num_regions, BitMap::unknown_range); + + for (uint i = start; i < start + num_regions; i++) { + assert(is_available(i), err_msg("Just made region %u available but is apparently not.", i)); + HeapRegion* hr = at(i); + if (G1CollectedHeap::heap()->hr_printer()->is_active()) { + G1CollectedHeap::heap()->hr_printer()->commit(hr->bottom(), hr->end()); + } + HeapWord* bottom = G1CollectedHeap::heap()->bottom_addr_for_region(i); + MemRegion mr(bottom, bottom + HeapRegion::GrainWords); + + hr->initialize(mr); + insert_into_free_list(at(i)); + } +} + +uint HeapRegionManager::expand_by(uint num_regions) { + return expand_at(0, num_regions); +} + +uint HeapRegionManager::expand_at(uint start, uint num_regions) { + if (num_regions == 0) { + return 0; + } + + uint cur = start; + uint idx_last_found = 0; + uint num_last_found = 0; + + uint expanded = 0; + + while (expanded < num_regions && + (num_last_found = find_unavailable_from_idx(cur, &idx_last_found)) > 0) { + uint to_expand = MIN2(num_regions - expanded, num_last_found); + make_regions_available(idx_last_found, to_expand); + expanded += to_expand; + cur = idx_last_found + num_last_found + 1; + } + + verify_optional(); + return expanded; +} + +uint HeapRegionManager::find_contiguous(size_t num, bool empty_only) { + uint found = 0; + size_t length_found = 0; + uint cur = 0; + + while (length_found < num && cur < max_length()) { + HeapRegion* hr = _regions.get_by_index(cur); + if ((!empty_only && !is_available(cur)) || (is_available(cur) && hr != NULL && hr->is_empty())) { + // This region is a potential candidate for allocation into. + length_found++; + } else { + // This region is not a candidate. The next region is the next possible one. + found = cur + 1; + length_found = 0; + } + cur++; + } + + if (length_found == num) { + for (uint i = found; i < (found + num); i++) { + HeapRegion* hr = _regions.get_by_index(i); + // sanity check + guarantee((!empty_only && !is_available(i)) || (is_available(i) && hr != NULL && hr->is_empty()), + err_msg("Found region sequence starting at " UINT32_FORMAT ", length " SIZE_FORMAT + " that is not empty at " UINT32_FORMAT ". Hr is " PTR_FORMAT, found, num, i, p2i(hr))); + } + return found; + } else { + return G1_NO_HRM_INDEX; + } +} + +HeapRegion* HeapRegionManager::next_region_in_heap(const HeapRegion* r) const { + guarantee(r != NULL, "Start region must be a valid region"); + guarantee(is_available(r->hrm_index()), err_msg("Trying to iterate starting from region %u which is not in the heap", r->hrm_index())); + for (uint i = r->hrm_index() + 1; i < _allocated_heapregions_length; i++) { + HeapRegion* hr = _regions.get_by_index(i); + if (is_available(i)) { + return hr; + } + } + return NULL; +} + +void HeapRegionManager::iterate(HeapRegionClosure* blk) const { + uint len = max_length(); + + for (uint i = 0; i < len; i++) { + if (!is_available(i)) { + continue; + } + guarantee(at(i) != NULL, err_msg("Tried to access region %u that has a NULL HeapRegion*", i)); + bool res = blk->doHeapRegion(at(i)); + if (res) { + blk->incomplete(); + return; + } + } +} + +uint HeapRegionManager::find_unavailable_from_idx(uint start_idx, uint* res_idx) const { + guarantee(res_idx != NULL, "checking"); + guarantee(start_idx <= (max_length() + 1), "checking"); + + uint num_regions = 0; + + uint cur = start_idx; + while (cur < max_length() && is_available(cur)) { + cur++; + } + if (cur == max_length()) { + return num_regions; + } + *res_idx = cur; + while (cur < max_length() && !is_available(cur)) { + cur++; + } + num_regions = cur - *res_idx; +#ifdef ASSERT + for (uint i = *res_idx; i < (*res_idx + num_regions); i++) { + assert(!is_available(i), "just checking"); + } + assert(cur == max_length() || num_regions == 0 || is_available(cur), + err_msg("The region at the current position %u must be available or at the end of the heap.", cur)); +#endif + return num_regions; +} + +uint HeapRegionManager::start_region_for_worker(uint worker_i, uint num_workers, uint num_regions) const { + return num_regions * worker_i / num_workers; +} + +void HeapRegionManager::par_iterate(HeapRegionClosure* blk, uint worker_id, uint num_workers, jint claim_value) const { + const uint start_index = start_region_for_worker(worker_id, num_workers, _allocated_heapregions_length); + + // Every worker will actually look at all regions, skipping over regions that + // are currently not committed. + // This also (potentially) iterates over regions newly allocated during GC. This + // is no problem except for some extra work. + for (uint count = 0; count < _allocated_heapregions_length; count++) { + const uint index = (start_index + count) % _allocated_heapregions_length; + assert(0 <= index && index < _allocated_heapregions_length, "sanity"); + // Skip over unavailable regions + if (!is_available(index)) { + continue; + } + HeapRegion* r = _regions.get_by_index(index); + // We'll ignore "continues humongous" regions (we'll process them + // when we come across their corresponding "start humongous" + // region) and regions already claimed. + if (r->claim_value() == claim_value || r->continuesHumongous()) { + continue; + } + // OK, try to claim it + if (!r->claimHeapRegion(claim_value)) { + continue; + } + // Success! + if (r->startsHumongous()) { + // If the region is "starts humongous" we'll iterate over its + // "continues humongous" first; in fact we'll do them + // first. The order is important. In one case, calling the + // closure on the "starts humongous" region might de-allocate + // and clear all its "continues humongous" regions and, as a + // result, we might end up processing them twice. So, we'll do + // them first (note: most closures will ignore them anyway) and + // then we'll do the "starts humongous" region. + for (uint ch_index = index + 1; ch_index < index + r->region_num(); ch_index++) { + HeapRegion* chr = _regions.get_by_index(ch_index); + + assert(chr->continuesHumongous(), "Must be humongous region"); + assert(chr->humongous_start_region() == r, + err_msg("Must work on humongous continuation of the original start region " + PTR_FORMAT ", but is " PTR_FORMAT, p2i(r), p2i(chr))); + assert(chr->claim_value() != claim_value, + "Must not have been claimed yet because claiming of humongous continuation first claims the start region"); + + bool claim_result = chr->claimHeapRegion(claim_value); + // We should always be able to claim it; no one else should + // be trying to claim this region. + guarantee(claim_result, "We should always be able to claim the continuesHumongous part of the humongous object"); + + bool res2 = blk->doHeapRegion(chr); + if (res2) { + return; + } + + // Right now, this holds (i.e., no closure that actually + // does something with "continues humongous" regions + // clears them). We might have to weaken it in the future, + // but let's leave these two asserts here for extra safety. + assert(chr->continuesHumongous(), "should still be the case"); + assert(chr->humongous_start_region() == r, "sanity"); + } + } + + bool res = blk->doHeapRegion(r); + if (res) { + return; + } + } +} + +uint HeapRegionManager::shrink_by(uint num_regions_to_remove) { + assert(length() > 0, "the region sequence should not be empty"); + assert(length() <= _allocated_heapregions_length, "invariant"); + assert(_allocated_heapregions_length > 0, "we should have at least one region committed"); + assert(num_regions_to_remove < length(), "We should never remove all regions"); + + if (num_regions_to_remove == 0) { + return 0; + } + + uint removed = 0; + uint cur = _allocated_heapregions_length - 1; + uint idx_last_found = 0; + uint num_last_found = 0; + + while ((removed < num_regions_to_remove) && + (num_last_found = find_empty_from_idx_reverse(cur, &idx_last_found)) > 0) { + uint to_remove = MIN2(num_regions_to_remove - removed, num_last_found); + + uncommit_regions(idx_last_found + num_last_found - to_remove, to_remove); + + cur -= num_last_found; + removed += to_remove; + } + + verify_optional(); + + return removed; +} + +uint HeapRegionManager::find_empty_from_idx_reverse(uint start_idx, uint* res_idx) const { + guarantee(start_idx < _allocated_heapregions_length, "checking"); + guarantee(res_idx != NULL, "checking"); + + uint num_regions_found = 0; + + jlong cur = start_idx; + while (cur != -1 && !(is_available(cur) && at(cur)->is_empty())) { + cur--; + } + if (cur == -1) { + return num_regions_found; + } + jlong old_cur = cur; + // cur indexes the first empty region + while (cur != -1 && is_available(cur) && at(cur)->is_empty()) { + cur--; + } + *res_idx = cur + 1; + num_regions_found = old_cur - cur; + +#ifdef ASSERT + for (uint i = *res_idx; i < (*res_idx + num_regions_found); i++) { + assert(at(i)->is_empty(), "just checking"); + } +#endif + return num_regions_found; +} + +void HeapRegionManager::verify() { + guarantee(length() <= _allocated_heapregions_length, + err_msg("invariant: _length: %u _allocated_length: %u", + length(), _allocated_heapregions_length)); + guarantee(_allocated_heapregions_length <= max_length(), + err_msg("invariant: _allocated_length: %u _max_length: %u", + _allocated_heapregions_length, max_length())); + + bool prev_committed = true; + uint num_committed = 0; + HeapWord* prev_end = heap_bottom(); + for (uint i = 0; i < _allocated_heapregions_length; i++) { + if (!is_available(i)) { + prev_committed = false; + continue; + } + num_committed++; + HeapRegion* hr = _regions.get_by_index(i); + guarantee(hr != NULL, err_msg("invariant: i: %u", i)); + guarantee(!prev_committed || hr->bottom() == prev_end, + err_msg("invariant i: %u "HR_FORMAT" prev_end: "PTR_FORMAT, + i, HR_FORMAT_PARAMS(hr), p2i(prev_end))); + guarantee(hr->hrm_index() == i, + err_msg("invariant: i: %u hrm_index(): %u", i, hr->hrm_index())); + // Asserts will fire if i is >= _length + HeapWord* addr = hr->bottom(); + guarantee(addr_to_region(addr) == hr, "sanity"); + // We cannot check whether the region is part of a particular set: at the time + // this method may be called, we have only completed allocation of the regions, + // but not put into a region set. + prev_committed = true; + if (hr->startsHumongous()) { + prev_end = hr->orig_end(); + } else { + prev_end = hr->end(); + } + } + for (uint i = _allocated_heapregions_length; i < max_length(); i++) { + guarantee(_regions.get_by_index(i) == NULL, err_msg("invariant i: %u", i)); + } + + guarantee(num_committed == _num_committed, err_msg("Found %u committed regions, but should be %u", num_committed, _num_committed)); + _free_list.verify(); +} + +#ifndef PRODUCT +void HeapRegionManager::verify_optional() { + verify(); +} +#endif // PRODUCT +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hotspot/src/share/vm/gc_implementation/g1/heapRegionManager.hpp Wed Jul 05 19:59:54 2017 +0200 @@ -0,0 +1,239 @@ +/* + * 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 + * 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. + * + */ + +#ifndef SHARE_VM_GC_IMPLEMENTATION_G1_HEAPREGIONMANAGER_HPP +#define SHARE_VM_GC_IMPLEMENTATION_G1_HEAPREGIONMANAGER_HPP + +#include "gc_implementation/g1/g1BiasedArray.hpp" +#include "gc_implementation/g1/g1RegionToSpaceMapper.hpp" +#include "gc_implementation/g1/heapRegionSet.hpp" + +class HeapRegion; +class HeapRegionClosure; +class FreeRegionList; + +class G1HeapRegionTable : public G1BiasedMappedArray<HeapRegion*> { + protected: + virtual HeapRegion* default_value() const { return NULL; } +}; + +// This class keeps track of the actual heap memory, auxiliary data +// and its metadata (i.e., HeapRegion instances) and the list of free regions. +// +// This allows maximum flexibility for deciding what to commit or uncommit given +// a request from outside. +// +// HeapRegions are kept in the _regions array in address order. A region's +// index in the array corresponds to its index in the heap (i.e., 0 is the +// region at the bottom of the heap, 1 is the one after it, etc.). Two +// regions that are consecutive in the array should also be adjacent in the +// address space (i.e., region(i).end() == region(i+1).bottom(). +// +// We create a HeapRegion when we commit the region's address space +// for the first time. When we uncommit the address space of a +// region we retain the HeapRegion to be able to re-use it in the +// future (in case we recommit it). +// +// We keep track of three lengths: +// +// * _num_committed (returned by length()) is the number of currently +// committed regions. These may not be contiguous. +// * _allocated_heapregions_length (not exposed outside this class) is the +// number of regions+1 for which we have HeapRegions. +// * max_length() returns the maximum number of regions the heap can have. +// + +class HeapRegionManager: public CHeapObj<mtGC> { + friend class VMStructs; + + G1HeapRegionTable _regions; + + G1RegionToSpaceMapper* _heap_mapper; + G1RegionToSpaceMapper* _prev_bitmap_mapper; + G1RegionToSpaceMapper* _next_bitmap_mapper; + G1RegionToSpaceMapper* _bot_mapper; + G1RegionToSpaceMapper* _cardtable_mapper; + G1RegionToSpaceMapper* _card_counts_mapper; + + FreeRegionList _free_list; + + // Each bit in this bitmap indicates that the corresponding region is available + // for allocation. + BitMap _available_map; + + // The number of regions committed in the heap. + uint _num_committed; + + // Internal only. The highest heap region +1 we allocated a HeapRegion instance for. + uint _allocated_heapregions_length; + + HeapWord* heap_bottom() const { return _regions.bottom_address_mapped(); } + HeapWord* heap_end() const {return _regions.end_address_mapped(); } + + void make_regions_available(uint index, uint num_regions = 1); + + // Pass down commit calls to the VirtualSpace. + void commit_regions(uint index, size_t num_regions = 1); + void uncommit_regions(uint index, size_t num_regions = 1); + + // Notify other data structures about change in the heap layout. + void update_committed_space(HeapWord* old_end, HeapWord* new_end); + // Calculate the starting region for each worker during parallel iteration so + // that they do not all start from the same region. + uint start_region_for_worker(uint worker_i, uint num_workers, uint num_regions) const; + + // Find a contiguous set of empty or uncommitted regions of length num and return + // the index of the first region or G1_NO_HRM_INDEX if the search was unsuccessful. + // If only_empty is true, only empty regions are considered. + // Searches from bottom to top of the heap, doing a first-fit. + uint find_contiguous(size_t num, bool only_empty); + // Finds the next sequence of unavailable regions starting from start_idx. Returns the + // length of the sequence found. If this result is zero, no such sequence could be found, + // otherwise res_idx indicates the start index of these regions. + uint find_unavailable_from_idx(uint start_idx, uint* res_idx) const; + // Finds the next sequence of empty regions starting from start_idx, going backwards in + // the heap. Returns the length of the sequence found. If this value is zero, no + // sequence could be found, otherwise res_idx contains the start index of this range. + uint find_empty_from_idx_reverse(uint start_idx, uint* res_idx) const; + // Allocate a new HeapRegion for the given index. + HeapRegion* new_heap_region(uint hrm_index); +#ifdef ASSERT +public: + bool is_free(HeapRegion* hr) const; +#endif + // Returns whether the given region is available for allocation. + bool is_available(uint region) const; + + public: + // Empty constructor, we'll initialize it with the initialize() method. + HeapRegionManager() : _regions(), _heap_mapper(NULL), _num_committed(0), + _next_bitmap_mapper(NULL), _prev_bitmap_mapper(NULL), _bot_mapper(NULL), + _allocated_heapregions_length(0), _available_map(), + _free_list("Free list", new MasterFreeRegionListMtSafeChecker()) + { } + + void initialize(G1RegionToSpaceMapper* heap_storage, + G1RegionToSpaceMapper* prev_bitmap, + G1RegionToSpaceMapper* next_bitmap, + G1RegionToSpaceMapper* bot, + G1RegionToSpaceMapper* cardtable, + G1RegionToSpaceMapper* card_counts); + + // Return the "dummy" region used for G1AllocRegion. This is currently a hardwired + // new HeapRegion that owns HeapRegion at index 0. Since at the moment we commit + // the heap from the lowest address, this region (and its associated data + // structures) are available and we do not need to check further. + HeapRegion* get_dummy_region() { return new_heap_region(0); } + + // Return the HeapRegion at the given index. Assume that the index + // is valid. + inline HeapRegion* at(uint index) const; + + // If addr is within the committed space return its corresponding + // HeapRegion, otherwise return NULL. + inline HeapRegion* addr_to_region(HeapWord* addr) const; + + // Insert the given region into the free region list. + inline void insert_into_free_list(HeapRegion* hr); + + // Insert the given region list into the global free region list. + void insert_list_into_free_list(FreeRegionList* list) { + _free_list.add_ordered(list); + } + + HeapRegion* allocate_free_region(bool is_old) { + HeapRegion* hr = _free_list.remove_region(is_old); + + if (hr != NULL) { + assert(hr->next() == NULL, "Single region should not have next"); + assert(is_available(hr->hrm_index()), "Must be committed"); + } + return hr; + } + + inline void allocate_free_regions_starting_at(uint first, uint num_regions); + + // Remove all regions from the free list. + void remove_all_free_regions() { + _free_list.remove_all(); + } + + // Return the number of committed free regions in the heap. + uint num_free_regions() const { + return _free_list.length(); + } + + size_t total_capacity_bytes() const { + return num_free_regions() * HeapRegion::GrainBytes; + } + + // Return the number of available (uncommitted) regions. + uint available() const { return max_length() - length(); } + + // Return the number of regions that have been committed in the heap. + uint length() const { return _num_committed; } + + // Return the maximum number of regions in the heap. + uint max_length() const { return (uint)_regions.length(); } + + MemRegion reserved() const { return MemRegion(heap_bottom(), heap_end()); } + + // Expand the sequence to reflect that the heap has grown. Either create new + // HeapRegions, or re-use existing ones. Returns the number of regions the + // sequence was expanded by. If a HeapRegion allocation fails, the resulting + // number of regions might be smaller than what's desired. + uint expand_by(uint num_regions); + + // Makes sure that the regions from start to start+num_regions-1 are available + // for allocation. Returns the number of regions that were committed to achieve + // this. + uint expand_at(uint start, uint num_regions); + + // Find a contiguous set of empty regions of length num. Returns the start index of + // that set, or G1_NO_HRM_INDEX. + uint find_contiguous_only_empty(size_t num) { return find_contiguous(num, true); } + // Find a contiguous set of empty or unavailable regions of length num. Returns the + // start index of that set, or G1_NO_HRM_INDEX. + uint find_contiguous_empty_or_unavailable(size_t num) { return find_contiguous(num, false); } + + HeapRegion* next_region_in_heap(const HeapRegion* r) const; + + // Apply blk->doHeapRegion() on all committed regions in address order, + // terminating the iteration early if doHeapRegion() returns true. + void iterate(HeapRegionClosure* blk) const; + + void par_iterate(HeapRegionClosure* blk, uint worker_id, uint no_of_par_workers, jint claim_value) const; + + // Uncommit up to num_regions_to_remove regions that are completely free. + // Return the actual number of uncommitted regions. + uint shrink_by(uint num_regions_to_remove); + + void verify(); + + // Do some sanity checking. + void verify_optional() PRODUCT_RETURN; +}; + +#endif // SHARE_VM_GC_IMPLEMENTATION_G1_HEAPREGIONMANAGER_HPP +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hotspot/src/share/vm/gc_implementation/g1/heapRegionManager.inline.hpp Wed Jul 05 19:59:54 2017 +0200 @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2001, 2014, 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. + * + */ + +#ifndef SHARE_VM_GC_IMPLEMENTATION_G1_HEAPREGIONMANAGER_INLINE_HPP +#define SHARE_VM_GC_IMPLEMENTATION_G1_HEAPREGIONMANAGER_INLINE_HPP + +#include "gc_implementation/g1/heapRegion.hpp" +#include "gc_implementation/g1/heapRegionManager.hpp" +#include "gc_implementation/g1/heapRegionSet.inline.hpp" + +inline HeapRegion* HeapRegionManager::addr_to_region(HeapWord* addr) const { + assert(addr < heap_end(), + err_msg("addr: "PTR_FORMAT" end: "PTR_FORMAT, p2i(addr), p2i(heap_end()))); + assert(addr >= heap_bottom(), + err_msg("addr: "PTR_FORMAT" bottom: "PTR_FORMAT, p2i(addr), p2i(heap_bottom()))); + + HeapRegion* hr = _regions.get_by_address(addr); + return hr; +} + +inline HeapRegion* HeapRegionManager::at(uint index) const { + assert(is_available(index), "pre-condition"); + HeapRegion* hr = _regions.get_by_index(index); + assert(hr != NULL, "sanity"); + assert(hr->hrm_index() == index, "sanity"); + return hr; +} + +inline void HeapRegionManager::insert_into_free_list(HeapRegion* hr) { + _free_list.add_ordered(hr); +} + +inline void HeapRegionManager::allocate_free_regions_starting_at(uint first, uint num_regions) { + _free_list.remove_starting_at(at(first), num_regions); +} + +#endif // SHARE_VM_GC_IMPLEMENTATION_G1_HEAPREGIONMANAGER_INLINE_HPP
--- a/hotspot/src/share/vm/gc_implementation/g1/heapRegionRemSet.cpp Thu Sep 04 13:26:26 2014 -0700 +++ b/hotspot/src/share/vm/gc_implementation/g1/heapRegionRemSet.cpp Wed Jul 05 19:59:54 2017 +0200 @@ -27,7 +27,7 @@ #include "gc_implementation/g1/g1BlockOffsetTable.inline.hpp" #include "gc_implementation/g1/g1CollectedHeap.inline.hpp" #include "gc_implementation/g1/heapRegionRemSet.hpp" -#include "gc_implementation/g1/heapRegionSeq.inline.hpp" +#include "gc_implementation/g1/heapRegionManager.inline.hpp" #include "memory/allocation.hpp" #include "memory/padded.inline.hpp" #include "memory/space.inline.hpp" @@ -420,7 +420,7 @@ } void OtherRegionsTable::add_reference(OopOrNarrowOopStar from, int tid) { - uint cur_hrs_ind = hr()->hrs_index(); + uint cur_hrm_ind = hr()->hrm_index(); if (G1TraceHeapRegionRememberedSet) { gclog_or_tty->print_cr("ORT::add_reference_work(" PTR_FORMAT "->" PTR_FORMAT ").", @@ -435,10 +435,10 @@ if (G1TraceHeapRegionRememberedSet) { gclog_or_tty->print_cr("Table for [" PTR_FORMAT "...): card %d (cache = %d)", hr()->bottom(), from_card, - FromCardCache::at((uint)tid, cur_hrs_ind)); + FromCardCache::at((uint)tid, cur_hrm_ind)); } - if (FromCardCache::contains_or_replace((uint)tid, cur_hrs_ind, from_card)) { + if (FromCardCache::contains_or_replace((uint)tid, cur_hrm_ind, from_card)) { if (G1TraceHeapRegionRememberedSet) { gclog_or_tty->print_cr(" from-card cache hit."); } @@ -448,7 +448,7 @@ // Note that this may be a continued H region. HeapRegion* from_hr = _g1h->heap_region_containing_raw(from); - RegionIdx_t from_hrs_ind = (RegionIdx_t) from_hr->hrs_index(); + RegionIdx_t from_hrs_ind = (RegionIdx_t) from_hr->hrm_index(); // If the region is already coarsened, return. if (_coarse_map.at(from_hrs_ind)) { @@ -495,7 +495,7 @@ if (G1TraceHeapRegionRememberedSet) { gclog_or_tty->print_cr(" [tid %d] sparse table entry " "overflow(f: %d, t: %u)", - tid, from_hrs_ind, cur_hrs_ind); + tid, from_hrs_ind, cur_hrm_ind); } } @@ -607,9 +607,9 @@ guarantee(max != NULL, "Since _n_fine_entries > 0"); // Set the corresponding coarse bit. - size_t max_hrs_index = (size_t) max->hr()->hrs_index(); - if (!_coarse_map.at(max_hrs_index)) { - _coarse_map.at_put(max_hrs_index, true); + size_t max_hrm_index = (size_t) max->hr()->hrm_index(); + if (!_coarse_map.at(max_hrm_index)) { + _coarse_map.at_put(max_hrm_index, true); _n_coarse_entries++; if (G1TraceHeapRegionRememberedSet) { gclog_or_tty->print("Coarsened entry in region [" PTR_FORMAT "...] " @@ -633,7 +633,7 @@ BitMap* region_bm, BitMap* card_bm) { // First eliminated garbage regions from the coarse map. if (G1RSScrubVerbose) { - gclog_or_tty->print_cr("Scrubbing region %u:", hr()->hrs_index()); + gclog_or_tty->print_cr("Scrubbing region %u:", hr()->hrm_index()); } assert(_coarse_map.size() == region_bm->size(), "Precondition"); @@ -656,9 +656,9 @@ // If the entire region is dead, eliminate. if (G1RSScrubVerbose) { gclog_or_tty->print_cr(" For other region %u:", - cur->hr()->hrs_index()); + cur->hr()->hrm_index()); } - if (!region_bm->at((size_t) cur->hr()->hrs_index())) { + if (!region_bm->at((size_t) cur->hr()->hrm_index())) { *prev = nxt; cur->set_collision_list_next(NULL); _n_fine_entries--; @@ -752,7 +752,7 @@ } void OtherRegionsTable::clear_fcc() { - FromCardCache::clear(hr()->hrs_index()); + FromCardCache::clear(hr()->hrm_index()); } void OtherRegionsTable::clear() { @@ -803,7 +803,7 @@ bool OtherRegionsTable::contains_reference_locked(OopOrNarrowOopStar from) const { HeapRegion* hr = _g1h->heap_region_containing_raw(from); - RegionIdx_t hr_ind = (RegionIdx_t) hr->hrs_index(); + RegionIdx_t hr_ind = (RegionIdx_t) hr->hrm_index(); // Is this region in the coarse map? if (_coarse_map.at(hr_ind)) return true; @@ -840,7 +840,7 @@ HeapRegionRemSet::HeapRegionRemSet(G1BlockOffsetSharedArray* bosa, HeapRegion* hr) : _bosa(bosa), - _m(Mutex::leaf, FormatBuffer<128>("HeapRegionRemSet lock #%u", hr->hrs_index()), true), + _m(Mutex::leaf, FormatBuffer<128>("HeapRegionRemSet lock #%u", hr->hrm_index()), true), _code_roots(), _other_regions(hr, &_m), _iter_state(Unclaimed), _iter_claimed(0) { reset_for_par_iteration(); }
--- a/hotspot/src/share/vm/gc_implementation/g1/heapRegionSeq.cpp Thu Sep 04 13:26:26 2014 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,450 +0,0 @@ -/* - * Copyright (c) 2001, 2014, 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. - * - */ - -#include "precompiled.hpp" -#include "gc_implementation/g1/heapRegion.hpp" -#include "gc_implementation/g1/heapRegionSeq.inline.hpp" -#include "gc_implementation/g1/heapRegionSet.inline.hpp" -#include "gc_implementation/g1/g1CollectedHeap.inline.hpp" -#include "gc_implementation/g1/concurrentG1Refine.hpp" -#include "memory/allocation.hpp" - -void HeapRegionSeq::initialize(G1RegionToSpaceMapper* heap_storage, - G1RegionToSpaceMapper* prev_bitmap, - G1RegionToSpaceMapper* next_bitmap, - G1RegionToSpaceMapper* bot, - G1RegionToSpaceMapper* cardtable, - G1RegionToSpaceMapper* card_counts) { - _allocated_heapregions_length = 0; - - _heap_mapper = heap_storage; - - _prev_bitmap_mapper = prev_bitmap; - _next_bitmap_mapper = next_bitmap; - - _bot_mapper = bot; - _cardtable_mapper = cardtable; - - _card_counts_mapper = card_counts; - - MemRegion reserved = heap_storage->reserved(); - _regions.initialize(reserved.start(), reserved.end(), HeapRegion::GrainBytes); - - _available_map.resize(_regions.length(), false); - _available_map.clear(); -} - -bool HeapRegionSeq::is_available(uint region) const { - return _available_map.at(region); -} - -#ifdef ASSERT -bool HeapRegionSeq::is_free(HeapRegion* hr) const { - return _free_list.contains(hr); -} -#endif - -HeapRegion* HeapRegionSeq::new_heap_region(uint hrs_index) { - HeapWord* bottom = G1CollectedHeap::heap()->bottom_addr_for_region(hrs_index); - MemRegion mr(bottom, bottom + HeapRegion::GrainWords); - assert(reserved().contains(mr), "invariant"); - return new HeapRegion(hrs_index, G1CollectedHeap::heap()->bot_shared(), mr); -} - -void HeapRegionSeq::commit_regions(uint index, size_t num_regions) { - guarantee(num_regions > 0, "Must commit more than zero regions"); - guarantee(_num_committed + num_regions <= max_length(), "Cannot commit more than the maximum amount of regions"); - - _num_committed += (uint)num_regions; - - _heap_mapper->commit_regions(index, num_regions); - - // Also commit auxiliary data - _prev_bitmap_mapper->commit_regions(index, num_regions); - _next_bitmap_mapper->commit_regions(index, num_regions); - - _bot_mapper->commit_regions(index, num_regions); - _cardtable_mapper->commit_regions(index, num_regions); - - _card_counts_mapper->commit_regions(index, num_regions); -} - -void HeapRegionSeq::uncommit_regions(uint start, size_t num_regions) { - guarantee(num_regions >= 1, err_msg("Need to specify at least one region to uncommit, tried to uncommit zero regions at %u", start)); - guarantee(_num_committed >= num_regions, "pre-condition"); - - // Print before uncommitting. - if (G1CollectedHeap::heap()->hr_printer()->is_active()) { - for (uint i = start; i < start + num_regions; i++) { - HeapRegion* hr = at(i); - G1CollectedHeap::heap()->hr_printer()->uncommit(hr->bottom(), hr->end()); - } - } - - _num_committed -= (uint)num_regions; - - _available_map.par_clear_range(start, start + num_regions, BitMap::unknown_range); - _heap_mapper->uncommit_regions(start, num_regions); - - // Also uncommit auxiliary data - _prev_bitmap_mapper->uncommit_regions(start, num_regions); - _next_bitmap_mapper->uncommit_regions(start, num_regions); - - _bot_mapper->uncommit_regions(start, num_regions); - _cardtable_mapper->uncommit_regions(start, num_regions); - - _card_counts_mapper->uncommit_regions(start, num_regions); -} - -void HeapRegionSeq::make_regions_available(uint start, uint num_regions) { - guarantee(num_regions > 0, "No point in calling this for zero regions"); - commit_regions(start, num_regions); - for (uint i = start; i < start + num_regions; i++) { - if (_regions.get_by_index(i) == NULL) { - HeapRegion* new_hr = new_heap_region(i); - _regions.set_by_index(i, new_hr); - _allocated_heapregions_length = MAX2(_allocated_heapregions_length, i + 1); - } - } - - _available_map.par_set_range(start, start + num_regions, BitMap::unknown_range); - - for (uint i = start; i < start + num_regions; i++) { - assert(is_available(i), err_msg("Just made region %u available but is apparently not.", i)); - HeapRegion* hr = at(i); - if (G1CollectedHeap::heap()->hr_printer()->is_active()) { - G1CollectedHeap::heap()->hr_printer()->commit(hr->bottom(), hr->end()); - } - HeapWord* bottom = G1CollectedHeap::heap()->bottom_addr_for_region(i); - MemRegion mr(bottom, bottom + HeapRegion::GrainWords); - - hr->initialize(mr); - insert_into_free_list(at(i)); - } -} - -uint HeapRegionSeq::expand_by(uint num_regions) { - return expand_at(0, num_regions); -} - -uint HeapRegionSeq::expand_at(uint start, uint num_regions) { - if (num_regions == 0) { - return 0; - } - - uint cur = start; - uint idx_last_found = 0; - uint num_last_found = 0; - - uint expanded = 0; - - while (expanded < num_regions && - (num_last_found = find_unavailable_from_idx(cur, &idx_last_found)) > 0) { - uint to_expand = MIN2(num_regions - expanded, num_last_found); - make_regions_available(idx_last_found, to_expand); - expanded += to_expand; - cur = idx_last_found + num_last_found + 1; - } - - verify_optional(); - return expanded; -} - -uint HeapRegionSeq::find_contiguous(size_t num, bool empty_only) { - uint found = 0; - size_t length_found = 0; - uint cur = 0; - - while (length_found < num && cur < max_length()) { - HeapRegion* hr = _regions.get_by_index(cur); - if ((!empty_only && !is_available(cur)) || (is_available(cur) && hr != NULL && hr->is_empty())) { - // This region is a potential candidate for allocation into. - length_found++; - } else { - // This region is not a candidate. The next region is the next possible one. - found = cur + 1; - length_found = 0; - } - cur++; - } - - if (length_found == num) { - for (uint i = found; i < (found + num); i++) { - HeapRegion* hr = _regions.get_by_index(i); - // sanity check - guarantee((!empty_only && !is_available(i)) || (is_available(i) && hr != NULL && hr->is_empty()), - err_msg("Found region sequence starting at " UINT32_FORMAT ", length " SIZE_FORMAT - " that is not empty at " UINT32_FORMAT ". Hr is " PTR_FORMAT, found, num, i, p2i(hr))); - } - return found; - } else { - return G1_NO_HRS_INDEX; - } -} - -HeapRegion* HeapRegionSeq::next_region_in_heap(const HeapRegion* r) const { - guarantee(r != NULL, "Start region must be a valid region"); - guarantee(is_available(r->hrs_index()), err_msg("Trying to iterate starting from region %u which is not in the heap", r->hrs_index())); - for (uint i = r->hrs_index() + 1; i < _allocated_heapregions_length; i++) { - HeapRegion* hr = _regions.get_by_index(i); - if (is_available(i)) { - return hr; - } - } - return NULL; -} - -void HeapRegionSeq::iterate(HeapRegionClosure* blk) const { - uint len = max_length(); - - for (uint i = 0; i < len; i++) { - if (!is_available(i)) { - continue; - } - guarantee(at(i) != NULL, err_msg("Tried to access region %u that has a NULL HeapRegion*", i)); - bool res = blk->doHeapRegion(at(i)); - if (res) { - blk->incomplete(); - return; - } - } -} - -uint HeapRegionSeq::find_unavailable_from_idx(uint start_idx, uint* res_idx) const { - guarantee(res_idx != NULL, "checking"); - guarantee(start_idx <= (max_length() + 1), "checking"); - - uint num_regions = 0; - - uint cur = start_idx; - while (cur < max_length() && is_available(cur)) { - cur++; - } - if (cur == max_length()) { - return num_regions; - } - *res_idx = cur; - while (cur < max_length() && !is_available(cur)) { - cur++; - } - num_regions = cur - *res_idx; -#ifdef ASSERT - for (uint i = *res_idx; i < (*res_idx + num_regions); i++) { - assert(!is_available(i), "just checking"); - } - assert(cur == max_length() || num_regions == 0 || is_available(cur), - err_msg("The region at the current position %u must be available or at the end of the heap.", cur)); -#endif - return num_regions; -} - -uint HeapRegionSeq::start_region_for_worker(uint worker_i, uint num_workers, uint num_regions) const { - return num_regions * worker_i / num_workers; -} - -void HeapRegionSeq::par_iterate(HeapRegionClosure* blk, uint worker_id, uint num_workers, jint claim_value) const { - const uint start_index = start_region_for_worker(worker_id, num_workers, _allocated_heapregions_length); - - // Every worker will actually look at all regions, skipping over regions that - // are currently not committed. - // This also (potentially) iterates over regions newly allocated during GC. This - // is no problem except for some extra work. - for (uint count = 0; count < _allocated_heapregions_length; count++) { - const uint index = (start_index + count) % _allocated_heapregions_length; - assert(0 <= index && index < _allocated_heapregions_length, "sanity"); - // Skip over unavailable regions - if (!is_available(index)) { - continue; - } - HeapRegion* r = _regions.get_by_index(index); - // We'll ignore "continues humongous" regions (we'll process them - // when we come across their corresponding "start humongous" - // region) and regions already claimed. - if (r->claim_value() == claim_value || r->continuesHumongous()) { - continue; - } - // OK, try to claim it - if (!r->claimHeapRegion(claim_value)) { - continue; - } - // Success! - if (r->startsHumongous()) { - // If the region is "starts humongous" we'll iterate over its - // "continues humongous" first; in fact we'll do them - // first. The order is important. In one case, calling the - // closure on the "starts humongous" region might de-allocate - // and clear all its "continues humongous" regions and, as a - // result, we might end up processing them twice. So, we'll do - // them first (note: most closures will ignore them anyway) and - // then we'll do the "starts humongous" region. - for (uint ch_index = index + 1; ch_index < index + r->region_num(); ch_index++) { - HeapRegion* chr = _regions.get_by_index(ch_index); - - assert(chr->continuesHumongous(), "Must be humongous region"); - assert(chr->humongous_start_region() == r, - err_msg("Must work on humongous continuation of the original start region " - PTR_FORMAT ", but is " PTR_FORMAT, p2i(r), p2i(chr))); - assert(chr->claim_value() != claim_value, - "Must not have been claimed yet because claiming of humongous continuation first claims the start region"); - - bool claim_result = chr->claimHeapRegion(claim_value); - // We should always be able to claim it; no one else should - // be trying to claim this region. - guarantee(claim_result, "We should always be able to claim the continuesHumongous part of the humongous object"); - - bool res2 = blk->doHeapRegion(chr); - if (res2) { - return; - } - - // Right now, this holds (i.e., no closure that actually - // does something with "continues humongous" regions - // clears them). We might have to weaken it in the future, - // but let's leave these two asserts here for extra safety. - assert(chr->continuesHumongous(), "should still be the case"); - assert(chr->humongous_start_region() == r, "sanity"); - } - } - - bool res = blk->doHeapRegion(r); - if (res) { - return; - } - } -} - -uint HeapRegionSeq::shrink_by(uint num_regions_to_remove) { - assert(length() > 0, "the region sequence should not be empty"); - assert(length() <= _allocated_heapregions_length, "invariant"); - assert(_allocated_heapregions_length > 0, "we should have at least one region committed"); - assert(num_regions_to_remove < length(), "We should never remove all regions"); - - if (num_regions_to_remove == 0) { - return 0; - } - - uint removed = 0; - uint cur = _allocated_heapregions_length - 1; - uint idx_last_found = 0; - uint num_last_found = 0; - - while ((removed < num_regions_to_remove) && - (num_last_found = find_empty_from_idx_reverse(cur, &idx_last_found)) > 0) { - // Only allow uncommit from the end of the heap. - if ((idx_last_found + num_last_found) != _allocated_heapregions_length) { - return 0; - } - uint to_remove = MIN2(num_regions_to_remove - removed, num_last_found); - - uncommit_regions(idx_last_found + num_last_found - to_remove, to_remove); - - cur -= num_last_found; - removed += to_remove; - } - - verify_optional(); - - return removed; -} - -uint HeapRegionSeq::find_empty_from_idx_reverse(uint start_idx, uint* res_idx) const { - guarantee(start_idx < _allocated_heapregions_length, "checking"); - guarantee(res_idx != NULL, "checking"); - - uint num_regions_found = 0; - - jlong cur = start_idx; - while (cur != -1 && !(is_available(cur) && at(cur)->is_empty())) { - cur--; - } - if (cur == -1) { - return num_regions_found; - } - jlong old_cur = cur; - // cur indexes the first empty region - while (cur != -1 && is_available(cur) && at(cur)->is_empty()) { - cur--; - } - *res_idx = cur + 1; - num_regions_found = old_cur - cur; - -#ifdef ASSERT - for (uint i = *res_idx; i < (*res_idx + num_regions_found); i++) { - assert(at(i)->is_empty(), "just checking"); - } -#endif - return num_regions_found; -} - -void HeapRegionSeq::verify() { - guarantee(length() <= _allocated_heapregions_length, - err_msg("invariant: _length: %u _allocated_length: %u", - length(), _allocated_heapregions_length)); - guarantee(_allocated_heapregions_length <= max_length(), - err_msg("invariant: _allocated_length: %u _max_length: %u", - _allocated_heapregions_length, max_length())); - - bool prev_committed = true; - uint num_committed = 0; - HeapWord* prev_end = heap_bottom(); - for (uint i = 0; i < _allocated_heapregions_length; i++) { - if (!is_available(i)) { - prev_committed = false; - continue; - } - num_committed++; - HeapRegion* hr = _regions.get_by_index(i); - guarantee(hr != NULL, err_msg("invariant: i: %u", i)); - guarantee(!prev_committed || hr->bottom() == prev_end, - err_msg("invariant i: %u "HR_FORMAT" prev_end: "PTR_FORMAT, - i, HR_FORMAT_PARAMS(hr), p2i(prev_end))); - guarantee(hr->hrs_index() == i, - err_msg("invariant: i: %u hrs_index(): %u", i, hr->hrs_index())); - // Asserts will fire if i is >= _length - HeapWord* addr = hr->bottom(); - guarantee(addr_to_region(addr) == hr, "sanity"); - // We cannot check whether the region is part of a particular set: at the time - // this method may be called, we have only completed allocation of the regions, - // but not put into a region set. - prev_committed = true; - if (hr->startsHumongous()) { - prev_end = hr->orig_end(); - } else { - prev_end = hr->end(); - } - } - for (uint i = _allocated_heapregions_length; i < max_length(); i++) { - guarantee(_regions.get_by_index(i) == NULL, err_msg("invariant i: %u", i)); - } - - guarantee(num_committed == _num_committed, err_msg("Found %u committed regions, but should be %u", num_committed, _num_committed)); - _free_list.verify(); -} - -#ifndef PRODUCT -void HeapRegionSeq::verify_optional() { - verify(); -} -#endif // PRODUCT -
--- a/hotspot/src/share/vm/gc_implementation/g1/heapRegionSeq.hpp Thu Sep 04 13:26:26 2014 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,239 +0,0 @@ -/* - * 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 - * 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. - * - */ - -#ifndef SHARE_VM_GC_IMPLEMENTATION_G1_HEAPREGIONSEQ_HPP -#define SHARE_VM_GC_IMPLEMENTATION_G1_HEAPREGIONSEQ_HPP - -#include "gc_implementation/g1/g1BiasedArray.hpp" -#include "gc_implementation/g1/g1RegionToSpaceMapper.hpp" -#include "gc_implementation/g1/heapRegionSet.hpp" - -class HeapRegion; -class HeapRegionClosure; -class FreeRegionList; - -class G1HeapRegionTable : public G1BiasedMappedArray<HeapRegion*> { - protected: - virtual HeapRegion* default_value() const { return NULL; } -}; - -// This class keeps track of the actual heap memory, auxiliary data -// and its metadata (i.e., HeapRegion instances) and the list of free regions. -// -// This allows maximum flexibility for deciding what to commit or uncommit given -// a request from outside. -// -// HeapRegions are kept in the _regions array in address order. A region's -// index in the array corresponds to its index in the heap (i.e., 0 is the -// region at the bottom of the heap, 1 is the one after it, etc.). Two -// regions that are consecutive in the array should also be adjacent in the -// address space (i.e., region(i).end() == region(i+1).bottom(). -// -// We create a HeapRegion when we commit the region's address space -// for the first time. When we uncommit the address space of a -// region we retain the HeapRegion to be able to re-use it in the -// future (in case we recommit it). -// -// We keep track of three lengths: -// -// * _num_committed (returned by length()) is the number of currently -// committed regions. These may not be contiguous. -// * _allocated_heapregions_length (not exposed outside this class) is the -// number of regions+1 for which we have HeapRegions. -// * max_length() returns the maximum number of regions the heap can have. -// - -class HeapRegionSeq: public CHeapObj<mtGC> { - friend class VMStructs; - - G1HeapRegionTable _regions; - - G1RegionToSpaceMapper* _heap_mapper; - G1RegionToSpaceMapper* _prev_bitmap_mapper; - G1RegionToSpaceMapper* _next_bitmap_mapper; - G1RegionToSpaceMapper* _bot_mapper; - G1RegionToSpaceMapper* _cardtable_mapper; - G1RegionToSpaceMapper* _card_counts_mapper; - - FreeRegionList _free_list; - - // Each bit in this bitmap indicates that the corresponding region is available - // for allocation. - BitMap _available_map; - - // The number of regions committed in the heap. - uint _num_committed; - - // Internal only. The highest heap region +1 we allocated a HeapRegion instance for. - uint _allocated_heapregions_length; - - HeapWord* heap_bottom() const { return _regions.bottom_address_mapped(); } - HeapWord* heap_end() const {return _regions.end_address_mapped(); } - - void make_regions_available(uint index, uint num_regions = 1); - - // Pass down commit calls to the VirtualSpace. - void commit_regions(uint index, size_t num_regions = 1); - void uncommit_regions(uint index, size_t num_regions = 1); - - // Notify other data structures about change in the heap layout. - void update_committed_space(HeapWord* old_end, HeapWord* new_end); - // Calculate the starting region for each worker during parallel iteration so - // that they do not all start from the same region. - uint start_region_for_worker(uint worker_i, uint num_workers, uint num_regions) const; - - // Find a contiguous set of empty or uncommitted regions of length num and return - // the index of the first region or G1_NO_HRS_INDEX if the search was unsuccessful. - // If only_empty is true, only empty regions are considered. - // Searches from bottom to top of the heap, doing a first-fit. - uint find_contiguous(size_t num, bool only_empty); - // Finds the next sequence of unavailable regions starting from start_idx. Returns the - // length of the sequence found. If this result is zero, no such sequence could be found, - // otherwise res_idx indicates the start index of these regions. - uint find_unavailable_from_idx(uint start_idx, uint* res_idx) const; - // Finds the next sequence of empty regions starting from start_idx, going backwards in - // the heap. Returns the length of the sequence found. If this value is zero, no - // sequence could be found, otherwise res_idx contains the start index of this range. - uint find_empty_from_idx_reverse(uint start_idx, uint* res_idx) const; - // Allocate a new HeapRegion for the given index. - HeapRegion* new_heap_region(uint hrs_index); -#ifdef ASSERT -public: - bool is_free(HeapRegion* hr) const; -#endif - // Returns whether the given region is available for allocation. - bool is_available(uint region) const; - - public: - // Empty constructor, we'll initialize it with the initialize() method. - HeapRegionSeq() : _regions(), _heap_mapper(NULL), _num_committed(0), - _next_bitmap_mapper(NULL), _prev_bitmap_mapper(NULL), _bot_mapper(NULL), - _allocated_heapregions_length(0), _available_map(), - _free_list("Free list", new MasterFreeRegionListMtSafeChecker()) - { } - - void initialize(G1RegionToSpaceMapper* heap_storage, - G1RegionToSpaceMapper* prev_bitmap, - G1RegionToSpaceMapper* next_bitmap, - G1RegionToSpaceMapper* bot, - G1RegionToSpaceMapper* cardtable, - G1RegionToSpaceMapper* card_counts); - - // Return the "dummy" region used for G1AllocRegion. This is currently a hardwired - // new HeapRegion that owns HeapRegion at index 0. Since at the moment we commit - // the heap from the lowest address, this region (and its associated data - // structures) are available and we do not need to check further. - HeapRegion* get_dummy_region() { return new_heap_region(0); } - - // Return the HeapRegion at the given index. Assume that the index - // is valid. - inline HeapRegion* at(uint index) const; - - // If addr is within the committed space return its corresponding - // HeapRegion, otherwise return NULL. - inline HeapRegion* addr_to_region(HeapWord* addr) const; - - // Insert the given region into the free region list. - inline void insert_into_free_list(HeapRegion* hr); - - // Insert the given region list into the global free region list. - void insert_list_into_free_list(FreeRegionList* list) { - _free_list.add_ordered(list); - } - - HeapRegion* allocate_free_region(bool is_old) { - HeapRegion* hr = _free_list.remove_region(is_old); - - if (hr != NULL) { - assert(hr->next() == NULL, "Single region should not have next"); - assert(is_available(hr->hrs_index()), "Must be committed"); - } - return hr; - } - - inline void allocate_free_regions_starting_at(uint first, uint num_regions); - - // Remove all regions from the free list. - void remove_all_free_regions() { - _free_list.remove_all(); - } - - // Return the number of committed free regions in the heap. - uint num_free_regions() const { - return _free_list.length(); - } - - size_t total_capacity_bytes() const { - return num_free_regions() * HeapRegion::GrainBytes; - } - - // Return the number of available (uncommitted) regions. - uint available() const { return max_length() - length(); } - - // Return the number of regions that have been committed in the heap. - uint length() const { return _num_committed; } - - // Return the maximum number of regions in the heap. - uint max_length() const { return (uint)_regions.length(); } - - MemRegion reserved() const { return MemRegion(heap_bottom(), heap_end()); } - - // Expand the sequence to reflect that the heap has grown. Either create new - // HeapRegions, or re-use existing ones. Returns the number of regions the - // sequence was expanded by. If a HeapRegion allocation fails, the resulting - // number of regions might be smaller than what's desired. - uint expand_by(uint num_regions); - - // Makes sure that the regions from start to start+num_regions-1 are available - // for allocation. Returns the number of regions that were committed to achieve - // this. - uint expand_at(uint start, uint num_regions); - - // Find a contiguous set of empty regions of length num. Returns the start index of - // that set, or G1_NO_HRS_INDEX. - uint find_contiguous_only_empty(size_t num) { return find_contiguous(num, true); } - // Find a contiguous set of empty or unavailable regions of length num. Returns the - // start index of that set, or G1_NO_HRS_INDEX. - uint find_contiguous_empty_or_unavailable(size_t num) { return find_contiguous(num, false); } - - HeapRegion* next_region_in_heap(const HeapRegion* r) const; - - // Apply blk->doHeapRegion() on all committed regions in address order, - // terminating the iteration early if doHeapRegion() returns true. - void iterate(HeapRegionClosure* blk) const; - - void par_iterate(HeapRegionClosure* blk, uint worker_id, uint no_of_par_workers, jint claim_value) const; - - // Uncommit up to num_regions_to_remove regions that are completely free. - // Return the actual number of uncommitted regions. - uint shrink_by(uint num_regions_to_remove); - - void verify(); - - // Do some sanity checking. - void verify_optional() PRODUCT_RETURN; -}; - -#endif // SHARE_VM_GC_IMPLEMENTATION_G1_HEAPREGIONSEQ_HPP -
--- a/hotspot/src/share/vm/gc_implementation/g1/heapRegionSeq.inline.hpp Thu Sep 04 13:26:26 2014 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,58 +0,0 @@ -/* - * Copyright (c) 2001, 2014, 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. - * - */ - -#ifndef SHARE_VM_GC_IMPLEMENTATION_G1_HEAPREGIONSEQ_INLINE_HPP -#define SHARE_VM_GC_IMPLEMENTATION_G1_HEAPREGIONSEQ_INLINE_HPP - -#include "gc_implementation/g1/heapRegion.hpp" -#include "gc_implementation/g1/heapRegionSeq.hpp" -#include "gc_implementation/g1/heapRegionSet.inline.hpp" - -inline HeapRegion* HeapRegionSeq::addr_to_region(HeapWord* addr) const { - assert(addr < heap_end(), - err_msg("addr: "PTR_FORMAT" end: "PTR_FORMAT, p2i(addr), p2i(heap_end()))); - assert(addr >= heap_bottom(), - err_msg("addr: "PTR_FORMAT" bottom: "PTR_FORMAT, p2i(addr), p2i(heap_bottom()))); - - HeapRegion* hr = _regions.get_by_address(addr); - return hr; -} - -inline HeapRegion* HeapRegionSeq::at(uint index) const { - assert(is_available(index), "pre-condition"); - HeapRegion* hr = _regions.get_by_index(index); - assert(hr != NULL, "sanity"); - assert(hr->hrs_index() == index, "sanity"); - return hr; -} - -inline void HeapRegionSeq::insert_into_free_list(HeapRegion* hr) { - _free_list.add_ordered(hr); -} - -inline void HeapRegionSeq::allocate_free_regions_starting_at(uint first, uint num_regions) { - _free_list.remove_starting_at(at(first), num_regions); -} - -#endif // SHARE_VM_GC_IMPLEMENTATION_G1_HEAPREGIONSEQ_INLINE_HPP
--- a/hotspot/src/share/vm/gc_implementation/g1/heapRegionSet.cpp Thu Sep 04 13:26:26 2014 -0700 +++ b/hotspot/src/share/vm/gc_implementation/g1/heapRegionSet.cpp Wed Jul 05 19:59:54 2017 +0200 @@ -39,11 +39,11 @@ #ifndef PRODUCT void HeapRegionSetBase::verify_region(HeapRegion* hr) { - assert(hr->containing_set() == this, err_msg("Inconsistent containing set for %u", hr->hrs_index())); - assert(!hr->is_young(), err_msg("Adding young region %u", hr->hrs_index())); // currently we don't use these sets for young regions - assert(hr->isHumongous() == regions_humongous(), err_msg("Wrong humongous state for region %u and set %s", hr->hrs_index(), name())); - assert(hr->is_empty() == regions_empty(), err_msg("Wrong empty state for region %u and set %s", hr->hrs_index(), name())); - assert(hr->rem_set()->verify_ready_for_par_iteration(), err_msg("Wrong iteration state %u", hr->hrs_index())); + assert(hr->containing_set() == this, err_msg("Inconsistent containing set for %u", hr->hrm_index())); + assert(!hr->is_young(), err_msg("Adding young region %u", hr->hrm_index())); // currently we don't use these sets for young regions + assert(hr->isHumongous() == regions_humongous(), err_msg("Wrong humongous state for region %u and set %s", hr->hrm_index(), name())); + assert(hr->is_empty() == regions_empty(), err_msg("Wrong empty state for region %u and set %s", hr->hrm_index(), name())); + assert(hr->rem_set()->verify_ready_for_par_iteration(), err_msg("Wrong iteration state %u", hr->hrm_index())); } #endif @@ -158,7 +158,7 @@ HeapRegion* curr_from = from_list->_head; while (curr_from != NULL) { - while (curr_to != NULL && curr_to->hrs_index() < curr_from->hrs_index()) { + while (curr_to != NULL && curr_to->hrm_index() < curr_from->hrm_index()) { curr_to = curr_to->next(); } @@ -183,7 +183,7 @@ } } - if (_tail->hrs_index() < from_list->_tail->hrs_index()) { + if (_tail->hrm_index() < from_list->_tail->hrm_index()) { _tail = from_list->_tail; } } @@ -309,8 +309,8 @@ if (curr->next() != NULL) { guarantee(curr->next()->prev() == curr, "Next or prev pointers messed up"); } - guarantee(curr->hrs_index() == 0 || curr->hrs_index() > last_index, "List should be sorted"); - last_index = curr->hrs_index(); + guarantee(curr->hrm_index() == 0 || curr->hrm_index() > last_index, "List should be sorted"); + last_index = curr->hrm_index(); capacity += curr->capacity(); @@ -319,7 +319,7 @@ curr = curr->next(); } - guarantee(_tail == prev0, err_msg("Expected %s to end with %u but it ended with %u.", name(), _tail->hrs_index(), prev0->hrs_index())); + guarantee(_tail == prev0, err_msg("Expected %s to end with %u but it ended with %u.", name(), _tail->hrm_index(), prev0->hrm_index())); guarantee(_tail == NULL || _tail->next() == NULL, "_tail should not have a next"); guarantee(length() == count, err_msg("%s count mismatch. Expected %u, actual %u.", name(), length(), count)); guarantee(total_capacity_bytes() == capacity, err_msg("%s capacity mismatch. Expected " SIZE_FORMAT ", actual " SIZE_FORMAT,
--- a/hotspot/src/share/vm/gc_implementation/g1/heapRegionSet.hpp Thu Sep 04 13:26:26 2014 -0700 +++ b/hotspot/src/share/vm/gc_implementation/g1/heapRegionSet.hpp Wed Jul 05 19:59:54 2017 +0200 @@ -238,14 +238,14 @@ // Add hr to the list. The region should not be a member of another set. // Assumes that the list is ordered and will preserve that order. The order - // is determined by hrs_index. + // is determined by hrm_index. inline void add_ordered(HeapRegion* hr); // Removes from head or tail based on the given argument. HeapRegion* remove_region(bool from_head); // Merge two ordered lists. The result is also ordered. The order is - // determined by hrs_index. + // determined by hrm_index. void add_ordered(FreeRegionList* from_list); // It empties the list by removing all regions from it.
--- a/hotspot/src/share/vm/gc_implementation/g1/heapRegionSet.inline.hpp Thu Sep 04 13:26:26 2014 -0700 +++ b/hotspot/src/share/vm/gc_implementation/g1/heapRegionSet.inline.hpp Wed Jul 05 19:59:54 2017 +0200 @@ -60,14 +60,14 @@ if (_head != NULL) { HeapRegion* curr; - if (_last != NULL && _last->hrs_index() < hr->hrs_index()) { + if (_last != NULL && _last->hrm_index() < hr->hrm_index()) { curr = _last; } else { curr = _head; } // Find first entry with a Region Index larger than entry to insert. - while (curr != NULL && curr->hrs_index() < hr->hrs_index()) { + while (curr != NULL && curr->hrm_index() < hr->hrm_index()) { curr = curr->next(); }
--- a/hotspot/src/share/vm/gc_implementation/g1/sparsePRT.cpp Thu Sep 04 13:26:26 2014 -0700 +++ b/hotspot/src/share/vm/gc_implementation/g1/sparsePRT.cpp Wed Jul 05 19:59:54 2017 +0200 @@ -453,7 +453,7 @@ bool SparsePRT::add_card(RegionIdx_t region_id, CardIdx_t card_index) { #if SPARSE_PRT_VERBOSE gclog_or_tty->print_cr(" Adding card %d from region %d to region %u sparse.", - card_index, region_id, _hr->hrs_index()); + card_index, region_id, _hr->hrm_index()); #endif if (_next->occupied_entries() * 2 > _next->capacity()) { expand(); @@ -505,7 +505,7 @@ #if SPARSE_PRT_VERBOSE gclog_or_tty->print_cr(" Expanded sparse table for %u to %d.", - _hr->hrs_index(), _next->capacity()); + _hr->hrm_index(), _next->capacity()); #endif for (size_t i = 0; i < last->capacity(); i++) { SparsePRTEntry* e = last->entry((int)i);
--- a/hotspot/src/share/vm/gc_implementation/g1/vmStructs_g1.hpp Thu Sep 04 13:26:26 2014 -0700 +++ b/hotspot/src/share/vm/gc_implementation/g1/vmStructs_g1.hpp Wed Jul 05 19:59:54 2017 +0200 @@ -26,7 +26,7 @@ #define SHARE_VM_GC_IMPLEMENTATION_G1_VMSTRUCTS_G1_HPP #include "gc_implementation/g1/heapRegion.hpp" -#include "gc_implementation/g1/heapRegionSeq.inline.hpp" +#include "gc_implementation/g1/heapRegionManager.inline.hpp" #include "gc_implementation/g1/g1CollectedHeap.inline.hpp" #define VM_STRUCTS_G1(nonstatic_field, static_field) \ @@ -42,10 +42,10 @@ nonstatic_field(G1HeapRegionTable, _bias, size_t) \ nonstatic_field(G1HeapRegionTable, _shift_by, uint) \ \ - nonstatic_field(HeapRegionSeq, _regions, G1HeapRegionTable) \ - nonstatic_field(HeapRegionSeq, _num_committed, uint) \ + nonstatic_field(HeapRegionManager, _regions, G1HeapRegionTable) \ + nonstatic_field(HeapRegionManager, _num_committed, uint) \ \ - nonstatic_field(G1CollectedHeap, _hrs, HeapRegionSeq) \ + nonstatic_field(G1CollectedHeap, _hrm, HeapRegionManager) \ nonstatic_field(G1CollectedHeap, _summary_bytes_used, size_t) \ nonstatic_field(G1CollectedHeap, _g1mm, G1MonitoringSupport*) \ nonstatic_field(G1CollectedHeap, _old_set, HeapRegionSetBase) \ @@ -72,7 +72,7 @@ \ declare_type(G1OffsetTableContigSpace, CompactibleSpace) \ declare_type(HeapRegion, G1OffsetTableContigSpace) \ - declare_toplevel_type(HeapRegionSeq) \ + declare_toplevel_type(HeapRegionManager) \ declare_toplevel_type(HeapRegionSetBase) \ declare_toplevel_type(HeapRegionSetCount) \ declare_toplevel_type(G1MonitoringSupport) \
--- a/hotspot/src/share/vm/gc_implementation/parallelScavenge/psGenerationCounters.cpp Thu Sep 04 13:26:26 2014 -0700 +++ b/hotspot/src/share/vm/gc_implementation/parallelScavenge/psGenerationCounters.cpp Wed Jul 05 19:59:54 2017 +0200 @@ -30,6 +30,8 @@ PSGenerationCounters::PSGenerationCounters(const char* name, int ordinal, int spaces, + size_t min_capacity, + size_t max_capacity, PSVirtualSpace* v): _ps_virtual_space(v) { @@ -52,11 +54,11 @@ cname = PerfDataManager::counter_name(_name_space, "minCapacity"); PerfDataManager::create_constant(SUN_GC, cname, PerfData::U_Bytes, - _ps_virtual_space->committed_size(), CHECK); + min_capacity, CHECK); cname = PerfDataManager::counter_name(_name_space, "maxCapacity"); PerfDataManager::create_constant(SUN_GC, cname, PerfData::U_Bytes, - _ps_virtual_space->reserved_size(), CHECK); + max_capacity, CHECK); cname = PerfDataManager::counter_name(_name_space, "capacity"); _current_size = PerfDataManager::create_variable(SUN_GC, cname,
--- a/hotspot/src/share/vm/gc_implementation/parallelScavenge/psGenerationCounters.hpp Thu Sep 04 13:26:26 2014 -0700 +++ b/hotspot/src/share/vm/gc_implementation/parallelScavenge/psGenerationCounters.hpp Wed Jul 05 19:59:54 2017 +0200 @@ -41,7 +41,7 @@ public: PSGenerationCounters(const char* name, int ordinal, int spaces, - PSVirtualSpace* v); + size_t min_capacity, size_t max_capacity, PSVirtualSpace* v); void update_all() { assert(_virtual_space == NULL, "Only one should be in use");
--- a/hotspot/src/share/vm/gc_implementation/parallelScavenge/psOldGen.cpp Thu Sep 04 13:26:26 2014 -0700 +++ b/hotspot/src/share/vm/gc_implementation/parallelScavenge/psOldGen.cpp Wed Jul 05 19:59:54 2017 +0200 @@ -149,8 +149,8 @@ void PSOldGen::initialize_performance_counters(const char* perf_data_name, int level) { // Generation Counters, generation 'level', 1 subspace - _gen_counters = new PSGenerationCounters(perf_data_name, level, 1, - virtual_space()); + _gen_counters = new PSGenerationCounters(perf_data_name, level, 1, _min_gen_size, + _max_gen_size, virtual_space()); _space_counters = new SpaceCounters(perf_data_name, 0, virtual_space()->reserved_size(), _object_space, _gen_counters);
--- a/hotspot/src/share/vm/gc_implementation/parallelScavenge/psYoungGen.cpp Thu Sep 04 13:26:26 2014 -0700 +++ b/hotspot/src/share/vm/gc_implementation/parallelScavenge/psYoungGen.cpp Wed Jul 05 19:59:54 2017 +0200 @@ -101,7 +101,8 @@ } // Generation Counters - generation 0, 3 subspaces - _gen_counters = new PSGenerationCounters("new", 0, 3, _virtual_space); + _gen_counters = new PSGenerationCounters("new", 0, 3, _min_gen_size, + _max_gen_size, _virtual_space); // Compute maximum space sizes for performance counters ParallelScavengeHeap* heap = (ParallelScavengeHeap*)Universe::heap();
--- a/hotspot/src/share/vm/gc_implementation/shared/generationCounters.cpp Thu Sep 04 13:26:26 2014 -0700 +++ b/hotspot/src/share/vm/gc_implementation/shared/generationCounters.cpp Wed Jul 05 19:59:54 2017 +0200 @@ -62,11 +62,12 @@ GenerationCounters::GenerationCounters(const char* name, int ordinal, int spaces, + size_t min_capacity, size_t max_capacity, VirtualSpace* v) : _virtual_space(v) { assert(v != NULL, "don't call this constructor if v == NULL"); initialize(name, ordinal, spaces, - v->committed_size(), v->reserved_size(), v->committed_size()); + min_capacity, max_capacity, v->committed_size()); } GenerationCounters::GenerationCounters(const char* name,
--- a/hotspot/src/share/vm/gc_implementation/shared/generationCounters.hpp Thu Sep 04 13:26:26 2014 -0700 +++ b/hotspot/src/share/vm/gc_implementation/shared/generationCounters.hpp Wed Jul 05 19:59:54 2017 +0200 @@ -66,7 +66,7 @@ public: GenerationCounters(const char* name, int ordinal, int spaces, - VirtualSpace* v); + size_t min_capacity, size_t max_capacity, VirtualSpace* v); ~GenerationCounters() { if (_name_space != NULL) FREE_C_HEAP_ARRAY(char, _name_space, mtGC);
--- a/hotspot/src/share/vm/memory/defNewGeneration.cpp Thu Sep 04 13:26:26 2014 -0700 +++ b/hotspot/src/share/vm/memory/defNewGeneration.cpp Wed Jul 05 19:59:54 2017 +0200 @@ -214,9 +214,11 @@ _max_eden_size = size - (2*_max_survivor_size); // allocate the performance counters + GenCollectorPolicy* gcp = (GenCollectorPolicy*) GenCollectedHeap::heap()->collector_policy(); // Generation counters -- generation 0, 3 subspaces - _gen_counters = new GenerationCounters("new", 0, 3, &_virtual_space); + _gen_counters = new GenerationCounters("new", 0, 3, + gcp->min_young_size(), gcp->max_young_size(), &_virtual_space); _gc_counters = new CollectorCounters(policy, 0); _eden_counters = new CSpaceCounters("eden", 0, _max_eden_size, _eden_space,
--- a/hotspot/src/share/vm/memory/filemap.cpp Thu Sep 04 13:26:26 2014 -0700 +++ b/hotspot/src/share/vm/memory/filemap.cpp Wed Jul 05 19:59:54 2017 +0200 @@ -445,7 +445,7 @@ // close and remove the file. See bug 6372906. close(); remove(_full_path); - fail_stop("Unable to write to shared archive file.", NULL); + fail_stop("Unable to write to shared archive file."); } } _file_offset += nbytes; @@ -463,7 +463,7 @@ // that the written file is the correct length. _file_offset -= 1; if (lseek(_fd, _file_offset, SEEK_SET) < 0) { - fail_stop("Unable to seek.", NULL); + fail_stop("Unable to seek."); } char zero = 0; write_bytes(&zero, 1); @@ -534,7 +534,7 @@ // other reserved memory (like the code cache). ReservedSpace rs(size, os::vm_allocation_granularity(), false, requested_addr); if (!rs.is_reserved()) { - fail_continue(err_msg("Unable to reserve shared space at required address " INTPTR_FORMAT, requested_addr)); + fail_continue("Unable to reserve shared space at required address " INTPTR_FORMAT, requested_addr); return rs; } // the reserved virtual memory is for mapping class data sharing archive @@ -558,7 +558,7 @@ requested_addr, size, si->_read_only, si->_allow_exec); if (base == NULL || base != si->_base) { - fail_continue(err_msg("Unable to map %s shared space at required address.", shared_region_name[i])); + fail_continue("Unable to map %s shared space at required address.", shared_region_name[i]); return NULL; } #ifdef _WINDOWS @@ -584,7 +584,7 @@ void FileMapInfo::assert_mark(bool check) { if (!check) { - fail_stop("Mark mismatch while restoring from shared file.", NULL); + fail_stop("Mark mismatch while restoring from shared file."); } } @@ -709,7 +709,7 @@ void FileMapInfo::stop_sharing_and_unmap(const char* msg) { FileMapInfo *map_info = FileMapInfo::current_info(); if (map_info) { - map_info->fail_continue(msg); + map_info->fail_continue("%s", msg); for (int i = 0; i < MetaspaceShared::n_regions; i++) { if (map_info->_header->_space[i]._base != NULL) { map_info->unmap_region(i); @@ -717,6 +717,6 @@ } } } else if (DumpSharedSpaces) { - fail_stop(msg, NULL); + fail_stop("%s", msg); } }
--- a/hotspot/src/share/vm/memory/filemap.hpp Thu Sep 04 13:26:26 2014 -0700 +++ b/hotspot/src/share/vm/memory/filemap.hpp Wed Jul 05 19:59:54 2017 +0200 @@ -190,8 +190,8 @@ bool remap_shared_readonly_as_readwrite(); // Errors. - static void fail_stop(const char *msg, ...); - static void fail_continue(const char *msg, ...); + static void fail_stop(const char *msg, ...) ATTRIBUTE_PRINTF(1, 2); + static void fail_continue(const char *msg, ...) ATTRIBUTE_PRINTF(1, 2); // Return true if given address is in the mapped shared space. bool is_in_shared_space(const void* p) NOT_CDS_RETURN_(false);
--- a/hotspot/src/share/vm/memory/metaspace.cpp Thu Sep 04 13:26:26 2014 -0700 +++ b/hotspot/src/share/vm/memory/metaspace.cpp Wed Jul 05 19:59:54 2017 +0200 @@ -3126,6 +3126,8 @@ if (DumpSharedSpaces) { #if INCLUDE_CDS + MetaspaceShared::estimate_regions_size(); + SharedReadOnlySize = align_size_up(SharedReadOnlySize, max_alignment); SharedReadWriteSize = align_size_up(SharedReadWriteSize, max_alignment); SharedMiscDataSize = align_size_up(SharedMiscDataSize, max_alignment);
--- a/hotspot/src/share/vm/memory/metaspaceShared.cpp Thu Sep 04 13:26:26 2014 -0700 +++ b/hotspot/src/share/vm/memory/metaspaceShared.cpp Wed Jul 05 19:59:54 2017 +0200 @@ -816,6 +816,7 @@ //tty->print_cr("Preload failed: %s", class_name); } } + fclose(file); } else { char errmsg[JVM_MAXPATHLEN]; os::lasterror(errmsg, JVM_MAXPATHLEN); @@ -1086,3 +1087,49 @@ } return true; } + +int MetaspaceShared::count_class(const char* classlist_file) { + if (classlist_file == NULL) { + return 0; + } + char class_name[256]; + int class_count = 0; + FILE* file = fopen(classlist_file, "r"); + if (file != NULL) { + while ((fgets(class_name, sizeof class_name, file)) != NULL) { + if (*class_name == '#') { // comment + continue; + } + class_count++; + } + fclose(file); + } else { + char errmsg[JVM_MAXPATHLEN]; + os::lasterror(errmsg, JVM_MAXPATHLEN); + tty->print_cr("Loading classlist failed: %s", errmsg); + exit(1); + } + + return class_count; +} + +// the sizes are good for typical large applications that have a lot of shared +// classes +void MetaspaceShared::estimate_regions_size() { + int class_count = count_class(SharedClassListFile); + class_count += count_class(ExtraSharedClassListFile); + + if (class_count > LargeThresholdClassCount) { + if (class_count < HugeThresholdClassCount) { + SET_ESTIMATED_SIZE(Large, ReadOnly); + SET_ESTIMATED_SIZE(Large, ReadWrite); + SET_ESTIMATED_SIZE(Large, MiscData); + SET_ESTIMATED_SIZE(Large, MiscCode); + } else { + SET_ESTIMATED_SIZE(Huge, ReadOnly); + SET_ESTIMATED_SIZE(Huge, ReadWrite); + SET_ESTIMATED_SIZE(Huge, MiscData); + SET_ESTIMATED_SIZE(Huge, MiscCode); + } + } +}
--- a/hotspot/src/share/vm/memory/metaspaceShared.hpp Thu Sep 04 13:26:26 2014 -0700 +++ b/hotspot/src/share/vm/memory/metaspaceShared.hpp Wed Jul 05 19:59:54 2017 +0200 @@ -30,6 +30,19 @@ #include "utilities/exceptions.hpp" #include "utilities/macros.hpp" +#define LargeSharedArchiveSize (300*M) +#define HugeSharedArchiveSize (800*M) +#define ReadOnlyRegionPercentage 0.4 +#define ReadWriteRegionPercentage 0.55 +#define MiscDataRegionPercentage 0.03 +#define MiscCodeRegionPercentage 0.02 +#define LargeThresholdClassCount 5000 +#define HugeThresholdClassCount 40000 + +#define SET_ESTIMATED_SIZE(type, region) \ + Shared ##region## Size = FLAG_IS_DEFAULT(Shared ##region## Size) ? \ + (type ## SharedArchiveSize * region ## RegionPercentage) : Shared ## region ## Size + class FileMapInfo; // Class Data Sharing Support @@ -112,5 +125,8 @@ static void link_one_shared_class(Klass* obj, TRAPS); static void check_one_shared_class(Klass* obj); static void link_and_cleanup_shared_classes(TRAPS); + + static int count_class(const char* classlist_file); + static void estimate_regions_size() NOT_CDS_RETURN; }; #endif // SHARE_VM_MEMORY_METASPACE_SHARED_HPP
--- a/hotspot/src/share/vm/memory/tenuredGeneration.cpp Thu Sep 04 13:26:26 2014 -0700 +++ b/hotspot/src/share/vm/memory/tenuredGeneration.cpp Wed Jul 05 19:59:54 2017 +0200 @@ -53,9 +53,11 @@ // initialize performance counters const char* gen_name = "old"; + GenCollectorPolicy* gcp = (GenCollectorPolicy*) GenCollectedHeap::heap()->collector_policy(); // Generation Counters -- generation 1, 1 subspace - _gen_counters = new GenerationCounters(gen_name, 1, 1, &_virtual_space); + _gen_counters = new GenerationCounters(gen_name, 1, 1, + gcp->min_old_size(), gcp->max_old_size(), &_virtual_space); _gc_counters = new CollectorCounters("MSC", 1);
--- a/hotspot/src/share/vm/oops/instanceKlass.cpp Thu Sep 04 13:26:26 2014 -0700 +++ b/hotspot/src/share/vm/oops/instanceKlass.cpp Wed Jul 05 19:59:54 2017 +0200 @@ -68,7 +68,7 @@ #include "gc_implementation/g1/g1CollectedHeap.inline.hpp" #include "gc_implementation/g1/g1OopClosures.inline.hpp" #include "gc_implementation/g1/g1RemSet.inline.hpp" -#include "gc_implementation/g1/heapRegionSeq.inline.hpp" +#include "gc_implementation/g1/heapRegionManager.inline.hpp" #include "gc_implementation/parNew/parOopClosures.inline.hpp" #include "gc_implementation/parallelScavenge/parallelScavengeHeap.inline.hpp" #include "gc_implementation/parallelScavenge/psPromotionManager.inline.hpp"
--- a/hotspot/src/share/vm/oops/instanceMirrorKlass.cpp Thu Sep 04 13:26:26 2014 -0700 +++ b/hotspot/src/share/vm/oops/instanceMirrorKlass.cpp Wed Jul 05 19:59:54 2017 +0200 @@ -42,7 +42,7 @@ #include "gc_implementation/g1/g1CollectedHeap.inline.hpp" #include "gc_implementation/g1/g1OopClosures.inline.hpp" #include "gc_implementation/g1/g1RemSet.inline.hpp" -#include "gc_implementation/g1/heapRegionSeq.inline.hpp" +#include "gc_implementation/g1/heapRegionManager.inline.hpp" #include "gc_implementation/parNew/parOopClosures.inline.hpp" #include "gc_implementation/parallelScavenge/psPromotionManager.inline.hpp" #include "gc_implementation/parallelScavenge/psScavenge.inline.hpp"
--- a/hotspot/src/share/vm/oops/instanceRefKlass.cpp Thu Sep 04 13:26:26 2014 -0700 +++ b/hotspot/src/share/vm/oops/instanceRefKlass.cpp Wed Jul 05 19:59:54 2017 +0200 @@ -38,7 +38,7 @@ #include "gc_implementation/g1/g1CollectedHeap.inline.hpp" #include "gc_implementation/g1/g1OopClosures.inline.hpp" #include "gc_implementation/g1/g1RemSet.inline.hpp" -#include "gc_implementation/g1/heapRegionSeq.inline.hpp" +#include "gc_implementation/g1/heapRegionManager.inline.hpp" #include "gc_implementation/parNew/parOopClosures.inline.hpp" #include "gc_implementation/parallelScavenge/psPromotionManager.inline.hpp" #include "gc_implementation/parallelScavenge/psScavenge.inline.hpp"
--- a/hotspot/src/share/vm/oops/objArrayKlass.cpp Thu Sep 04 13:26:26 2014 -0700 +++ b/hotspot/src/share/vm/oops/objArrayKlass.cpp Wed Jul 05 19:59:54 2017 +0200 @@ -51,7 +51,7 @@ #include "gc_implementation/g1/g1CollectedHeap.inline.hpp" #include "gc_implementation/g1/g1OopClosures.inline.hpp" #include "gc_implementation/g1/g1RemSet.inline.hpp" -#include "gc_implementation/g1/heapRegionSeq.inline.hpp" +#include "gc_implementation/g1/heapRegionManager.inline.hpp" #include "gc_implementation/parNew/parOopClosures.inline.hpp" #include "gc_implementation/parallelScavenge/psCompactionManager.hpp" #include "gc_implementation/parallelScavenge/psPromotionManager.inline.hpp"
--- a/hotspot/src/share/vm/opto/type.cpp Thu Sep 04 13:26:26 2014 -0700 +++ b/hotspot/src/share/vm/opto/type.cpp Wed Jul 05 19:59:54 2017 +0200 @@ -1708,8 +1708,8 @@ // Make a TypeTuple from the range of a method signature const TypeTuple *TypeTuple::make_range(ciSignature* sig) { ciType* return_type = sig->return_type(); - uint total_fields = TypeFunc::Parms + return_type->size(); - const Type **field_array = fields(total_fields); + uint arg_cnt = return_type->size(); + const Type **field_array = fields(arg_cnt); switch (return_type->basic_type()) { case T_LONG: field_array[TypeFunc::Parms] = TypeLong::LONG; @@ -1734,26 +1734,26 @@ default: ShouldNotReachHere(); } - return (TypeTuple*)(new TypeTuple(total_fields,field_array))->hashcons(); + return (TypeTuple*)(new TypeTuple(TypeFunc::Parms + arg_cnt, field_array))->hashcons(); } // Make a TypeTuple from the domain of a method signature const TypeTuple *TypeTuple::make_domain(ciInstanceKlass* recv, ciSignature* sig) { - uint total_fields = TypeFunc::Parms + sig->size(); + uint arg_cnt = sig->size(); uint pos = TypeFunc::Parms; const Type **field_array; if (recv != NULL) { - total_fields++; - field_array = fields(total_fields); + arg_cnt++; + field_array = fields(arg_cnt); // Use get_const_type here because it respects UseUniqueSubclasses: field_array[pos++] = get_const_type(recv)->join_speculative(TypePtr::NOTNULL); } else { - field_array = fields(total_fields); + field_array = fields(arg_cnt); } int i = 0; - while (pos < total_fields) { + while (pos < TypeFunc::Parms + arg_cnt) { ciType* type = sig->type_at(i); switch (type->basic_type()) { @@ -1780,7 +1780,8 @@ } i++; } - return (TypeTuple*)(new TypeTuple(total_fields,field_array))->hashcons(); + + return (TypeTuple*)(new TypeTuple(TypeFunc::Parms + arg_cnt, field_array))->hashcons(); } const TypeTuple *TypeTuple::make( uint cnt, const Type **fields ) { @@ -1789,6 +1790,7 @@ //------------------------------fields----------------------------------------- // Subroutine call type with space allocated for argument types +// Memory for Control, I_O, Memory, FramePtr, and ReturnAdr is allocated implicitly const Type **TypeTuple::fields( uint arg_cnt ) { const Type **flds = (const Type **)(Compile::current()->type_arena()->Amalloc_4((TypeFunc::Parms+arg_cnt)*sizeof(Type*) )); flds[TypeFunc::Control ] = Type::CONTROL;
--- a/hotspot/src/share/vm/opto/type.hpp Thu Sep 04 13:26:26 2014 -0700 +++ b/hotspot/src/share/vm/opto/type.hpp Wed Jul 05 19:59:54 2017 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2014, 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 @@ -635,6 +635,7 @@ static const TypeTuple *make_domain(ciInstanceKlass* recv, ciSignature *sig); // Subroutine call type with space allocated for argument types + // Memory for Control, I_O, Memory, FramePtr, and ReturnAdr is allocated implicitly static const Type **fields( uint arg_cnt ); virtual const Type *xmeet( const Type *t ) const;
--- a/hotspot/src/share/vm/runtime/java.cpp Thu Sep 04 13:26:26 2014 -0700 +++ b/hotspot/src/share/vm/runtime/java.cpp Wed Jul 05 19:59:54 2017 +0200 @@ -430,6 +430,8 @@ } } +jint volatile vm_getting_terminated = 0; + // Note: before_exit() can be executed only once, if more than one threads // are trying to shutdown the VM at the same time, only one thread // can run before_exit() and all other threads must wait. @@ -460,6 +462,8 @@ } } + OrderAccess::release_store(&vm_getting_terminated, 1); + // The only difference between this and Win32's _onexit procs is that // this version is invoked before any threads get killed. ExitProc* current = exit_procs;
--- a/hotspot/src/share/vm/services/mallocTracker.hpp Thu Sep 04 13:26:26 2014 -0700 +++ b/hotspot/src/share/vm/services/mallocTracker.hpp Wed Jul 05 19:59:54 2017 +0200 @@ -171,8 +171,9 @@ // Total malloc'd memory used by arenas size_t total_arena() const; - inline size_t thread_count() { - return by_type(mtThreadStack)->malloc_count(); + inline size_t thread_count() const { + MallocMemorySnapshot* s = const_cast<MallocMemorySnapshot*>(this); + return s->by_type(mtThreadStack)->malloc_count(); } void reset();
--- a/hotspot/src/share/vm/services/memBaseline.cpp Thu Sep 04 13:26:26 2014 -0700 +++ b/hotspot/src/share/vm/services/memBaseline.cpp Wed Jul 05 19:59:54 2017 +0200 @@ -70,15 +70,13 @@ */ class MallocAllocationSiteWalker : public MallocSiteWalker { private: - SortedLinkedList<MallocSite, compare_malloc_size, ResourceObj::ARENA> - _malloc_sites; + SortedLinkedList<MallocSite, compare_malloc_size> _malloc_sites; size_t _count; // Entries in MallocSiteTable with size = 0 and count = 0, // when the malloc site is not longer there. public: - MallocAllocationSiteWalker(Arena* arena) : _count(0), _malloc_sites(arena) { - } + MallocAllocationSiteWalker() : _count(0) { } inline size_t count() const { return _count; } @@ -109,13 +107,12 @@ // Walk all virtual memory regions for baselining class VirtualMemoryAllocationWalker : public VirtualMemoryWalker { private: - SortedLinkedList<ReservedMemoryRegion, compare_virtual_memory_base, ResourceObj::ARENA> + SortedLinkedList<ReservedMemoryRegion, compare_virtual_memory_base> _virtual_memory_regions; size_t _count; public: - VirtualMemoryAllocationWalker(Arena* a) : _count(0), _virtual_memory_regions(a) { - } + VirtualMemoryAllocationWalker() : _count(0) { } bool do_allocation_site(const ReservedMemoryRegion* rgn) { if (rgn->size() >= MemBaseline::SIZE_THRESHOLD) { @@ -136,39 +133,30 @@ bool MemBaseline::baseline_summary() { - assert(_malloc_memory_snapshot == NULL, "Malloc baseline not yet reset"); - assert(_virtual_memory_snapshot == NULL, "Virtual baseline not yet reset"); - - _malloc_memory_snapshot = new (arena()) MallocMemorySnapshot(); - _virtual_memory_snapshot = new (arena()) VirtualMemorySnapshot(); - if (_malloc_memory_snapshot == NULL || _virtual_memory_snapshot == NULL) { - return false; - } - MallocMemorySummary::snapshot(_malloc_memory_snapshot); - VirtualMemorySummary::snapshot(_virtual_memory_snapshot); + MallocMemorySummary::snapshot(&_malloc_memory_snapshot); + VirtualMemorySummary::snapshot(&_virtual_memory_snapshot); return true; } bool MemBaseline::baseline_allocation_sites() { - assert(arena() != NULL, "Just check"); // Malloc allocation sites - MallocAllocationSiteWalker malloc_walker(arena()); + MallocAllocationSiteWalker malloc_walker; if (!MallocSiteTable::walk_malloc_site(&malloc_walker)) { return false; } - _malloc_sites.set_head(malloc_walker.malloc_sites()->head()); + _malloc_sites.move(malloc_walker.malloc_sites()); // The malloc sites are collected in size order _malloc_sites_order = by_size; // Virtual memory allocation sites - VirtualMemoryAllocationWalker virtual_memory_walker(arena()); + VirtualMemoryAllocationWalker virtual_memory_walker; if (!VirtualMemoryTracker::walk_virtual_memory(&virtual_memory_walker)) { return false; } // Virtual memory allocations are collected in call stack order - _virtual_memory_allocations.set_head(virtual_memory_walker.virtual_memory_allocations()->head()); + _virtual_memory_allocations.move(virtual_memory_walker.virtual_memory_allocations()); if (!aggregate_virtual_memory_allocation_sites()) { return false; @@ -180,11 +168,6 @@ } bool MemBaseline::baseline(bool summaryOnly) { - if (arena() == NULL) { - _arena = new (std::nothrow, mtNMT) Arena(mtNMT); - if (arena() == NULL) return false; - } - reset(); _class_count = InstanceKlass::number_of_instance_classes(); @@ -211,8 +194,7 @@ } bool MemBaseline::aggregate_virtual_memory_allocation_sites() { - SortedLinkedList<VirtualMemoryAllocationSite, compare_allocation_site, ResourceObj::ARENA> - allocation_sites(arena()); + SortedLinkedList<VirtualMemoryAllocationSite, compare_allocation_site> allocation_sites; VirtualMemoryAllocationIterator itr = virtual_memory_allocations(); const ReservedMemoryRegion* rgn; @@ -230,12 +212,12 @@ site->commit_memory(rgn->committed_size()); } - _virtual_memory_sites.set_head(allocation_sites.head()); + _virtual_memory_sites.move(&allocation_sites); return true; } MallocSiteIterator MemBaseline::malloc_sites(SortingOrder order) { - assert(!_malloc_sites.is_empty(), "Detail baseline?"); + assert(!_malloc_sites.is_empty(), "Not detail baseline"); switch(order) { case by_size: malloc_sites_to_size_order(); @@ -251,7 +233,7 @@ } VirtualMemorySiteIterator MemBaseline::virtual_memory_sites(SortingOrder order) { - assert(!_virtual_memory_sites.is_empty(), "Detail baseline?"); + assert(!_virtual_memory_sites.is_empty(), "Not detail baseline"); switch(order) { case by_size: virtual_memory_sites_to_size_order(); @@ -270,8 +252,7 @@ // Sorting allocations sites in different orders void MemBaseline::malloc_sites_to_size_order() { if (_malloc_sites_order != by_size) { - SortedLinkedList<MallocSite, compare_malloc_size, ResourceObj::ARENA> - tmp(arena()); + SortedLinkedList<MallocSite, compare_malloc_size> tmp; // Add malloc sites to sorted linked list to sort into size order tmp.move(&_malloc_sites); @@ -283,8 +264,7 @@ void MemBaseline::malloc_sites_to_allocation_site_order() { if (_malloc_sites_order != by_site) { - SortedLinkedList<MallocSite, compare_malloc_site, ResourceObj::ARENA> - tmp(arena()); + SortedLinkedList<MallocSite, compare_malloc_site> tmp; // Add malloc sites to sorted linked list to sort into site (address) order tmp.move(&_malloc_sites); _malloc_sites.set_head(tmp.head()); @@ -295,8 +275,7 @@ void MemBaseline::virtual_memory_sites_to_size_order() { if (_virtual_memory_sites_order != by_size) { - SortedLinkedList<VirtualMemoryAllocationSite, compare_virtual_memory_size, ResourceObj::ARENA> - tmp(arena()); + SortedLinkedList<VirtualMemoryAllocationSite, compare_virtual_memory_size> tmp; tmp.move(&_virtual_memory_sites); @@ -308,10 +287,9 @@ void MemBaseline::virtual_memory_sites_to_reservation_site_order() { if (_virtual_memory_sites_order != by_size) { - SortedLinkedList<VirtualMemoryAllocationSite, compare_virtual_memory_site, ResourceObj::ARENA> - tmp(arena()); + SortedLinkedList<VirtualMemoryAllocationSite, compare_virtual_memory_site> tmp; - tmp.add(&_virtual_memory_sites); + tmp.move(&_virtual_memory_sites); _virtual_memory_sites.set_head(tmp.head()); tmp.set_head(NULL);
--- a/hotspot/src/share/vm/services/memBaseline.hpp Thu Sep 04 13:26:26 2014 -0700 +++ b/hotspot/src/share/vm/services/memBaseline.hpp Wed Jul 05 19:59:54 2017 +0200 @@ -61,28 +61,22 @@ }; private: - // All baseline data is stored in this arena - Arena* _arena; - // Summary information - MallocMemorySnapshot* _malloc_memory_snapshot; - VirtualMemorySnapshot* _virtual_memory_snapshot; + MallocMemorySnapshot _malloc_memory_snapshot; + VirtualMemorySnapshot _virtual_memory_snapshot; size_t _class_count; // Allocation sites information // Malloc allocation sites - LinkedListImpl<MallocSite, ResourceObj::ARENA> - _malloc_sites; + LinkedListImpl<MallocSite> _malloc_sites; // All virtual memory allocations - LinkedListImpl<ReservedMemoryRegion, ResourceObj::ARENA> - _virtual_memory_allocations; + LinkedListImpl<ReservedMemoryRegion> _virtual_memory_allocations; // Virtual memory allocations by allocation sites, always in by_address // order - LinkedListImpl<VirtualMemoryAllocationSite, ResourceObj::ARENA> - _virtual_memory_sites; + LinkedListImpl<VirtualMemoryAllocationSite> _virtual_memory_sites; SortingOrder _malloc_sites_order; SortingOrder _virtual_memory_sites_order; @@ -93,30 +87,23 @@ // create a memory baseline MemBaseline(): _baseline_type(Not_baselined), - _class_count(0), - _arena(NULL), - _malloc_memory_snapshot(NULL), - _virtual_memory_snapshot(NULL), - _malloc_sites(NULL) { + _class_count(0) { } ~MemBaseline() { reset(); - if (_arena != NULL) { - delete _arena; - } } bool baseline(bool summaryOnly = true); BaselineType baseline_type() const { return _baseline_type; } - MallocMemorySnapshot* malloc_memory_snapshot() const { - return _malloc_memory_snapshot; + MallocMemorySnapshot* malloc_memory_snapshot() { + return &_malloc_memory_snapshot; } - VirtualMemorySnapshot* virtual_memory_snapshot() const { - return _virtual_memory_snapshot; + VirtualMemorySnapshot* virtual_memory_snapshot() { + return &_virtual_memory_snapshot; } MallocSiteIterator malloc_sites(SortingOrder order); @@ -133,10 +120,8 @@ // memory size_t total_reserved_memory() const { assert(baseline_type() != Not_baselined, "Not yet baselined"); - assert(_virtual_memory_snapshot != NULL, "No virtual memory snapshot"); - assert(_malloc_memory_snapshot != NULL, "No malloc memory snapshot"); - size_t amount = _malloc_memory_snapshot->total() + - _virtual_memory_snapshot->total_reserved(); + size_t amount = _malloc_memory_snapshot.total() + + _virtual_memory_snapshot.total_reserved(); return amount; } @@ -144,32 +129,30 @@ // virtual memory size_t total_committed_memory() const { assert(baseline_type() != Not_baselined, "Not yet baselined"); - assert(_virtual_memory_snapshot != NULL, - "Not a snapshot"); - size_t amount = _malloc_memory_snapshot->total() + - _virtual_memory_snapshot->total_committed(); + size_t amount = _malloc_memory_snapshot.total() + + _virtual_memory_snapshot.total_committed(); return amount; } size_t total_arena_memory() const { assert(baseline_type() != Not_baselined, "Not yet baselined"); - assert(_malloc_memory_snapshot != NULL, "Not yet baselined"); - return _malloc_memory_snapshot->total_arena(); + return _malloc_memory_snapshot.total_arena(); } size_t malloc_tracking_overhead() const { assert(baseline_type() != Not_baselined, "Not yet baselined"); - return _malloc_memory_snapshot->malloc_overhead()->size(); + MemBaseline* bl = const_cast<MemBaseline*>(this); + return bl->_malloc_memory_snapshot.malloc_overhead()->size(); } - const MallocMemory* malloc_memory(MEMFLAGS flag) const { - assert(_malloc_memory_snapshot != NULL, "Not a snapshot"); - return _malloc_memory_snapshot->by_type(flag); + MallocMemory* malloc_memory(MEMFLAGS flag) { + assert(baseline_type() != Not_baselined, "Not yet baselined"); + return _malloc_memory_snapshot.by_type(flag); } - const VirtualMemory* virtual_memory(MEMFLAGS flag) const { - assert(_virtual_memory_snapshot != NULL, "Not a snapshot"); - return _virtual_memory_snapshot->by_type(flag); + VirtualMemory* virtual_memory(MEMFLAGS flag) { + assert(baseline_type() != Not_baselined, "Not yet baselined"); + return _virtual_memory_snapshot.by_type(flag); } @@ -180,24 +163,19 @@ size_t thread_count() const { assert(baseline_type() != Not_baselined, "Not yet baselined"); - assert(_malloc_memory_snapshot != NULL, "Baselined?"); - return _malloc_memory_snapshot->thread_count(); + return _malloc_memory_snapshot.thread_count(); } // reset the baseline for reuse void reset() { _baseline_type = Not_baselined; - _malloc_memory_snapshot = NULL; - _virtual_memory_snapshot = NULL; + _malloc_memory_snapshot.reset(); + _virtual_memory_snapshot.reset(); _class_count = 0; - _malloc_sites = NULL; - _virtual_memory_sites = NULL; - _virtual_memory_allocations = NULL; - - if (_arena != NULL) { - _arena->destruct_contents(); - } + _malloc_sites.clear(); + _virtual_memory_sites.clear(); + _virtual_memory_allocations.clear(); } private: @@ -210,8 +188,6 @@ // Aggregate virtual memory allocation by allocation sites bool aggregate_virtual_memory_allocation_sites(); - Arena* arena() { return _arena; } - // Sorting allocation sites in different orders // Sort allocation sites in size order void malloc_sites_to_size_order();
--- a/hotspot/test/compiler/classUnloading/anonymousClass/TestAnonymousClassUnloading.java Thu Sep 04 13:26:26 2014 -0700 +++ b/hotspot/test/compiler/classUnloading/anonymousClass/TestAnonymousClassUnloading.java Wed Jul 05 19:59:54 2017 +0200 @@ -35,7 +35,9 @@ * @summary "Tests unloading of anonymous classes." * @library /testlibrary /testlibrary/whitebox * @compile TestAnonymousClassUnloading.java - * @run main ClassFileInstaller TestAnonymousClassUnloading sun.hotspot.WhiteBox + * @run main ClassFileInstaller TestAnonymousClassUnloading + * sun.hotspot.WhiteBox + * sun.hotspot.WhiteBox$WhiteBoxPermission * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:-BackgroundCompilation TestAnonymousClassUnloading */ public class TestAnonymousClassUnloading {
--- a/hotspot/test/compiler/classUnloading/methodUnloading/TestMethodUnloading.java Thu Sep 04 13:26:26 2014 -0700 +++ b/hotspot/test/compiler/classUnloading/methodUnloading/TestMethodUnloading.java Wed Jul 05 19:59:54 2017 +0200 @@ -36,7 +36,7 @@ * @build WorkerClass * @run main ClassFileInstaller sun.hotspot.WhiteBox * sun.hotspot.WhiteBox$WhiteBoxPermission - * @run main/othervm -Xbootclasspath/a:. -XX:+IgnoreUnrecognizedVMOptions -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:-BackgroundCompilation -XX:-UseCompressedOops -XX:+UseParallelGC -XX:CompileOnly=TestMethodUnloading::doWork TestMethodUnloading + * @run main/othervm -Xbootclasspath/a:. -XX:+IgnoreUnrecognizedVMOptions -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:-BackgroundCompilation -XX:-UseCompressedOops -XX:CompileOnly=TestMethodUnloading::doWork TestMethodUnloading */ public class TestMethodUnloading { private static final String workerClassName = "WorkerClass";
--- a/hotspot/test/compiler/rtm/cli/TestPrintPreciseRTMLockingStatisticsOptionOnSupportedConfig.java Thu Sep 04 13:26:26 2014 -0700 +++ b/hotspot/test/compiler/rtm/cli/TestPrintPreciseRTMLockingStatisticsOptionOnSupportedConfig.java Wed Jul 05 19:59:54 2017 +0200 @@ -54,16 +54,19 @@ CommandLineOptionTest.verifyOptionValueForSameVM(optionName, TestPrintPreciseRTMLockingStatisticsBase.DEFAULT_VALUE, CommandLineOptionTest.UNLOCK_DIAGNOSTIC_VM_OPTIONS, + CommandLineOptionTest.UNLOCK_EXPERIMENTAL_VM_OPTIONS, "-XX:+UseRTMLocking"); CommandLineOptionTest.verifyOptionValueForSameVM(optionName, TestPrintPreciseRTMLockingStatisticsBase.DEFAULT_VALUE, CommandLineOptionTest.UNLOCK_DIAGNOSTIC_VM_OPTIONS, + CommandLineOptionTest.UNLOCK_EXPERIMENTAL_VM_OPTIONS, "-XX:-UseRTMLocking", prepareOptionValue("true")); // verify that option could be turned on CommandLineOptionTest.verifyOptionValueForSameVM(optionName, "true", CommandLineOptionTest.UNLOCK_DIAGNOSTIC_VM_OPTIONS, + CommandLineOptionTest.UNLOCK_EXPERIMENTAL_VM_OPTIONS, "-XX:+UseRTMLocking", prepareOptionValue("true")); }
--- a/hotspot/test/compiler/rtm/cli/TestUseRTMDeoptOptionOnSupportedConfig.java Thu Sep 04 13:26:26 2014 -0700 +++ b/hotspot/test/compiler/rtm/cli/TestUseRTMDeoptOptionOnSupportedConfig.java Wed Jul 05 19:59:54 2017 +0200 @@ -63,13 +63,16 @@ // verify default value CommandLineOptionTest.verifyOptionValueForSameVM("UseRTMDeopt", TestUseRTMDeoptOptionOnSupportedConfig.DEFAULT_VALUE, + CommandLineOptionTest.UNLOCK_EXPERIMENTAL_VM_OPTIONS, "-XX:+UseRTMLocking"); // verify that option is off when UseRTMLocking is off - CommandLineOptionTest.verifyOptionValueForSameVM("UseRTMDeopt", - "false", "-XX:-UseRTMLocking", "-XX:+UseRTMDeopt"); + CommandLineOptionTest.verifyOptionValueForSameVM("UseRTMDeopt", "false", + CommandLineOptionTest.UNLOCK_EXPERIMENTAL_VM_OPTIONS, + "-XX:-UseRTMLocking", "-XX:+UseRTMDeopt"); // verify that option could be turned on - CommandLineOptionTest.verifyOptionValueForSameVM("UseRTMDeopt", - "true", "-XX:+UseRTMLocking", "-XX:+UseRTMDeopt"); + CommandLineOptionTest.verifyOptionValueForSameVM("UseRTMDeopt", "true", + CommandLineOptionTest.UNLOCK_EXPERIMENTAL_VM_OPTIONS, + "-XX:+UseRTMLocking", "-XX:+UseRTMDeopt"); } public static void main(String args[]) throws Throwable {
--- a/hotspot/test/compiler/rtm/cli/TestUseRTMLockingOptionOnSupportedConfig.java Thu Sep 04 13:26:26 2014 -0700 +++ b/hotspot/test/compiler/rtm/cli/TestUseRTMLockingOptionOnSupportedConfig.java Wed Jul 05 19:59:54 2017 +0200 @@ -59,24 +59,31 @@ new String[]{ RTMGenericCommandLineOptionTest.RTM_INSTR_ERROR, unrecongnizedOption - }, ExitCode.OK, "-XX:+UseRTMLocking" + }, ExitCode.OK, + CommandLineOptionTest.UNLOCK_EXPERIMENTAL_VM_OPTIONS, + "-XX:+UseRTMLocking" ); CommandLineOptionTest.verifySameJVMStartup(null, new String[]{ RTMGenericCommandLineOptionTest.RTM_INSTR_ERROR, unrecongnizedOption - }, ExitCode.OK, "-XX:-UseRTMLocking" + }, ExitCode.OK, + CommandLineOptionTest.UNLOCK_EXPERIMENTAL_VM_OPTIONS, + "-XX:-UseRTMLocking" ); // verify that UseRTMLocking is of by default CommandLineOptionTest.verifyOptionValueForSameVM("UseRTMLocking", - TestUseRTMLockingOptionOnSupportedConfig.DEFAULT_VALUE); + TestUseRTMLockingOptionOnSupportedConfig.DEFAULT_VALUE, + CommandLineOptionTest.UNLOCK_EXPERIMENTAL_VM_OPTIONS); // verify that we can change UseRTMLocking value CommandLineOptionTest.verifyOptionValueForSameVM("UseRTMLocking", TestUseRTMLockingOptionOnSupportedConfig.DEFAULT_VALUE, + CommandLineOptionTest.UNLOCK_EXPERIMENTAL_VM_OPTIONS, "-XX:-UseRTMLocking"); CommandLineOptionTest.verifyOptionValueForSameVM("UseRTMLocking", - "true", "-XX:+UseRTMLocking"); + "true", CommandLineOptionTest.UNLOCK_EXPERIMENTAL_VM_OPTIONS, + "-XX:+UseRTMLocking"); } public static void main(String args[]) throws Throwable {
--- a/hotspot/test/compiler/rtm/cli/TestUseRTMLockingOptionWithBiasedLocking.java Thu Sep 04 13:26:26 2014 -0700 +++ b/hotspot/test/compiler/rtm/cli/TestUseRTMLockingOptionWithBiasedLocking.java Wed Jul 05 19:59:54 2017 +0200 @@ -54,18 +54,22 @@ // verify that we will not get a warning CommandLineOptionTest.verifySameJVMStartup(null, new String[] { warningMessage }, ExitCode.OK, + CommandLineOptionTest.UNLOCK_EXPERIMENTAL_VM_OPTIONS, "-XX:+UseRTMLocking", "-XX:-UseBiasedLocking"); // verify that we will get a warning CommandLineOptionTest.verifySameJVMStartup( new String[] { warningMessage }, null, ExitCode.OK, + CommandLineOptionTest.UNLOCK_EXPERIMENTAL_VM_OPTIONS, "-XX:+UseRTMLocking", "-XX:+UseBiasedLocking"); // verify that UseBiasedLocking is false when we use rtm locking CommandLineOptionTest.verifyOptionValueForSameVM("UseBiasedLocking", - "false", "-XX:+UseRTMLocking"); + "false", CommandLineOptionTest.UNLOCK_EXPERIMENTAL_VM_OPTIONS, + "-XX:+UseRTMLocking"); // verify that we can't turn on biased locking when // using rtm locking CommandLineOptionTest.verifyOptionValueForSameVM("UseBiasedLocking", - "false", "-XX:+UseRTMLocking", "-XX:+UseBiasedLocking"); + "false", CommandLineOptionTest.UNLOCK_EXPERIMENTAL_VM_OPTIONS, + "-XX:+UseRTMLocking", "-XX:+UseBiasedLocking"); } public static void main(String args[]) throws Throwable {
--- a/hotspot/test/gc/g1/TestHumongousShrinkHeap.java Thu Sep 04 13:26:26 2014 -0700 +++ b/hotspot/test/gc/g1/TestHumongousShrinkHeap.java Wed Jul 05 19:59:54 2017 +0200 @@ -22,9 +22,8 @@ */ /** - * @ignore 8041506, 8041946, 8042051 * @test TestHumongousShrinkHeap - * @bug 8036025 + * @bug 8036025 8056043 * @summary Verify that heap shrinks after GC in the presence of fragmentation due to humongous objects * @library /testlibrary * @run main/othervm -XX:MinHeapFreeRatio=10 -XX:MaxHeapFreeRatio=50 -XX:+UseG1GC -XX:G1HeapRegionSize=1M -verbose:gc TestHumongousShrinkHeap
--- a/hotspot/test/gc/g1/TestStringDeduplicationTools.java Thu Sep 04 13:26:26 2014 -0700 +++ b/hotspot/test/gc/g1/TestStringDeduplicationTools.java Wed Jul 05 19:59:54 2017 +0200 @@ -129,8 +129,19 @@ return list; } + /** + * Verifies that the given list contains expected number of unique strings. + * It's possible that deduplication hasn't completed yet, so the method + * will perform several attempts to check with a little pause between. + * The method throws RuntimeException to signal that verification failed. + * + * @param list strings to check + * @param uniqueExpected expected number of unique strings + * @throws RuntimeException if check fails + */ private static void verifyStrings(ArrayList<String> list, int uniqueExpected) { - for (;;) { + boolean passed = false; + for (int attempts = 0; attempts < 10; attempts++) { // Check number of deduplicated strings ArrayList<Object> unique = new ArrayList<Object>(uniqueExpected); for (String string: list) { @@ -153,11 +164,11 @@ ", uniqueExpected=" + uniqueExpected); if (unique.size() == uniqueExpected) { - System.out.println("Deduplication completed"); + System.out.println("Deduplication completed (as fast as " + attempts + " iterations)"); + passed = true; break; } else { System.out.println("Deduplication not completed, waiting..."); - // Give the deduplication thread time to complete try { Thread.sleep(1000); @@ -166,6 +177,9 @@ } } } + if (!passed) { + throw new RuntimeException("String verification failed"); + } } private static OutputAnalyzer runTest(String... extraArgs) throws Exception { @@ -247,14 +261,20 @@ forceDeduplication(ageThreshold, FullGC); // Wait for deduplication to occur - while (getValue(dupString1) != getValue(baseString)) { + for (int attempts = 0; attempts < 10; attempts++) { + if (getValue(dupString1) == getValue(baseString)) { + break; + } System.out.println("Waiting..."); try { - Thread.sleep(100); + Thread.sleep(1000); } catch (Exception e) { throw new RuntimeException(e); } } + if (getValue(dupString1) != getValue(baseString)) { + throw new RuntimeException("Deduplication has not occurred"); + } // Create a new duplicate of baseString StringBuilder sb2 = new StringBuilder(baseString);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hotspot/test/gc/whitebox/TestWBGC.java Wed Jul 05 19:59:54 2017 +0200 @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2014, 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 TestWBGC + * @bug 8055098 + * @summary Test verify that WB methods isObjectInOldGen and youngGC works correctly. + * @library /testlibrary /testlibrary/whitebox + * @build TestWBGC + * @run main ClassFileInstaller sun.hotspot.WhiteBox + * @run driver TestWBGC + */ +import com.oracle.java.testlibrary.*; +import sun.hotspot.WhiteBox; + +public class TestWBGC { + + public static void main(String args[]) throws Exception { + ProcessBuilder pb = ProcessTools.createJavaProcessBuilder( + true, + "-Xbootclasspath/a:.", + "-XX:+UnlockDiagnosticVMOptions", + "-XX:+WhiteBoxAPI", + "-XX:MaxTenuringThreshold=1", + "-XX:+PrintGC", + GCYoungTest.class.getName()); + + OutputAnalyzer output = new OutputAnalyzer(pb.start()); + System.out.println(output.getStdout()); + output.shouldHaveExitValue(0); + output.shouldContain("WhiteBox Initiated Young GC"); + output.shouldNotContain("Full"); + // To be sure that we don't provoke Full GC additionaly to young + } + + public static class GCYoungTest { + static WhiteBox wb = WhiteBox.getWhiteBox(); + public static Object obj; + + public static void main(String args[]) { + obj = new Object(); + Asserts.assertFalse(wb.isObjectInOldGen(obj)); + wb.youngGC(); + wb.youngGC(); + // 2 young GC is needed to promote object into OldGen + Asserts.assertTrue(wb.isObjectInOldGen(obj)); + } + } +}
--- a/hotspot/test/runtime/CompressedOops/CompressedClassPointers.java Thu Sep 04 13:26:26 2014 -0700 +++ b/hotspot/test/runtime/CompressedOops/CompressedClassPointers.java Wed Jul 05 19:59:54 2017 +0200 @@ -26,7 +26,6 @@ * @bug 8024927 * @summary Testing address of compressed class pointer space as best as possible. * @library /testlibrary - * @ignore 8055164 */ import com.oracle.java.testlibrary.*; @@ -89,7 +88,6 @@ "-version"); OutputAnalyzer output = new OutputAnalyzer(pb.start()); output.shouldContain("HeapBaseMinAddress must be at least"); - output.shouldContain("HotSpot"); output.shouldHaveExitValue(0); }
--- a/hotspot/test/runtime/NMT/CommandLineEmptyArgument.java Thu Sep 04 13:26:26 2014 -0700 +++ b/hotspot/test/runtime/NMT/CommandLineEmptyArgument.java Wed Jul 05 19:59:54 2017 +0200 @@ -26,7 +26,6 @@ * @key nmt * @summary Empty argument to NMT should result in an informative error message * @library /testlibrary - * @ignore 8055051 */ import com.oracle.java.testlibrary.*;
--- a/hotspot/test/runtime/NMT/JcmdDetailDiff.java Thu Sep 04 13:26:26 2014 -0700 +++ b/hotspot/test/runtime/NMT/JcmdDetailDiff.java Wed Jul 05 19:59:54 2017 +0200 @@ -62,21 +62,18 @@ output = new OutputAnalyzer(pb.start()); output.shouldContain("Test (reserved=256KB +256KB, committed=0KB)"); - output.shouldContain("WB_NMTReserveMemory"); wb.NMTCommitMemory(addr, commitSize); pb.command(new String[] { JDKToolFinder.getJDKTool("jcmd"), pid, "VM.native_memory", "detail.diff", "scale=KB"}); output = new OutputAnalyzer(pb.start()); output.shouldContain("Test (reserved=256KB +256KB, committed=128KB +128KB)"); - output.shouldContain("WB_NMTReserveMemory"); wb.NMTUncommitMemory(addr, commitSize); pb.command(new String[] { JDKToolFinder.getJDKTool("jcmd"), pid, "VM.native_memory", "detail.diff", "scale=KB"}); output = new OutputAnalyzer(pb.start()); output.shouldContain("Test (reserved=256KB +256KB, committed=0KB)"); - output.shouldContain("WB_NMTReserveMemory"); wb.NMTReleaseMemory(addr, reserveSize); pb.command(new String[] { JDKToolFinder.getJDKTool("jcmd"), pid, "VM.native_memory", "detail.diff", "scale=KB"});
--- a/hotspot/test/runtime/NMT/MallocSiteHashOverflow.java Thu Sep 04 13:26:26 2014 -0700 +++ b/hotspot/test/runtime/NMT/MallocSiteHashOverflow.java Wed Jul 05 19:59:54 2017 +0200 @@ -22,10 +22,9 @@ */ /* - * @key stress * @test * @summary Test corner case that overflows malloc site hashtable bucket - * @key nmt jcmd + * @key nmt jcmd stress * @library /testlibrary /testlibrary/whitebox * @ignore - This test is disabled since it will stress NMT and timeout during normal testing * @build MallocSiteHashOverflow
--- a/hotspot/test/runtime/NMT/MallocStressTest.java Thu Sep 04 13:26:26 2014 -0700 +++ b/hotspot/test/runtime/NMT/MallocStressTest.java Wed Jul 05 19:59:54 2017 +0200 @@ -22,10 +22,9 @@ */ /* - * @key stress * @test * @summary Stress test for malloc tracking - * @key nmt jcmd + * @key nmt jcmd stress * @library /testlibrary /testlibrary/whitebox * @build MallocStressTest * @ignore - This test is disabled since it will stress NMT and timeout during normal testing
--- a/hotspot/test/runtime/NMT/NMTWithCDS.java Thu Sep 04 13:26:26 2014 -0700 +++ b/hotspot/test/runtime/NMT/NMTWithCDS.java Wed Jul 05 19:59:54 2017 +0200 @@ -34,14 +34,15 @@ public static void main(String[] args) throws Exception { ProcessBuilder pb; - pb = ProcessTools.createJavaProcessBuilder("-XX:SharedArchiveFile=./sample.jsa", "-Xshare:dump"); + pb = ProcessTools.createJavaProcessBuilder( + "-XX:+UnlockDiagnosticVMOptions", "-XX:SharedArchiveFile=./sample.jsa", "-Xshare:dump"); OutputAnalyzer output = new OutputAnalyzer(pb.start()); try { output.shouldContain("Loading classes to share"); output.shouldHaveExitValue(0); pb = ProcessTools.createJavaProcessBuilder( - "-XX:NativeMemoryTracking=detail", "-XX:SharedArchiveFile=./sample.jsa", "-Xshare:on", "-version"); + "-XX:+UnlockDiagnosticVMOptions", "-XX:NativeMemoryTracking=detail", "-XX:SharedArchiveFile=./sample.jsa", "-Xshare:on", "-version"); output = new OutputAnalyzer(pb.start()); output.shouldContain("sharing"); output.shouldHaveExitValue(0);
--- a/hotspot/test/runtime/NMT/VirtualAllocCommitUncommitRecommit.java Thu Sep 04 13:26:26 2014 -0700 +++ b/hotspot/test/runtime/NMT/VirtualAllocCommitUncommitRecommit.java Wed Jul 05 19:59:54 2017 +0200 @@ -26,7 +26,6 @@ * @summary Test reserve/commit/uncommit/release of virtual memory and that we track it correctly * @key nmt jcmd * @library /testlibrary /testlibrary/whitebox - * @ignore * @build VirtualAllocCommitUncommitRecommit * @run main ClassFileInstaller sun.hotspot.WhiteBox * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:NativeMemoryTracking=detail VirtualAllocCommitUncommitRecommit @@ -43,8 +42,8 @@ public static void main(String args[]) throws Exception { OutputAnalyzer output; - long commitSize = 4 * 1024; // 4KB - long reserveSize = 1024 * 1024; // 1024KB + long commitSize = 128 * 1024; // 128KB + long reserveSize = 4 * 1024 * 1024; // 4096KB long addr; String pid = Integer.toString(ProcessTools.getProcessId()); @@ -63,11 +62,11 @@ "VM.native_memory", "detail" }); output = new OutputAnalyzer(pb.start()); - output.shouldContain("Test (reserved=1024KB, committed=0KB)"); + output.shouldContain("Test (reserved=4096KB, committed=0KB)"); if (has_nmt_detail) { output.shouldMatch("\\[0x[0]*" + Long.toHexString(addr) + " - 0x[0]*" + Long.toHexString(addr + reserveSize) - + "\\] reserved 1024KB for Test"); + + "\\] reserved 4096KB for Test"); } long addrA = addr; @@ -84,24 +83,24 @@ wb.NMTCommitMemory(addrD, commitSize); output = new OutputAnalyzer(pb.start()); - output.shouldContain("Test (reserved=1024KB, committed=16KB)"); + output.shouldContain("Test (reserved=4096KB, committed=512KB)"); if (has_nmt_detail) { output.shouldMatch("\\[0x[0]*" + Long.toHexString(addr) + " - 0x[0]*" + Long.toHexString(addr + reserveSize) - + "\\] reserved 1024KB for Test"); + + "\\] reserved 4096KB for Test"); } // uncommit BC wb.NMTUncommitMemory(addrB, commitSize); wb.NMTUncommitMemory(addrC, commitSize); output = new OutputAnalyzer(pb.start()); - output.shouldContain("Test (reserved=1024KB, committed=8KB)"); + output.shouldContain("Test (reserved=4096KB, committed=256KB)"); if (has_nmt_detail) { output.shouldMatch("\\[0x[0]*" + Long.toHexString(addr) + " - 0x[0]*" + Long.toHexString(addr + reserveSize) - + "\\] reserved 1024KB for Test"); + + "\\] reserved 4096KB for Test"); } // commit EF @@ -109,22 +108,22 @@ wb.NMTCommitMemory(addrF, commitSize); output = new OutputAnalyzer(pb.start()); - output.shouldContain("Test (reserved=1024KB, committed=16KB)"); + output.shouldContain("Test (reserved=4096KB, committed=512KB)"); if (has_nmt_detail) { output.shouldMatch("\\[0x[0]*" + Long.toHexString(addr) + " - 0x[0]*" + Long.toHexString(addr + reserveSize) - + "\\] reserved 1024KB for Test"); + + "\\] reserved 4096KB for Test"); } // uncommit A wb.NMTUncommitMemory(addrA, commitSize); output = new OutputAnalyzer(pb.start()); - output.shouldContain("Test (reserved=1024KB, committed=12KB)"); + output.shouldContain("Test (reserved=4096KB, committed=384KB)"); if (has_nmt_detail) { output.shouldMatch("\\[0x[0]*" + Long.toHexString(addr) + " - 0x[0]*" + Long.toHexString(addr + reserveSize) - + "\\] reserved 1024KB for Test"); + + "\\] reserved 4096KB for Test"); } // commit ABC @@ -133,11 +132,11 @@ wb.NMTCommitMemory(addrC, commitSize); output = new OutputAnalyzer(pb.start()); - output.shouldContain("Test (reserved=1024KB, committed=24KB)"); + output.shouldContain("Test (reserved=4096KB, committed=768KB)"); if (has_nmt_detail) { output.shouldMatch("\\[0x[0]*" + Long.toHexString(addr) + " - 0x[0]*" + Long.toHexString(addr + reserveSize) - + "\\] reserved 1024KB for Test"); + + "\\] reserved 4096KB for Test"); } // uncommit ABCDEF @@ -149,11 +148,11 @@ wb.NMTUncommitMemory(addrF, commitSize); output = new OutputAnalyzer(pb.start()); - output.shouldContain("Test (reserved=1024KB, committed=0KB)"); + output.shouldContain("Test (reserved=4096KB, committed=0KB)"); if (has_nmt_detail) { output.shouldMatch("\\[0x[0]*" + Long.toHexString(addr) + " - 0x[0]*" + Long.toHexString(addr + reserveSize) - + "\\] reserved 1024KB for Test"); + + "\\] reserved 4096KB for Test"); } // release @@ -161,6 +160,6 @@ output = new OutputAnalyzer(pb.start()); output.shouldNotContain("Test (reserved="); output.shouldNotMatch("\\[0x[0]*" + Long.toHexString(addr) + " - 0x[0]*" - + Long.toHexString(addr + reserveSize) + "\\] reserved"); + + Long.toHexString(addr + reserveSize) + "\\] reserved 4096KB for Test"); } }
--- a/hotspot/test/runtime/jsig/Test8017498.sh Thu Sep 04 13:26:26 2014 -0700 +++ b/hotspot/test/runtime/jsig/Test8017498.sh Wed Jul 05 19:59:54 2017 +0200 @@ -31,15 +31,14 @@ ## @bug 8022301 ## @bug 8025519 ## @summary sigaction(sig) results in process hang/timed-out if sig is much greater than SIGRTMAX -## @ignore 8041727 ## @run shell/timeout=60 Test8017498.sh ## -if [ "${TESTSRC}" = "" ] -then - TESTSRC=${PWD} +if [ -z "${TESTSRC}" ]; then + TESTSRC="${PWD}" echo "TESTSRC not set. Using "${TESTSRC}" as default" fi + echo "TESTSRC=${TESTSRC}" ## Adding common setup Variables for running shell tests. . ${TESTSRC}/../../test_env.sh @@ -52,13 +51,13 @@ Linux) echo "Testing on Linux" gcc_cmd=`which gcc` - if [ "x$gcc_cmd" == "x" ]; then + if [ -z "$gcc_cmd" ]; then echo "WARNING: gcc not found. Cannot execute test." 2>&1 exit 0; fi MY_LD_PRELOAD=${TESTJAVA}${FS}jre${FS}lib${FS}${VM_CPU}${FS}libjsig.so - if [ "$VM_BITS" == "32" ] && [ "$VM_CPU" != "arm" ] && [ "$VM_CPU" != "ppc" ]; then - EXTRA_CFLAG=-m32 + if [ "$VM_BITS" = "32" ] && [ "$VM_CPU" != "arm" ] && [ "$VM_CPU" != "ppc" ]; then + EXTRA_CFLAG=-m32 fi echo MY_LD_PRELOAD = ${MY_LD_PRELOAD} ;; @@ -70,7 +69,7 @@ THIS_DIR=. -cp ${TESTSRC}${FS}*.java ${THIS_DIR} +cp "${TESTSRC}${FS}"*.java "${THIS_DIR}" ${COMPILEJAVA}${FS}bin${FS}javac *.java $gcc_cmd -DLINUX -fPIC -shared \ @@ -80,16 +79,19 @@ -I${COMPILEJAVA}${FS}include${FS}linux \ ${TESTSRC}${FS}TestJNI.c +if [ $? -ne 0 ] ; then + echo "Compile failed, Ignoring failed compilation and forcing the test to pass" + exit 0 +fi + # run the java test in the background cmd="LD_PRELOAD=$MY_LD_PRELOAD \ ${TESTJAVA}${FS}bin${FS}java \ -Djava.library.path=. -server TestJNI 100" -echo "$cmd > test.out 2>&1" -eval $cmd > test.out 2>&1 +echo "$cmd > test.out" +eval $cmd > test.out -grep "old handler" test.out > ${NULL} -if [ $? = 0 ] -then +if grep "old handler" test.out > ${NULL}; then echo "Test Passed" exit 0 fi
--- a/jdk/.hgtags Thu Sep 04 13:26:26 2014 -0700 +++ b/jdk/.hgtags Wed Jul 05 19:59:54 2017 +0200 @@ -271,3 +271,4 @@ dde9f5cfde5f46e62ceb5fab81151578e5277aef jdk9-b26 f0870554049807d3392bd7976ab114f7f2b7bafa jdk9-b27 1828f73b35cfe35e460e41fd6e087ab1f83e0621 jdk9-b28 +2da27e8e2c865e154f0c2eb9009f011a44649b11 jdk9-b29
--- a/jdk/make/CreateJars.gmk Thu Sep 04 13:26:26 2014 -0700 +++ b/jdk/make/CreateJars.gmk Wed Jul 05 19:59:54 2017 +0200 @@ -76,13 +76,24 @@ iw ja ko lt lv mk ms mt nl no pl pt ro ru sk sl sq sr sv \ th tr uk vi zh -LOCALEDATA_INCLUDES := $(addprefix sun/text/resources/, $(LOCALEDATA_INCLUDE_LOCALES)) \ +LOCALEDATA_INCLUDES := sun/util/resources/provider/NonEnLocaleDataMetaInfo.class +LOCALEDATA_INCLUDES += $(addprefix sun/text/resources/, $(LOCALEDATA_INCLUDE_LOCALES)) \ $(addprefix sun/util/resources/, $(LOCALEDATA_INCLUDE_LOCALES)) -$(eval $(call SetupArchive,BUILD_LOCALEDATA_JAR, , \ - SRCS := $(JDK_OUTPUTDIR)/modules/jdk.localedata, \ +LOCALEDATA_SERVICES_DIR := $(IMAGES_OUTPUTDIR)/localemetainfo + +LOCALEDATA_METAINF_SERVICES := $(LOCALEDATA_SERVICES_DIR)/META-INF/services/sun.util.locale.provider.LocaleDataMetaInfo + +$(LOCALEDATA_METAINF_SERVICES): $(JDK_TOPDIR)/src/jdk.localedata/META-INF/localedata-services/sun.util.locale.provider.LocaleDataMetaInfo + $(install-file) + +$(eval $(call SetupArchive,BUILD_LOCALEDATA_JAR, \ + $(LOCALEDATA_METAINF_SERVICES), \ + SRCS := $(JDK_OUTPUTDIR)/modules/jdk.localedata \ + $(LOCALEDATA_SERVICES_DIR), \ SUFFIXES := .class _dict _th, \ INCLUDES := $(LOCALEDATA_INCLUDES), \ + EXTRA_FILES := META-INF/services/sun.util.locale.provider.LocaleDataMetaInfo, \ JAR := $(IMAGES_OUTPUTDIR)/lib/ext/localedata.jar, \ SKIP_METAINF := true)) @@ -210,6 +221,8 @@ sun/tools/tree \ sun/tools/util \ sun/util/cldr/CLDRLocaleDataMetaInfo.class \ + sun/util/resources/provider/NonEnLocaleDataMetaInfo.class \ + META-INF/services/sun.util.locale.provider.LocaleDataMetaInfo \ sun/util/resources/cldr \ $(LOCALEDATA_INCLUDES) \ com/oracle/jrockit/jfr \ @@ -429,13 +442,23 @@ CLDRDATA_JAR_DST := $(IMAGES_OUTPUTDIR)/lib/ext/cldrdata.jar -$(eval $(call SetupArchive,BUILD_CLDRDATA_JAR, , \ +CLDR_SERVICES_DIR := $(IMAGES_OUTPUTDIR)/cldrmetainfo + +CLDR_METAINF_SERVICES := $(CLDR_SERVICES_DIR)/META-INF/services/sun.util.locale.provider.LocaleDataMetaInfo + +$(CLDR_METAINF_SERVICES): $(JDK_TOPDIR)/src/jdk.localedata/META-INF/cldrdata-services/sun.util.locale.provider.LocaleDataMetaInfo + $(install-file) + +$(eval $(call SetupArchive,BUILD_CLDRDATA_JAR, \ + $(CLDR_METAINF_SERVICES), \ SRCS := $(JDK_OUTPUTDIR)/modules/jdk.localedata \ - $(JDK_OUTPUTDIR)/modules/java.base, \ + $(JDK_OUTPUTDIR)/modules/java.base \ + $(CLDR_SERVICES_DIR), \ SUFFIXES := .class, \ INCLUDES := sun/text/resources/cldr \ sun/util/cldr/CLDRLocaleDataMetaInfo.class \ sun/util/resources/cldr, \ + EXTRA_FILES := META-INF/services/sun.util.locale.provider.LocaleDataMetaInfo, \ JAR := $(CLDRDATA_JAR_DST), \ EXTRA_MANIFEST_ATTR := CLDR-Version: $(CLDRVERSION), \ SKIP_METAINF := true))
--- a/jdk/make/copy/Copy-java.desktop.gmk Thu Sep 04 13:26:26 2014 -0700 +++ b/jdk/make/copy/Copy-java.desktop.gmk Wed Jul 05 19:59:54 2017 +0200 @@ -43,25 +43,6 @@ ################################################################################ -ICCPROFILE_DEST_DIR := $(LIB_DST_DIR)/cmm - -ifdef OPENJDK - ICCPROFILE_SRC_DIR := $(JDK_TOPDIR)/src/java.desktop/share/conf/cmm/lcms -else - ICCPROFILE_SRC_DIR := $(JDK_TOPDIR)/src/closed/java.desktop/share/conf/cmm/kcms -endif - -ICCPROFILE_SRCS := $(wildcard $(ICCPROFILE_SRC_DIR)/*.pf) -ICCPROFILE_TARGET_FILES := $(subst $(ICCPROFILE_SRC_DIR),$(ICCPROFILE_DEST_DIR),$(ICCPROFILE_SRCS)) - -$(ICCPROFILE_DEST_DIR)%.pf: $(ICCPROFILE_SRC_DIR)%.pf - $(call install-file) - $(CHMOD) 444 $@ - -DESKTOP_CONF_FILES += $(ICCPROFILE_TARGET_FILES) - -################################################################################ - ifneq ($(FREETYPE_BUNDLE_LIB_PATH), ) # We need to bundle the freetype library, so it will be available at runtime as well as link time. #
--- a/jdk/make/data/currency/CurrencyData.properties Thu Sep 04 13:26:26 2014 -0700 +++ b/jdk/make/data/currency/CurrencyData.properties Wed Jul 05 19:59:54 2017 +0200 @@ -1,5 +1,5 @@ # -# Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2000, 2014, 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 @@ -28,7 +28,7 @@ # Version of the currency code information in this class. # It is a serial number that accompanies with each amendment. -dataVersion=156 +dataVersion=159 # List of all valid ISO 4217 currency codes. # To ensure compatibility, do not remove codes. @@ -332,7 +332,7 @@ # LIECHTENSTEIN LI=CHF # LITHUANIA -LT=LTL +LT=LTL;2014-12-31-22-00-00;EUR # LUXEMBOURG LU=EUR # MACAU
--- a/jdk/make/gensrc/GensrcLocaleDataMetaInfo.gmk Thu Sep 04 13:26:26 2014 -0700 +++ b/jdk/make/gensrc/GensrcLocaleDataMetaInfo.gmk Wed Jul 05 19:59:54 2017 +0200 @@ -58,7 +58,14 @@ # Locales that don't have any resource files should be included here. ALL_NON_EN_LOCALES := ja-JP-JP nb-NO nn-NO th-TH-TH -SED_ARGS := -e 's|$(HASH)warn This file is preprocessed before being compiled|// -- This file was mechanically generated: Do not edit! -- //|g' +SED_ENARGS := -e 's|$(HASH)warn This file is preprocessed before being compiled|// -- This file was mechanically generated: Do not edit! -- //|g' +SED_NONENARGS := $(SED_ENARGS) + +# Fill in the languages and package names +SED_ENARGS += -e 's/$(HASH)Lang$(HASH)/En/' \ + -e 's/$(HASH)Package$(HASH)/sun.util.locale.provider/' +SED_NONENARGS += -e 's/$(HASH)Lang$(HASH)/NonEn/' \ + -e 's/$(HASH)Package$(HASH)/sun.util.resources.provider/' # This macro creates a sed expression that substitues for example: # #FormatData_ENLocales# with: en% locales. @@ -78,8 +85,8 @@ ALL_NON_EN_LOCALES += $$($1_NON_EN_LOCALES) # Don't sed in a space if there are no locales. - SED_ARGS += -e 's/$$(HASH)$1_ENLocales$$(HASH)/$$(if $$($1_EN_LOCALES),$$(SPACE)$$($1_EN_LOCALES),)/g' - SED_ARGS += -e 's/$$(HASH)$1_NonENLocales$$(HASH)/$$(if $$($1_NON_EN_LOCALES),$$(SPACE)$$($1_NON_EN_LOCALES),)/g' + SED_ENARGS += -e 's/$$(HASH)$1_Locales$$(HASH)/$$(if $$($1_EN_LOCALES),$$(SPACE)$$($1_EN_LOCALES),)/g' + SED_NONENARGS += -e 's/$$(HASH)$1_Locales$$(HASH)/$$(if $$($1_NON_EN_LOCALES),$$(SPACE)$$($1_NON_EN_LOCALES),)/g' endef #sun.text.resources.FormatData @@ -106,17 +113,25 @@ #sun.util.resources.CalendarData $(eval $(call CaptureLocale,CalendarData)) -SED_ARGS += -e 's/$(HASH)AvailableLocales_ENLocales$(HASH)/$(sort $(ALL_EN_LOCALES))/g' -SED_ARGS += -e 's/$(HASH)AvailableLocales_NonENLocales$(HASH)/$(sort $(ALL_NON_EN_LOCALES))/g' +SED_ENARGS += -e 's/$(HASH)AvailableLocales_Locales$(HASH)/$(sort $(ALL_EN_LOCALES))/g' +SED_NONENARGS += -e 's/$(HASH)AvailableLocales_Locales$(HASH)/$(sort $(ALL_NON_EN_LOCALES))/g' -$(JDK_OUTPUTDIR)/gensrc/java.base/sun/util/locale/provider/LocaleDataMetaInfo.java: \ +$(JDK_OUTPUTDIR)/gensrc/java.base/sun/util/locale/provider/EnLocaleDataMetaInfo.java: \ $(JDK_TOPDIR)/src/java.base/share/classes/sun/util/locale/provider/LocaleDataMetaInfo-XLocales.java.template $(MKDIR) -p $(@D) - $(ECHO) Creating sun/util/LocaleDataMetaInfo.java from $(words $(LOCALE_RESOURCES)) found resources. + $(ECHO) Creating sun/util/locale/provider/EnLocaleDataMetaInfo.java from $(words $(LOCALE_RESOURCES)) found resources. $(PRINTF) "PREV_LOCALE_RESOURCES:=$(LOCALE_RESOURCES)" > $(JDK_OUTPUTDIR)/gensrc/_the.locale_resources - $(SED) $(SED_ARGS) $< > $@ + $(SED) $(SED_ENARGS) $< > $@ -GENSRC_LOCALEDATAMETAINFO := $(JDK_OUTPUTDIR)/gensrc/java.base/sun/util/locale/provider/LocaleDataMetaInfo.java +$(JDK_OUTPUTDIR)/gensrc/jdk.localedata/sun/util/resources/provider/NonEnLocaleDataMetaInfo.java: \ + $(JDK_TOPDIR)/src/java.base/share/classes/sun/util/locale/provider/LocaleDataMetaInfo-XLocales.java.template + $(MKDIR) -p $(@D) + $(ECHO) Creating sun/util/resources/provider/NonEnLocaleDataMetaInfo.java from $(words $(LOCALE_RESOURCES)) found resources. + $(PRINTF) "PREV_LOCALE_RESOURCES:=$(LOCALE_RESOURCES)" > $(JDK_OUTPUTDIR)/gensrc/_the.locale_resources + $(SED) $(SED_NONENARGS) $< > $@ + +GENSRC_LOCALEDATAMETAINFO := $(JDK_OUTPUTDIR)/gensrc/java.base/sun/util/locale/provider/EnLocaleDataMetaInfo.java \ + $(JDK_OUTPUTDIR)/gensrc/jdk.localedata/sun/util/resources/provider/NonEnLocaleDataMetaInfo.java ################################################################################
--- a/jdk/make/profile-includes.txt Thu Sep 04 13:26:26 2014 -0700 +++ b/jdk/make/profile-includes.txt Wed Jul 05 19:59:54 2017 +0200 @@ -168,11 +168,6 @@ $(OPENJDK_TARGET_CPU_LEGACY_LIB)/$(LIBRARY_PREFIX)t2k$(SHARED_LIBRARY_SUFFIX) \ $(OPENJDK_TARGET_CPU_LEGACY_LIB)/$(LIBRARY_PREFIX)unpack$(SHARED_LIBRARY_SUFFIX) \ charsets.jar \ - cmm/CIEXYZ.pf \ - cmm/GRAY.pf \ - cmm/LINEAR_RGB.pf \ - cmm/PYCC.pf \ - cmm/sRGB.pf \ ext/cldrdata.jar \ ext/dnsns.jar \ ext/nashorn.jar \
--- a/jdk/make/src/classes/build/tools/cldrconverter/CLDRConverter.java Thu Sep 04 13:26:26 2014 -0700 +++ b/jdk/make/src/classes/build/tools/cldrconverter/CLDRConverter.java Wed Jul 05 19:59:54 2017 +0200 @@ -431,7 +431,7 @@ allLocales.addAll(metaInfo.get("LocaleNames")); allLocales.addAll(metaInfo.get("CalendarData")); allLocales.addAll(metaInfo.get("FormatData")); - metaInfo.put("All", allLocales); + metaInfo.put("AvailableLocales", allLocales); } bundleGenerator.generateMetaInfo(metaInfo);
--- a/jdk/make/src/classes/build/tools/cldrconverter/ResourceBundleGenerator.java Thu Sep 04 13:26:26 2014 -0700 +++ b/jdk/make/src/classes/build/tools/cldrconverter/ResourceBundleGenerator.java Wed Jul 05 19:59:54 2017 +0200 @@ -159,8 +159,10 @@ out.println(CopyrightHeaders.getOpenJDKCopyright()); out.println("package sun.util.cldr;\n\n" - + "import java.util.ListResourceBundle;\n"); - out.printf("public class %s extends ListResourceBundle {\n", METAINFO_CLASS); + + "import java.util.ListResourceBundle;\n" + + "import sun.util.locale.provider.LocaleProviderAdapter;\n" + + "import sun.util.locale.provider.LocaleDataMetaInfo;\n"); + out.printf("public class %s extends ListResourceBundle implements LocaleDataMetaInfo {\n", METAINFO_CLASS); out.println(" @Override\n" + " protected final Object[][] getContents() {\n" + " final Object[][] data = new Object[][] {"); @@ -168,7 +170,15 @@ out.printf(" { \"%s\",\n", key); out.printf(" \"%s\" },\n", toLocaleList(metaInfo.get(key))); } - out.println(" };\n return data;\n }\n}"); + out.println(" };\n return data;\n }\n\n"); + + out.println(" public LocaleProviderAdapter.Type getType() {\n" + + " return LocaleProviderAdapter.Type.CLDR;\n" + + " }\n\n"); + + out.println(" public String availableLanguageTags(String category) {\n" + + " return getString(category);\n" + + " };\n}"); } }
--- a/jdk/src/demo/share/java2d/J2DBench/Makefile Thu Sep 04 13:26:26 2014 -0700 +++ b/jdk/src/demo/share/java2d/J2DBench/Makefile Wed Jul 05 19:59:54 2017 +0200 @@ -72,6 +72,7 @@ J2DBENCH_RESOURCES = \ $(CLASSES)/j2dbench/tests/iio/images \ + $(CLASSES)/j2dbench/tests/cmm/images \ $(CLASSES)/j2dbench/tests/text/textdata SCM_DIRs = .hg .svn CVS RCS SCCS Codemgr_wsdata deleted_files @@ -101,6 +102,10 @@ cp -r $< $@ cd $@ && rm -rf $(SCM_DIRs) +$(CLASSES)/j2dbench/tests/cmm/images: $(RESOURCES)/cmm_images + cp -r $< $@ + cd $@ && rm -rf $(SCM_DIRs) + $(CLASSES)/j2dbench.manifest: echo "Main-Class: j2dbench.J2DBench" > $@ @@ -115,7 +120,7 @@ mkdirs: $(DIST) $(CLASSES) $(CLASSES)/j2dbench/%.class: $(SOURCEPATH)/j2dbench/%.java - javac -source 1.2 -target 1.2 -d $(CLASSES) -sourcepath $(SOURCEPATH) $< + javac -g:none -source 1.6 -target 1.6 -d $(CLASSES) -sourcepath $(SOURCEPATH) $< clean: rm -rf $(CLASSES)
--- a/jdk/src/demo/share/java2d/J2DBench/README Thu Sep 04 13:26:26 2014 -0700 +++ b/jdk/src/demo/share/java2d/J2DBench/README Wed Jul 05 19:59:54 2017 +0200 @@ -19,10 +19,9 @@ Minimum requirements ----------------------------------------------------------------------- -The benchmark requires at least jdk1.4 to compile and -at least jdk1.2** to run. - -** Note: the goal is to make the benchmark run on 1.1.x as well. +The benchmark requires at least jdk1.4 to compile and run. Note that +source/target is set to 1.6 in the makefile and build.xml, because of +support in jdk 9 compiler. ----------------------------------------------------------------------- How To Compile @@ -125,7 +124,7 @@ After the options file is created, start J2DBench in batch mode to run the benchmarks for the default pipeline: -#> java -jar dest/J2DBench.jar -batch -loadopts options/default.opt \ +#> java -jar dist/J2DBench.jar -batch -loadopts options/default.opt \ -saveres default.res -title "Rendering - Default ppl" \ -desc "Rendering tests with the default pipeline" @@ -136,13 +135,13 @@ "Run Tests" in the J2DBench dialog) Now run the benchmark with opengl pipeline: -#> java -Dsun.java2d.opengl=True -jar dest/J2DBench.jar -batch \ +#> java -Dsun.java2d.opengl=True -jar dist/J2DBench.jar -batch \ -loadopts options/default.opt \ -saveres opengl.res -title "Rendering - OpenGL" \ -desc "Rendering tests with OpenGL pipeline" Now let's analyze the results using J2DAnalyzer: -#> java -jar dest/J2DAnalyzer.jar default.res opengl.res +#> java -jar dist/J2DAnalyzer.jar default.res opengl.res Note that you can compare more than two sets of results, see J2DAnalyzer's help page.
--- a/jdk/src/demo/share/java2d/J2DBench/build.xml Thu Sep 04 13:26:26 2014 -0700 +++ b/jdk/src/demo/share/java2d/J2DBench/build.xml Wed Jul 05 19:59:54 2017 +0200 @@ -49,7 +49,7 @@ <target name="compile" depends="init" description="compile the source " > <!-- Compile the java code from ${src} into ${build} --> - <javac debug="flase" source="1.5" target="1.5" srcdir="${src}" destdir="${build}"/> + <javac debug="off" source="1.6" target="1.6" srcdir="${src}" destdir="${build}"/> </target> <target name="run" depends="dist"
--- a/jdk/src/demo/share/java2d/J2DBench/src/j2dbench/tests/cmm/CMMTests.java Thu Sep 04 13:26:26 2014 -0700 +++ b/jdk/src/demo/share/java2d/J2DBench/src/j2dbench/tests/cmm/CMMTests.java Wed Jul 05 19:59:54 2017 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2014, Oracle and/or its affiliates. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -39,23 +39,24 @@ package j2dbench.tests.cmm; -import j2dbench.Group; -import j2dbench.Option; -import j2dbench.Result; -import j2dbench.Test; -import j2dbench.TestEnvironment; import java.awt.color.ColorSpace; import java.awt.color.ICC_ColorSpace; import java.awt.color.ICC_Profile; import java.io.IOException; import java.io.InputStream; +import j2dbench.Group; +import j2dbench.Option; +import j2dbench.Result; +import j2dbench.Test; +import j2dbench.TestEnvironment; + public class CMMTests extends Test { protected static Group cmmRoot; protected static Group cmmOptRoot; protected static Option csList; - protected static Option usePlatfromProfiles; + protected static Option usePlatformProfiles; public static void init() { cmmRoot = new Group("cmm", "Color Management Benchmarks"); @@ -64,9 +65,9 @@ cmmOptRoot = new Group(cmmRoot, "opts", "General Options"); /* - usePlatfromProfiles = - new Option.Enable(cmmOptRoot, "csPlatfrom", - "Use Platfrom Profiles", false); + usePlatformProfiles = + new Option.Enable(cmmOptRoot, "csPlatform", + "Use Platform Profiles", false); */ int[] colorspaces = new int[] { ColorSpace.CS_sRGB, @@ -92,10 +93,10 @@ protected static ColorSpace getColorSpace(TestEnvironment env) { ColorSpace cs; - Boolean usePlatfrom = true; //(Boolean)env.getModifier(usePlatfromProfiles); + boolean usePlatform = true; //(Boolean)env.getModifier(usePlatformProfiles); int cs_code = env.getIntValue(csList); - if (usePlatfrom) { + if (usePlatform) { cs = ColorSpace.getInstance(cs_code); } else { String resource = "profiles/"; @@ -136,17 +137,14 @@ addDependencies(cmmOptRoot, true); } - @Override public Object initTest(TestEnvironment te, Result result) { throw new UnsupportedOperationException("Not supported yet."); } - @Override public void runTest(Object o, int i) { throw new UnsupportedOperationException("Not supported yet."); } - @Override public void cleanupTest(TestEnvironment te, Object o) { throw new UnsupportedOperationException("Not supported yet."); }
--- a/jdk/src/demo/share/java2d/J2DBench/src/j2dbench/tests/cmm/ColorConvertOpTests.java Thu Sep 04 13:26:26 2014 -0700 +++ b/jdk/src/demo/share/java2d/J2DBench/src/j2dbench/tests/cmm/ColorConvertOpTests.java Wed Jul 05 19:59:54 2017 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2014, Oracle and/or its affiliates. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -39,11 +39,6 @@ package j2dbench.tests.cmm; -import j2dbench.Group; -import j2dbench.Option; -import j2dbench.Result; -import j2dbench.TestEnvironment; -import j2dbench.tests.iio.IIOTests; import java.awt.AlphaComposite; import java.awt.Color; import java.awt.Graphics2D; @@ -53,15 +48,22 @@ import java.awt.image.ColorConvertOp; import java.awt.image.Raster; import java.awt.image.WritableRaster; + import javax.imageio.ImageIO; +import j2dbench.Group; +import j2dbench.Option; +import j2dbench.Result; +import j2dbench.TestEnvironment; +import j2dbench.tests.iio.IIOTests; + public class ColorConvertOpTests extends ColorConversionTests { - private static enum ImageContent { - BLANK("bank", "Blank (opaque black)"), - RANDOM("random", "Random"), - VECTOR("vector", "Vector Art"), - PHOTO("photo", "Photograph"); + private static class ImageContent { + static ImageContent BLANK = new ImageContent("bank", "Blank (opaque black)"); + static ImageContent RANDOM = new ImageContent("random", "Random"); + static ImageContent VECTOR = new ImageContent("vector", "Vector Art"); + static ImageContent PHOTO= new ImageContent("photo", "Photograph"); public final String name; public final String descr; @@ -70,15 +72,19 @@ this.name = name; this.descr = descr; } + + public static ImageContent[] values() { + return new ImageContent[]{BLANK, RANDOM, VECTOR, PHOTO}; + } } - private static enum ImageType { - INT_ARGB(BufferedImage.TYPE_INT_ARGB, "INT_ARGB", "TYPE_INT_ARGB"), - INT_RGB(BufferedImage.TYPE_INT_RGB, "INT_RGB", "TYPE_INT_RGB"), - INT_BGR(BufferedImage.TYPE_INT_BGR, "INT_BGR", "TYPE_INT_BGR"), - BYTE_3BYTE_BGR(BufferedImage.TYPE_3BYTE_BGR, "3BYTE_BGR", "TYPE_3BYTE_BGR"), - BYTE_4BYTE_ABGR(BufferedImage.TYPE_4BYTE_ABGR, "4BYTE_BGR", "TYPE_4BYTE_BGR"), - COMPATIBLE_DST(0, "Compatible", "Compatible destination"); + private static class ImageType { + static ImageType INT_ARGB = new ImageType(BufferedImage.TYPE_INT_ARGB, "INT_ARGB", "TYPE_INT_ARGB"); + static ImageType INT_RGB = new ImageType(BufferedImage.TYPE_INT_RGB, "INT_RGB", "TYPE_INT_RGB"); + static ImageType INT_BGR = new ImageType(BufferedImage.TYPE_INT_BGR, "INT_BGR", "TYPE_INT_BGR"); + static ImageType BYTE_3BYTE_BGR = new ImageType(BufferedImage.TYPE_3BYTE_BGR, "3BYTE_BGR", "TYPE_3BYTE_BGR"); + static ImageType BYTE_4BYTE_ABGR = new ImageType(BufferedImage.TYPE_4BYTE_ABGR, "4BYTE_BGR", "TYPE_4BYTE_BGR"); + static ImageType COMPATIBLE_DST = new ImageType(0, "Compatible", "Compatible destination"); private ImageType(int type, String abbr, String descr) { this.type = type; @@ -89,11 +95,16 @@ public final int type; public final String abbrev; public final String descr; + + public static ImageType[] values() { + return new ImageType[]{INT_ARGB, INT_RGB, INT_BGR, + BYTE_3BYTE_BGR, BYTE_4BYTE_ABGR, COMPATIBLE_DST}; + } } - private static enum ListType { - SRC("srcType", "Source Images"), - DST("dstType", "Destination Images"); + private static class ListType { + static ListType SRC = new ListType("srcType", "Source Images"); + static ListType DST = new ListType("dstType", "Destination Images"); private ListType(String name, String description) { this.name = name; @@ -320,64 +331,55 @@ BufferedImage image; image = new BufferedImage(width, height, type); boolean hasAlpha = image.getColorModel().hasAlpha(); - switch (contentType) { - case RANDOM: - for (int y = 0; y < height; y++) { - for (int x = 0; x < width; x++) { - int rgb = (int)(Math.random() * 0xffffff); - if (hasAlpha) { - rgb |= 0x7f000000; - } - image.setRGB(x, y, rgb); + if (contentType == ImageContent.RANDOM) { + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + int rgb = (int) (Math.random() * 0xffffff); + if (hasAlpha) { + rgb |= 0x7f000000; } + image.setRGB(x, y, rgb); } - break; - case VECTOR: - { - Graphics2D g = image.createGraphics(); - if (hasAlpha) { - // fill background with a translucent color - g.setComposite(AlphaComposite.getInstance( - AlphaComposite.SRC, 0.5f)); - } - g.setColor(Color.blue); - g.fillRect(0, 0, width, height); - g.setComposite(AlphaComposite.Src); - g.setColor(Color.yellow); - g.fillOval(2, 2, width-4, height-4); - g.setColor(Color.red); - g.fillOval(4, 4, width-8, height-8); - g.setColor(Color.green); - g.fillRect(8, 8, width-16, height-16); - g.setColor(Color.white); - g.drawLine(0, 0, width, height); - g.drawLine(0, height, width, 0); - g.dispose(); - break; - } - case PHOTO: - { - Image photo = null; - try { - photo = ImageIO.read( - IIOTests.class.getResourceAsStream("images/photo.jpg")); - } catch (Exception e) { - System.err.println("error loading photo"); - e.printStackTrace(); - } - Graphics2D g = image.createGraphics(); - if (hasAlpha) { - g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC, - 0.5f)); - } - g.drawImage(photo, 0, 0, width, height, null); - g.dispose(); - break; - } - default: - break; + } } - + if (contentType == ImageContent.VECTOR) { + Graphics2D g = image.createGraphics(); + if (hasAlpha) { + // fill background with a translucent color + g.setComposite(AlphaComposite.getInstance( + AlphaComposite.SRC, 0.5f)); + } + g.setColor(Color.blue); + g.fillRect(0, 0, width, height); + g.setComposite(AlphaComposite.Src); + g.setColor(Color.yellow); + g.fillOval(2, 2, width-4, height-4); + g.setColor(Color.red); + g.fillOval(4, 4, width-8, height-8); + g.setColor(Color.green); + g.fillRect(8, 8, width-16, height-16); + g.setColor(Color.white); + g.drawLine(0, 0, width, height); + g.drawLine(0, height, width, 0); + g.dispose(); + } + if (contentType == ImageContent.PHOTO) { + Image photo = null; + try { + photo = ImageIO.read( + IIOTests.class.getResourceAsStream("images/photo.jpg")); + } catch (Exception e) { + System.err.println("error loading photo"); + e.printStackTrace(); + } + Graphics2D g = image.createGraphics(); + if (hasAlpha) { + g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC, + 0.5f)); + } + g.drawImage(photo, 0, 0, width, height, null); + g.dispose(); + } return image; } }
--- a/jdk/src/demo/share/java2d/J2DBench/src/j2dbench/tests/cmm/DataConversionTests.java Thu Sep 04 13:26:26 2014 -0700 +++ b/jdk/src/demo/share/java2d/J2DBench/src/j2dbench/tests/cmm/DataConversionTests.java Wed Jul 05 19:59:54 2017 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2014, Oracle and/or its affiliates. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -39,10 +39,11 @@ package j2dbench.tests.cmm; +import java.awt.color.ColorSpace; + import j2dbench.Group; import j2dbench.Result; import j2dbench.TestEnvironment; -import java.awt.color.ColorSpace; public class DataConversionTests extends ColorConversionTests { @@ -92,13 +93,11 @@ } } - @Override public Object initTest(TestEnvironment env, Result result) { ColorSpace cs = getColorSpace(env); return new Context(env, result, cs); } - @Override public void cleanupTest(TestEnvironment te, Object o) { }
--- a/jdk/src/demo/share/java2d/J2DBench/src/j2dbench/tests/cmm/EmbeddedProfileTests.java Thu Sep 04 13:26:26 2014 -0700 +++ b/jdk/src/demo/share/java2d/J2DBench/src/j2dbench/tests/cmm/EmbeddedProfileTests.java Wed Jul 05 19:59:54 2017 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -39,16 +39,18 @@ package j2dbench.tests.cmm; +import java.awt.image.BufferedImage; +import java.io.IOException; +import java.net.URL; + +import javax.imageio.ImageIO; +import javax.imageio.ImageReader; +import javax.imageio.stream.ImageInputStream; + import j2dbench.Group; import j2dbench.Option; import j2dbench.Result; import j2dbench.TestEnvironment; -import java.awt.image.BufferedImage; -import java.io.IOException; -import java.net.URL; -import javax.imageio.ImageIO; -import javax.imageio.ImageReader; -import javax.imageio.stream.ImageInputStream; /* This benchmark verifies how changes in cmm library affects image decoding */ public class EmbeddedProfileTests extends ColorConversionTests { @@ -68,10 +70,10 @@ new ReadImageTest(); } - private static enum IccImageResource { - SMALL("images/img_icc_small.jpg", "512x512", "Small: 512x512"), - MEDIUM("images/img_icc_medium.jpg", "2048x2048", "Medium: 2048x2048"), - LARGE("images/img_icc_large.jpg", "4096x4096", "Large: 4096x4096"); + private static class IccImageResource { + static IccImageResource SMALL = new IccImageResource("images/img_icc_small.jpg", "512x512", "Small: 512x512"); + static IccImageResource MEDIUM = new IccImageResource("images/img_icc_medium.jpg", "2048x2048", "Medium: 2048x2048"); + static IccImageResource LARGE = new IccImageResource("images/img_icc_large.jpg", "4096x4096", "Large: 4096x4096"); private IccImageResource(String file, String name, String description) { this.url = CMMTests.class.getResource(file); @@ -82,6 +84,10 @@ public final URL url; public final String abbrev; public final String description; + + public static IccImageResource[] values() { + return new IccImageResource[]{SMALL, MEDIUM, LARGE}; + } } private static Option createImageList() { @@ -145,7 +151,7 @@ try { iis = ImageIO.createImageInputStream(url.openStream()); - reader = ImageIO.getImageReaders(iis).next(); + reader = (ImageReader) ImageIO.getImageReaders(iis).next(); } catch (IOException e) { throw new RuntimeException("Unable to run the becnhmark", e); }
--- a/jdk/src/demo/share/java2d/J2DBench/src/j2dbench/tests/cmm/ProfileTests.java Thu Sep 04 13:26:26 2014 -0700 +++ b/jdk/src/demo/share/java2d/J2DBench/src/j2dbench/tests/cmm/ProfileTests.java Wed Jul 05 19:59:54 2017 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2014, Oracle and/or its affiliates. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -38,12 +38,12 @@ */ package j2dbench.tests.cmm; +import java.awt.color.ICC_ColorSpace; +import java.awt.color.ICC_Profile; + import j2dbench.Group; import j2dbench.Result; import j2dbench.TestEnvironment; -import java.awt.color.ColorSpace; -import java.awt.color.ICC_ColorSpace; -import java.awt.color.ICC_Profile; public class ProfileTests extends CMMTests { @@ -73,13 +73,11 @@ } } - @Override public Object initTest(TestEnvironment env, Result res) { ICC_ColorSpace cs = (ICC_ColorSpace) getColorSpace(env); return new Context(cs.getProfile(), env, res); } - @Override public void cleanupTest(TestEnvironment env, Object o) { } @@ -91,7 +89,6 @@ "getData(icSigHead)"); } - @Override public void runTest(Object ctx, int numReps) { final Context ictx = (Context) ctx; final ICC_Profile profile = ictx.profile; @@ -115,7 +112,6 @@ "getNumComponents"); } - @Override public void runTest(Object ctx, int numReps) { final Context ictx = (Context) ctx; final ICC_Profile profile = ictx.profile;
--- a/jdk/src/java.base/share/classes/java/util/concurrent/CompletableFuture.java Thu Sep 04 13:26:26 2014 -0700 +++ b/jdk/src/java.base/share/classes/java/util/concurrent/CompletableFuture.java Wed Jul 05 19:59:54 2017 +0200 @@ -50,7 +50,6 @@ import java.util.concurrent.CancellationException; import java.util.concurrent.CompletionException; import java.util.concurrent.CompletionStage; -import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.locks.LockSupport; /** @@ -77,9 +76,9 @@ * <li>All <em>async</em> methods without an explicit Executor * argument are performed using the {@link ForkJoinPool#commonPool()} * (unless it does not support a parallelism level of at least two, in - * which case, a new Thread is used). To simplify monitoring, - * debugging, and tracking, all generated asynchronous tasks are - * instances of the marker interface {@link + * which case, a new Thread is created to run each task). To simplify + * monitoring, debugging, and tracking, all generated asynchronous + * tasks are instances of the marker interface {@link * AsynchronousCompletionTask}. </li> * * <li>All CompletionStage methods are implemented independently of @@ -113,141 +112,1556 @@ /* * Overview: * - * 1. Non-nullness of field result (set via CAS) indicates done. - * An AltResult is used to box null as a result, as well as to - * hold exceptions. Using a single field makes completion fast - * and simple to detect and trigger, at the expense of a lot of - * encoding and decoding that infiltrates many methods. One minor - * simplification relies on the (static) NIL (to box null results) - * being the only AltResult with a null exception field, so we - * don't usually need explicit comparisons with NIL. The CF - * exception propagation mechanics surrounding decoding rely on - * unchecked casts of decoded results really being unchecked, - * where user type errors are caught at point of use, as is - * currently the case in Java. These are highlighted by using - * SuppressWarnings-annotated temporaries. + * A CompletableFuture may have dependent completion actions, + * collected in a linked stack. It atomically completes by CASing + * a result field, and then pops off and runs those actions. This + * applies across normal vs exceptional outcomes, sync vs async + * actions, binary triggers, and various forms of completions. * - * 2. Waiters are held in a Treiber stack similar to the one used - * in FutureTask, Phaser, and SynchronousQueue. See their - * internal documentation for algorithmic details. + * Non-nullness of field result (set via CAS) indicates done. An + * AltResult is used to box null as a result, as well as to hold + * exceptions. Using a single field makes completion simple to + * detect and trigger. Encoding and decoding is straightforward + * but adds to the sprawl of trapping and associating exceptions + * with targets. Minor simplifications rely on (static) NIL (to + * box null results) being the only AltResult with a null + * exception field, so we don't usually need explicit comparisons. + * Even though some of the generics casts are unchecked (see + * SuppressWarnings annotations), they are placed to be + * appropriate even if checked. * - * 3. Completions are also kept in a list/stack, and pulled off - * and run when completion is triggered. (We could even use the - * same stack as for waiters, but would give up the potential - * parallelism obtained because woken waiters help release/run - * others -- see method postComplete). Because post-processing - * may race with direct calls, class Completion opportunistically - * extends AtomicInteger so callers can claim the action via - * compareAndSet(0, 1). The Completion.run methods are all - * written a boringly similar uniform way (that sometimes includes - * unnecessary-looking checks, kept to maintain uniformity). - * There are enough dimensions upon which they differ that - * attempts to factor commonalities while maintaining efficiency - * require more lines of code than they would save. + * Dependent actions are represented by Completion objects linked + * as Treiber stacks headed by field "stack". There are Completion + * classes for each kind of action, grouped into single-input + * (UniCompletion), two-input (BiCompletion), projected + * (BiCompletions using either (not both) of two inputs), shared + * (CoCompletion, used by the second of two sources), zero-input + * source actions, and Signallers that unblock waiters. Class + * Completion extends ForkJoinTask to enable async execution + * (adding no space overhead because we exploit its "tag" methods + * to maintain claims). It is also declared as Runnable to allow + * usage with arbitrary executors. * - * 4. The exported then/and/or methods do support a bit of - * factoring (see doThenApply etc). They must cope with the - * intrinsic races surrounding addition of a dependent action - * versus performing the action directly because the task is - * already complete. For example, a CF may not be complete upon - * entry, so a dependent completion is added, but by the time it - * is added, the target CF is complete, so must be directly - * executed. This is all done while avoiding unnecessary object - * construction in safe-bypass cases. + * Support for each kind of CompletionStage relies on a separate + * class, along with two CompletableFuture methods: + * + * * A Completion class with name X corresponding to function, + * prefaced with "Uni", "Bi", or "Or". Each class contains + * fields for source(s), actions, and dependent. They are + * boringly similar, differing from others only with respect to + * underlying functional forms. We do this so that users don't + * encounter layers of adaptors in common usages. We also + * include "Relay" classes/methods that don't correspond to user + * methods; they copy results from one stage to another. + * + * * Boolean CompletableFuture method x(...) (for example + * uniApply) takes all of the arguments needed to check that an + * action is triggerable, and then either runs the action or + * arranges its async execution by executing its Completion + * argument, if present. The method returns true if known to be + * complete. + * + * * Completion method tryFire(int mode) invokes the associated x + * method with its held arguments, and on success cleans up. + * The mode argument allows tryFire to be called twice (SYNC, + * then ASYNC); the first to screen and trap exceptions while + * arranging to execute, and the second when called from a + * task. (A few classes are not used async so take slightly + * different forms.) The claim() callback suppresses function + * invocation if already claimed by another thread. + * + * * CompletableFuture method xStage(...) is called from a public + * stage method of CompletableFuture x. It screens user + * arguments and invokes and/or creates the stage object. If + * not async and x is already complete, the action is run + * immediately. Otherwise a Completion c is created, pushed to + * x's stack (unless done), and started or triggered via + * c.tryFire. This also covers races possible if x completes + * while pushing. Classes with two inputs (for example BiApply) + * deal with races across both while pushing actions. The + * second completion is a CoCompletion pointing to the first, + * shared so that at most one performs the action. The + * multiple-arity methods allOf and anyOf do this pairwise to + * form trees of completions. + * + * Note that the generic type parameters of methods vary according + * to whether "this" is a source, dependent, or completion. + * + * Method postComplete is called upon completion unless the target + * is guaranteed not to be observable (i.e., not yet returned or + * linked). Multiple threads can call postComplete, which + * atomically pops each dependent action, and tries to trigger it + * via method tryFire, in NESTED mode. Triggering can propagate + * recursively, so NESTED mode returns its completed dependent (if + * one exists) for further processing by its caller (see method + * postFire). + * + * Blocking methods get() and join() rely on Signaller Completions + * that wake up waiting threads. The mechanics are similar to + * Treiber stack wait-nodes used in FutureTask, Phaser, and + * SynchronousQueue. See their internal documentation for + * algorithmic details. + * + * Without precautions, CompletableFutures would be prone to + * garbage accumulation as chains of Completions build up, each + * pointing back to its sources. So we null out fields as soon as + * possible (see especially method Completion.detach). The + * screening checks needed anyway harmlessly ignore null arguments + * that may have been obtained during races with threads nulling + * out fields. We also try to unlink fired Completions from + * stacks that might never be popped (see method postFire). + * Completion fields need not be declared as final or volatile + * because they are only visible to other threads upon safe + * publication. */ - // preliminaries + volatile Object result; // Either the result or boxed AltResult + volatile Completion stack; // Top of Treiber stack of dependent actions - static final class AltResult { - final Throwable ex; // null only for NIL - AltResult(Throwable ex) { this.ex = ex; } + final boolean internalComplete(Object r) { // CAS from null to r + return UNSAFE.compareAndSwapObject(this, RESULT, null, r); } + final boolean casStack(Completion cmp, Completion val) { + return UNSAFE.compareAndSwapObject(this, STACK, cmp, val); + } + + /** Returns true if successfully pushed c onto stack. */ + final boolean tryPushStack(Completion c) { + Completion h = stack; + lazySetNext(c, h); + return UNSAFE.compareAndSwapObject(this, STACK, h, c); + } + + /** Unconditionally pushes c onto stack, retrying if necessary. */ + final void pushStack(Completion c) { + do {} while (!tryPushStack(c)); + } + + /* ------------- Encoding and decoding outcomes -------------- */ + + static final class AltResult { // See above + final Throwable ex; // null only for NIL + AltResult(Throwable x) { this.ex = x; } + } + + /** The encoding of the null value. */ static final AltResult NIL = new AltResult(null); - // Fields + /** Completes with the null value, unless already completed. */ + final boolean completeNull() { + return UNSAFE.compareAndSwapObject(this, RESULT, null, + NIL); + } - volatile Object result; // Either the result or boxed AltResult - volatile WaitNode waiters; // Treiber stack of threads blocked on get() - volatile CompletionNode completions; // list (Treiber stack) of completions + /** Returns the encoding of the given non-exceptional value. */ + final Object encodeValue(T t) { + return (t == null) ? NIL : t; + } - // Basic utilities for triggering and processing completions + /** Completes with a non-exceptional result, unless already completed. */ + final boolean completeValue(T t) { + return UNSAFE.compareAndSwapObject(this, RESULT, null, + (t == null) ? NIL : t); + } /** - * Removes and signals all waiting threads and runs all completions. + * Returns the encoding of the given (non-null) exception as a + * wrapped CompletionException unless it is one already. + */ + static AltResult encodeThrowable(Throwable x) { + return new AltResult((x instanceof CompletionException) ? x : + new CompletionException(x)); + } + + /** Completes with an exceptional result, unless already completed. */ + final boolean completeThrowable(Throwable x) { + return UNSAFE.compareAndSwapObject(this, RESULT, null, + encodeThrowable(x)); + } + + /** + * Returns the encoding of the given (non-null) exception as a + * wrapped CompletionException unless it is one already. May + * return the given Object r (which must have been the result of a + * source future) if it is equivalent, i.e. if this is a simple + * relay of an existing CompletionException. + */ + static Object encodeThrowable(Throwable x, Object r) { + if (!(x instanceof CompletionException)) + x = new CompletionException(x); + else if (r instanceof AltResult && x == ((AltResult)r).ex) + return r; + return new AltResult(x); + } + + /** + * Completes with the given (non-null) exceptional result as a + * wrapped CompletionException unless it is one already, unless + * already completed. May complete with the given Object r + * (which must have been the result of a source future) if it is + * equivalent, i.e. if this is a simple propagation of an + * existing CompletionException. + */ + final boolean completeThrowable(Throwable x, Object r) { + return UNSAFE.compareAndSwapObject(this, RESULT, null, + encodeThrowable(x, r)); + } + + /** + * Returns the encoding of the given arguments: if the exception + * is non-null, encodes as AltResult. Otherwise uses the given + * value, boxed as NIL if null. + */ + Object encodeOutcome(T t, Throwable x) { + return (x == null) ? (t == null) ? NIL : t : encodeThrowable(x); + } + + /** + * Returns the encoding of a copied outcome; if exceptional, + * rewraps as a CompletionException, else returns argument. + */ + static Object encodeRelay(Object r) { + Throwable x; + return (((r instanceof AltResult) && + (x = ((AltResult)r).ex) != null && + !(x instanceof CompletionException)) ? + new AltResult(new CompletionException(x)) : r); + } + + /** + * Completes with r or a copy of r, unless already completed. + * If exceptional, r is first coerced to a CompletionException. + */ + final boolean completeRelay(Object r) { + return UNSAFE.compareAndSwapObject(this, RESULT, null, + encodeRelay(r)); + } + + /** + * Reports result using Future.get conventions. + */ + private static <T> T reportGet(Object r) + throws InterruptedException, ExecutionException { + if (r == null) // by convention below, null means interrupted + throw new InterruptedException(); + if (r instanceof AltResult) { + Throwable x, cause; + if ((x = ((AltResult)r).ex) == null) + return null; + if (x instanceof CancellationException) + throw (CancellationException)x; + if ((x instanceof CompletionException) && + (cause = x.getCause()) != null) + x = cause; + throw new ExecutionException(x); + } + @SuppressWarnings("unchecked") T t = (T) r; + return t; + } + + /** + * Decodes outcome to return result or throw unchecked exception. + */ + private static <T> T reportJoin(Object r) { + if (r instanceof AltResult) { + Throwable x; + if ((x = ((AltResult)r).ex) == null) + return null; + if (x instanceof CancellationException) + throw (CancellationException)x; + if (x instanceof CompletionException) + throw (CompletionException)x; + throw new CompletionException(x); + } + @SuppressWarnings("unchecked") T t = (T) r; + return t; + } + + /* ------------- Async task preliminaries -------------- */ + + /** + * A marker interface identifying asynchronous tasks produced by + * {@code async} methods. This may be useful for monitoring, + * debugging, and tracking asynchronous activities. + * + * @since 1.8 + */ + public static interface AsynchronousCompletionTask { + } + + private static final boolean useCommonPool = + (ForkJoinPool.getCommonPoolParallelism() > 1); + + /** + * Default executor -- ForkJoinPool.commonPool() unless it cannot + * support parallelism. + */ + private static final Executor asyncPool = useCommonPool ? + ForkJoinPool.commonPool() : new ThreadPerTaskExecutor(); + + /** Fallback if ForkJoinPool.commonPool() cannot support parallelism */ + static final class ThreadPerTaskExecutor implements Executor { + public void execute(Runnable r) { new Thread(r).start(); } + } + + /** + * Null-checks user executor argument, and translates uses of + * commonPool to asyncPool in case parallelism disabled. + */ + static Executor screenExecutor(Executor e) { + if (!useCommonPool && e == ForkJoinPool.commonPool()) + return asyncPool; + if (e == null) throw new NullPointerException(); + return e; + } + + // Modes for Completion.tryFire. Signedness matters. + static final int SYNC = 0; + static final int ASYNC = 1; + static final int NESTED = -1; + + /* ------------- Base Completion classes and operations -------------- */ + + @SuppressWarnings("serial") + abstract static class Completion extends ForkJoinTask<Void> + implements Runnable, AsynchronousCompletionTask { + volatile Completion next; // Treiber stack link + + /** + * Performs completion action if triggered, returning a + * dependent that may need propagation, if one exists. + * + * @param mode SYNC, ASYNC, or NESTED + */ + abstract CompletableFuture<?> tryFire(int mode); + + /** Returns true if possibly still triggerable. Used by cleanStack. */ + abstract boolean isLive(); + + public final void run() { tryFire(ASYNC); } + public final boolean exec() { tryFire(ASYNC); return true; } + public final Void getRawResult() { return null; } + public final void setRawResult(Void v) {} + } + + static void lazySetNext(Completion c, Completion next) { + UNSAFE.putOrderedObject(c, NEXT, next); + } + + /** + * Pops and tries to trigger all reachable dependents. Call only + * when known to be done. */ final void postComplete() { - WaitNode q; Thread t; - while ((q = waiters) != null) { - if (UNSAFE.compareAndSwapObject(this, WAITERS, q, q.next) && - (t = q.thread) != null) { - q.thread = null; - LockSupport.unpark(t); + /* + * On each step, variable f holds current dependents to pop + * and run. It is extended along only one path at a time, + * pushing others to avoid unbounded recursion. + */ + CompletableFuture<?> f = this; Completion h; + while ((h = f.stack) != null || + (f != this && (h = (f = this).stack) != null)) { + CompletableFuture<?> d; Completion t; + if (f.casStack(h, t = h.next)) { + if (t != null) { + if (f != this) { + pushStack(h); + continue; + } + h.next = null; // detach + } + f = (d = h.tryFire(NESTED)) == null ? this : d; } } + } - CompletionNode h; Completion c; - while ((h = completions) != null) { - if (UNSAFE.compareAndSwapObject(this, COMPLETIONS, h, h.next) && - (c = h.completion) != null) - c.run(); + /** Traverses stack and unlinks dead Completions. */ + final void cleanStack() { + for (Completion p = null, q = stack; q != null;) { + Completion s = q.next; + if (q.isLive()) { + p = q; + q = s; + } + else if (p == null) { + casStack(q, s); + q = stack; + } + else { + p.next = s; + if (p.isLive()) + q = s; + else { + p = null; // restart + q = stack; + } + } + } + } + + /* ------------- One-input Completions -------------- */ + + /** A Completion with a source, dependent, and executor. */ + @SuppressWarnings("serial") + abstract static class UniCompletion<T,V> extends Completion { + Executor executor; // executor to use (null if none) + CompletableFuture<V> dep; // the dependent to complete + CompletableFuture<T> src; // source for action + + UniCompletion(Executor executor, CompletableFuture<V> dep, + CompletableFuture<T> src) { + this.executor = executor; this.dep = dep; this.src = src; + } + + /** + * Returns true if action can be run. Call only when known to + * be triggerable. Uses FJ tag bit to ensure that only one + * thread claims ownership. If async, starts as task -- a + * later call to tryFire will run action. + */ + final boolean claim() { + Executor e = executor; + if (compareAndSetForkJoinTaskTag((short)0, (short)1)) { + if (e == null) + return true; + executor = null; // disable + e.execute(this); + } + return false; + } + + final boolean isLive() { return dep != null; } + } + + /** Pushes the given completion (if it exists) unless done. */ + final void push(UniCompletion<?,?> c) { + if (c != null) { + while (result == null && !tryPushStack(c)) + lazySetNext(c, null); // clear on failure } } /** - * Triggers completion with the encoding of the given arguments: - * if the exception is non-null, encodes it as a wrapped - * CompletionException unless it is one already. Otherwise uses - * the given result, boxed as NIL if null. + * Post-processing by dependent after successful UniCompletion + * tryFire. Tries to clean stack of source a, and then either runs + * postComplete or returns this to caller, depending on mode. */ - final void internalComplete(T v, Throwable ex) { - if (result == null) - UNSAFE.compareAndSwapObject - (this, RESULT, null, - (ex == null) ? (v == null) ? NIL : v : - new AltResult((ex instanceof CompletionException) ? ex : - new CompletionException(ex))); - postComplete(); // help out even if not triggered + final CompletableFuture<T> postFire(CompletableFuture<?> a, int mode) { + if (a != null && a.stack != null) { + if (mode < 0 || a.result == null) + a.cleanStack(); + else + a.postComplete(); + } + if (result != null && stack != null) { + if (mode < 0) + return this; + else + postComplete(); + } + return null; } - /** - * If triggered, helps release and/or process completions. - */ - final void helpPostComplete() { - if (result != null) - postComplete(); + @SuppressWarnings("serial") + static final class UniApply<T,V> extends UniCompletion<T,V> { + Function<? super T,? extends V> fn; + UniApply(Executor executor, CompletableFuture<V> dep, + CompletableFuture<T> src, + Function<? super T,? extends V> fn) { + super(executor, dep, src); this.fn = fn; + } + final CompletableFuture<V> tryFire(int mode) { + CompletableFuture<V> d; CompletableFuture<T> a; + if ((d = dep) == null || + !d.uniApply(a = src, fn, mode > 0 ? null : this)) + return null; + dep = null; src = null; fn = null; + return d.postFire(a, mode); + } } - /* ------------- waiting for completions -------------- */ + final <S> boolean uniApply(CompletableFuture<S> a, + Function<? super S,? extends T> f, + UniApply<S,T> c) { + Object r; Throwable x; + if (a == null || (r = a.result) == null || f == null) + return false; + tryComplete: if (result == null) { + if (r instanceof AltResult) { + if ((x = ((AltResult)r).ex) != null) { + completeThrowable(x, r); + break tryComplete; + } + r = null; + } + try { + if (c != null && !c.claim()) + return false; + @SuppressWarnings("unchecked") S s = (S) r; + completeValue(f.apply(s)); + } catch (Throwable ex) { + completeThrowable(ex); + } + } + return true; + } - /** Number of processors, for spin control */ - static final int NCPU = Runtime.getRuntime().availableProcessors(); + private <V> CompletableFuture<V> uniApplyStage( + Executor e, Function<? super T,? extends V> f) { + if (f == null) throw new NullPointerException(); + CompletableFuture<V> d = new CompletableFuture<V>(); + if (e != null || !d.uniApply(this, f, null)) { + UniApply<T,V> c = new UniApply<T,V>(e, d, this, f); + push(c); + c.tryFire(SYNC); + } + return d; + } + + @SuppressWarnings("serial") + static final class UniAccept<T> extends UniCompletion<T,Void> { + Consumer<? super T> fn; + UniAccept(Executor executor, CompletableFuture<Void> dep, + CompletableFuture<T> src, Consumer<? super T> fn) { + super(executor, dep, src); this.fn = fn; + } + final CompletableFuture<Void> tryFire(int mode) { + CompletableFuture<Void> d; CompletableFuture<T> a; + if ((d = dep) == null || + !d.uniAccept(a = src, fn, mode > 0 ? null : this)) + return null; + dep = null; src = null; fn = null; + return d.postFire(a, mode); + } + } + + final <S> boolean uniAccept(CompletableFuture<S> a, + Consumer<? super S> f, UniAccept<S> c) { + Object r; Throwable x; + if (a == null || (r = a.result) == null || f == null) + return false; + tryComplete: if (result == null) { + if (r instanceof AltResult) { + if ((x = ((AltResult)r).ex) != null) { + completeThrowable(x, r); + break tryComplete; + } + r = null; + } + try { + if (c != null && !c.claim()) + return false; + @SuppressWarnings("unchecked") S s = (S) r; + f.accept(s); + completeNull(); + } catch (Throwable ex) { + completeThrowable(ex); + } + } + return true; + } + + private CompletableFuture<Void> uniAcceptStage(Executor e, + Consumer<? super T> f) { + if (f == null) throw new NullPointerException(); + CompletableFuture<Void> d = new CompletableFuture<Void>(); + if (e != null || !d.uniAccept(this, f, null)) { + UniAccept<T> c = new UniAccept<T>(e, d, this, f); + push(c); + c.tryFire(SYNC); + } + return d; + } + + @SuppressWarnings("serial") + static final class UniRun<T> extends UniCompletion<T,Void> { + Runnable fn; + UniRun(Executor executor, CompletableFuture<Void> dep, + CompletableFuture<T> src, Runnable fn) { + super(executor, dep, src); this.fn = fn; + } + final CompletableFuture<Void> tryFire(int mode) { + CompletableFuture<Void> d; CompletableFuture<T> a; + if ((d = dep) == null || + !d.uniRun(a = src, fn, mode > 0 ? null : this)) + return null; + dep = null; src = null; fn = null; + return d.postFire(a, mode); + } + } + + final boolean uniRun(CompletableFuture<?> a, Runnable f, UniRun<?> c) { + Object r; Throwable x; + if (a == null || (r = a.result) == null || f == null) + return false; + if (result == null) { + if (r instanceof AltResult && (x = ((AltResult)r).ex) != null) + completeThrowable(x, r); + else + try { + if (c != null && !c.claim()) + return false; + f.run(); + completeNull(); + } catch (Throwable ex) { + completeThrowable(ex); + } + } + return true; + } + + private CompletableFuture<Void> uniRunStage(Executor e, Runnable f) { + if (f == null) throw new NullPointerException(); + CompletableFuture<Void> d = new CompletableFuture<Void>(); + if (e != null || !d.uniRun(this, f, null)) { + UniRun<T> c = new UniRun<T>(e, d, this, f); + push(c); + c.tryFire(SYNC); + } + return d; + } + + @SuppressWarnings("serial") + static final class UniWhenComplete<T> extends UniCompletion<T,T> { + BiConsumer<? super T, ? super Throwable> fn; + UniWhenComplete(Executor executor, CompletableFuture<T> dep, + CompletableFuture<T> src, + BiConsumer<? super T, ? super Throwable> fn) { + super(executor, dep, src); this.fn = fn; + } + final CompletableFuture<T> tryFire(int mode) { + CompletableFuture<T> d; CompletableFuture<T> a; + if ((d = dep) == null || + !d.uniWhenComplete(a = src, fn, mode > 0 ? null : this)) + return null; + dep = null; src = null; fn = null; + return d.postFire(a, mode); + } + } + + final boolean uniWhenComplete(CompletableFuture<T> a, + BiConsumer<? super T,? super Throwable> f, + UniWhenComplete<T> c) { + Object r; T t; Throwable x = null; + if (a == null || (r = a.result) == null || f == null) + return false; + if (result == null) { + try { + if (c != null && !c.claim()) + return false; + if (r instanceof AltResult) { + x = ((AltResult)r).ex; + t = null; + } else { + @SuppressWarnings("unchecked") T tr = (T) r; + t = tr; + } + f.accept(t, x); + if (x == null) { + internalComplete(r); + return true; + } + } catch (Throwable ex) { + if (x == null) + x = ex; + } + completeThrowable(x, r); + } + return true; + } + + private CompletableFuture<T> uniWhenCompleteStage( + Executor e, BiConsumer<? super T, ? super Throwable> f) { + if (f == null) throw new NullPointerException(); + CompletableFuture<T> d = new CompletableFuture<T>(); + if (e != null || !d.uniWhenComplete(this, f, null)) { + UniWhenComplete<T> c = new UniWhenComplete<T>(e, d, this, f); + push(c); + c.tryFire(SYNC); + } + return d; + } + + @SuppressWarnings("serial") + static final class UniHandle<T,V> extends UniCompletion<T,V> { + BiFunction<? super T, Throwable, ? extends V> fn; + UniHandle(Executor executor, CompletableFuture<V> dep, + CompletableFuture<T> src, + BiFunction<? super T, Throwable, ? extends V> fn) { + super(executor, dep, src); this.fn = fn; + } + final CompletableFuture<V> tryFire(int mode) { + CompletableFuture<V> d; CompletableFuture<T> a; + if ((d = dep) == null || + !d.uniHandle(a = src, fn, mode > 0 ? null : this)) + return null; + dep = null; src = null; fn = null; + return d.postFire(a, mode); + } + } + + final <S> boolean uniHandle(CompletableFuture<S> a, + BiFunction<? super S, Throwable, ? extends T> f, + UniHandle<S,T> c) { + Object r; S s; Throwable x; + if (a == null || (r = a.result) == null || f == null) + return false; + if (result == null) { + try { + if (c != null && !c.claim()) + return false; + if (r instanceof AltResult) { + x = ((AltResult)r).ex; + s = null; + } else { + x = null; + @SuppressWarnings("unchecked") S ss = (S) r; + s = ss; + } + completeValue(f.apply(s, x)); + } catch (Throwable ex) { + completeThrowable(ex); + } + } + return true; + } + + private <V> CompletableFuture<V> uniHandleStage( + Executor e, BiFunction<? super T, Throwable, ? extends V> f) { + if (f == null) throw new NullPointerException(); + CompletableFuture<V> d = new CompletableFuture<V>(); + if (e != null || !d.uniHandle(this, f, null)) { + UniHandle<T,V> c = new UniHandle<T,V>(e, d, this, f); + push(c); + c.tryFire(SYNC); + } + return d; + } + + @SuppressWarnings("serial") + static final class UniExceptionally<T> extends UniCompletion<T,T> { + Function<? super Throwable, ? extends T> fn; + UniExceptionally(CompletableFuture<T> dep, CompletableFuture<T> src, + Function<? super Throwable, ? extends T> fn) { + super(null, dep, src); this.fn = fn; + } + final CompletableFuture<T> tryFire(int mode) { // never ASYNC + // assert mode != ASYNC; + CompletableFuture<T> d; CompletableFuture<T> a; + if ((d = dep) == null || !d.uniExceptionally(a = src, fn, this)) + return null; + dep = null; src = null; fn = null; + return d.postFire(a, mode); + } + } + + final boolean uniExceptionally(CompletableFuture<T> a, + Function<? super Throwable, ? extends T> f, + UniExceptionally<T> c) { + Object r; Throwable x; + if (a == null || (r = a.result) == null || f == null) + return false; + if (result == null) { + try { + if (r instanceof AltResult && (x = ((AltResult)r).ex) != null) { + if (c != null && !c.claim()) + return false; + completeValue(f.apply(x)); + } else + internalComplete(r); + } catch (Throwable ex) { + completeThrowable(ex); + } + } + return true; + } + + private CompletableFuture<T> uniExceptionallyStage( + Function<Throwable, ? extends T> f) { + if (f == null) throw new NullPointerException(); + CompletableFuture<T> d = new CompletableFuture<T>(); + if (!d.uniExceptionally(this, f, null)) { + UniExceptionally<T> c = new UniExceptionally<T>(d, this, f); + push(c); + c.tryFire(SYNC); + } + return d; + } + + @SuppressWarnings("serial") + static final class UniRelay<T> extends UniCompletion<T,T> { // for Compose + UniRelay(CompletableFuture<T> dep, CompletableFuture<T> src) { + super(null, dep, src); + } + final CompletableFuture<T> tryFire(int mode) { + CompletableFuture<T> d; CompletableFuture<T> a; + if ((d = dep) == null || !d.uniRelay(a = src)) + return null; + src = null; dep = null; + return d.postFire(a, mode); + } + } + + final boolean uniRelay(CompletableFuture<T> a) { + Object r; + if (a == null || (r = a.result) == null) + return false; + if (result == null) // no need to claim + completeRelay(r); + return true; + } + + @SuppressWarnings("serial") + static final class UniCompose<T,V> extends UniCompletion<T,V> { + Function<? super T, ? extends CompletionStage<V>> fn; + UniCompose(Executor executor, CompletableFuture<V> dep, + CompletableFuture<T> src, + Function<? super T, ? extends CompletionStage<V>> fn) { + super(executor, dep, src); this.fn = fn; + } + final CompletableFuture<V> tryFire(int mode) { + CompletableFuture<V> d; CompletableFuture<T> a; + if ((d = dep) == null || + !d.uniCompose(a = src, fn, mode > 0 ? null : this)) + return null; + dep = null; src = null; fn = null; + return d.postFire(a, mode); + } + } + + final <S> boolean uniCompose( + CompletableFuture<S> a, + Function<? super S, ? extends CompletionStage<T>> f, + UniCompose<S,T> c) { + Object r; Throwable x; + if (a == null || (r = a.result) == null || f == null) + return false; + tryComplete: if (result == null) { + if (r instanceof AltResult) { + if ((x = ((AltResult)r).ex) != null) { + completeThrowable(x, r); + break tryComplete; + } + r = null; + } + try { + if (c != null && !c.claim()) + return false; + @SuppressWarnings("unchecked") S s = (S) r; + CompletableFuture<T> g = f.apply(s).toCompletableFuture(); + if (g.result == null || !uniRelay(g)) { + UniRelay<T> copy = new UniRelay<T>(this, g); + g.push(copy); + copy.tryFire(SYNC); + if (result == null) + return false; + } + } catch (Throwable ex) { + completeThrowable(ex); + } + } + return true; + } + + private <V> CompletableFuture<V> uniComposeStage( + Executor e, Function<? super T, ? extends CompletionStage<V>> f) { + if (f == null) throw new NullPointerException(); + Object r; Throwable x; + if (e == null && (r = result) != null) { + // try to return function result directly + if (r instanceof AltResult) { + if ((x = ((AltResult)r).ex) != null) { + return new CompletableFuture<V>(encodeThrowable(x, r)); + } + r = null; + } + try { + @SuppressWarnings("unchecked") T t = (T) r; + return f.apply(t).toCompletableFuture(); + } catch (Throwable ex) { + return new CompletableFuture<V>(encodeThrowable(ex)); + } + } + CompletableFuture<V> d = new CompletableFuture<V>(); + UniCompose<T,V> c = new UniCompose<T,V>(e, d, this, f); + push(c); + c.tryFire(SYNC); + return d; + } + + /* ------------- Two-input Completions -------------- */ + + /** A Completion for an action with two sources */ + @SuppressWarnings("serial") + abstract static class BiCompletion<T,U,V> extends UniCompletion<T,V> { + CompletableFuture<U> snd; // second source for action + BiCompletion(Executor executor, CompletableFuture<V> dep, + CompletableFuture<T> src, CompletableFuture<U> snd) { + super(executor, dep, src); this.snd = snd; + } + } + + /** A Completion delegating to a BiCompletion */ + @SuppressWarnings("serial") + static final class CoCompletion extends Completion { + BiCompletion<?,?,?> base; + CoCompletion(BiCompletion<?,?,?> base) { this.base = base; } + final CompletableFuture<?> tryFire(int mode) { + BiCompletion<?,?,?> c; CompletableFuture<?> d; + if ((c = base) == null || (d = c.tryFire(mode)) == null) + return null; + base = null; // detach + return d; + } + final boolean isLive() { + BiCompletion<?,?,?> c; + return (c = base) != null && c.dep != null; + } + } + + /** Pushes completion to this and b unless both done. */ + final void bipush(CompletableFuture<?> b, BiCompletion<?,?,?> c) { + if (c != null) { + Object r; + while ((r = result) == null && !tryPushStack(c)) + lazySetNext(c, null); // clear on failure + if (b != null && b != this && b.result == null) { + Completion q = (r != null) ? c : new CoCompletion(c); + while (b.result == null && !b.tryPushStack(q)) + lazySetNext(q, null); // clear on failure + } + } + } + + /** Post-processing after successful BiCompletion tryFire. */ + final CompletableFuture<T> postFire(CompletableFuture<?> a, + CompletableFuture<?> b, int mode) { + if (b != null && b.stack != null) { // clean second source + if (mode < 0 || b.result == null) + b.cleanStack(); + else + b.postComplete(); + } + return postFire(a, mode); + } + + @SuppressWarnings("serial") + static final class BiApply<T,U,V> extends BiCompletion<T,U,V> { + BiFunction<? super T,? super U,? extends V> fn; + BiApply(Executor executor, CompletableFuture<V> dep, + CompletableFuture<T> src, CompletableFuture<U> snd, + BiFunction<? super T,? super U,? extends V> fn) { + super(executor, dep, src, snd); this.fn = fn; + } + final CompletableFuture<V> tryFire(int mode) { + CompletableFuture<V> d; + CompletableFuture<T> a; + CompletableFuture<U> b; + if ((d = dep) == null || + !d.biApply(a = src, b = snd, fn, mode > 0 ? null : this)) + return null; + dep = null; src = null; snd = null; fn = null; + return d.postFire(a, b, mode); + } + } + + final <R,S> boolean biApply(CompletableFuture<R> a, + CompletableFuture<S> b, + BiFunction<? super R,? super S,? extends T> f, + BiApply<R,S,T> c) { + Object r, s; Throwable x; + if (a == null || (r = a.result) == null || + b == null || (s = b.result) == null || f == null) + return false; + tryComplete: if (result == null) { + if (r instanceof AltResult) { + if ((x = ((AltResult)r).ex) != null) { + completeThrowable(x, r); + break tryComplete; + } + r = null; + } + if (s instanceof AltResult) { + if ((x = ((AltResult)s).ex) != null) { + completeThrowable(x, s); + break tryComplete; + } + s = null; + } + try { + if (c != null && !c.claim()) + return false; + @SuppressWarnings("unchecked") R rr = (R) r; + @SuppressWarnings("unchecked") S ss = (S) s; + completeValue(f.apply(rr, ss)); + } catch (Throwable ex) { + completeThrowable(ex); + } + } + return true; + } + + private <U,V> CompletableFuture<V> biApplyStage( + Executor e, CompletionStage<U> o, + BiFunction<? super T,? super U,? extends V> f) { + CompletableFuture<U> b; + if (f == null || (b = o.toCompletableFuture()) == null) + throw new NullPointerException(); + CompletableFuture<V> d = new CompletableFuture<V>(); + if (e != null || !d.biApply(this, b, f, null)) { + BiApply<T,U,V> c = new BiApply<T,U,V>(e, d, this, b, f); + bipush(b, c); + c.tryFire(SYNC); + } + return d; + } + + @SuppressWarnings("serial") + static final class BiAccept<T,U> extends BiCompletion<T,U,Void> { + BiConsumer<? super T,? super U> fn; + BiAccept(Executor executor, CompletableFuture<Void> dep, + CompletableFuture<T> src, CompletableFuture<U> snd, + BiConsumer<? super T,? super U> fn) { + super(executor, dep, src, snd); this.fn = fn; + } + final CompletableFuture<Void> tryFire(int mode) { + CompletableFuture<Void> d; + CompletableFuture<T> a; + CompletableFuture<U> b; + if ((d = dep) == null || + !d.biAccept(a = src, b = snd, fn, mode > 0 ? null : this)) + return null; + dep = null; src = null; snd = null; fn = null; + return d.postFire(a, b, mode); + } + } + + final <R,S> boolean biAccept(CompletableFuture<R> a, + CompletableFuture<S> b, + BiConsumer<? super R,? super S> f, + BiAccept<R,S> c) { + Object r, s; Throwable x; + if (a == null || (r = a.result) == null || + b == null || (s = b.result) == null || f == null) + return false; + tryComplete: if (result == null) { + if (r instanceof AltResult) { + if ((x = ((AltResult)r).ex) != null) { + completeThrowable(x, r); + break tryComplete; + } + r = null; + } + if (s instanceof AltResult) { + if ((x = ((AltResult)s).ex) != null) { + completeThrowable(x, s); + break tryComplete; + } + s = null; + } + try { + if (c != null && !c.claim()) + return false; + @SuppressWarnings("unchecked") R rr = (R) r; + @SuppressWarnings("unchecked") S ss = (S) s; + f.accept(rr, ss); + completeNull(); + } catch (Throwable ex) { + completeThrowable(ex); + } + } + return true; + } + + private <U> CompletableFuture<Void> biAcceptStage( + Executor e, CompletionStage<U> o, + BiConsumer<? super T,? super U> f) { + CompletableFuture<U> b; + if (f == null || (b = o.toCompletableFuture()) == null) + throw new NullPointerException(); + CompletableFuture<Void> d = new CompletableFuture<Void>(); + if (e != null || !d.biAccept(this, b, f, null)) { + BiAccept<T,U> c = new BiAccept<T,U>(e, d, this, b, f); + bipush(b, c); + c.tryFire(SYNC); + } + return d; + } + + @SuppressWarnings("serial") + static final class BiRun<T,U> extends BiCompletion<T,U,Void> { + Runnable fn; + BiRun(Executor executor, CompletableFuture<Void> dep, + CompletableFuture<T> src, + CompletableFuture<U> snd, + Runnable fn) { + super(executor, dep, src, snd); this.fn = fn; + } + final CompletableFuture<Void> tryFire(int mode) { + CompletableFuture<Void> d; + CompletableFuture<T> a; + CompletableFuture<U> b; + if ((d = dep) == null || + !d.biRun(a = src, b = snd, fn, mode > 0 ? null : this)) + return null; + dep = null; src = null; snd = null; fn = null; + return d.postFire(a, b, mode); + } + } + + final boolean biRun(CompletableFuture<?> a, CompletableFuture<?> b, + Runnable f, BiRun<?,?> c) { + Object r, s; Throwable x; + if (a == null || (r = a.result) == null || + b == null || (s = b.result) == null || f == null) + return false; + if (result == null) { + if (r instanceof AltResult && (x = ((AltResult)r).ex) != null) + completeThrowable(x, r); + else if (s instanceof AltResult && (x = ((AltResult)s).ex) != null) + completeThrowable(x, s); + else + try { + if (c != null && !c.claim()) + return false; + f.run(); + completeNull(); + } catch (Throwable ex) { + completeThrowable(ex); + } + } + return true; + } + + private CompletableFuture<Void> biRunStage(Executor e, CompletionStage<?> o, + Runnable f) { + CompletableFuture<?> b; + if (f == null || (b = o.toCompletableFuture()) == null) + throw new NullPointerException(); + CompletableFuture<Void> d = new CompletableFuture<Void>(); + if (e != null || !d.biRun(this, b, f, null)) { + BiRun<T,?> c = new BiRun<>(e, d, this, b, f); + bipush(b, c); + c.tryFire(SYNC); + } + return d; + } + + @SuppressWarnings("serial") + static final class BiRelay<T,U> extends BiCompletion<T,U,Void> { // for And + BiRelay(CompletableFuture<Void> dep, + CompletableFuture<T> src, + CompletableFuture<U> snd) { + super(null, dep, src, snd); + } + final CompletableFuture<Void> tryFire(int mode) { + CompletableFuture<Void> d; + CompletableFuture<T> a; + CompletableFuture<U> b; + if ((d = dep) == null || !d.biRelay(a = src, b = snd)) + return null; + src = null; snd = null; dep = null; + return d.postFire(a, b, mode); + } + } + + boolean biRelay(CompletableFuture<?> a, CompletableFuture<?> b) { + Object r, s; Throwable x; + if (a == null || (r = a.result) == null || + b == null || (s = b.result) == null) + return false; + if (result == null) { + if (r instanceof AltResult && (x = ((AltResult)r).ex) != null) + completeThrowable(x, r); + else if (s instanceof AltResult && (x = ((AltResult)s).ex) != null) + completeThrowable(x, s); + else + completeNull(); + } + return true; + } + + /** Recursively constructs a tree of completions. */ + static CompletableFuture<Void> andTree(CompletableFuture<?>[] cfs, + int lo, int hi) { + CompletableFuture<Void> d = new CompletableFuture<Void>(); + if (lo > hi) // empty + d.result = NIL; + else { + CompletableFuture<?> a, b; + int mid = (lo + hi) >>> 1; + if ((a = (lo == mid ? cfs[lo] : + andTree(cfs, lo, mid))) == null || + (b = (lo == hi ? a : (hi == mid+1) ? cfs[hi] : + andTree(cfs, mid+1, hi))) == null) + throw new NullPointerException(); + if (!d.biRelay(a, b)) { + BiRelay<?,?> c = new BiRelay<>(d, a, b); + a.bipush(b, c); + c.tryFire(SYNC); + } + } + return d; + } + + /* ------------- Projected (Ored) BiCompletions -------------- */ + + /** Pushes completion to this and b unless either done. */ + final void orpush(CompletableFuture<?> b, BiCompletion<?,?,?> c) { + if (c != null) { + while ((b == null || b.result == null) && result == null) { + if (tryPushStack(c)) { + if (b != null && b != this && b.result == null) { + Completion q = new CoCompletion(c); + while (result == null && b.result == null && + !b.tryPushStack(q)) + lazySetNext(q, null); // clear on failure + } + break; + } + lazySetNext(c, null); // clear on failure + } + } + } + + @SuppressWarnings("serial") + static final class OrApply<T,U extends T,V> extends BiCompletion<T,U,V> { + Function<? super T,? extends V> fn; + OrApply(Executor executor, CompletableFuture<V> dep, + CompletableFuture<T> src, + CompletableFuture<U> snd, + Function<? super T,? extends V> fn) { + super(executor, dep, src, snd); this.fn = fn; + } + final CompletableFuture<V> tryFire(int mode) { + CompletableFuture<V> d; + CompletableFuture<T> a; + CompletableFuture<U> b; + if ((d = dep) == null || + !d.orApply(a = src, b = snd, fn, mode > 0 ? null : this)) + return null; + dep = null; src = null; snd = null; fn = null; + return d.postFire(a, b, mode); + } + } + + final <R,S extends R> boolean orApply(CompletableFuture<R> a, + CompletableFuture<S> b, + Function<? super R, ? extends T> f, + OrApply<R,S,T> c) { + Object r; Throwable x; + if (a == null || b == null || + ((r = a.result) == null && (r = b.result) == null) || f == null) + return false; + tryComplete: if (result == null) { + try { + if (c != null && !c.claim()) + return false; + if (r instanceof AltResult) { + if ((x = ((AltResult)r).ex) != null) { + completeThrowable(x, r); + break tryComplete; + } + r = null; + } + @SuppressWarnings("unchecked") R rr = (R) r; + completeValue(f.apply(rr)); + } catch (Throwable ex) { + completeThrowable(ex); + } + } + return true; + } + + private <U extends T,V> CompletableFuture<V> orApplyStage( + Executor e, CompletionStage<U> o, + Function<? super T, ? extends V> f) { + CompletableFuture<U> b; + if (f == null || (b = o.toCompletableFuture()) == null) + throw new NullPointerException(); + CompletableFuture<V> d = new CompletableFuture<V>(); + if (e != null || !d.orApply(this, b, f, null)) { + OrApply<T,U,V> c = new OrApply<T,U,V>(e, d, this, b, f); + orpush(b, c); + c.tryFire(SYNC); + } + return d; + } + + @SuppressWarnings("serial") + static final class OrAccept<T,U extends T> extends BiCompletion<T,U,Void> { + Consumer<? super T> fn; + OrAccept(Executor executor, CompletableFuture<Void> dep, + CompletableFuture<T> src, + CompletableFuture<U> snd, + Consumer<? super T> fn) { + super(executor, dep, src, snd); this.fn = fn; + } + final CompletableFuture<Void> tryFire(int mode) { + CompletableFuture<Void> d; + CompletableFuture<T> a; + CompletableFuture<U> b; + if ((d = dep) == null || + !d.orAccept(a = src, b = snd, fn, mode > 0 ? null : this)) + return null; + dep = null; src = null; snd = null; fn = null; + return d.postFire(a, b, mode); + } + } + + final <R,S extends R> boolean orAccept(CompletableFuture<R> a, + CompletableFuture<S> b, + Consumer<? super R> f, + OrAccept<R,S> c) { + Object r; Throwable x; + if (a == null || b == null || + ((r = a.result) == null && (r = b.result) == null) || f == null) + return false; + tryComplete: if (result == null) { + try { + if (c != null && !c.claim()) + return false; + if (r instanceof AltResult) { + if ((x = ((AltResult)r).ex) != null) { + completeThrowable(x, r); + break tryComplete; + } + r = null; + } + @SuppressWarnings("unchecked") R rr = (R) r; + f.accept(rr); + completeNull(); + } catch (Throwable ex) { + completeThrowable(ex); + } + } + return true; + } + + private <U extends T> CompletableFuture<Void> orAcceptStage( + Executor e, CompletionStage<U> o, Consumer<? super T> f) { + CompletableFuture<U> b; + if (f == null || (b = o.toCompletableFuture()) == null) + throw new NullPointerException(); + CompletableFuture<Void> d = new CompletableFuture<Void>(); + if (e != null || !d.orAccept(this, b, f, null)) { + OrAccept<T,U> c = new OrAccept<T,U>(e, d, this, b, f); + orpush(b, c); + c.tryFire(SYNC); + } + return d; + } + + @SuppressWarnings("serial") + static final class OrRun<T,U> extends BiCompletion<T,U,Void> { + Runnable fn; + OrRun(Executor executor, CompletableFuture<Void> dep, + CompletableFuture<T> src, + CompletableFuture<U> snd, + Runnable fn) { + super(executor, dep, src, snd); this.fn = fn; + } + final CompletableFuture<Void> tryFire(int mode) { + CompletableFuture<Void> d; + CompletableFuture<T> a; + CompletableFuture<U> b; + if ((d = dep) == null || + !d.orRun(a = src, b = snd, fn, mode > 0 ? null : this)) + return null; + dep = null; src = null; snd = null; fn = null; + return d.postFire(a, b, mode); + } + } + + final boolean orRun(CompletableFuture<?> a, CompletableFuture<?> b, + Runnable f, OrRun<?,?> c) { + Object r; Throwable x; + if (a == null || b == null || + ((r = a.result) == null && (r = b.result) == null) || f == null) + return false; + if (result == null) { + try { + if (c != null && !c.claim()) + return false; + if (r instanceof AltResult && (x = ((AltResult)r).ex) != null) + completeThrowable(x, r); + else { + f.run(); + completeNull(); + } + } catch (Throwable ex) { + completeThrowable(ex); + } + } + return true; + } + + private CompletableFuture<Void> orRunStage(Executor e, CompletionStage<?> o, + Runnable f) { + CompletableFuture<?> b; + if (f == null || (b = o.toCompletableFuture()) == null) + throw new NullPointerException(); + CompletableFuture<Void> d = new CompletableFuture<Void>(); + if (e != null || !d.orRun(this, b, f, null)) { + OrRun<T,?> c = new OrRun<>(e, d, this, b, f); + orpush(b, c); + c.tryFire(SYNC); + } + return d; + } + + @SuppressWarnings("serial") + static final class OrRelay<T,U> extends BiCompletion<T,U,Object> { // for Or + OrRelay(CompletableFuture<Object> dep, CompletableFuture<T> src, + CompletableFuture<U> snd) { + super(null, dep, src, snd); + } + final CompletableFuture<Object> tryFire(int mode) { + CompletableFuture<Object> d; + CompletableFuture<T> a; + CompletableFuture<U> b; + if ((d = dep) == null || !d.orRelay(a = src, b = snd)) + return null; + src = null; snd = null; dep = null; + return d.postFire(a, b, mode); + } + } + + final boolean orRelay(CompletableFuture<?> a, CompletableFuture<?> b) { + Object r; + if (a == null || b == null || + ((r = a.result) == null && (r = b.result) == null)) + return false; + if (result == null) + completeRelay(r); + return true; + } + + /** Recursively constructs a tree of completions. */ + static CompletableFuture<Object> orTree(CompletableFuture<?>[] cfs, + int lo, int hi) { + CompletableFuture<Object> d = new CompletableFuture<Object>(); + if (lo <= hi) { + CompletableFuture<?> a, b; + int mid = (lo + hi) >>> 1; + if ((a = (lo == mid ? cfs[lo] : + orTree(cfs, lo, mid))) == null || + (b = (lo == hi ? a : (hi == mid+1) ? cfs[hi] : + orTree(cfs, mid+1, hi))) == null) + throw new NullPointerException(); + if (!d.orRelay(a, b)) { + OrRelay<?,?> c = new OrRelay<>(d, a, b); + a.orpush(b, c); + c.tryFire(SYNC); + } + } + return d; + } + + /* ------------- Zero-input Async forms -------------- */ + + @SuppressWarnings("serial") + static final class AsyncSupply<T> extends ForkJoinTask<Void> + implements Runnable, AsynchronousCompletionTask { + CompletableFuture<T> dep; Supplier<T> fn; + AsyncSupply(CompletableFuture<T> dep, Supplier<T> fn) { + this.dep = dep; this.fn = fn; + } + + public final Void getRawResult() { return null; } + public final void setRawResult(Void v) {} + public final boolean exec() { run(); return true; } + + public void run() { + CompletableFuture<T> d; Supplier<T> f; + if ((d = dep) != null && (f = fn) != null) { + dep = null; fn = null; + if (d.result == null) { + try { + d.completeValue(f.get()); + } catch (Throwable ex) { + d.completeThrowable(ex); + } + } + d.postComplete(); + } + } + } + + static <U> CompletableFuture<U> asyncSupplyStage(Executor e, + Supplier<U> f) { + if (f == null) throw new NullPointerException(); + CompletableFuture<U> d = new CompletableFuture<U>(); + e.execute(new AsyncSupply<U>(d, f)); + return d; + } + + @SuppressWarnings("serial") + static final class AsyncRun extends ForkJoinTask<Void> + implements Runnable, AsynchronousCompletionTask { + CompletableFuture<Void> dep; Runnable fn; + AsyncRun(CompletableFuture<Void> dep, Runnable fn) { + this.dep = dep; this.fn = fn; + } + + public final Void getRawResult() { return null; } + public final void setRawResult(Void v) {} + public final boolean exec() { run(); return true; } + + public void run() { + CompletableFuture<Void> d; Runnable f; + if ((d = dep) != null && (f = fn) != null) { + dep = null; fn = null; + if (d.result == null) { + try { + f.run(); + d.completeNull(); + } catch (Throwable ex) { + d.completeThrowable(ex); + } + } + d.postComplete(); + } + } + } + + static CompletableFuture<Void> asyncRunStage(Executor e, Runnable f) { + if (f == null) throw new NullPointerException(); + CompletableFuture<Void> d = new CompletableFuture<Void>(); + e.execute(new AsyncRun(d, f)); + return d; + } + + /* ------------- Signallers -------------- */ /** - * Heuristic spin value for waitingGet() before blocking on - * multiprocessors + * Completion for recording and releasing a waiting thread. This + * class implements ManagedBlocker to avoid starvation when + * blocking actions pile up in ForkJoinPools. */ - static final int SPINS = (NCPU > 1) ? 1 << 8 : 0; - - /** - * Linked nodes to record waiting threads in a Treiber stack. See - * other classes such as Phaser and SynchronousQueue for more - * detailed explanation. This class implements ManagedBlocker to - * avoid starvation when blocking actions pile up in - * ForkJoinPools. - */ - static final class WaitNode implements ForkJoinPool.ManagedBlocker { - long nanos; // wait time if timed - final long deadline; // non-zero if timed + @SuppressWarnings("serial") + static final class Signaller extends Completion + implements ForkJoinPool.ManagedBlocker { + long nanos; // wait time if timed + final long deadline; // non-zero if timed volatile int interruptControl; // > 0: interruptible, < 0: interrupted volatile Thread thread; - volatile WaitNode next; - WaitNode(boolean interruptible, long nanos, long deadline) { + + Signaller(boolean interruptible, long nanos, long deadline) { this.thread = Thread.currentThread(); this.interruptControl = interruptible ? 1 : 0; this.nanos = nanos; this.deadline = deadline; } + final CompletableFuture<?> tryFire(int ignore) { + Thread w; // no need to atomically claim + if ((w = thread) != null) { + thread = null; + LockSupport.unpark(w); + } + return null; + } public boolean isReleasable() { if (thread == null) return true; @@ -273,6 +1687,7 @@ LockSupport.parkNanos(this, nanos); return isReleasable(); } + final boolean isLive() { return thread != null; } } /** @@ -280,1832 +1695,89 @@ * interrupted. */ private Object waitingGet(boolean interruptible) { - WaitNode q = null; + Signaller q = null; boolean queued = false; - int spins = SPINS; - for (Object r;;) { - if ((r = result) != null) { - if (q != null) { // suppress unpark - q.thread = null; - if (q.interruptControl < 0) { - if (interruptible) { - removeWaiter(q); - return null; - } - Thread.currentThread().interrupt(); - } - } - postComplete(); // help release others - return r; - } + int spins = -1; + Object r; + while ((r = result) == null) { + if (spins < 0) + spins = (Runtime.getRuntime().availableProcessors() > 1) ? + 1 << 8 : 0; // Use brief spin-wait on multiprocessors else if (spins > 0) { - int rnd = ThreadLocalRandom.nextSecondarySeed(); - if (rnd == 0) - rnd = ThreadLocalRandom.current().nextInt(); - if (rnd >= 0) + if (ThreadLocalRandom.nextSecondarySeed() >= 0) --spins; } else if (q == null) - q = new WaitNode(interruptible, 0L, 0L); + q = new Signaller(interruptible, 0L, 0L); else if (!queued) - queued = UNSAFE.compareAndSwapObject(this, WAITERS, - q.next = waiters, q); + queued = tryPushStack(q); else if (interruptible && q.interruptControl < 0) { - removeWaiter(q); + q.thread = null; + cleanStack(); return null; } else if (q.thread != null && result == null) { try { ForkJoinPool.managedBlock(q); - } catch (InterruptedException ex) { + } catch (InterruptedException ie) { q.interruptControl = -1; } } } + if (q != null) { + q.thread = null; + if (q.interruptControl < 0) { + if (interruptible) + r = null; // report interruption + else + Thread.currentThread().interrupt(); + } + } + postComplete(); + return r; } /** - * Awaits completion or aborts on interrupt or timeout. - * - * @param nanos time to wait - * @return raw result + * Returns raw result after waiting, or null if interrupted, or + * throws TimeoutException on timeout. */ - private Object timedAwaitDone(long nanos) - throws InterruptedException, TimeoutException { - WaitNode q = null; + private Object timedGet(long nanos) throws TimeoutException { + if (Thread.interrupted()) + return null; + if (nanos <= 0L) + throw new TimeoutException(); + long d = System.nanoTime() + nanos; + Signaller q = new Signaller(true, nanos, d == 0L ? 1L : d); // avoid 0 boolean queued = false; - for (Object r;;) { - if ((r = result) != null) { - if (q != null) { - q.thread = null; - if (q.interruptControl < 0) { - removeWaiter(q); - throw new InterruptedException(); - } - } - postComplete(); - return r; - } - else if (q == null) { - if (nanos <= 0L) - throw new TimeoutException(); - long d = System.nanoTime() + nanos; - q = new WaitNode(true, nanos, d == 0L ? 1L : d); // avoid 0 - } - else if (!queued) - queued = UNSAFE.compareAndSwapObject(this, WAITERS, - q.next = waiters, q); - else if (q.interruptControl < 0) { - removeWaiter(q); - throw new InterruptedException(); - } - else if (q.nanos <= 0L) { - if (result == null) { - removeWaiter(q); - throw new TimeoutException(); - } + Object r; + // We intentionally don't spin here (as waitingGet does) because + // the call to nanoTime() above acts much like a spin. + while ((r = result) == null) { + if (!queued) + queued = tryPushStack(q); + else if (q.interruptControl < 0 || q.nanos <= 0L) { + q.thread = null; + cleanStack(); + if (q.interruptControl < 0) + return null; + throw new TimeoutException(); } else if (q.thread != null && result == null) { try { ForkJoinPool.managedBlock(q); - } catch (InterruptedException ex) { + } catch (InterruptedException ie) { q.interruptControl = -1; } } } + if (q.interruptControl < 0) + r = null; + q.thread = null; + postComplete(); + return r; } - /** - * Tries to unlink a timed-out or interrupted wait node to avoid - * accumulating garbage. Internal nodes are simply unspliced - * without CAS since it is harmless if they are traversed anyway - * by releasers. To avoid effects of unsplicing from already - * removed nodes, the list is retraversed in case of an apparent - * race. This is slow when there are a lot of nodes, but we don't - * expect lists to be long enough to outweigh higher-overhead - * schemes. - */ - private void removeWaiter(WaitNode node) { - if (node != null) { - node.thread = null; - retry: - for (;;) { // restart on removeWaiter race - for (WaitNode pred = null, q = waiters, s; q != null; q = s) { - s = q.next; - if (q.thread != null) - pred = q; - else if (pred != null) { - pred.next = s; - if (pred.thread == null) // check for race - continue retry; - } - else if (!UNSAFE.compareAndSwapObject(this, WAITERS, q, s)) - continue retry; - } - break; - } - } - } - - /* ------------- Async tasks -------------- */ - - /** - * A marker interface identifying asynchronous tasks produced by - * {@code async} methods. This may be useful for monitoring, - * debugging, and tracking asynchronous activities. - * - * @since 1.8 - */ - public static interface AsynchronousCompletionTask { - } - - /** Base class can act as either FJ or plain Runnable */ - @SuppressWarnings("serial") - abstract static class Async extends ForkJoinTask<Void> - implements Runnable, AsynchronousCompletionTask { - public final Void getRawResult() { return null; } - public final void setRawResult(Void v) { } - public final void run() { exec(); } - } - - /** - * Starts the given async task using the given executor, unless - * the executor is ForkJoinPool.commonPool and it has been - * disabled, in which case starts a new thread. - */ - static void execAsync(Executor e, Async r) { - if (e == ForkJoinPool.commonPool() && - ForkJoinPool.getCommonPoolParallelism() <= 1) - new Thread(r).start(); - else - e.execute(r); - } - - static final class AsyncRun extends Async { - final Runnable fn; - final CompletableFuture<Void> dst; - AsyncRun(Runnable fn, CompletableFuture<Void> dst) { - this.fn = fn; this.dst = dst; - } - public final boolean exec() { - CompletableFuture<Void> d; Throwable ex; - if ((d = this.dst) != null && d.result == null) { - try { - fn.run(); - ex = null; - } catch (Throwable rex) { - ex = rex; - } - d.internalComplete(null, ex); - } - return true; - } - private static final long serialVersionUID = 5232453952276885070L; - } - - static final class AsyncSupply<U> extends Async { - final Supplier<U> fn; - final CompletableFuture<U> dst; - AsyncSupply(Supplier<U> fn, CompletableFuture<U> dst) { - this.fn = fn; this.dst = dst; - } - public final boolean exec() { - CompletableFuture<U> d; U u; Throwable ex; - if ((d = this.dst) != null && d.result == null) { - try { - u = fn.get(); - ex = null; - } catch (Throwable rex) { - ex = rex; - u = null; - } - d.internalComplete(u, ex); - } - return true; - } - private static final long serialVersionUID = 5232453952276885070L; - } - - static final class AsyncApply<T,U> extends Async { - final T arg; - final Function<? super T,? extends U> fn; - final CompletableFuture<U> dst; - AsyncApply(T arg, Function<? super T,? extends U> fn, - CompletableFuture<U> dst) { - this.arg = arg; this.fn = fn; this.dst = dst; - } - public final boolean exec() { - CompletableFuture<U> d; U u; Throwable ex; - if ((d = this.dst) != null && d.result == null) { - try { - u = fn.apply(arg); - ex = null; - } catch (Throwable rex) { - ex = rex; - u = null; - } - d.internalComplete(u, ex); - } - return true; - } - private static final long serialVersionUID = 5232453952276885070L; - } - - static final class AsyncCombine<T,U,V> extends Async { - final T arg1; - final U arg2; - final BiFunction<? super T,? super U,? extends V> fn; - final CompletableFuture<V> dst; - AsyncCombine(T arg1, U arg2, - BiFunction<? super T,? super U,? extends V> fn, - CompletableFuture<V> dst) { - this.arg1 = arg1; this.arg2 = arg2; this.fn = fn; this.dst = dst; - } - public final boolean exec() { - CompletableFuture<V> d; V v; Throwable ex; - if ((d = this.dst) != null && d.result == null) { - try { - v = fn.apply(arg1, arg2); - ex = null; - } catch (Throwable rex) { - ex = rex; - v = null; - } - d.internalComplete(v, ex); - } - return true; - } - private static final long serialVersionUID = 5232453952276885070L; - } - - static final class AsyncAccept<T> extends Async { - final T arg; - final Consumer<? super T> fn; - final CompletableFuture<?> dst; - AsyncAccept(T arg, Consumer<? super T> fn, - CompletableFuture<?> dst) { - this.arg = arg; this.fn = fn; this.dst = dst; - } - public final boolean exec() { - CompletableFuture<?> d; Throwable ex; - if ((d = this.dst) != null && d.result == null) { - try { - fn.accept(arg); - ex = null; - } catch (Throwable rex) { - ex = rex; - } - d.internalComplete(null, ex); - } - return true; - } - private static final long serialVersionUID = 5232453952276885070L; - } - - static final class AsyncAcceptBoth<T,U> extends Async { - final T arg1; - final U arg2; - final BiConsumer<? super T,? super U> fn; - final CompletableFuture<?> dst; - AsyncAcceptBoth(T arg1, U arg2, - BiConsumer<? super T,? super U> fn, - CompletableFuture<?> dst) { - this.arg1 = arg1; this.arg2 = arg2; this.fn = fn; this.dst = dst; - } - public final boolean exec() { - CompletableFuture<?> d; Throwable ex; - if ((d = this.dst) != null && d.result == null) { - try { - fn.accept(arg1, arg2); - ex = null; - } catch (Throwable rex) { - ex = rex; - } - d.internalComplete(null, ex); - } - return true; - } - private static final long serialVersionUID = 5232453952276885070L; - } - - static final class AsyncCompose<T,U> extends Async { - final T arg; - final Function<? super T, ? extends CompletionStage<U>> fn; - final CompletableFuture<U> dst; - AsyncCompose(T arg, - Function<? super T, ? extends CompletionStage<U>> fn, - CompletableFuture<U> dst) { - this.arg = arg; this.fn = fn; this.dst = dst; - } - public final boolean exec() { - CompletableFuture<U> d, fr; U u; Throwable ex; - if ((d = this.dst) != null && d.result == null) { - try { - CompletionStage<U> cs = fn.apply(arg); - fr = (cs == null) ? null : cs.toCompletableFuture(); - ex = (fr == null) ? new NullPointerException() : null; - } catch (Throwable rex) { - ex = rex; - fr = null; - } - if (ex != null) - u = null; - else { - Object r = fr.result; - if (r == null) - r = fr.waitingGet(false); - if (r instanceof AltResult) { - ex = ((AltResult)r).ex; - u = null; - } - else { - @SuppressWarnings("unchecked") U ur = (U) r; - u = ur; - } - } - d.internalComplete(u, ex); - } - return true; - } - private static final long serialVersionUID = 5232453952276885070L; - } - - static final class AsyncWhenComplete<T> extends Async { - final T arg1; - final Throwable arg2; - final BiConsumer<? super T,? super Throwable> fn; - final CompletableFuture<T> dst; - AsyncWhenComplete(T arg1, Throwable arg2, - BiConsumer<? super T,? super Throwable> fn, - CompletableFuture<T> dst) { - this.arg1 = arg1; this.arg2 = arg2; this.fn = fn; this.dst = dst; - } - public final boolean exec() { - CompletableFuture<T> d; - if ((d = this.dst) != null && d.result == null) { - Throwable ex = arg2; - try { - fn.accept(arg1, ex); - } catch (Throwable rex) { - if (ex == null) - ex = rex; - } - d.internalComplete(arg1, ex); - } - return true; - } - private static final long serialVersionUID = 5232453952276885070L; - } - - /* ------------- Completions -------------- */ - - /** - * Simple linked list nodes to record completions, used in - * basically the same way as WaitNodes. (We separate nodes from - * the Completions themselves mainly because for the And and Or - * methods, the same Completion object resides in two lists.) - */ - static final class CompletionNode { - final Completion completion; - volatile CompletionNode next; - CompletionNode(Completion completion) { this.completion = completion; } - } - - // Opportunistically subclass AtomicInteger to use compareAndSet to claim. - @SuppressWarnings("serial") - abstract static class Completion extends AtomicInteger implements Runnable { - } - - static final class ThenApply<T,U> extends Completion { - final CompletableFuture<? extends T> src; - final Function<? super T,? extends U> fn; - final CompletableFuture<U> dst; - final Executor executor; - ThenApply(CompletableFuture<? extends T> src, - Function<? super T,? extends U> fn, - CompletableFuture<U> dst, - Executor executor) { - this.src = src; this.fn = fn; this.dst = dst; - this.executor = executor; - } - public final void run() { - final CompletableFuture<? extends T> a; - final Function<? super T,? extends U> fn; - final CompletableFuture<U> dst; - Object r; T t; Throwable ex; - if ((dst = this.dst) != null && - (fn = this.fn) != null && - (a = this.src) != null && - (r = a.result) != null && - compareAndSet(0, 1)) { - if (r instanceof AltResult) { - ex = ((AltResult)r).ex; - t = null; - } - else { - ex = null; - @SuppressWarnings("unchecked") T tr = (T) r; - t = tr; - } - Executor e = executor; - U u = null; - if (ex == null) { - try { - if (e != null) - execAsync(e, new AsyncApply<T,U>(t, fn, dst)); - else - u = fn.apply(t); - } catch (Throwable rex) { - ex = rex; - } - } - if (e == null || ex != null) - dst.internalComplete(u, ex); - } - } - private static final long serialVersionUID = 5232453952276885070L; - } - - static final class ThenAccept<T> extends Completion { - final CompletableFuture<? extends T> src; - final Consumer<? super T> fn; - final CompletableFuture<?> dst; - final Executor executor; - ThenAccept(CompletableFuture<? extends T> src, - Consumer<? super T> fn, - CompletableFuture<?> dst, - Executor executor) { - this.src = src; this.fn = fn; this.dst = dst; - this.executor = executor; - } - public final void run() { - final CompletableFuture<? extends T> a; - final Consumer<? super T> fn; - final CompletableFuture<?> dst; - Object r; T t; Throwable ex; - if ((dst = this.dst) != null && - (fn = this.fn) != null && - (a = this.src) != null && - (r = a.result) != null && - compareAndSet(0, 1)) { - if (r instanceof AltResult) { - ex = ((AltResult)r).ex; - t = null; - } - else { - ex = null; - @SuppressWarnings("unchecked") T tr = (T) r; - t = tr; - } - Executor e = executor; - if (ex == null) { - try { - if (e != null) - execAsync(e, new AsyncAccept<T>(t, fn, dst)); - else - fn.accept(t); - } catch (Throwable rex) { - ex = rex; - } - } - if (e == null || ex != null) - dst.internalComplete(null, ex); - } - } - private static final long serialVersionUID = 5232453952276885070L; - } - - static final class ThenRun extends Completion { - final CompletableFuture<?> src; - final Runnable fn; - final CompletableFuture<Void> dst; - final Executor executor; - ThenRun(CompletableFuture<?> src, - Runnable fn, - CompletableFuture<Void> dst, - Executor executor) { - this.src = src; this.fn = fn; this.dst = dst; - this.executor = executor; - } - public final void run() { - final CompletableFuture<?> a; - final Runnable fn; - final CompletableFuture<Void> dst; - Object r; Throwable ex; - if ((dst = this.dst) != null && - (fn = this.fn) != null && - (a = this.src) != null && - (r = a.result) != null && - compareAndSet(0, 1)) { - if (r instanceof AltResult) - ex = ((AltResult)r).ex; - else - ex = null; - Executor e = executor; - if (ex == null) { - try { - if (e != null) - execAsync(e, new AsyncRun(fn, dst)); - else - fn.run(); - } catch (Throwable rex) { - ex = rex; - } - } - if (e == null || ex != null) - dst.internalComplete(null, ex); - } - } - private static final long serialVersionUID = 5232453952276885070L; - } - - static final class ThenCombine<T,U,V> extends Completion { - final CompletableFuture<? extends T> src; - final CompletableFuture<? extends U> snd; - final BiFunction<? super T,? super U,? extends V> fn; - final CompletableFuture<V> dst; - final Executor executor; - ThenCombine(CompletableFuture<? extends T> src, - CompletableFuture<? extends U> snd, - BiFunction<? super T,? super U,? extends V> fn, - CompletableFuture<V> dst, - Executor executor) { - this.src = src; this.snd = snd; - this.fn = fn; this.dst = dst; - this.executor = executor; - } - public final void run() { - final CompletableFuture<? extends T> a; - final CompletableFuture<? extends U> b; - final BiFunction<? super T,? super U,? extends V> fn; - final CompletableFuture<V> dst; - Object r, s; T t; U u; Throwable ex; - if ((dst = this.dst) != null && - (fn = this.fn) != null && - (a = this.src) != null && - (r = a.result) != null && - (b = this.snd) != null && - (s = b.result) != null && - compareAndSet(0, 1)) { - if (r instanceof AltResult) { - ex = ((AltResult)r).ex; - t = null; - } - else { - ex = null; - @SuppressWarnings("unchecked") T tr = (T) r; - t = tr; - } - if (ex != null) - u = null; - else if (s instanceof AltResult) { - ex = ((AltResult)s).ex; - u = null; - } - else { - @SuppressWarnings("unchecked") U us = (U) s; - u = us; - } - Executor e = executor; - V v = null; - if (ex == null) { - try { - if (e != null) - execAsync(e, new AsyncCombine<T,U,V>(t, u, fn, dst)); - else - v = fn.apply(t, u); - } catch (Throwable rex) { - ex = rex; - } - } - if (e == null || ex != null) - dst.internalComplete(v, ex); - } - } - private static final long serialVersionUID = 5232453952276885070L; - } - - static final class ThenAcceptBoth<T,U> extends Completion { - final CompletableFuture<? extends T> src; - final CompletableFuture<? extends U> snd; - final BiConsumer<? super T,? super U> fn; - final CompletableFuture<Void> dst; - final Executor executor; - ThenAcceptBoth(CompletableFuture<? extends T> src, - CompletableFuture<? extends U> snd, - BiConsumer<? super T,? super U> fn, - CompletableFuture<Void> dst, - Executor executor) { - this.src = src; this.snd = snd; - this.fn = fn; this.dst = dst; - this.executor = executor; - } - public final void run() { - final CompletableFuture<? extends T> a; - final CompletableFuture<? extends U> b; - final BiConsumer<? super T,? super U> fn; - final CompletableFuture<Void> dst; - Object r, s; T t; U u; Throwable ex; - if ((dst = this.dst) != null && - (fn = this.fn) != null && - (a = this.src) != null && - (r = a.result) != null && - (b = this.snd) != null && - (s = b.result) != null && - compareAndSet(0, 1)) { - if (r instanceof AltResult) { - ex = ((AltResult)r).ex; - t = null; - } - else { - ex = null; - @SuppressWarnings("unchecked") T tr = (T) r; - t = tr; - } - if (ex != null) - u = null; - else if (s instanceof AltResult) { - ex = ((AltResult)s).ex; - u = null; - } - else { - @SuppressWarnings("unchecked") U us = (U) s; - u = us; - } - Executor e = executor; - if (ex == null) { - try { - if (e != null) - execAsync(e, new AsyncAcceptBoth<T,U>(t, u, fn, dst)); - else - fn.accept(t, u); - } catch (Throwable rex) { - ex = rex; - } - } - if (e == null || ex != null) - dst.internalComplete(null, ex); - } - } - private static final long serialVersionUID = 5232453952276885070L; - } - - static final class RunAfterBoth extends Completion { - final CompletableFuture<?> src; - final CompletableFuture<?> snd; - final Runnable fn; - final CompletableFuture<Void> dst; - final Executor executor; - RunAfterBoth(CompletableFuture<?> src, - CompletableFuture<?> snd, - Runnable fn, - CompletableFuture<Void> dst, - Executor executor) { - this.src = src; this.snd = snd; - this.fn = fn; this.dst = dst; - this.executor = executor; - } - public final void run() { - final CompletableFuture<?> a; - final CompletableFuture<?> b; - final Runnable fn; - final CompletableFuture<Void> dst; - Object r, s; Throwable ex; - if ((dst = this.dst) != null && - (fn = this.fn) != null && - (a = this.src) != null && - (r = a.result) != null && - (b = this.snd) != null && - (s = b.result) != null && - compareAndSet(0, 1)) { - if (r instanceof AltResult) - ex = ((AltResult)r).ex; - else - ex = null; - if (ex == null && (s instanceof AltResult)) - ex = ((AltResult)s).ex; - Executor e = executor; - if (ex == null) { - try { - if (e != null) - execAsync(e, new AsyncRun(fn, dst)); - else - fn.run(); - } catch (Throwable rex) { - ex = rex; - } - } - if (e == null || ex != null) - dst.internalComplete(null, ex); - } - } - private static final long serialVersionUID = 5232453952276885070L; - } - - static final class AndCompletion extends Completion { - final CompletableFuture<?> src; - final CompletableFuture<?> snd; - final CompletableFuture<Void> dst; - AndCompletion(CompletableFuture<?> src, - CompletableFuture<?> snd, - CompletableFuture<Void> dst) { - this.src = src; this.snd = snd; this.dst = dst; - } - public final void run() { - final CompletableFuture<?> a; - final CompletableFuture<?> b; - final CompletableFuture<Void> dst; - Object r, s; Throwable ex; - if ((dst = this.dst) != null && - (a = this.src) != null && - (r = a.result) != null && - (b = this.snd) != null && - (s = b.result) != null && - compareAndSet(0, 1)) { - if (r instanceof AltResult) - ex = ((AltResult)r).ex; - else - ex = null; - if (ex == null && (s instanceof AltResult)) - ex = ((AltResult)s).ex; - dst.internalComplete(null, ex); - } - } - private static final long serialVersionUID = 5232453952276885070L; - } - - static final class ApplyToEither<T,U> extends Completion { - final CompletableFuture<? extends T> src; - final CompletableFuture<? extends T> snd; - final Function<? super T,? extends U> fn; - final CompletableFuture<U> dst; - final Executor executor; - ApplyToEither(CompletableFuture<? extends T> src, - CompletableFuture<? extends T> snd, - Function<? super T,? extends U> fn, - CompletableFuture<U> dst, - Executor executor) { - this.src = src; this.snd = snd; - this.fn = fn; this.dst = dst; - this.executor = executor; - } - public final void run() { - final CompletableFuture<? extends T> a; - final CompletableFuture<? extends T> b; - final Function<? super T,? extends U> fn; - final CompletableFuture<U> dst; - Object r; T t; Throwable ex; - if ((dst = this.dst) != null && - (fn = this.fn) != null && - (((a = this.src) != null && (r = a.result) != null) || - ((b = this.snd) != null && (r = b.result) != null)) && - compareAndSet(0, 1)) { - if (r instanceof AltResult) { - ex = ((AltResult)r).ex; - t = null; - } - else { - ex = null; - @SuppressWarnings("unchecked") T tr = (T) r; - t = tr; - } - Executor e = executor; - U u = null; - if (ex == null) { - try { - if (e != null) - execAsync(e, new AsyncApply<T,U>(t, fn, dst)); - else - u = fn.apply(t); - } catch (Throwable rex) { - ex = rex; - } - } - if (e == null || ex != null) - dst.internalComplete(u, ex); - } - } - private static final long serialVersionUID = 5232453952276885070L; - } - - static final class AcceptEither<T> extends Completion { - final CompletableFuture<? extends T> src; - final CompletableFuture<? extends T> snd; - final Consumer<? super T> fn; - final CompletableFuture<Void> dst; - final Executor executor; - AcceptEither(CompletableFuture<? extends T> src, - CompletableFuture<? extends T> snd, - Consumer<? super T> fn, - CompletableFuture<Void> dst, - Executor executor) { - this.src = src; this.snd = snd; - this.fn = fn; this.dst = dst; - this.executor = executor; - } - public final void run() { - final CompletableFuture<? extends T> a; - final CompletableFuture<? extends T> b; - final Consumer<? super T> fn; - final CompletableFuture<Void> dst; - Object r; T t; Throwable ex; - if ((dst = this.dst) != null && - (fn = this.fn) != null && - (((a = this.src) != null && (r = a.result) != null) || - ((b = this.snd) != null && (r = b.result) != null)) && - compareAndSet(0, 1)) { - if (r instanceof AltResult) { - ex = ((AltResult)r).ex; - t = null; - } - else { - ex = null; - @SuppressWarnings("unchecked") T t