# HG changeset patch # User duke # Date 1499270471 -7200 # Node ID 4c63e7c939aa50240e7f870708f1b49f72824d19 # Parent f920d3637c9f0d98a73922a585caf5361233fe24# Parent a63bf1a4fc5c0d8503ed8d17538efa979936374c Merge diff -r f920d3637c9f -r 4c63e7c939aa .hgtags-top-repo --- a/.hgtags-top-repo Thu Jan 26 18:23:14 2012 -0800 +++ b/.hgtags-top-repo Wed Jul 05 18:01:11 2017 +0200 @@ -144,3 +144,4 @@ 5a5eaf6374bcbe23530899579fed17a05b7705f3 jdk8-b20 cc771d92284f71765eca14d6d08703c4af254c04 jdk8-b21 7ad075c809952e355d25030605da6af30456ed74 jdk8-b22 +60d6f64a86b1e511169d264727f6d51415978df0 jdk8-b23 diff -r f920d3637c9f -r 4c63e7c939aa hotspot/.hgtags --- a/hotspot/.hgtags Thu Jan 26 18:23:14 2012 -0800 +++ b/hotspot/.hgtags Wed Jul 05 18:01:11 2017 +0200 @@ -213,3 +213,5 @@ 513351373923f74a7c91755748b95c9771e59f96 hs23-b10 24727fb37561779077fdfa5a33342246f20e5c0f jdk8-b22 dcc292399a39113957eebbd3e487b7e05e2c79fc hs23-b11 +e850d8e7ea54b91c7aa656e297f0f9f38dd4c296 jdk8-b23 +9e177d44b10fe92ecffa965fef9c5ac5433c1b46 hs23-b12 diff -r f920d3637c9f -r 4c63e7c939aa hotspot/agent/src/share/classes/sun/jvm/hotspot/gc_implementation/g1/G1CollectedHeap.java --- a/hotspot/agent/src/share/classes/sun/jvm/hotspot/gc_implementation/g1/G1CollectedHeap.java Thu Jan 26 18:23:14 2012 -0800 +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/gc_implementation/g1/G1CollectedHeap.java Wed Jul 05 18:01:11 2017 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2012, 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 @@ -49,8 +49,12 @@ static private long g1CommittedFieldOffset; // size_t _summary_bytes_used; static private CIntegerField summaryBytesUsedField; - // G1MonitoringSupport* _g1mm + // G1MonitoringSupport* _g1mm; static private AddressField g1mmField; + // MasterOldRegionSet _old_set; + static private long oldSetFieldOffset; + // MasterHumongousRegionSet _humongous_set; + static private long humongousSetFieldOffset; static { VM.registerVMInitializedObserver(new Observer() { @@ -67,12 +71,14 @@ g1CommittedFieldOffset = type.getField("_g1_committed").getOffset(); summaryBytesUsedField = type.getCIntegerField("_summary_bytes_used"); g1mmField = type.getAddressField("_g1mm"); + oldSetFieldOffset = type.getField("_old_set").getOffset(); + humongousSetFieldOffset = type.getField("_humongous_set").getOffset(); } public long capacity() { Address g1CommittedAddr = addr.addOffsetTo(g1CommittedFieldOffset); - MemRegion g1_committed = new MemRegion(g1CommittedAddr); - return g1_committed.byteSize(); + MemRegion g1Committed = new MemRegion(g1CommittedAddr); + return g1Committed.byteSize(); } public long used() { @@ -94,6 +100,18 @@ return (G1MonitoringSupport) VMObjectFactory.newObject(G1MonitoringSupport.class, g1mmAddr); } + public HeapRegionSetBase oldSet() { + Address oldSetAddr = addr.addOffsetTo(oldSetFieldOffset); + return (HeapRegionSetBase) VMObjectFactory.newObject(HeapRegionSetBase.class, + oldSetAddr); + } + + public HeapRegionSetBase humongousSet() { + Address humongousSetAddr = addr.addOffsetTo(humongousSetFieldOffset); + return (HeapRegionSetBase) VMObjectFactory.newObject(HeapRegionSetBase.class, + humongousSetAddr); + } + private Iterator heapRegionIterator() { return hrs().heapRegionIterator(); } diff -r f920d3637c9f -r 4c63e7c939aa hotspot/agent/src/share/classes/sun/jvm/hotspot/gc_implementation/g1/G1MonitoringSupport.java --- a/hotspot/agent/src/share/classes/sun/jvm/hotspot/gc_implementation/g1/G1MonitoringSupport.java Thu Jan 26 18:23:14 2012 -0800 +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/gc_implementation/g1/G1MonitoringSupport.java Wed Jul 05 18:01:11 2017 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2012, 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 @@ -77,6 +77,10 @@ return edenUsedField.getValue(addr); } + public long edenRegionNum() { + return edenUsed() / HeapRegion.grainBytes(); + } + public long survivorCommitted() { return survivorCommittedField.getValue(addr); } @@ -85,6 +89,10 @@ return survivorUsedField.getValue(addr); } + public long survivorRegionNum() { + return survivorUsed() / HeapRegion.grainBytes(); + } + public long oldCommitted() { return oldCommittedField.getValue(addr); } diff -r f920d3637c9f -r 4c63e7c939aa hotspot/agent/src/share/classes/sun/jvm/hotspot/gc_implementation/g1/HeapRegionSetBase.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/gc_implementation/g1/HeapRegionSetBase.java Wed Jul 05 18:01:11 2017 +0200 @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2012, 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 HeapRegionSetBase. Represents a group of regions. + +public class HeapRegionSetBase extends VMObject { + // size_t _length; + static private CIntegerField lengthField; + // size_t _region_num; + static private CIntegerField regionNumField; + // size_t _total_used_bytes; + static private CIntegerField totalUsedBytesField; + + 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("HeapRegionSetBase"); + + lengthField = type.getCIntegerField("_length"); + regionNumField = type.getCIntegerField("_region_num"); + totalUsedBytesField = type.getCIntegerField("_total_used_bytes"); + } + + public long length() { + return lengthField.getValue(addr); + } + + public long regionNum() { + return regionNumField.getValue(addr); + } + + public long totalUsedBytes() { + return totalUsedBytesField.getValue(addr); + } + + public HeapRegionSetBase(Address addr) { + super(addr); + } +} diff -r f920d3637c9f -r 4c63e7c939aa hotspot/agent/src/share/classes/sun/jvm/hotspot/tools/HeapSummary.java --- a/hotspot/agent/src/share/classes/sun/jvm/hotspot/tools/HeapSummary.java Thu Jan 26 18:23:14 2012 -0800 +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/tools/HeapSummary.java Wed Jul 05 18:01:11 2017 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2012, 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 @@ -67,6 +67,7 @@ printValue("SurvivorRatio = ", getFlagValue("SurvivorRatio", flagMap)); printValMB("PermSize = ", getFlagValue("PermSize", flagMap)); printValMB("MaxPermSize = ", getFlagValue("MaxPermSize", flagMap)); + printValMB("G1HeapRegionSize = ", HeapRegion.grainBytes()); System.out.println(); System.out.println("Heap Usage:"); @@ -100,11 +101,20 @@ } else if (sharedHeap instanceof G1CollectedHeap) { G1CollectedHeap g1h = (G1CollectedHeap) sharedHeap; G1MonitoringSupport g1mm = g1h.g1mm(); - System.out.println("G1 Young Generation"); - printG1Space("Eden Space:", g1mm.edenUsed(), g1mm.edenCommitted()); - printG1Space("From Space:", g1mm.survivorUsed(), g1mm.survivorCommitted()); - printG1Space("To Space:", 0, 0); - printG1Space("G1 Old Generation", g1mm.oldUsed(), g1mm.oldCommitted()); + long edenRegionNum = g1mm.edenRegionNum(); + long survivorRegionNum = g1mm.survivorRegionNum(); + HeapRegionSetBase oldSet = g1h.oldSet(); + HeapRegionSetBase humongousSet = g1h.humongousSet(); + long oldRegionNum = oldSet.regionNum() + humongousSet.regionNum(); + printG1Space("G1 Heap:", g1h.n_regions(), + g1h.used(), g1h.capacity()); + System.out.println("G1 Young Generation:"); + printG1Space("Eden Space:", edenRegionNum, + g1mm.edenUsed(), g1mm.edenCommitted()); + printG1Space("Survivor Space:", survivorRegionNum, + g1mm.survivorUsed(), g1mm.survivorCommitted()); + printG1Space("G1 Old Generation:", oldRegionNum, + g1mm.oldUsed(), g1mm.oldCommitted()); } else { throw new RuntimeException("unknown SharedHeap type : " + heap.getClass()); } @@ -216,9 +226,11 @@ System.out.println(alignment + (double)space.used() * 100.0 / space.capacity() + "% used"); } - private void printG1Space(String spaceName, long used, long capacity) { + private void printG1Space(String spaceName, long regionNum, + long used, long capacity) { long free = capacity - used; System.out.println(spaceName); + printValue("regions = ", regionNum); printValMB("capacity = ", capacity); printValMB("used = ", used); printValMB("free = ", free); diff -r f920d3637c9f -r 4c63e7c939aa hotspot/make/bsd/Makefile --- a/hotspot/make/bsd/Makefile Thu Jan 26 18:23:14 2012 -0800 +++ b/hotspot/make/bsd/Makefile Wed Jul 05 18:01:11 2017 +0200 @@ -208,7 +208,7 @@ TARGETS_SHARK = $(addsuffix shark,$(TARGETS)) BUILDTREE_MAKE = $(GAMMADIR)/make/$(OSNAME)/makefiles/buildtree.make -BUILDTREE_VARS = GAMMADIR=$(GAMMADIR) OS_FAMILY=$(OSNAME) SRCARCH=$(SRCARCH) BUILDARCH=$(BUILDARCH) LIBARCH=$(LIBARCH) +BUILDTREE_VARS = GAMMADIR=$(GAMMADIR) OS_FAMILY=$(OSNAME) SRCARCH=$(SRCARCH) BUILDARCH=$(BUILDARCH) LIBARCH=$(LIBARCH) LIBRARY_SUFFIX=$(LIBRARY_SUFFIX) BUILDTREE_VARS += HOTSPOT_RELEASE_VERSION=$(HOTSPOT_RELEASE_VERSION) HOTSPOT_BUILD_VERSION=$(HOTSPOT_BUILD_VERSION) JRE_RELEASE_VERSION=$(JRE_RELEASE_VERSION) BUILDTREE = $(MAKE) -f $(BUILDTREE_MAKE) $(BUILDTREE_VARS) diff -r f920d3637c9f -r 4c63e7c939aa hotspot/make/bsd/makefiles/buildtree.make --- a/hotspot/make/bsd/makefiles/buildtree.make Thu Jan 26 18:23:14 2012 -0800 +++ b/hotspot/make/bsd/makefiles/buildtree.make Wed Jul 05 18:01:11 2017 +0200 @@ -162,20 +162,6 @@ endif endif -ifeq ($(OS_VENDOR), Darwin) - # MACOSX FIXME: we should be able to run test_gamma (see MACOSX_PORT-214) - ifeq ($(ALWAYS_PASS_TEST_GAMMA),) - # ALWAYS_PASS_TEST_GAMMA wasn't set so we default to true on MacOS X - # until MACOSX_PORT-214 is fixed - ALWAYS_PASS_TEST_GAMMA=true - endif -endif -ifeq ($(ALWAYS_PASS_TEST_GAMMA), true) - TEST_GAMMA_STATUS= echo 'exit 0'; -else - TEST_GAMMA_STATUS= -endif - BUILDTREE_VARS += HOTSPOT_RELEASE_VERSION=$(HS_BUILD_VER) HOTSPOT_BUILD_VERSION= JRE_RELEASE_VERSION=$(JRE_RELEASE_VERSION) BUILDTREE = \ @@ -353,12 +339,10 @@ $(BUILDTREE_COMMENT); \ [ -n "$$JAVA_HOME" ] && { echo ": \$${JAVA_HOME:=$${JAVA_HOME}}"; }; \ { \ - echo "LD_LIBRARY_PATH=.:$${LD_LIBRARY_PATH:+$$LD_LIBRARY_PATH:}\$${JAVA_HOME}/jre/lib/${LIBARCH}/native_threads:\$${JAVA_HOME}/jre/lib/${LIBARCH}:${GCC_LIB}"; \ - echo "DYLD_LIBRARY_PATH=.:$${DYLD_LIBRARY_PATH:+$$DYLD_LIBRARY_PATH:}\$${JAVA_HOME}/jre/lib/${LIBARCH}/native_threads:\$${JAVA_HOME}/jre/lib/${LIBARCH}:${GCC_LIB}"; \ echo "CLASSPATH=$${CLASSPATH:+$$CLASSPATH:}.:\$${JAVA_HOME}/jre/lib/rt.jar:\$${JAVA_HOME}/jre/lib/i18n.jar"; \ } | sed s:$${JAVA_HOME:--------}:\$${JAVA_HOME}:g; \ echo "HOTSPOT_BUILD_USER=\"$${LOGNAME:-$$USER} in `basename $(GAMMADIR)`\""; \ - echo "export JAVA_HOME LD_LIBRARY_PATH DYLD_LIBRARY_PATH CLASSPATH HOTSPOT_BUILD_USER"; \ + echo "export JAVA_HOME CLASSPATH HOTSPOT_BUILD_USER"; \ ) > $@ env.csh: env.sh @@ -412,7 +396,7 @@ JAVA_FLAG/64 = -d64 WRONG_DATA_MODE_MSG = \ - echo "JAVA_HOME must point to $(DATA_MODE)bit JDK." + echo "JAVA_HOME must point to a $(DATA_MODE)-bit OpenJDK." CROSS_COMPILING_MSG = \ echo "Cross compiling for ARCH $(CROSS_COMPILE_ARCH), skipping gamma run." @@ -420,20 +404,78 @@ test_gamma: $(BUILDTREE_MAKE) $(GAMMADIR)/make/test/Queens.java @echo Creating $@ ... $(QUIETLY) ( \ - echo '#!/bin/sh'; \ + echo "#!/bin/sh"; \ + echo ""; \ $(BUILDTREE_COMMENT); \ - echo '. ./env.sh'; \ - echo "if [ \"$(CROSS_COMPILE_ARCH)\" != \"\" ]; then { $(CROSS_COMPILING_MSG); exit 0; }; fi"; \ - echo "if [ -z \$$JAVA_HOME ]; then { $(NO_JAVA_HOME_MSG); exit 0; }; fi"; \ - echo "if ! \$${JAVA_HOME}/bin/java $(JAVA_FLAG) -fullversion 2>&1 > /dev/null"; \ - echo "then"; \ - echo " $(WRONG_DATA_MODE_MSG); exit 0;"; \ + echo ""; \ + echo "# Include environment settings for gamma run"; \ + echo ""; \ + echo ". ./env.sh"; \ + echo ""; \ + echo "# Do not run gamma test for cross compiles"; \ + echo ""; \ + echo "if [ -n \"$(CROSS_COMPILE_ARCH)\" ]; then "; \ + echo " $(CROSS_COMPILING_MSG)"; \ + echo " exit 0"; \ + echo "fi"; \ + echo ""; \ + echo "# Make sure JAVA_HOME is set as it is required for gamma"; \ + echo ""; \ + echo "if [ -z \"\$${JAVA_HOME}\" ]; then "; \ + echo " $(NO_JAVA_HOME_MSG)"; \ + echo " exit 0"; \ + echo "fi"; \ + echo ""; \ + echo "# Check JAVA_HOME version to be used for the test"; \ + echo ""; \ + echo "\$${JAVA_HOME}/bin/java $(JAVA_FLAG) -fullversion > /dev/null 2>&1"; \ + echo "if [ \$$? -ne 0 ]; then "; \ + echo " $(WRONG_DATA_MODE_MSG)"; \ + echo " exit 0"; \ echo "fi"; \ + echo ""; \ + echo "# Use gamma_g if it exists"; \ + echo ""; \ + echo "GAMMA_PROG=gamma"; \ + echo "if [ -f gamma_g ]; then "; \ + echo " GAMMA_PROG=gamma_g"; \ + echo "fi"; \ + echo ""; \ + echo "if [ \"$(OS_VENDOR)\" = \"Darwin\" ]; then "; \ + echo " # Ensure architecture for gamma and JAVA_HOME is the same."; \ + echo " # NOTE: gamma assumes the OpenJDK directory layout."; \ + echo ""; \ + echo " GAMMA_ARCH=\"\`file \$${GAMMA_PROG} | awk '{print \$$NF}'\`\""; \ + echo " JVM_LIB=\"\$${JAVA_HOME}/jre/lib/libjava.$(LIBRARY_SUFFIX)\""; \ + echo " if [ ! -f \$${JVM_LIB} ]; then"; \ + echo " JVM_LIB=\"\$${JAVA_HOME}/jre/lib/$${LIBARCH}/libjava.$(LIBRARY_SUFFIX)\""; \ + echo " fi"; \ + echo " if [ ! -f \$${JVM_LIB} ] || [ -z \"\`file \$${JVM_LIB} | grep \$${GAMMA_ARCH}\`\" ]; then "; \ + echo " $(WRONG_DATA_MODE_MSG)"; \ + echo " exit 0"; \ + echo " fi"; \ + echo "fi"; \ + echo ""; \ + echo "# Compile Queens program for test"; \ + echo ""; \ echo "rm -f Queens.class"; \ echo "\$${JAVA_HOME}/bin/javac -d . $(GAMMADIR)/make/test/Queens.java"; \ - echo '[ -f gamma_g ] && { gamma=gamma_g; }'; \ - echo './$${gamma:-gamma} $(TESTFLAGS) Queens < /dev/null'; \ - $(TEST_GAMMA_STATUS) \ + echo ""; \ + echo "# Set library path solely for gamma launcher test run"; \ + echo ""; \ + echo "LD_LIBRARY_PATH=.:$${LD_LIBRARY_PATH:+$$LD_LIBRARY_PATH:}\$${JAVA_HOME}/jre/lib/${LIBARCH}/native_threads:\$${JAVA_HOME}/jre/lib/${LIBARCH}:${GCC_LIB}"; \ + echo "export LD_LIBRARY_PATH"; \ + echo "unset LD_LIBRARY_PATH_32"; \ + echo "unset LD_LIBRARY_PATH_64"; \ + echo ""; \ + echo "if [ \"$(OS_VENDOR)\" = \"Darwin\" ]; then "; \ + echo " DYLD_LIBRARY_PATH=.:$${DYLD_LIBRARY_PATH:+$$DYLD_LIBRARY_PATH:}\$${JAVA_HOME}/jre/lib/native_threads:\$${JAVA_HOME}/jre/lib:$${DYLD_LIBRARY_PATH:+$$DYLD_LIBRARY_PATH:}\$${JAVA_HOME}/jre/lib/${LIBARCH}/native_threads:\$${JAVA_HOME}/jre/lib/${LIBARCH}:${GCC_LIB}"; \ + echo " export DYLD_LIBRARY_PATH"; \ + echo "fi"; \ + echo ""; \ + echo "# Use the gamma launcher and JAVA_HOME to run the test"; \ + echo ""; \ + echo "./\$${GAMMA_PROG} $(TESTFLAGS) Queens < /dev/null"; \ ) > $@ $(QUIETLY) chmod +x $@ diff -r f920d3637c9f -r 4c63e7c939aa hotspot/make/bsd/makefiles/defs.make --- a/hotspot/make/bsd/makefiles/defs.make Thu Jan 26 18:23:14 2012 -0800 +++ b/hotspot/make/bsd/makefiles/defs.make Wed Jul 05 18:01:11 2017 +0200 @@ -142,6 +142,7 @@ # client and server subdirectories have symbolic links to ../libjsig.so EXPORT_LIST += $(EXPORT_JRE_LIB_ARCH_DIR)/libjsig.$(LIBRARY_SUFFIX) EXPORT_SERVER_DIR = $(EXPORT_JRE_LIB_ARCH_DIR)/server +EXPORT_CLIENT_DIR = $(EXPORT_JRE_LIB_ARCH_DIR)/client ifndef BUILD_CLIENT_ONLY EXPORT_LIST += $(EXPORT_SERVER_DIR)/Xusage.txt @@ -150,7 +151,6 @@ ifneq ($(ZERO_BUILD), true) ifeq ($(ARCH_DATA_MODEL), 32) - EXPORT_CLIENT_DIR = $(EXPORT_JRE_LIB_ARCH_DIR)/client EXPORT_LIST += $(EXPORT_CLIENT_DIR)/Xusage.txt EXPORT_LIST += $(EXPORT_CLIENT_DIR)/libjvm.$(LIBRARY_SUFFIX) endif diff -r f920d3637c9f -r 4c63e7c939aa hotspot/make/bsd/makefiles/launcher.make --- a/hotspot/make/bsd/makefiles/launcher.make Thu Jan 26 18:23:14 2012 -0800 +++ b/hotspot/make/bsd/makefiles/launcher.make Wed Jul 05 18:01:11 2017 +0200 @@ -50,7 +50,24 @@ LIBS_LAUNCHER += $(STATIC_STDCXX) $(LIBS) else LAUNCHER.o = launcher.o - LFLAGS_LAUNCHER += -L`pwd` + LFLAGS_LAUNCHER += -L`pwd` + + # The gamma launcher runs the JDK from $JAVA_HOME, overriding the JVM with a + # freshly built JVM at ./libjvm.{so|dylib}. This is accomplished by setting + # the library searchpath using ({DY}LD_LIBRARY_PATH) to find the local JVM + # first. Gamma dlopen()s libjava from $JAVA_HOME/jre/lib{/$arch}, which is + # statically linked with CoreFoundation framework libs. Unfortunately, gamma's + # unique searchpath results in some unresolved symbols in the framework + # libraries, because JDK libraries are inadvertently discovered first on the + # searchpath, e.g. libjpeg. On Mac OS X, filenames are case *insensitive*. + # So, the actual filename collision is libjpeg.dylib and libJPEG.dylib. + # To resolve this, gamma needs to also statically link with the CoreFoundation + # framework libraries. + + ifeq ($(OS_VENDOR),Darwin) + LFLAGS_LAUNCHER += -framework CoreFoundation + endif + LIBS_LAUNCHER += -l$(JVM) $(LIBS) endif diff -r f920d3637c9f -r 4c63e7c939aa hotspot/make/bsd/makefiles/vm.make --- a/hotspot/make/bsd/makefiles/vm.make Thu Jan 26 18:23:14 2012 -0800 +++ b/hotspot/make/bsd/makefiles/vm.make Wed Jul 05 18:01:11 2017 +0200 @@ -337,8 +337,8 @@ $(LIBJVM).dSYM: $(LIBJVM) dsymutil $(LIBJVM) -# no launcher or libjvm_db for macosx -build: $(LIBJVM) $(LIBJSIG) $(BUILDLIBSAPROC) dtraceCheck $(LIBJVM).dSYM +# no libjvm_db for macosx +build: $(LIBJVM) $(LAUNCHER) $(LIBJSIG) $(BUILDLIBSAPROC) dtraceCheck $(LIBJVM).dSYM echo "Doing vm.make build:" else build: $(LIBJVM) $(LAUNCHER) $(LIBJSIG) $(LIBJVM_DB) $(BUILDLIBSAPROC) diff -r f920d3637c9f -r 4c63e7c939aa hotspot/make/hotspot_version --- a/hotspot/make/hotspot_version Thu Jan 26 18:23:14 2012 -0800 +++ b/hotspot/make/hotspot_version Wed Jul 05 18:01:11 2017 +0200 @@ -35,7 +35,7 @@ HS_MAJOR_VER=23 HS_MINOR_VER=0 -HS_BUILD_NUMBER=11 +HS_BUILD_NUMBER=12 JDK_MAJOR_VER=1 JDK_MINOR_VER=8 diff -r f920d3637c9f -r 4c63e7c939aa hotspot/make/jprt.properties --- a/hotspot/make/jprt.properties Thu Jan 26 18:23:14 2012 -0800 +++ b/hotspot/make/jprt.properties Wed Jul 05 18:01:11 2017 +0200 @@ -174,6 +174,10 @@ jprt.my.linux.armsflt.ejdk7=linux_armsflt_2.6 jprt.my.linux.armsflt=${jprt.my.linux.armsflt.${jprt.tools.default.release}} +jprt.my.macosx.x64.jdk8=macosx_x64_10.7 +jprt.my.macosx.x64.jdk7=macosx_x64_10.7 +jprt.my.macosx.x64=${jprt.my.macosx.x64.${jprt.tools.default.release}} + jprt.my.windows.i586.jdk8=windows_i586_5.1 jprt.my.windows.i586.jdk7=windows_i586_5.1 jprt.my.windows.i586.jdk7b107=windows_i586_5.0 @@ -211,6 +215,7 @@ ${jprt.my.solaris.x64}-{product|fastdebug|debug}, \ ${jprt.my.linux.i586}-{product|fastdebug|debug}, \ ${jprt.my.linux.x64}-{product|fastdebug}, \ + ${jprt.my.macosx.x64}-{product|fastdebug|debug}, \ ${jprt.my.windows.i586}-{product|fastdebug|debug}, \ ${jprt.my.windows.x64}-{product|fastdebug|debug} @@ -416,6 +421,30 @@ ${jprt.my.linux.x64}-{product|fastdebug}-c2-jbb_G1, \ ${jprt.my.linux.x64}-{product|fastdebug}-c2-jbb_ParOldGC +jprt.my.macosx.x64.test.targets = \ + ${jprt.my.macosx.x64}-{product|fastdebug}-c2-jvm98, \ + ${jprt.my.macosx.x64}-{product|fastdebug}-c2-jvm98_nontiered, \ + ${jprt.my.macosx.x64}-{product|fastdebug}-c2-scimark, \ + ${jprt.my.macosx.x64}-{product|fastdebug}-c2-GCBasher_default, \ + ${jprt.my.macosx.x64}-{product|fastdebug}-c2-GCBasher_SerialGC, \ + ${jprt.my.macosx.x64}-{product|fastdebug}-c2-GCBasher_ParallelGC, \ + ${jprt.my.macosx.x64}-{product|fastdebug}-c2-GCBasher_ParNewGC, \ + ${jprt.my.macosx.x64}-{product|fastdebug}-c2-GCBasher_CMS, \ + ${jprt.my.macosx.x64}-{product|fastdebug}-c2-GCBasher_G1, \ + ${jprt.my.macosx.x64}-{product|fastdebug}-c2-GCBasher_ParOldGC, \ + ${jprt.my.macosx.x64}-{product|fastdebug}-c2-GCOld_default, \ + ${jprt.my.macosx.x64}-{product|fastdebug}-c2-GCOld_SerialGC, \ + ${jprt.my.macosx.x64}-{product|fastdebug}-c2-GCOld_ParallelGC, \ + ${jprt.my.macosx.x64}-{product|fastdebug}-c2-GCOld_ParNewGC, \ + ${jprt.my.macosx.x64}-{product|fastdebug}-c2-GCOld_CMS, \ + ${jprt.my.macosx.x64}-{product|fastdebug}-c2-GCOld_G1, \ + ${jprt.my.macosx.x64}-{product|fastdebug}-c2-GCOld_ParOldGC \ + ${jprt.my.macosx.x64}-{product|fastdebug}-c2-jbb_default, \ + ${jprt.my.macosx.x64}-{product|fastdebug}-c2-jbb_default_tiered, \ + ${jprt.my.macosx.x64}-{product|fastdebug}-c2-jbb_ParallelGC, \ + ${jprt.my.macosx.x64}-{product|fastdebug}-c2-jbb_G1, \ + ${jprt.my.macosx.x64}-{product|fastdebug}-c2-jbb_ParOldGC + jprt.my.windows.i586.test.targets = \ ${jprt.my.windows.i586}-{product|fastdebug}-{c1|c2}-jvm98, \ ${jprt.my.windows.i586}-{product|fastdebug}-c2-jvm98_nontiered, \ @@ -492,6 +521,7 @@ ${jprt.my.solaris.x64.test.targets}, \ ${jprt.my.linux.i586.test.targets}, \ ${jprt.my.linux.x64.test.targets}, \ + ${jprt.my.macosx.x64.test.targets}, \ ${jprt.my.windows.i586.test.targets}, \ ${jprt.my.windows.x64.test.targets}, \ ${jprt.test.targets.open} @@ -538,6 +568,7 @@ ${jprt.my.solaris.x64}-*-c2-servertest, \ ${jprt.my.linux.i586}-*-c2-servertest, \ ${jprt.my.linux.x64}-*-c2-servertest, \ + ${jprt.my.macosx.x64}-*-c2-servertest, \ ${jprt.my.windows.i586}-*-c2-servertest, \ ${jprt.my.windows.x64}-*-c2-servertest @@ -548,6 +579,7 @@ ${jprt.my.solaris.x64}-fastdebug-c2-internalvmtests, \ ${jprt.my.linux.i586}-fastdebug-c2-internalvmtests, \ ${jprt.my.linux.x64}-fastdebug-c2-internalvmtests, \ + ${jprt.my.macosx.x64}-fastdebug-c2-internalvmtests, \ ${jprt.my.windows.i586}-fastdebug-c2-internalvmtests, \ ${jprt.my.windows.x64}-fastdebug-c2-internalvmtests diff -r f920d3637c9f -r 4c63e7c939aa hotspot/make/linux/makefiles/buildtree.make --- a/hotspot/make/linux/makefiles/buildtree.make Thu Jan 26 18:23:14 2012 -0800 +++ b/hotspot/make/linux/makefiles/buildtree.make Wed Jul 05 18:01:11 2017 +0200 @@ -326,11 +326,10 @@ $(BUILDTREE_COMMENT); \ [ -n "$$JAVA_HOME" ] && { echo ": \$${JAVA_HOME:=$${JAVA_HOME}}"; }; \ { \ - echo "LD_LIBRARY_PATH=.:$${LD_LIBRARY_PATH:+$$LD_LIBRARY_PATH:}\$${JAVA_HOME}/jre/lib/${LIBARCH}/native_threads:\$${JAVA_HOME}/jre/lib/${LIBARCH}:${GCC_LIB}"; \ echo "CLASSPATH=$${CLASSPATH:+$$CLASSPATH:}.:\$${JAVA_HOME}/jre/lib/rt.jar:\$${JAVA_HOME}/jre/lib/i18n.jar"; \ } | sed s:$${JAVA_HOME:--------}:\$${JAVA_HOME}:g; \ echo "HOTSPOT_BUILD_USER=\"$${LOGNAME:-$$USER} in `basename $(GAMMADIR)`\""; \ - echo "export JAVA_HOME LD_LIBRARY_PATH CLASSPATH HOTSPOT_BUILD_USER"; \ + echo "export JAVA_HOME CLASSPATH HOTSPOT_BUILD_USER"; \ ) > $@ env.csh: env.sh @@ -384,7 +383,7 @@ JAVA_FLAG/64 = -d64 WRONG_DATA_MODE_MSG = \ - echo "JAVA_HOME must point to $(DATA_MODE)bit JDK." + echo "JAVA_HOME must point to a $(DATA_MODE)-bit OpenJDK." CROSS_COMPILING_MSG = \ echo "Cross compiling for ARCH $(CROSS_COMPILE_ARCH), skipping gamma run." @@ -392,19 +391,78 @@ test_gamma: $(BUILDTREE_MAKE) $(GAMMADIR)/make/test/Queens.java @echo Creating $@ ... $(QUIETLY) ( \ - echo '#!/bin/sh'; \ + echo "#!/bin/sh"; \ + echo ""; \ $(BUILDTREE_COMMENT); \ - echo '. ./env.sh'; \ - echo "if [ \"$(CROSS_COMPILE_ARCH)\" != \"\" ]; then { $(CROSS_COMPILING_MSG); exit 0; }; fi"; \ - echo "if [ -z \$$JAVA_HOME ]; then { $(NO_JAVA_HOME_MSG); exit 0; }; fi"; \ - echo "if ! \$${JAVA_HOME}/bin/java $(JAVA_FLAG) -fullversion 2>&1 > /dev/null"; \ - echo "then"; \ - echo " $(WRONG_DATA_MODE_MSG); exit 0;"; \ + echo ""; \ + echo "# Include environment settings for gamma run"; \ + echo ""; \ + echo ". ./env.sh"; \ + echo ""; \ + echo "# Do not run gamma test for cross compiles"; \ + echo ""; \ + echo "if [ -n \"$(CROSS_COMPILE_ARCH)\" ]; then "; \ + echo " $(CROSS_COMPILING_MSG)"; \ + echo " exit 0"; \ + echo "fi"; \ + echo ""; \ + echo "# Make sure JAVA_HOME is set as it is required for gamma"; \ + echo ""; \ + echo "if [ -z \"\$${JAVA_HOME}\" ]; then "; \ + echo " $(NO_JAVA_HOME_MSG)"; \ + echo " exit 0"; \ + echo "fi"; \ + echo ""; \ + echo "# Check JAVA_HOME version to be used for the test"; \ + echo ""; \ + echo "\$${JAVA_HOME}/bin/java $(JAVA_FLAG) -fullversion > /dev/null 2>&1"; \ + echo "if [ \$$? -ne 0 ]; then "; \ + echo " $(WRONG_DATA_MODE_MSG)"; \ + echo " exit 0"; \ echo "fi"; \ + echo ""; \ + echo "# Use gamma_g if it exists"; \ + echo ""; \ + echo "GAMMA_PROG=gamma"; \ + echo "if [ -f gamma_g ]; then "; \ + echo " GAMMA_PROG=gamma_g"; \ + echo "fi"; \ + echo ""; \ + echo "if [ \"$(OS_VENDOR)\" = \"Darwin\" ]; then "; \ + echo " # Ensure architecture for gamma and JAVA_HOME is the same."; \ + echo " # NOTE: gamma assumes the OpenJDK directory layout."; \ + echo ""; \ + echo " GAMMA_ARCH=\"\`file \$${GAMMA_PROG} | awk '{print \$$NF}'\`\""; \ + echo " JVM_LIB=\"\$${JAVA_HOME}/jre/lib/libjava.$(LIBRARY_SUFFIX)\""; \ + echo " if [ ! -f \$${JVM_LIB} ]; then"; \ + echo " JVM_LIB=\"\$${JAVA_HOME}/jre/lib/$${LIBARCH}/libjava.$(LIBRARY_SUFFIX)\""; \ + echo " fi"; \ + echo " if [ ! -f \$${JVM_LIB} ] || [ -z \"\`file \$${JVM_LIB} | grep \$${GAMMA_ARCH}\`\" ]; then "; \ + echo " $(WRONG_DATA_MODE_MSG)"; \ + echo " exit 0"; \ + echo " fi"; \ + echo "fi"; \ + echo ""; \ + echo "# Compile Queens program for test"; \ + echo ""; \ echo "rm -f Queens.class"; \ echo "\$${JAVA_HOME}/bin/javac -d . $(GAMMADIR)/make/test/Queens.java"; \ - echo '[ -f gamma_g ] && { gamma=gamma_g; }'; \ - echo './$${gamma:-gamma} $(TESTFLAGS) Queens < /dev/null'; \ + echo ""; \ + echo "# Set library path solely for gamma launcher test run"; \ + echo ""; \ + echo "LD_LIBRARY_PATH=.:$${LD_LIBRARY_PATH:+$$LD_LIBRARY_PATH:}\$${JAVA_HOME}/jre/lib/${LIBARCH}/native_threads:\$${JAVA_HOME}/jre/lib/${LIBARCH}:${GCC_LIB}"; \ + echo "export LD_LIBRARY_PATH"; \ + echo "unset LD_LIBRARY_PATH_32"; \ + echo "unset LD_LIBRARY_PATH_64"; \ + echo ""; \ + echo "if [ \"$(OS_VENDOR)\" = \"Darwin\" ]; then "; \ + echo " DYLD_LIBRARY_PATH=.:$${DYLD_LIBRARY_PATH:+$$DYLD_LIBRARY_PATH:}\$${JAVA_HOME}/jre/lib/native_threads:\$${JAVA_HOME}/jre/lib:$${DYLD_LIBRARY_PATH:+$$DYLD_LIBRARY_PATH:}\$${JAVA_HOME}/jre/lib/${LIBARCH}/native_threads:\$${JAVA_HOME}/jre/lib/${LIBARCH}:${GCC_LIB}"; \ + echo " export DYLD_LIBRARY_PATH"; \ + echo "fi"; \ + echo ""; \ + echo "# Use the gamma launcher and JAVA_HOME to run the test"; \ + echo ""; \ + echo "./\$${GAMMA_PROG} $(TESTFLAGS) Queens < /dev/null"; \ ) > $@ $(QUIETLY) chmod +x $@ diff -r f920d3637c9f -r 4c63e7c939aa hotspot/make/solaris/makefiles/buildtree.make --- a/hotspot/make/solaris/makefiles/buildtree.make Thu Jan 26 18:23:14 2012 -0800 +++ b/hotspot/make/solaris/makefiles/buildtree.make Wed Jul 05 18:01:11 2017 +0200 @@ -118,7 +118,7 @@ BUILDTREE_MAKE = $(GAMMADIR)/make/$(OS_FAMILY)/makefiles/buildtree.make BUILDTREE_TARGETS = Makefile flags.make flags_vm.make vm.make adlc.make jvmti.make sa.make \ - env.ksh env.csh jdkpath.sh .dbxrc test_gamma + env.sh env.csh jdkpath.sh .dbxrc test_gamma BUILDTREE_VARS = GAMMADIR=$(GAMMADIR) OS_FAMILY=$(OS_FAMILY) \ ARCH=$(ARCH) BUILDARCH=$(BUILDARCH) LIBARCH=$(LIBARCH) VARIANT=$(VARIANT) @@ -313,22 +313,19 @@ echo "include \$$(GAMMADIR)/make/$(OS_FAMILY)/makefiles/$(@F)"; \ ) > $@ -env.ksh: $(BUILDTREE_MAKE) +env.sh: $(BUILDTREE_MAKE) @echo Creating $@ ... $(QUIETLY) ( \ $(BUILDTREE_COMMENT); \ [ -n "$$JAVA_HOME" ] && { echo ": \$${JAVA_HOME:=$${JAVA_HOME}}"; }; \ { \ - echo "LD_LIBRARY_PATH=.:$${LD_LIBRARY_PATH:+$$LD_LIBRARY_PATH:}\$${JAVA_HOME}/jre/lib/${LIBARCH}/native_threads:\$${JAVA_HOME}/jre/lib/${LIBARCH}:${GCC_LIB}"; \ - echo "unset LD_LIBRARY_PATH_32"; \ - echo "unset LD_LIBRARY_PATH_64"; \ echo "CLASSPATH=$${CLASSPATH:+$$CLASSPATH:}.:\$${JAVA_HOME}/jre/lib/rt.jar:\$${JAVA_HOME}/jre/lib/i18n.jar"; \ } | sed s:$${JAVA_HOME:--------}:\$${JAVA_HOME}:g; \ echo "HOTSPOT_BUILD_USER=\"$${LOGNAME:-$$USER} in `basename $(GAMMADIR)`\""; \ echo "export JAVA_HOME LD_LIBRARY_PATH CLASSPATH HOTSPOT_BUILD_USER"; \ ) > $@ -env.csh: env.ksh +env.csh: env.sh @echo Creating $@ ... $(QUIETLY) ( \ $(BUILDTREE_COMMENT); \ @@ -384,23 +381,86 @@ JAVA_FLAG/64 = -d64 WRONG_DATA_MODE_MSG = \ - echo "JAVA_HOME must point to $(DATA_MODE)bit JDK." + echo "JAVA_HOME must point to a $(DATA_MODE)-bit OpenJDK." + +CROSS_COMPILING_MSG = \ + echo "Cross compiling for ARCH $(CROSS_COMPILE_ARCH), skipping gamma run." test_gamma: $(BUILDTREE_MAKE) $(GAMMADIR)/make/test/Queens.java @echo Creating $@ ... $(QUIETLY) ( \ - echo '#!/bin/ksh'; \ + echo "#!/bin/sh"; \ + echo ""; \ $(BUILDTREE_COMMENT); \ - echo '. ./env.ksh'; \ - echo "if [ -z \$$JAVA_HOME ]; then { $(NO_JAVA_HOME_MSG); exit 0; }; fi"; \ - echo "if ! \$${JAVA_HOME}/bin/java $(JAVA_FLAG) -fullversion 2>&1 > /dev/null"; \ - echo "then"; \ - echo " $(WRONG_DATA_MODE_MSG); exit 0;"; \ + echo ""; \ + echo "# Include environment settings for gamma run"; \ + echo ""; \ + echo ". ./env.sh"; \ + echo ""; \ + echo "# Do not run gamma test for cross compiles"; \ + echo ""; \ + echo "if [ -n \"$(CROSS_COMPILE_ARCH)\" ]; then "; \ + echo " $(CROSS_COMPILING_MSG)"; \ + echo " exit 0"; \ + echo "fi"; \ + echo ""; \ + echo "# Make sure JAVA_HOME is set as it is required for gamma"; \ + echo ""; \ + echo "if [ -z \"\$${JAVA_HOME}\" ]; then "; \ + echo " $(NO_JAVA_HOME_MSG)"; \ + echo " exit 0"; \ + echo "fi"; \ + echo ""; \ + echo "# Check JAVA_HOME version to be used for the test"; \ + echo ""; \ + echo "\$${JAVA_HOME}/bin/java $(JAVA_FLAG) -fullversion > /dev/null 2>&1"; \ + echo "if [ \$$? -ne 0 ]; then "; \ + echo " $(WRONG_DATA_MODE_MSG)"; \ + echo " exit 0"; \ echo "fi"; \ + echo ""; \ + echo "# Use gamma_g if it exists"; \ + echo ""; \ + echo "GAMMA_PROG=gamma"; \ + echo "if [ -f gamma_g ]; then "; \ + echo " GAMMA_PROG=gamma_g"; \ + echo "fi"; \ + echo ""; \ + echo "if [ \"$(OS_VENDOR)\" = \"Darwin\" ]; then "; \ + echo " # Ensure architecture for gamma and JAVA_HOME is the same."; \ + echo " # NOTE: gamma assumes the OpenJDK directory layout."; \ + echo ""; \ + echo " GAMMA_ARCH=\"\`file \$${GAMMA_PROG} | awk '{print \$$NF}'\`\""; \ + echo " JVM_LIB=\"\$${JAVA_HOME}/jre/lib/libjava.$(LIBRARY_SUFFIX)\""; \ + echo " if [ ! -f \$${JVM_LIB} ]; then"; \ + echo " JVM_LIB=\"\$${JAVA_HOME}/jre/lib/$${LIBARCH}/libjava.$(LIBRARY_SUFFIX)\""; \ + echo " fi"; \ + echo " if [ ! -f \$${JVM_LIB} ] || [ -z \"\`file \$${JVM_LIB} | grep \$${GAMMA_ARCH}\`\" ]; then "; \ + echo " $(WRONG_DATA_MODE_MSG)"; \ + echo " exit 0"; \ + echo " fi"; \ + echo "fi"; \ + echo ""; \ + echo "# Compile Queens program for test"; \ + echo ""; \ echo "rm -f Queens.class"; \ echo "\$${JAVA_HOME}/bin/javac -d . $(GAMMADIR)/make/test/Queens.java"; \ - echo '[ -f gamma_g ] && { gamma=gamma_g; }'; \ - echo './$${gamma:-gamma} $(TESTFLAGS) Queens < /dev/null'; \ + echo ""; \ + echo "# Set library path solely for gamma launcher test run"; \ + echo ""; \ + echo "LD_LIBRARY_PATH=.:$${LD_LIBRARY_PATH:+$$LD_LIBRARY_PATH:}\$${JAVA_HOME}/jre/lib/${LIBARCH}/native_threads:\$${JAVA_HOME}/jre/lib/${LIBARCH}:${GCC_LIB}"; \ + echo "export LD_LIBRARY_PATH"; \ + echo "unset LD_LIBRARY_PATH_32"; \ + echo "unset LD_LIBRARY_PATH_64"; \ + echo ""; \ + echo "if [ \"$(OS_VENDOR)\" = \"Darwin\" ]; then "; \ + echo " DYLD_LIBRARY_PATH=.:$${DYLD_LIBRARY_PATH:+$$DYLD_LIBRARY_PATH:}\$${JAVA_HOME}/jre/lib/native_threads:\$${JAVA_HOME}/jre/lib:$${DYLD_LIBRARY_PATH:+$$DYLD_LIBRARY_PATH:}\$${JAVA_HOME}/jre/lib/${LIBARCH}/native_threads:\$${JAVA_HOME}/jre/lib/${LIBARCH}:${GCC_LIB}"; \ + echo " export DYLD_LIBRARY_PATH"; \ + echo "fi"; \ + echo ""; \ + echo "# Use the gamma launcher and JAVA_HOME to run the test"; \ + echo ""; \ + echo "./\$${GAMMA_PROG} $(TESTFLAGS) Queens < /dev/null"; \ ) > $@ $(QUIETLY) chmod +x $@ diff -r f920d3637c9f -r 4c63e7c939aa hotspot/src/cpu/sparc/vm/frame_sparc.cpp --- a/hotspot/src/cpu/sparc/vm/frame_sparc.cpp Thu Jan 26 18:23:14 2012 -0800 +++ b/hotspot/src/cpu/sparc/vm/frame_sparc.cpp Wed Jul 05 18:01:11 2017 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2012, 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 @@ -810,7 +810,7 @@ } -#ifdef ASSERT +#ifndef PRODUCT #define DESCRIBE_FP_OFFSET(name) \ values.describe(frame_no, fp() + frame::name##_offset, #name) @@ -820,11 +820,19 @@ values.describe(frame_no, sp() + w, err_msg("register save area word %d", w), 1); } - if (is_interpreted_frame()) { + if (is_ricochet_frame()) { + MethodHandles::RicochetFrame::describe(this, values, frame_no); + } else if (is_interpreted_frame()) { DESCRIBE_FP_OFFSET(interpreter_frame_d_scratch_fp); DESCRIBE_FP_OFFSET(interpreter_frame_l_scratch_fp); DESCRIBE_FP_OFFSET(interpreter_frame_padding); DESCRIBE_FP_OFFSET(interpreter_frame_oop_temp); + + // esp, according to Lesp (e.g. not depending on bci), if seems valid + intptr_t* esp = *interpreter_frame_esp_addr(); + if ((esp >= sp()) && (esp < fp())) { + values.describe(-1, esp, "*Lesp"); + } } if (!is_compiled_frame()) { @@ -844,4 +852,3 @@ // unused... but returns fp() to minimize changes introduced by 7087445 return fp(); } - diff -r f920d3637c9f -r 4c63e7c939aa hotspot/src/cpu/sparc/vm/methodHandles_sparc.cpp --- a/hotspot/src/cpu/sparc/vm/methodHandles_sparc.cpp Thu Jan 26 18:23:14 2012 -0800 +++ b/hotspot/src/cpu/sparc/vm/methodHandles_sparc.cpp Wed Jul 05 18:01:11 2017 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2008, 2012, 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 @@ -177,7 +177,7 @@ BLOCK_COMMENT("ricochet_blob.bounce"); if (VerifyMethodHandles) RicochetFrame::verify_clean(_masm); - trace_method_handle(_masm, "ricochet_blob.bounce"); + trace_method_handle(_masm, "return/ricochet_blob.bounce"); __ JMP(L1_continuation, 0); __ delayed()->nop(); @@ -268,14 +268,16 @@ } // Emit code to verify that FP is pointing at a valid ricochet frame. -#ifdef ASSERT +#ifndef PRODUCT enum { ARG_LIMIT = 255, SLOP = 45, // use this parameter for checking for garbage stack movements: UNREASONABLE_STACK_MOVE = (ARG_LIMIT + SLOP) // the slop defends against false alarms due to fencepost errors }; +#endif +#ifdef ASSERT void MethodHandles::RicochetFrame::verify_clean(MacroAssembler* _masm) { // The stack should look like this: // ... keep1 | dest=42 | keep2 | magic | handler | magic | recursive args | [RF] @@ -1001,31 +1003,142 @@ } #ifndef PRODUCT +void MethodHandles::RicochetFrame::describe(const frame* fr, FrameValues& values, int frame_no) { + RicochetFrame* rf = new RicochetFrame(*fr); + + // ricochet slots (kept in registers for sparc) + values.describe(frame_no, rf->register_addr(I5_savedSP), err_msg("exact_sender_sp reg for #%d", frame_no)); + values.describe(frame_no, rf->register_addr(L5_conversion), err_msg("conversion reg for #%d", frame_no)); + values.describe(frame_no, rf->register_addr(L4_saved_args_base), err_msg("saved_args_base reg for #%d", frame_no)); + values.describe(frame_no, rf->register_addr(L3_saved_args_layout), err_msg("saved_args_layout reg for #%d", frame_no)); + values.describe(frame_no, rf->register_addr(L2_saved_target), err_msg("saved_target reg for #%d", frame_no)); + values.describe(frame_no, rf->register_addr(L1_continuation), err_msg("continuation reg for #%d", frame_no)); + + // relevant ricochet targets (in caller frame) + values.describe(-1, rf->saved_args_base(), err_msg("*saved_args_base for #%d", frame_no)); + values.describe(-1, (intptr_t *)(STACK_BIAS+(uintptr_t)rf->exact_sender_sp()), err_msg("*exact_sender_sp+STACK_BIAS for #%d", frame_no)); +} +#endif // ASSERT + +#ifndef PRODUCT extern "C" void print_method_handle(oop mh); void trace_method_handle_stub(const char* adaptername, oopDesc* mh, - intptr_t* saved_sp) { + intptr_t* saved_sp, + intptr_t* args, + intptr_t* tracing_fp) { bool has_mh = (strstr(adaptername, "return/") == NULL); // return adapters don't have mh - tty->print_cr("MH %s mh="INTPTR_FORMAT " saved_sp=" INTPTR_FORMAT, adaptername, (intptr_t) mh, saved_sp); - if (has_mh) + + tty->print_cr("MH %s mh="INTPTR_FORMAT " saved_sp=" INTPTR_FORMAT " args=" INTPTR_FORMAT, adaptername, (intptr_t) mh, saved_sp, args); + + if (Verbose) { + // dumping last frame with frame::describe + + JavaThread* p = JavaThread::active(); + + ResourceMark rm; + PRESERVE_EXCEPTION_MARK; // may not be needed by safer and unexpensive here + FrameValues values; + + // Note: We want to allow trace_method_handle from any call site. + // While trace_method_handle creates a frame, it may be entered + // without a valid return PC in O7 (e.g. not just after a call). + // Walking that frame could lead to failures due to that invalid PC. + // => carefully detect that frame when doing the stack walking + + // walk up to the right frame using the "tracing_fp" argument + intptr_t* cur_sp = StubRoutines::Sparc::flush_callers_register_windows_func()(); + frame cur_frame(cur_sp, frame::unpatchable, NULL); + + while (cur_frame.fp() != (intptr_t *)(STACK_BIAS+(uintptr_t)tracing_fp)) { + cur_frame = os::get_sender_for_C_frame(&cur_frame); + } + + // safely create a frame and call frame::describe + intptr_t *dump_sp = cur_frame.sender_sp(); + intptr_t *dump_fp = cur_frame.link(); + + bool walkable = has_mh; // whether the traced frame shoud be walkable + + // the sender for cur_frame is the caller of trace_method_handle + if (walkable) { + // The previous definition of walkable may have to be refined + // if new call sites cause the next frame constructor to start + // failing. Alternatively, frame constructors could be + // modified to support the current or future non walkable + // frames (but this is more intrusive and is not considered as + // part of this RFE, which will instead use a simpler output). + frame dump_frame = frame(dump_sp, + cur_frame.sp(), // younger_sp + false); // no adaptation + dump_frame.describe(values, 1); + } else { + // Robust dump for frames which cannot be constructed from sp/younger_sp + // Add descriptions without building a Java frame to avoid issues + values.describe(-1, dump_fp, "fp for #1 "); + values.describe(-1, dump_sp, "sp"); + } + + bool has_args = has_mh; // whether Gargs is meaningful + + // mark args, if seems valid (may not be valid for some adapters) + if (has_args) { + if ((args >= dump_sp) && (args < dump_fp)) { + values.describe(-1, args, "*G4_args"); + } + } + + // mark saved_sp, if seems valid (may not be valid for some adapters) + intptr_t *unbiased_sp = (intptr_t *)(STACK_BIAS+(uintptr_t)saved_sp); + if ((unbiased_sp >= dump_sp - UNREASONABLE_STACK_MOVE) && (unbiased_sp < dump_fp)) { + values.describe(-1, unbiased_sp, "*saved_sp+STACK_BIAS"); + } + + // Note: the unextended_sp may not be correct + tty->print_cr(" stack layout:"); + values.print(p); + } + + if (has_mh) { print_method_handle(mh); + } } + void MethodHandles::trace_method_handle(MacroAssembler* _masm, const char* adaptername) { if (!TraceMethodHandles) return; BLOCK_COMMENT("trace_method_handle {"); // save: Gargs, O5_savedSP - __ save_frame(16); + __ save_frame(16); // need space for saving required FPU state + __ set((intptr_t) adaptername, O0); __ mov(G3_method_handle, O1); __ mov(I5_savedSP, O2); + __ mov(Gargs, O3); + __ mov(I6, O4); // frame identifier for safe stack walking + + // Save scratched registers that might be needed. Robustness is more + // important than optimizing the saves for this debug only code. + + // save FP result, valid at some call sites (adapter_opt_return_float, ...) + Address d_save(FP, -sizeof(jdouble) + STACK_BIAS); + __ stf(FloatRegisterImpl::D, Ftos_d, d_save); + // Safely save all globals but G2 (handled by call_VM_leaf) and G7 + // (OS reserved). __ mov(G3_method_handle, L3); __ mov(Gargs, L4); __ mov(G5_method_type, L5); - __ call_VM_leaf(L7, CAST_FROM_FN_PTR(address, trace_method_handle_stub)); + __ mov(G6, L6); + __ mov(G1, L1); + + __ call_VM_leaf(L2 /* for G2 */, CAST_FROM_FN_PTR(address, trace_method_handle_stub)); __ mov(L3, G3_method_handle); __ mov(L4, Gargs); __ mov(L5, G5_method_type); + __ mov(L6, G6); + __ mov(L1, G1); + __ ldf(FloatRegisterImpl::D, d_save, Ftos_d); + __ restore(); BLOCK_COMMENT("} trace_method_handle"); } @@ -1250,7 +1363,7 @@ move_typed_arg(_masm, arg_type, false, prim_value_addr, Address(O0_argslot, 0), - O2_scratch); // must be an even register for !_LP64 long moves (uses O2/O3) + O2_scratch); // must be an even register for !_LP64 long moves (uses O2/O3) } if (direct_to_method) { diff -r f920d3637c9f -r 4c63e7c939aa hotspot/src/cpu/sparc/vm/methodHandles_sparc.hpp --- a/hotspot/src/cpu/sparc/vm/methodHandles_sparc.hpp Thu Jan 26 18:23:14 2012 -0800 +++ b/hotspot/src/cpu/sparc/vm/methodHandles_sparc.hpp Wed Jul 05 18:01:11 2017 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2012, 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 @@ -145,6 +145,8 @@ } static void verify_clean(MacroAssembler* _masm) NOT_DEBUG_RETURN; + + static void describe(const frame* fr, FrameValues& values, int frame_no) PRODUCT_RETURN; }; // Additional helper methods for MethodHandles code generation: diff -r f920d3637c9f -r 4c63e7c939aa hotspot/src/cpu/x86/vm/frame_x86.cpp --- a/hotspot/src/cpu/x86/vm/frame_x86.cpp Thu Jan 26 18:23:14 2012 -0800 +++ b/hotspot/src/cpu/x86/vm/frame_x86.cpp Wed Jul 05 18:01:11 2017 +0200 @@ -651,13 +651,15 @@ return &interpreter_frame_tos_address()[index]; } -#ifdef ASSERT +#ifndef PRODUCT #define DESCRIBE_FP_OFFSET(name) \ values.describe(frame_no, fp() + frame::name##_offset, #name) void frame::describe_pd(FrameValues& values, int frame_no) { - if (is_interpreted_frame()) { + if (is_ricochet_frame()) { + MethodHandles::RicochetFrame::describe(this, values, frame_no); + } else if (is_interpreted_frame()) { DESCRIBE_FP_OFFSET(interpreter_frame_sender_sp); DESCRIBE_FP_OFFSET(interpreter_frame_last_sp); DESCRIBE_FP_OFFSET(interpreter_frame_method); @@ -667,7 +669,6 @@ DESCRIBE_FP_OFFSET(interpreter_frame_bcx); DESCRIBE_FP_OFFSET(interpreter_frame_initial_sp); } - } #endif diff -r f920d3637c9f -r 4c63e7c939aa hotspot/src/cpu/x86/vm/methodHandles_x86.cpp --- a/hotspot/src/cpu/x86/vm/methodHandles_x86.cpp Thu Jan 26 18:23:14 2012 -0800 +++ b/hotspot/src/cpu/x86/vm/methodHandles_x86.cpp Wed Jul 05 18:01:11 2017 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2012, 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 @@ -279,14 +279,16 @@ } // Emit code to verify that RBP is pointing at a valid ricochet frame. -#ifdef ASSERT +#ifndef PRODUCT enum { ARG_LIMIT = 255, SLOP = 4, // use this parameter for checking for garbage stack movements: UNREASONABLE_STACK_MOVE = (ARG_LIMIT + SLOP) // the slop defends against false alarms due to fencepost errors }; +#endif +#ifdef ASSERT void MethodHandles::RicochetFrame::verify_clean(MacroAssembler* _masm) { // The stack should look like this: // ... keep1 | dest=42 | keep2 | RF | magic | handler | magic | recursive args | @@ -990,6 +992,26 @@ BLOCK_COMMENT("} move_return_value"); } +#ifndef PRODUCT +#define DESCRIBE_RICOCHET_OFFSET(rf, name) \ + values.describe(frame_no, (intptr_t *) (((uintptr_t)rf) + MethodHandles::RicochetFrame::name##_offset_in_bytes()), #name) + +void MethodHandles::RicochetFrame::describe(const frame* fr, FrameValues& values, int frame_no) { + address bp = (address) fr->fp(); + RicochetFrame* rf = (RicochetFrame*)(bp - sender_link_offset_in_bytes()); + + // ricochet slots + DESCRIBE_RICOCHET_OFFSET(rf, exact_sender_sp); + DESCRIBE_RICOCHET_OFFSET(rf, conversion); + DESCRIBE_RICOCHET_OFFSET(rf, saved_args_base); + DESCRIBE_RICOCHET_OFFSET(rf, saved_args_layout); + DESCRIBE_RICOCHET_OFFSET(rf, saved_target); + DESCRIBE_RICOCHET_OFFSET(rf, continuation); + + // relevant ricochet targets (in caller frame) + values.describe(-1, rf->saved_args_base(), err_msg("*saved_args_base for #%d", frame_no)); +} +#endif // ASSERT #ifndef PRODUCT extern "C" void print_method_handle(oop mh); @@ -1001,6 +1023,7 @@ intptr_t* saved_bp) { // called as a leaf from native code: do not block the JVM! bool has_mh = (strstr(adaptername, "return/") == NULL); // return adapters don't have rcx_mh + intptr_t* last_sp = (intptr_t*) saved_bp[frame::interpreter_frame_last_sp_offset]; intptr_t* base_sp = last_sp; typedef MethodHandles::RicochetFrame RicochetFrame; @@ -1030,13 +1053,64 @@ tty->cr(); if (last_sp != saved_sp && last_sp != NULL) tty->print_cr("*** last_sp="PTR_FORMAT, (intptr_t)last_sp); - int stack_dump_count = 16; - if (stack_dump_count < (int)(saved_bp + 2 - saved_sp)) - stack_dump_count = (int)(saved_bp + 2 - saved_sp); - if (stack_dump_count > 64) stack_dump_count = 48; - for (i = 0; i < stack_dump_count; i += 4) { - tty->print_cr(" dump at SP[%d] "PTR_FORMAT": "PTR_FORMAT" "PTR_FORMAT" "PTR_FORMAT" "PTR_FORMAT, - i, (intptr_t) &entry_sp[i+0], entry_sp[i+0], entry_sp[i+1], entry_sp[i+2], entry_sp[i+3]); + + { + // dumping last frame with frame::describe + + JavaThread* p = JavaThread::active(); + + ResourceMark rm; + PRESERVE_EXCEPTION_MARK; // may not be needed by safer and unexpensive here + FrameValues values; + + // Note: We want to allow trace_method_handle from any call site. + // While trace_method_handle creates a frame, it may be entered + // without a PC on the stack top (e.g. not just after a call). + // Walking that frame could lead to failures due to that invalid PC. + // => carefully detect that frame when doing the stack walking + + // Current C frame + frame cur_frame = os::current_frame(); + + // Robust search of trace_calling_frame (independant of inlining). + // Assumes saved_regs comes from a pusha in the trace_calling_frame. + assert(cur_frame.sp() < saved_regs, "registers not saved on stack ?"); + frame trace_calling_frame = os::get_sender_for_C_frame(&cur_frame); + while (trace_calling_frame.fp() < saved_regs) { + trace_calling_frame = os::get_sender_for_C_frame(&trace_calling_frame); + } + + // safely create a frame and call frame::describe + intptr_t *dump_sp = trace_calling_frame.sender_sp(); + intptr_t *dump_fp = trace_calling_frame.link(); + + bool walkable = has_mh; // whether the traced frame shoud be walkable + + if (walkable) { + // The previous definition of walkable may have to be refined + // if new call sites cause the next frame constructor to start + // failing. Alternatively, frame constructors could be + // modified to support the current or future non walkable + // frames (but this is more intrusive and is not considered as + // part of this RFE, which will instead use a simpler output). + frame dump_frame = frame(dump_sp, dump_fp); + dump_frame.describe(values, 1); + } else { + // Stack may not be walkable (invalid PC above FP): + // Add descriptions without building a Java frame to avoid issues + values.describe(-1, dump_fp, "fp for #1 "); + values.describe(-1, dump_sp, "sp for #1"); + } + + // mark saved_sp if seems valid + if (has_mh) { + if ((saved_sp >= dump_sp - UNREASONABLE_STACK_MOVE) && (saved_sp < dump_fp)) { + values.describe(-1, saved_sp, "*saved_sp"); + } + } + + tty->print_cr(" stack layout:"); + values.print(p); } if (has_mh) print_method_handle(mh); @@ -1066,26 +1140,49 @@ void MethodHandles::trace_method_handle(MacroAssembler* _masm, const char* adaptername) { if (!TraceMethodHandles) return; BLOCK_COMMENT("trace_method_handle {"); - __ push(rax); - __ lea(rax, Address(rsp, wordSize * NOT_LP64(6) LP64_ONLY(14))); // entry_sp __ pusha(); + __ enter(); + __ andptr(rsp, -16); // align stack if needed for FPU state __ pusha(); - __ mov(rbx, rsp); - __ enter(); + __ mov(rbx, rsp); // for retreiving saved_regs + // Note: saved_regs must be in the entered frame for the + // robust stack walking implemented in trace_method_handle_stub. + + // save FP result, valid at some call sites (adapter_opt_return_float, ...) + __ increment(rsp, -2 * wordSize); + if (UseSSE >= 2) { + __ movdbl(Address(rsp, 0), xmm0); + } else if (UseSSE == 1) { + __ movflt(Address(rsp, 0), xmm0); + } else { + __ fst_d(Address(rsp, 0)); + } + // incoming state: // rcx: method handle // r13 or rsi: saved sp // To avoid calling convention issues, build a record on the stack and pass the pointer to that instead. + // Note: fix the increment below if pushing more arguments __ push(rbp); // saved_bp - __ push(rsi); // saved_sp - __ push(rax); // entry_sp + __ push(saved_last_sp_register()); // saved_sp + __ push(rbp); // entry_sp (with extra align space) __ push(rbx); // pusha saved_regs __ push(rcx); // mh - __ push(rcx); // adaptername + __ push(rcx); // slot for adaptername __ movptr(Address(rsp, 0), (intptr_t) adaptername); __ super_call_VM_leaf(CAST_FROM_FN_PTR(address, trace_method_handle_stub_wrapper), rsp); - __ leave(); + __ increment(rsp, 6 * wordSize); // MethodHandleStubArguments + + if (UseSSE >= 2) { + __ movdbl(xmm0, Address(rsp, 0)); + } else if (UseSSE == 1) { + __ movflt(xmm0, Address(rsp, 0)); + } else { + __ fld_d(Address(rsp, 0)); + } + __ increment(rsp, 2 * wordSize); + __ popa(); - __ pop(rax); + __ leave(); BLOCK_COMMENT("} trace_method_handle"); } #endif //PRODUCT diff -r f920d3637c9f -r 4c63e7c939aa hotspot/src/cpu/x86/vm/methodHandles_x86.hpp --- a/hotspot/src/cpu/x86/vm/methodHandles_x86.hpp Thu Jan 26 18:23:14 2012 -0800 +++ b/hotspot/src/cpu/x86/vm/methodHandles_x86.hpp Wed Jul 05 18:01:11 2017 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010, 2012, 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 @@ -224,6 +224,8 @@ } static void verify_clean(MacroAssembler* _masm) NOT_DEBUG_RETURN; + + static void describe(const frame* fr, FrameValues& values, int frame_no) PRODUCT_RETURN; }; // Additional helper methods for MethodHandles code generation: diff -r f920d3637c9f -r 4c63e7c939aa hotspot/src/cpu/zero/vm/frame_zero.cpp --- a/hotspot/src/cpu/zero/vm/frame_zero.cpp Thu Jan 26 18:23:14 2012 -0800 +++ b/hotspot/src/cpu/zero/vm/frame_zero.cpp Wed Jul 05 18:01:11 2017 +0200 @@ -418,7 +418,7 @@ } } -#ifdef ASSERT +#ifndef PRODUCT void frame::describe_pd(FrameValues& values, int frame_no) { diff -r f920d3637c9f -r 4c63e7c939aa hotspot/src/os/bsd/vm/os_bsd.cpp --- a/hotspot/src/os/bsd/vm/os_bsd.cpp Thu Jan 26 18:23:14 2012 -0800 +++ b/hotspot/src/os/bsd/vm/os_bsd.cpp Wed Jul 05 18:01:11 2017 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2012, 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 @@ -301,6 +301,12 @@ #error Add appropriate cpu_arch setting #endif +// Compiler variant +#ifdef COMPILER2 +#define COMPILER_VARIANT "server" +#else +#define COMPILER_VARIANT "client" +#endif #ifndef _ALLBSD_SOURCE // pid_t gettid() @@ -2507,7 +2513,7 @@ static char saved_jvm_path[MAXPATHLEN] = {0}; -// Find the full path to the current module, libjvm.so or libjvm_g.so +// Find the full path to the current module, libjvm or libjvm_g void os::jvm_path(char *buf, jint buflen) { // Error checking. if (buflen < MAXPATHLEN) { @@ -2532,11 +2538,11 @@ if (Arguments::created_by_gamma_launcher()) { // Support for the gamma launcher. Typical value for buf is - // "/jre/lib///libjvm.so". If "/jre/lib/" appears at + // "/jre/lib///libjvm". If "/jre/lib/" appears at // the right place in the string, then assume we are installed in a JDK and - // we're done. Otherwise, check for a JAVA_HOME environment variable and fix - // up the path so it looks like libjvm.so is installed there (append a - // fake suffix hotspot/libjvm.so). + // we're done. Otherwise, check for a JAVA_HOME environment variable and + // construct a path to the JVM being overridden. + const char *p = buf + strlen(buf) - 1; for (int count = 0; p > buf && count < 5; ++count) { for (--p; p > buf && *p != '/'; --p) @@ -2550,7 +2556,7 @@ char* jrelib_p; int len; - // Check the current module name "libjvm.so" or "libjvm_g.so". + // Check the current module name "libjvm" or "libjvm_g". p = strrchr(buf, '/'); assert(strstr(p, "/libjvm") == p, "invalid library name"); p = strstr(p, "_g") ? "_g" : ""; @@ -2563,19 +2569,32 @@ // modules image doesn't have "jre" subdirectory len = strlen(buf); jrelib_p = buf + len; - snprintf(jrelib_p, buflen-len, "/jre/lib/%s", cpu_arch); + + // Add the appropriate library subdir + snprintf(jrelib_p, buflen-len, "/jre/lib"); if (0 != access(buf, F_OK)) { - snprintf(jrelib_p, buflen-len, "/lib/%s", cpu_arch); + snprintf(jrelib_p, buflen-len, "/lib"); } + // Add the appropriate client or server subdir + len = strlen(buf); + jrelib_p = buf + len; + snprintf(jrelib_p, buflen-len, "/%s", COMPILER_VARIANT); + if (0 != access(buf, F_OK)) { + snprintf(jrelib_p, buflen-len, ""); + } + + // If the path exists within JAVA_HOME, add the JVM library name + // to complete the path to JVM being overridden. Otherwise fallback + // to the path to the current library. if (0 == access(buf, F_OK)) { - // Use current module name "libjvm[_g].so" instead of - // "libjvm"debug_only("_g")".so" since for fastdebug version - // we should have "libjvm.so" but debug_only("_g") adds "_g"! + // Use current module name "libjvm[_g]" instead of + // "libjvm"debug_only("_g")"" since for fastdebug version + // we should have "libjvm" but debug_only("_g") adds "_g"! len = strlen(buf); - snprintf(buf + len, buflen-len, "/hotspot/libjvm%s.so", p); + snprintf(buf + len, buflen-len, "/libjvm%s%s", p, JNI_LIB_SUFFIX); } else { - // Go back to path of .so + // Fall back to path of current library rp = realpath(dli_fname, buf); if (rp == NULL) return; @@ -3570,26 +3589,28 @@ // It is only used when ThreadPriorityPolicy=1 and requires root privilege. #if defined(_ALLBSD_SOURCE) && !defined(__APPLE__) -int os::java_to_os_priority[MaxPriority + 1] = { +int os::java_to_os_priority[CriticalPriority + 1] = { 19, // 0 Entry should never be used 0, // 1 MinPriority 3, // 2 6, // 3 - 10, // 4 - 15, // 5 NormPriority - 18, // 6 - - 21, // 7 - 25, // 8 - 28, // 9 NearMaxPriority - - 31 // 10 MaxPriority + 10, // 4 + 15, // 5 NormPriority + 18, // 6 + + 21, // 7 + 25, // 8 + 28, // 9 NearMaxPriority + + 31, // 10 MaxPriority + + 31 // 11 CriticalPriority }; #elif defined(__APPLE__) /* Using Mach high-level priority assignments */ -int os::java_to_os_priority[MaxPriority + 1] = { +int os::java_to_os_priority[CriticalPriority + 1] = { 0, // 0 Entry should never be used (MINPRI_USER) 27, // 1 MinPriority @@ -3604,10 +3625,12 @@ 34, // 8 35, // 9 NearMaxPriority - 36 // 10 MaxPriority + 36, // 10 MaxPriority + + 36 // 11 CriticalPriority }; #else -int os::java_to_os_priority[MaxPriority + 1] = { +int os::java_to_os_priority[CriticalPriority + 1] = { 19, // 0 Entry should never be used 4, // 1 MinPriority @@ -3622,7 +3645,9 @@ -3, // 8 -4, // 9 NearMaxPriority - -5 // 10 MaxPriority + -5, // 10 MaxPriority + + -5 // 11 CriticalPriority }; #endif @@ -3638,6 +3663,9 @@ ThreadPriorityPolicy = 0; } } + if (UseCriticalJavaThreadPriority) { + os::java_to_os_priority[MaxPriority] = os::java_to_os_priority[CriticalPriority]; + } return 0; } diff -r f920d3637c9f -r 4c63e7c939aa hotspot/src/os/linux/vm/os_linux.cpp --- a/hotspot/src/os/linux/vm/os_linux.cpp Thu Jan 26 18:23:14 2012 -0800 +++ b/hotspot/src/os/linux/vm/os_linux.cpp Wed Jul 05 18:01:11 2017 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2012, 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 @@ -3383,7 +3383,7 @@ // this reason, the code should not be used as default (ThreadPriorityPolicy=0). // It is only used when ThreadPriorityPolicy=1 and requires root privilege. -int os::java_to_os_priority[MaxPriority + 1] = { +int os::java_to_os_priority[CriticalPriority + 1] = { 19, // 0 Entry should never be used 4, // 1 MinPriority @@ -3398,7 +3398,9 @@ -3, // 8 -4, // 9 NearMaxPriority - -5 // 10 MaxPriority + -5, // 10 MaxPriority + + -5 // 11 CriticalPriority }; static int prio_init() { @@ -3413,6 +3415,9 @@ ThreadPriorityPolicy = 0; } } + if (UseCriticalJavaThreadPriority) { + os::java_to_os_priority[MaxPriority] = os::java_to_os_priority[CriticalPriority]; + } return 0; } diff -r f920d3637c9f -r 4c63e7c939aa hotspot/src/os/posix/launcher/java_md.c --- a/hotspot/src/os/posix/launcher/java_md.c Thu Jan 26 18:23:14 2012 -0800 +++ b/hotspot/src/os/posix/launcher/java_md.c Wed Jul 05 18:01:11 2017 +0200 @@ -701,6 +701,14 @@ char libjava[MAXPATHLEN]; if (GetApplicationHome(path, pathsize)) { + + /* Is the JRE universal, i.e. no arch dir? */ + sprintf(libjava, "%s/jre/lib/" JAVA_DLL, path); + if (access(libjava, F_OK) == 0) { + strcat(path, "/jre"); + goto found; + } + /* Is JRE co-located with the application? */ sprintf(libjava, "%s/lib/%s/" JAVA_DLL, path, arch); if (access(libjava, F_OK) == 0) { @@ -734,7 +742,7 @@ ifn->GetDefaultJavaVMInitArgs = JNI_GetDefaultJavaVMInitArgs; return JNI_TRUE; #else - Dl_info dlinfo; + Dl_info dlinfo; void *libjvm; if (_launcher_debug) { diff -r f920d3637c9f -r 4c63e7c939aa hotspot/src/os/solaris/vm/osThread_solaris.hpp --- a/hotspot/src/os/solaris/vm/osThread_solaris.hpp Thu Jan 26 18:23:14 2012 -0800 +++ b/hotspot/src/os/solaris/vm/osThread_solaris.hpp Wed Jul 05 18:01:11 2017 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2012, 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,17 +28,17 @@ // This is embedded via include into the class OSThread private: - - thread_t _thread_id; // Solaris thread id - unsigned int _lwp_id; // lwp ID, only used with bound threads - sigset_t _caller_sigmask; // Caller's signal mask - bool _vm_created_thread; // true if the VM create this thread - // false if primary thread or attached thread + thread_t _thread_id; // Solaris thread id + uint _lwp_id; // lwp ID, only used with bound threads + int _native_priority; // Saved native priority when starting + // a bound thread + sigset_t _caller_sigmask; // Caller's signal mask + bool _vm_created_thread; // true if the VM created this thread, + // false if primary thread or attached thread public: - - thread_t thread_id() const { return _thread_id; } - - unsigned int lwp_id() const { return _lwp_id; } + thread_t thread_id() const { return _thread_id; } + uint lwp_id() const { return _lwp_id; } + int native_priority() const { return _native_priority; } // Set and get state of _vm_created_thread flag void set_vm_created() { _vm_created_thread = true; } @@ -62,8 +62,9 @@ return true; } #endif - void set_thread_id(thread_t id) { _thread_id = id; } - void set_lwp_id(unsigned int id){ _lwp_id = id; } + void set_thread_id(thread_t id) { _thread_id = id; } + void set_lwp_id(uint id) { _lwp_id = id; } + void set_native_priority(int prio) { _native_priority = prio; } // *************************************************************** // interrupt support. interrupts (using signals) are used to get diff -r f920d3637c9f -r 4c63e7c939aa hotspot/src/os/solaris/vm/os_solaris.cpp --- a/hotspot/src/os/solaris/vm/os_solaris.cpp Thu Jan 26 18:23:14 2012 -0800 +++ b/hotspot/src/os/solaris/vm/os_solaris.cpp Wed Jul 05 18:01:11 2017 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2012, 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 @@ -114,6 +114,7 @@ # include # include # include +# include # include # include # include @@ -129,8 +130,8 @@ #ifdef _GNU_SOURCE // See bug #6514594 extern "C" int madvise(caddr_t, size_t, int); -extern "C" int memcntl(caddr_t addr, size_t len, int cmd, caddr_t arg, - int attr, int mask); +extern "C" int memcntl(caddr_t addr, size_t len, int cmd, caddr_t arg, + int attr, int mask); #endif //_GNU_SOURCE /* @@ -215,8 +216,9 @@ #define MaximumPriority 127 // Values for ThreadPriorityPolicy == 1 -int prio_policy1[MaxPriority+1] = { -99999, 0, 16, 32, 48, 64, - 80, 96, 112, 124, 127 }; +int prio_policy1[CriticalPriority+1] = { + -99999, 0, 16, 32, 48, 64, + 80, 96, 112, 124, 127, 127 }; // System parameters used internally static clock_t clock_tics_per_sec = 100; @@ -1048,15 +1050,22 @@ } // If the creator called set priority before we started, - // we need to call set priority now that we have an lwp. - // Get the priority from libthread and set the priority - // for the new Solaris lwp. + // we need to call set_native_priority now that we have an lwp. + // We used to get the priority from thr_getprio (we called + // thr_setprio way back in create_thread) and pass it to + // set_native_priority, but Solaris scales the priority + // in java_to_os_priority, so when we read it back here, + // we pass trash to set_native_priority instead of what's + // in java_to_os_priority. So we save the native priority + // in the osThread and recall it here. + if ( osthr->thread_id() != -1 ) { if ( UseThreadPriorities ) { - thr_getprio(osthr->thread_id(), &prio); + int prio = osthr->native_priority(); if (ThreadPriorityVerbose) { - tty->print_cr("Starting Thread " INTPTR_FORMAT ", LWP is " INTPTR_FORMAT ", setting priority: %d\n", - osthr->thread_id(), osthr->lwp_id(), prio ); + tty->print_cr("Starting Thread " INTPTR_FORMAT ", LWP is " + INTPTR_FORMAT ", setting priority: %d\n", + osthr->thread_id(), osthr->lwp_id(), prio); } os::set_native_priority(thread, prio); } @@ -1353,13 +1362,12 @@ // Remember that we created this thread so we can set priority on it osthread->set_vm_created(); - // Set the default thread priority otherwise use NormalPriority - - if ( UseThreadPriorities ) { - thr_setprio(tid, (DefaultThreadPriority == -1) ? + // Set the default thread priority. If using bound threads, setting + // lwp priority will be delayed until thread start. + set_native_priority(thread, + DefaultThreadPriority == -1 ? java_to_os_priority[NormPriority] : DefaultThreadPriority); - } // Initial thread state is INITIALIZED, not SUSPENDED osthread->set_state(INITIALIZED); @@ -3728,7 +3736,7 @@ } SchedInfo; -static SchedInfo tsLimits, iaLimits, rtLimits; +static SchedInfo tsLimits, iaLimits, rtLimits, fxLimits; #ifdef ASSERT static int ReadBackValidate = 1; @@ -3739,6 +3747,8 @@ static int myCur = 0; static bool priocntl_enable = false; +static const int criticalPrio = 60; // FX/60 is critical thread class/priority on T4 +static int java_MaxPriority_to_os_priority = 0; // Saved mapping // Call the version of priocntl suitable for all supported versions // of Solaris. We need to call through this wrapper so that we can @@ -3783,19 +3793,27 @@ if (os::Solaris::T2_libthread() || UseBoundThreads) { // If ThreadPriorityPolicy is 1, switch tables if (ThreadPriorityPolicy == 1) { - for (i = 0 ; i < MaxPriority+1; i++) + for (i = 0 ; i < CriticalPriority+1; i++) os::java_to_os_priority[i] = prio_policy1[i]; } + if (UseCriticalJavaThreadPriority) { + // MaxPriority always maps to the FX scheduling class and criticalPrio. + // See set_native_priority() and set_lwp_class_and_priority(). + // Save original MaxPriority mapping in case attempt to + // use critical priority fails. + java_MaxPriority_to_os_priority = os::java_to_os_priority[MaxPriority]; + // Set negative to distinguish from other priorities + os::java_to_os_priority[MaxPriority] = -criticalPrio; + } } // Not using Bound Threads, set to ThreadPolicy 1 else { - for ( i = 0 ; i < MaxPriority+1; i++ ) { + for ( i = 0 ; i < CriticalPriority+1; i++ ) { os::java_to_os_priority[i] = prio_policy1[i]; } return 0; } - // Get IDs for a set of well-known scheduling classes. // TODO-FIXME: GETCLINFO returns the current # of classes in the // the system. We should have a loop that iterates over the @@ -3828,24 +3846,33 @@ rtLimits.maxPrio = ((rtinfo_t*)ClassInfo.pc_clinfo)->rt_maxpri; rtLimits.minPrio = 0; + strcpy(ClassInfo.pc_clname, "FX"); + ClassInfo.pc_cid = -1; + rslt = (*priocntl_ptr)(PC_VERSION, P_ALL, 0, PC_GETCID, (caddr_t)&ClassInfo); + if (rslt < 0) return errno; + assert(ClassInfo.pc_cid != -1, "cid for FX class is -1"); + fxLimits.schedPolicy = ClassInfo.pc_cid; + fxLimits.maxPrio = ((fxinfo_t*)ClassInfo.pc_clinfo)->fx_maxupri; + fxLimits.minPrio = 0; // Query our "current" scheduling class. - // This will normally be IA,TS or, rarely, RT. - memset (&ParmInfo, 0, sizeof(ParmInfo)); + // This will normally be IA, TS or, rarely, FX or RT. + memset(&ParmInfo, 0, sizeof(ParmInfo)); ParmInfo.pc_cid = PC_CLNULL; - rslt = (*priocntl_ptr) (PC_VERSION, P_PID, P_MYID, PC_GETPARMS, (caddr_t)&ParmInfo ); - if ( rslt < 0 ) return errno; + rslt = (*priocntl_ptr) (PC_VERSION, P_PID, P_MYID, PC_GETPARMS, (caddr_t)&ParmInfo); + if (rslt < 0) return errno; myClass = ParmInfo.pc_cid; // We now know our scheduling classId, get specific information - // the class. + // about the class. ClassInfo.pc_cid = myClass; ClassInfo.pc_clname[0] = 0; - rslt = (*priocntl_ptr) (PC_VERSION, (idtype)0, 0, PC_GETCLINFO, (caddr_t)&ClassInfo ); - if ( rslt < 0 ) return errno; - - if (ThreadPriorityVerbose) - tty->print_cr ("lwp_priocntl_init: Class=%d(%s)...", myClass, ClassInfo.pc_clname); + rslt = (*priocntl_ptr) (PC_VERSION, (idtype)0, 0, PC_GETCLINFO, (caddr_t)&ClassInfo); + if (rslt < 0) return errno; + + if (ThreadPriorityVerbose) { + tty->print_cr("lwp_priocntl_init: Class=%d(%s)...", myClass, ClassInfo.pc_clname); + } memset(&ParmInfo, 0, sizeof(pcparms_t)); ParmInfo.pc_cid = PC_CLNULL; @@ -3865,6 +3892,11 @@ myMin = tsLimits.minPrio; myMax = tsLimits.maxPrio; myMax = MIN2(myMax, (int)tsInfo->ts_uprilim); // clamp - restrict + } else if (ParmInfo.pc_cid == fxLimits.schedPolicy) { + fxparms_t *fxInfo = (fxparms_t*)ParmInfo.pc_clparms; + myMin = fxLimits.minPrio; + myMax = fxLimits.maxPrio; + myMax = MIN2(myMax, (int)fxInfo->fx_uprilim); // clamp - restrict } else { // No clue - punt if (ThreadPriorityVerbose) @@ -3872,8 +3904,9 @@ return EINVAL; // no clue, punt } - if (ThreadPriorityVerbose) - tty->print_cr ("Thread priority Range: [%d..%d]\n", myMin, myMax); + if (ThreadPriorityVerbose) { + tty->print_cr ("Thread priority Range: [%d..%d]\n", myMin, myMax); + } priocntl_enable = true; // Enable changing priorities return 0; @@ -3882,6 +3915,7 @@ #define IAPRI(x) ((iaparms_t *)((x).pc_clparms)) #define RTPRI(x) ((rtparms_t *)((x).pc_clparms)) #define TSPRI(x) ((tsparms_t *)((x).pc_clparms)) +#define FXPRI(x) ((fxparms_t *)((x).pc_clparms)) // scale_to_lwp_priority @@ -3900,13 +3934,13 @@ } -// set_lwp_priority +// set_lwp_class_and_priority // -// Set the priority of the lwp. This call should only be made -// when using bound threads (T2 threads are bound by default). +// Set the class and priority of the lwp. This call should only +// be made when using bound threads (T2 threads are bound by default). // -int set_lwp_priority (int ThreadID, int lwpid, int newPrio ) -{ +int set_lwp_class_and_priority(int ThreadID, int lwpid, + int newPrio, int new_class, bool scale) { int rslt; int Actual, Expected, prv; pcparms_t ParmInfo; // for GET-SET @@ -3927,19 +3961,20 @@ return EINVAL; } - // If lwp hasn't started yet, just return // the _start routine will call us again. if ( lwpid <= 0 ) { if (ThreadPriorityVerbose) { - tty->print_cr ("deferring the set_lwp_priority of thread " INTPTR_FORMAT " to %d, lwpid not set", + tty->print_cr ("deferring the set_lwp_class_and_priority of thread " + INTPTR_FORMAT " to %d, lwpid not set", ThreadID, newPrio); } return 0; } if (ThreadPriorityVerbose) { - tty->print_cr ("set_lwp_priority(" INTPTR_FORMAT "@" INTPTR_FORMAT " %d) ", + tty->print_cr ("set_lwp_class_and_priority(" + INTPTR_FORMAT "@" INTPTR_FORMAT " %d) ", ThreadID, lwpid, newPrio); } @@ -3948,40 +3983,70 @@ rslt = (*priocntl_ptr)(PC_VERSION, P_LWPID, lwpid, PC_GETPARMS, (caddr_t)&ParmInfo); if (rslt < 0) return errno; - if (ParmInfo.pc_cid == rtLimits.schedPolicy) { + int cur_class = ParmInfo.pc_cid; + ParmInfo.pc_cid = (id_t)new_class; + + if (new_class == rtLimits.schedPolicy) { rtparms_t *rtInfo = (rtparms_t*)ParmInfo.pc_clparms; - rtInfo->rt_pri = scale_to_lwp_priority (rtLimits.minPrio, rtLimits.maxPrio, newPrio); + rtInfo->rt_pri = scale ? scale_to_lwp_priority(rtLimits.minPrio, + rtLimits.maxPrio, newPrio) + : newPrio; rtInfo->rt_tqsecs = RT_NOCHANGE; rtInfo->rt_tqnsecs = RT_NOCHANGE; if (ThreadPriorityVerbose) { tty->print_cr("RT: %d->%d\n", newPrio, rtInfo->rt_pri); } - } else if (ParmInfo.pc_cid == iaLimits.schedPolicy) { - iaparms_t *iaInfo = (iaparms_t*)ParmInfo.pc_clparms; - int maxClamped = MIN2(iaLimits.maxPrio, (int)iaInfo->ia_uprilim); - iaInfo->ia_upri = scale_to_lwp_priority(iaLimits.minPrio, maxClamped, newPrio); - iaInfo->ia_uprilim = IA_NOCHANGE; + } else if (new_class == iaLimits.schedPolicy) { + iaparms_t* iaInfo = (iaparms_t*)ParmInfo.pc_clparms; + int maxClamped = MIN2(iaLimits.maxPrio, + cur_class == new_class + ? (int)iaInfo->ia_uprilim : iaLimits.maxPrio); + iaInfo->ia_upri = scale ? scale_to_lwp_priority(iaLimits.minPrio, + maxClamped, newPrio) + : newPrio; + iaInfo->ia_uprilim = cur_class == new_class + ? IA_NOCHANGE : (pri_t)iaLimits.maxPrio; iaInfo->ia_mode = IA_NOCHANGE; + iaInfo->ia_nice = cur_class == new_class ? IA_NOCHANGE : NZERO; if (ThreadPriorityVerbose) { - tty->print_cr ("IA: [%d...%d] %d->%d\n", - iaLimits.minPrio, maxClamped, newPrio, iaInfo->ia_upri); + tty->print_cr("IA: [%d...%d] %d->%d\n", + iaLimits.minPrio, maxClamped, newPrio, iaInfo->ia_upri); } - } else if (ParmInfo.pc_cid == tsLimits.schedPolicy) { - tsparms_t *tsInfo = (tsparms_t*)ParmInfo.pc_clparms; - int maxClamped = MIN2(tsLimits.maxPrio, (int)tsInfo->ts_uprilim); - prv = tsInfo->ts_upri; - tsInfo->ts_upri = scale_to_lwp_priority(tsLimits.minPrio, maxClamped, newPrio); - tsInfo->ts_uprilim = IA_NOCHANGE; + } else if (new_class == tsLimits.schedPolicy) { + tsparms_t* tsInfo = (tsparms_t*)ParmInfo.pc_clparms; + int maxClamped = MIN2(tsLimits.maxPrio, + cur_class == new_class + ? (int)tsInfo->ts_uprilim : tsLimits.maxPrio); + tsInfo->ts_upri = scale ? scale_to_lwp_priority(tsLimits.minPrio, + maxClamped, newPrio) + : newPrio; + tsInfo->ts_uprilim = cur_class == new_class + ? TS_NOCHANGE : (pri_t)tsLimits.maxPrio; if (ThreadPriorityVerbose) { - tty->print_cr ("TS: %d [%d...%d] %d->%d\n", - prv, tsLimits.minPrio, maxClamped, newPrio, tsInfo->ts_upri); + tty->print_cr("TS: [%d...%d] %d->%d\n", + tsLimits.minPrio, maxClamped, newPrio, tsInfo->ts_upri); } - if (prv == tsInfo->ts_upri) return 0; + } else if (new_class == fxLimits.schedPolicy) { + fxparms_t* fxInfo = (fxparms_t*)ParmInfo.pc_clparms; + int maxClamped = MIN2(fxLimits.maxPrio, + cur_class == new_class + ? (int)fxInfo->fx_uprilim : fxLimits.maxPrio); + fxInfo->fx_upri = scale ? scale_to_lwp_priority(fxLimits.minPrio, + maxClamped, newPrio) + : newPrio; + fxInfo->fx_uprilim = cur_class == new_class + ? FX_NOCHANGE : (pri_t)fxLimits.maxPrio; + fxInfo->fx_tqsecs = FX_NOCHANGE; + fxInfo->fx_tqnsecs = FX_NOCHANGE; + if (ThreadPriorityVerbose) { + tty->print_cr("FX: [%d...%d] %d->%d\n", + fxLimits.minPrio, maxClamped, newPrio, fxInfo->fx_upri); + } } else { - if ( ThreadPriorityVerbose ) { - tty->print_cr ("Unknown scheduling class\n"); + if (ThreadPriorityVerbose) { + tty->print_cr("Unknown new scheduling class %d\n", new_class); } - return EINVAL; // no clue, punt + return EINVAL; // no clue, punt } rslt = (*priocntl_ptr)(PC_VERSION, P_LWPID, lwpid, PC_SETPARMS, (caddr_t)&ParmInfo); @@ -4016,16 +4081,20 @@ } else if (ParmInfo.pc_cid == tsLimits.schedPolicy) { Actual = TSPRI(ReadBack)->ts_upri; Expected = TSPRI(ParmInfo)->ts_upri; + } else if (ParmInfo.pc_cid == fxLimits.schedPolicy) { + Actual = FXPRI(ReadBack)->fx_upri; + Expected = FXPRI(ParmInfo)->fx_upri; } else { - if ( ThreadPriorityVerbose ) { - tty->print_cr("set_lwp_priority: unexpected class in readback: %d\n", ParmInfo.pc_cid); + if (ThreadPriorityVerbose) { + tty->print_cr("set_lwp_class_and_priority: unexpected class in readback: %d\n", + ParmInfo.pc_cid); } } if (Actual != Expected) { - if ( ThreadPriorityVerbose ) { - tty->print_cr ("set_lwp_priority(%d %d) Class=%d: actual=%d vs expected=%d\n", - lwpid, newPrio, ReadBack.pc_cid, Actual, Expected); + if (ThreadPriorityVerbose) { + tty->print_cr ("set_lwp_class_and_priority(%d %d) Class=%d: actual=%d vs expected=%d\n", + lwpid, newPrio, ReadBack.pc_cid, Actual, Expected); } } #endif @@ -4033,8 +4102,6 @@ return 0; } - - // Solaris only gives access to 128 real priorities at a time, // so we expand Java's ten to fill this range. This would be better // if we dynamically adjusted relative priorities. @@ -4055,8 +4122,7 @@ // which do not explicitly alter their thread priorities. // - -int os::java_to_os_priority[MaxPriority + 1] = { +int os::java_to_os_priority[CriticalPriority + 1] = { -99999, // 0 Entry should never be used 0, // 1 MinPriority @@ -4071,17 +4137,51 @@ 127, // 8 127, // 9 NearMaxPriority - 127 // 10 MaxPriority + 127, // 10 MaxPriority + + -criticalPrio // 11 CriticalPriority }; - OSReturn os::set_native_priority(Thread* thread, int newpri) { + OSThread* osthread = thread->osthread(); + + // Save requested priority in case the thread hasn't been started + osthread->set_native_priority(newpri); + + // Check for critical priority request + bool fxcritical = false; + if (newpri == -criticalPrio) { + fxcritical = true; + newpri = criticalPrio; + } + assert(newpri >= MinimumPriority && newpri <= MaximumPriority, "bad priority mapping"); - if ( !UseThreadPriorities ) return OS_OK; - int status = thr_setprio(thread->osthread()->thread_id(), newpri); - if ( os::Solaris::T2_libthread() || (UseBoundThreads && thread->osthread()->is_vm_created()) ) - status |= (set_lwp_priority (thread->osthread()->thread_id(), - thread->osthread()->lwp_id(), newpri )); + if (!UseThreadPriorities) return OS_OK; + + int status = 0; + + if (!fxcritical) { + // Use thr_setprio only if we have a priority that thr_setprio understands + status = thr_setprio(thread->osthread()->thread_id(), newpri); + } + + if (os::Solaris::T2_libthread() || + (UseBoundThreads && osthread->is_vm_created())) { + int lwp_status = + set_lwp_class_and_priority(osthread->thread_id(), + osthread->lwp_id(), + newpri, + fxcritical ? fxLimits.schedPolicy : myClass, + !fxcritical); + if (lwp_status != 0 && fxcritical) { + // Try again, this time without changing the scheduling class + newpri = java_MaxPriority_to_os_priority; + lwp_status = set_lwp_class_and_priority(osthread->thread_id(), + osthread->lwp_id(), + newpri, myClass, false); + } + status |= lwp_status; + } return (status == 0) ? OS_OK : OS_ERR; } diff -r f920d3637c9f -r 4c63e7c939aa hotspot/src/os/windows/vm/os_windows.cpp --- a/hotspot/src/os/windows/vm/os_windows.cpp Thu Jan 26 18:23:14 2012 -0800 +++ b/hotspot/src/os/windows/vm/os_windows.cpp Wed Jul 05 18:01:11 2017 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2012, 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 @@ -3296,7 +3296,7 @@ // so we compress Java's ten down to seven. It would be better // if we dynamically adjusted relative priorities. -int os::java_to_os_priority[MaxPriority + 1] = { +int os::java_to_os_priority[CriticalPriority + 1] = { THREAD_PRIORITY_IDLE, // 0 Entry should never be used THREAD_PRIORITY_LOWEST, // 1 MinPriority THREAD_PRIORITY_LOWEST, // 2 @@ -3307,10 +3307,11 @@ THREAD_PRIORITY_ABOVE_NORMAL, // 7 THREAD_PRIORITY_ABOVE_NORMAL, // 8 THREAD_PRIORITY_HIGHEST, // 9 NearMaxPriority - THREAD_PRIORITY_HIGHEST // 10 MaxPriority + THREAD_PRIORITY_HIGHEST, // 10 MaxPriority + THREAD_PRIORITY_HIGHEST // 11 CriticalPriority }; -int prio_policy1[MaxPriority + 1] = { +int prio_policy1[CriticalPriority + 1] = { THREAD_PRIORITY_IDLE, // 0 Entry should never be used THREAD_PRIORITY_LOWEST, // 1 MinPriority THREAD_PRIORITY_LOWEST, // 2 @@ -3321,17 +3322,21 @@ THREAD_PRIORITY_ABOVE_NORMAL, // 7 THREAD_PRIORITY_HIGHEST, // 8 THREAD_PRIORITY_HIGHEST, // 9 NearMaxPriority - THREAD_PRIORITY_TIME_CRITICAL // 10 MaxPriority + THREAD_PRIORITY_TIME_CRITICAL, // 10 MaxPriority + THREAD_PRIORITY_TIME_CRITICAL // 11 CriticalPriority }; static int prio_init() { // If ThreadPriorityPolicy is 1, switch tables if (ThreadPriorityPolicy == 1) { int i; - for (i = 0; i < MaxPriority + 1; i++) { + for (i = 0; i < CriticalPriority + 1; i++) { os::java_to_os_priority[i] = prio_policy1[i]; } } + if (UseCriticalJavaThreadPriority) { + os::java_to_os_priority[MaxPriority] = os::java_to_os_priority[CriticalPriority] ; + } return 0; } diff -r f920d3637c9f -r 4c63e7c939aa hotspot/src/share/vm/c1/c1_LIR.hpp --- a/hotspot/src/share/vm/c1/c1_LIR.hpp Thu Jan 26 18:23:14 2012 -0800 +++ b/hotspot/src/share/vm/c1/c1_LIR.hpp Wed Jul 05 18:01:11 2017 +0200 @@ -1354,9 +1354,10 @@ CodeStub* _stub; // if this is a branch to a stub, this is the stub public: - LIR_OpBranch(LIR_Condition cond, Label* lbl) + LIR_OpBranch(LIR_Condition cond, BasicType type, Label* lbl) : LIR_Op(lir_branch, LIR_OprFact::illegalOpr, (CodeEmitInfo*) NULL) , _cond(cond) + , _type(type) , _label(lbl) , _block(NULL) , _ublock(NULL) @@ -2053,7 +2054,7 @@ void jump(CodeStub* stub) { append(new LIR_OpBranch(lir_cond_always, T_ILLEGAL, stub)); } - void branch(LIR_Condition cond, Label* lbl) { append(new LIR_OpBranch(cond, lbl)); } + void branch(LIR_Condition cond, BasicType type, Label* lbl) { append(new LIR_OpBranch(cond, type, lbl)); } void branch(LIR_Condition cond, BasicType type, BlockBegin* block) { assert(type != T_FLOAT && type != T_DOUBLE, "no fp comparisons"); append(new LIR_OpBranch(cond, type, block)); diff -r f920d3637c9f -r 4c63e7c939aa hotspot/src/share/vm/c1/c1_LIRGenerator.cpp --- a/hotspot/src/share/vm/c1/c1_LIRGenerator.cpp Thu Jan 26 18:23:14 2012 -0800 +++ b/hotspot/src/share/vm/c1/c1_LIRGenerator.cpp Wed Jul 05 18:01:11 2017 +0200 @@ -2350,7 +2350,7 @@ } else { LabelObj* L = new LabelObj(); __ cmp(lir_cond_less, value, low_key); - __ branch(lir_cond_less, L->label()); + __ branch(lir_cond_less, T_INT, L->label()); __ cmp(lir_cond_lessEqual, value, high_key); __ branch(lir_cond_lessEqual, T_INT, dest); __ branch_destination(L->label()); diff -r f920d3637c9f -r 4c63e7c939aa hotspot/src/share/vm/c1/c1_Runtime1.cpp --- a/hotspot/src/share/vm/c1/c1_Runtime1.cpp Thu Jan 26 18:23:14 2012 -0800 +++ b/hotspot/src/share/vm/c1/c1_Runtime1.cpp Wed Jul 05 18:01:11 2017 +0200 @@ -413,8 +413,9 @@ } bci = branch_bci + offset; } - + assert(!HAS_PENDING_EXCEPTION, "Should not have any exceptions pending"); osr_nm = CompilationPolicy::policy()->event(enclosing_method, method, branch_bci, bci, level, nm, THREAD); + assert(!HAS_PENDING_EXCEPTION, "Event handler should not throw any exceptions"); return osr_nm; } diff -r f920d3637c9f -r 4c63e7c939aa hotspot/src/share/vm/classfile/javaClasses.cpp --- a/hotspot/src/share/vm/classfile/javaClasses.cpp Thu Jan 26 18:23:14 2012 -0800 +++ b/hotspot/src/share/vm/classfile/javaClasses.cpp Wed Jul 05 18:01:11 2017 +0200 @@ -1347,7 +1347,13 @@ return _backtrace(); } - inline void push(methodOop method, short bci, TRAPS) { + inline void push(methodOop method, int bci, TRAPS) { + // Smear the -1 bci to 0 since the array only holds unsigned + // shorts. The later line number lookup would just smear the -1 + // to a 0 even if it could be recorded. + if (bci == SynchronizationEntryBCI) bci = 0; + assert(bci == (jushort)bci, "doesn't fit"); + if (_index >= trace_chunk_size) { methodHandle mhandle(THREAD, method); expand(CHECK); @@ -1574,8 +1580,13 @@ int chunk_count = 0; for (;!st.at_end(); st.next()) { - // add element - bcis->ushort_at_put(chunk_count, st.bci()); + // Add entry and smear the -1 bci to 0 since the array only holds + // unsigned shorts. The later line number lookup would just smear + // the -1 to a 0 even if it could be recorded. + int bci = st.bci(); + if (bci == SynchronizationEntryBCI) bci = 0; + assert(bci == (jushort)bci, "doesn't fit"); + bcis->ushort_at_put(chunk_count, bci); methods->obj_at_put(chunk_count, st.method()); chunk_count++; diff -r f920d3637c9f -r 4c63e7c939aa hotspot/src/share/vm/compiler/compileBroker.cpp --- a/hotspot/src/share/vm/compiler/compileBroker.cpp Thu Jan 26 18:23:14 2012 -0800 +++ b/hotspot/src/share/vm/compiler/compileBroker.cpp Wed Jul 05 18:01:11 2017 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2012, 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 @@ -855,23 +855,23 @@ // Note that this only sets the JavaThread _priority field, which by // definition is limited to Java priorities and not OS priorities. // The os-priority is set in the CompilerThread startup code itself + java_lang_Thread::set_priority(thread_oop(), NearMaxPriority); - // CLEANUP PRIORITIES: This -if- statement hids a bug whereby the compiler - // threads never have their OS priority set. The assumption here is to - // enable the Performance group to do flag tuning, figure out a suitable - // CompilerThreadPriority, and then remove this 'if' statement (and - // comment) and unconditionally set the priority. + + // Note that we cannot call os::set_priority because it expects Java + // priorities and we are *explicitly* using OS priorities so that it's + // possible to set the compiler thread priority higher than any Java + // thread. - // Compiler Threads should be at the highest Priority - if ( CompilerThreadPriority != -1 ) - os::set_native_priority( compiler_thread, CompilerThreadPriority ); - else - os::set_native_priority( compiler_thread, os::java_to_os_priority[NearMaxPriority]); - - // Note that I cannot call os::set_priority because it expects Java - // priorities and I am *explicitly* using OS priorities so that it's - // possible to set the compiler thread priority higher than any Java - // thread. + int native_prio = CompilerThreadPriority; + if (native_prio == -1) { + if (UseCriticalCompilerThreadPriority) { + native_prio = os::java_to_os_priority[CriticalPriority]; + } else { + native_prio = os::java_to_os_priority[NearMaxPriority]; + } + } + os::set_native_priority(compiler_thread, native_prio); java_lang_Thread::set_daemon(thread_oop()); @@ -879,6 +879,7 @@ Threads::add(compiler_thread); Thread::start(compiler_thread); } + // Let go of Threads_lock before yielding os::yield(); // make sure that the compiler thread is started early (especially helpful on SOLARIS) @@ -961,7 +962,7 @@ methodHandle hot_method, int hot_count, const char* comment, - TRAPS) { + Thread* thread) { // do nothing if compiler thread(s) is not available if (!_initialized ) { return; @@ -1037,7 +1038,7 @@ // Acquire our lock. { - MutexLocker locker(queue->lock(), THREAD); + MutexLocker locker(queue->lock(), thread); // Make sure the method has not slipped into the queues since // last we checked; note that those checks were "fast bail-outs". @@ -1119,7 +1120,7 @@ nmethod* CompileBroker::compile_method(methodHandle method, int osr_bci, int comp_level, methodHandle hot_method, int hot_count, - const char* comment, TRAPS) { + const char* comment, Thread* THREAD) { // make sure arguments make sense assert(method->method_holder()->klass_part()->oop_is_instance(), "not an instance method"); assert(osr_bci == InvocationEntryBci || (0 <= osr_bci && osr_bci < method->code_size()), "bci out of range"); @@ -1173,10 +1174,10 @@ assert(!HAS_PENDING_EXCEPTION, "No exception should be present"); // some prerequisites that are compiler specific if (compiler(comp_level)->is_c2() || compiler(comp_level)->is_shark()) { - method->constants()->resolve_string_constants(CHECK_0); + method->constants()->resolve_string_constants(CHECK_AND_CLEAR_NULL); // Resolve all classes seen in the signature of the method // we are compiling. - methodOopDesc::load_signature_classes(method, CHECK_0); + methodOopDesc::load_signature_classes(method, CHECK_AND_CLEAR_NULL); } // If the method is native, do the lookup in the thread requesting @@ -1230,7 +1231,7 @@ return NULL; } } else { - compile_method_base(method, osr_bci, comp_level, hot_method, hot_count, comment, CHECK_0); + compile_method_base(method, osr_bci, comp_level, hot_method, hot_count, comment, THREAD); } // return requested nmethod diff -r f920d3637c9f -r 4c63e7c939aa hotspot/src/share/vm/compiler/compileBroker.hpp --- a/hotspot/src/share/vm/compiler/compileBroker.hpp Thu Jan 26 18:23:14 2012 -0800 +++ b/hotspot/src/share/vm/compiler/compileBroker.hpp Wed Jul 05 18:01:11 2017 +0200 @@ -333,7 +333,7 @@ methodHandle hot_method, int hot_count, const char* comment, - TRAPS); + Thread* thread); static CompileQueue* compile_queue(int comp_level) { if (is_c2_compile(comp_level)) return _c2_method_queue; if (is_c1_compile(comp_level)) return _c1_method_queue; @@ -363,7 +363,7 @@ int comp_level, methodHandle hot_method, int hot_count, - const char* comment, TRAPS); + const char* comment, Thread* thread); static void compiler_thread_loop(); diff -r f920d3637c9f -r 4c63e7c939aa hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepThread.cpp --- a/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepThread.cpp Thu Jan 26 18:23:14 2012 -0800 +++ b/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepThread.cpp Wed Jul 05 18:01:11 2017 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2012, 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 @@ -75,10 +75,25 @@ set_name("Concurrent Mark-Sweep GC Thread"); if (os::create_thread(this, os::cgc_thread)) { - // XXX: need to set this to low priority - // unless "agressive mode" set; priority - // should be just less than that of VMThread. - os::set_priority(this, NearMaxPriority); + // An old comment here said: "Priority should be just less + // than that of VMThread". Since the VMThread runs at + // NearMaxPriority, the old comment was inaccurate, but + // changing the default priority to NearMaxPriority-1 + // could change current behavior, so the default of + // NearMaxPriority stays in place. + // + // Note that there's a possibility of the VMThread + // starving if UseCriticalCMSThreadPriority is on. + // That won't happen on Solaris for various reasons, + // but may well happen on non-Solaris platforms. + int native_prio; + if (UseCriticalCMSThreadPriority) { + native_prio = os::java_to_os_priority[CriticalPriority]; + } else { + native_prio = os::java_to_os_priority[NearMaxPriority]; + } + os::set_native_priority(this, native_prio); + if (!DisableStartThread) { os::start_thread(this); } diff -r f920d3637c9f -r 4c63e7c939aa hotspot/src/share/vm/gc_implementation/g1/concurrentMark.cpp --- a/hotspot/src/share/vm/gc_implementation/g1/concurrentMark.cpp Thu Jan 26 18:23:14 2012 -0800 +++ b/hotspot/src/share/vm/gc_implementation/g1/concurrentMark.cpp Wed Jul 05 18:01:11 2017 +0200 @@ -42,8 +42,7 @@ #include "runtime/handles.inline.hpp" #include "runtime/java.hpp" -// -// CMS Bit Map Wrapper +// Concurrent marking bit map wrapper CMBitMapRO::CMBitMapRO(ReservedSpace rs, int shifter) : _bm((uintptr_t*)NULL,0), @@ -53,13 +52,13 @@ ReservedSpace brs(ReservedSpace::allocation_align_size_up( (_bmWordSize >> (_shifter + LogBitsPerByte)) + 1)); - guarantee(brs.is_reserved(), "couldn't allocate CMS bit map"); + guarantee(brs.is_reserved(), "couldn't allocate concurrent marking bit map"); // For now we'll just commit all of the bit map up fromt. // Later on we'll try to be more parsimonious with swap. guarantee(_virtual_space.initialize(brs, brs.size()), - "couldn't reseve backing store for CMS bit map"); + "couldn't reseve backing store for concurrent marking bit map"); assert(_virtual_space.committed_size() == brs.size(), - "didn't reserve backing store for all of CMS bit map?"); + "didn't reserve backing store for all of concurrent marking bit map?"); _bm.set_map((uintptr_t*)_virtual_space.low()); assert(_virtual_space.committed_size() << (_shifter + LogBitsPerByte) >= _bmWordSize, "inconsistency in bit map sizing"); @@ -104,17 +103,6 @@ return (int) (diff >> _shifter); } -bool CMBitMapRO::iterate(BitMapClosure* cl, MemRegion mr) { - HeapWord* left = MAX2(_bmStartWord, mr.start()); - HeapWord* right = MIN2(_bmStartWord + _bmWordSize, mr.end()); - if (right > left) { - // Right-open interval [leftOffset, rightOffset). - return _bm.iterate(cl, heapWordToOffset(left), heapWordToOffset(right)); - } else { - return true; - } -} - void CMBitMapRO::mostly_disjoint_range_union(BitMap* from_bitmap, size_t from_start_index, HeapWord* to_start_word, @@ -431,8 +419,6 @@ assert(newOop->is_oop(), "Expected an oop"); assert(bm == NULL || bm->isMarked((HeapWord*)newOop), "only grey objects on this stack"); - // iterate over the oops in this oop, marking and pushing - // the ones in CMS generation. newOop->oop_iterate(cl); if (yield_after && _cm->do_yield_check()) { res = false; @@ -474,6 +460,84 @@ && !nextMarkBitMap()->isMarked((HeapWord*)obj))); } +CMRootRegions::CMRootRegions() : + _young_list(NULL), _cm(NULL), _scan_in_progress(false), + _should_abort(false), _next_survivor(NULL) { } + +void CMRootRegions::init(G1CollectedHeap* g1h, ConcurrentMark* cm) { + _young_list = g1h->young_list(); + _cm = cm; +} + +void CMRootRegions::prepare_for_scan() { + assert(!scan_in_progress(), "pre-condition"); + + // Currently, only survivors can be root regions. + assert(_next_survivor == NULL, "pre-condition"); + _next_survivor = _young_list->first_survivor_region(); + _scan_in_progress = (_next_survivor != NULL); + _should_abort = false; +} + +HeapRegion* CMRootRegions::claim_next() { + if (_should_abort) { + // If someone has set the should_abort flag, we return NULL to + // force the caller to bail out of their loop. + return NULL; + } + + // Currently, only survivors can be root regions. + HeapRegion* res = _next_survivor; + if (res != NULL) { + MutexLockerEx x(RootRegionScan_lock, Mutex::_no_safepoint_check_flag); + // Read it again in case it changed while we were waiting for the lock. + res = _next_survivor; + if (res != NULL) { + if (res == _young_list->last_survivor_region()) { + // We just claimed the last survivor so store NULL to indicate + // that we're done. + _next_survivor = NULL; + } else { + _next_survivor = res->get_next_young_region(); + } + } else { + // Someone else claimed the last survivor while we were trying + // to take the lock so nothing else to do. + } + } + assert(res == NULL || res->is_survivor(), "post-condition"); + + return res; +} + +void CMRootRegions::scan_finished() { + assert(scan_in_progress(), "pre-condition"); + + // Currently, only survivors can be root regions. + if (!_should_abort) { + assert(_next_survivor == NULL, "we should have claimed all survivors"); + } + _next_survivor = NULL; + + { + MutexLockerEx x(RootRegionScan_lock, Mutex::_no_safepoint_check_flag); + _scan_in_progress = false; + RootRegionScan_lock->notify_all(); + } +} + +bool CMRootRegions::wait_until_scan_finished() { + if (!scan_in_progress()) return false; + + { + MutexLockerEx x(RootRegionScan_lock, Mutex::_no_safepoint_check_flag); + while (scan_in_progress()) { + RootRegionScan_lock->wait(Mutex::_no_safepoint_check_flag); + } + } + return true; +} + #ifdef _MSC_VER // the use of 'this' below gets a warning, make it go away #pragma warning( disable:4355 ) // 'this' : used in base member initializer list #endif // _MSC_VER @@ -498,6 +562,7 @@ _card_bm((rs.size() + CardTableModRefBS::card_size - 1) >> CardTableModRefBS::card_shift, false /* in_resource_area*/), + _prevMarkBitMap(&_markBitMap1), _nextMarkBitMap(&_markBitMap2), _at_least_one_mark_complete(false), @@ -526,7 +591,11 @@ _cleanup_times(), _total_counting_time(0.0), _total_rs_scrub_time(0.0), - _parallel_workers(NULL) { + + _parallel_workers(NULL), + + _count_card_bitmaps(NULL), + _count_marked_bytes(NULL) { CMVerboseLevel verbose_level = (CMVerboseLevel) G1MarkingVerboseLevel; if (verbose_level < no_verbose) { verbose_level = no_verbose; @@ -557,9 +626,16 @@ SATBMarkQueueSet& satb_qs = JavaThread::satb_mark_queue_set(); satb_qs.set_buffer_size(G1SATBBufferSize); + _root_regions.init(_g1h, this); + _tasks = NEW_C_HEAP_ARRAY(CMTask*, _max_task_num); _accum_task_vtime = NEW_C_HEAP_ARRAY(double, _max_task_num); + _count_card_bitmaps = NEW_C_HEAP_ARRAY(BitMap, _max_task_num); + _count_marked_bytes = NEW_C_HEAP_ARRAY(size_t*, _max_task_num); + + BitMap::idx_t card_bm_size = _card_bm.size(); + // so that the assertion in MarkingTaskQueue::task_queue doesn't fail _active_tasks = _max_task_num; for (int i = 0; i < (int) _max_task_num; ++i) { @@ -567,10 +643,26 @@ task_queue->initialize(); _task_queues->register_queue(i, task_queue); - _tasks[i] = new CMTask(i, this, task_queue, _task_queues); + _count_card_bitmaps[i] = BitMap(card_bm_size, false); + _count_marked_bytes[i] = NEW_C_HEAP_ARRAY(size_t, max_regions); + + _tasks[i] = new CMTask(i, this, + _count_marked_bytes[i], + &_count_card_bitmaps[i], + task_queue, _task_queues); + _accum_task_vtime[i] = 0.0; } + // Calculate the card number for the bottom of the heap. Used + // in biasing indexes into the accounting card bitmaps. + _heap_bottom_card_num = + intptr_t(uintptr_t(_g1h->reserved_region().start()) >> + CardTableModRefBS::card_shift); + + // Clear all the liveness counting data + clear_all_count_data(); + if (ConcGCThreads > ParallelGCThreads) { vm_exit_during_initialization("Can't have more ConcGCThreads " "than ParallelGCThreads."); @@ -750,11 +842,6 @@ ShouldNotReachHere(); } -// This closure is used to mark refs into the g1 generation -// from external roots in the CMS bit map. -// Called at the first checkpoint. -// - void ConcurrentMark::clearNextBitmap() { G1CollectedHeap* g1h = G1CollectedHeap::heap(); G1CollectorPolicy* g1p = g1h->g1_policy(); @@ -794,6 +881,9 @@ assert(!g1h->mark_in_progress(), "invariant"); } + // Clear the liveness counting data + clear_all_count_data(); + // Repeat the asserts from above. guarantee(cmThread()->during_cycle(), "invariant"); guarantee(!g1h->mark_in_progress(), "invariant"); @@ -854,6 +944,8 @@ satb_mq_set.set_active_all_threads(true, /* new active value */ false /* expected_active */); + _root_regions.prepare_for_scan(); + // update_g1_committed() will be called at the end of an evac pause // when marking is on. So, it's also called at the end of the // initial-mark pause to update the heap end, if the heap expands @@ -1147,6 +1239,69 @@ return 0; } +void ConcurrentMark::scanRootRegion(HeapRegion* hr, uint worker_id) { + // Currently, only survivors can be root regions. + assert(hr->next_top_at_mark_start() == hr->bottom(), "invariant"); + G1RootRegionScanClosure cl(_g1h, this, worker_id); + + const uintx interval = PrefetchScanIntervalInBytes; + HeapWord* curr = hr->bottom(); + const HeapWord* end = hr->top(); + while (curr < end) { + Prefetch::read(curr, interval); + oop obj = oop(curr); + int size = obj->oop_iterate(&cl); + assert(size == obj->size(), "sanity"); + curr += size; + } +} + +class CMRootRegionScanTask : public AbstractGangTask { +private: + ConcurrentMark* _cm; + +public: + CMRootRegionScanTask(ConcurrentMark* cm) : + AbstractGangTask("Root Region Scan"), _cm(cm) { } + + void work(uint worker_id) { + assert(Thread::current()->is_ConcurrentGC_thread(), + "this should only be done by a conc GC thread"); + + CMRootRegions* root_regions = _cm->root_regions(); + HeapRegion* hr = root_regions->claim_next(); + while (hr != NULL) { + _cm->scanRootRegion(hr, worker_id); + hr = root_regions->claim_next(); + } + } +}; + +void ConcurrentMark::scanRootRegions() { + // scan_in_progress() will have been set to true only if there was + // at least one root region to scan. So, if it's false, we + // should not attempt to do any further work. + if (root_regions()->scan_in_progress()) { + _parallel_marking_threads = calc_parallel_marking_threads(); + assert(parallel_marking_threads() <= max_parallel_marking_threads(), + "Maximum number of marking threads exceeded"); + uint active_workers = MAX2(1U, parallel_marking_threads()); + + CMRootRegionScanTask task(this); + if (parallel_marking_threads() > 0) { + _parallel_workers->set_active_workers((int) active_workers); + _parallel_workers->run_task(&task); + } else { + task.work(0); + } + + // It's possible that has_aborted() is true here without actually + // aborting the survivor scan earlier. This is OK as it's + // mainly used for sanity checking. + root_regions()->scan_finished(); + } +} + void ConcurrentMark::markFromRoots() { // we might be tempted to assert that: // assert(asynch == !SafepointSynchronize::is_at_safepoint(), @@ -1225,6 +1380,10 @@ gclog_or_tty->print_cr("\nRemark led to restart for overflow."); } } else { + // Aggregate the per-task counting data that we have accumulated + // while marking. + aggregate_count_data(); + SATBMarkQueueSet& satb_mq_set = JavaThread::satb_mark_queue_set(); // We're done with marking. // This is the end of the marking cycle, we're expected all @@ -1262,48 +1421,41 @@ g1p->record_concurrent_mark_remark_end(); } -#define CARD_BM_TEST_MODE 0 - +// Used to calculate the # live objects per region +// for verification purposes class CalcLiveObjectsClosure: public HeapRegionClosure { CMBitMapRO* _bm; ConcurrentMark* _cm; - bool _changed; - bool _yield; - size_t _words_done; - size_t _tot_live; - size_t _tot_used; - size_t _regions_done; - double _start_vtime_sec; - BitMap* _region_bm; BitMap* _card_bm; + + // Debugging + size_t _tot_words_done; + size_t _tot_live; + size_t _tot_used; + + size_t _region_marked_bytes; + intptr_t _bottom_card_num; - bool _final; void mark_card_num_range(intptr_t start_card_num, intptr_t last_card_num) { - for (intptr_t i = start_card_num; i <= last_card_num; i++) { -#if CARD_BM_TEST_MODE - guarantee(_card_bm->at(i - _bottom_card_num), "Should already be set."); -#else - _card_bm->par_at_put(i - _bottom_card_num, 1); -#endif + assert(start_card_num <= last_card_num, "sanity"); + BitMap::idx_t start_idx = start_card_num - _bottom_card_num; + BitMap::idx_t last_idx = last_card_num - _bottom_card_num; + + for (BitMap::idx_t i = start_idx; i <= last_idx; i += 1) { + _card_bm->par_at_put(i, 1); } } public: - CalcLiveObjectsClosure(bool final, - CMBitMapRO *bm, ConcurrentMark *cm, + CalcLiveObjectsClosure(CMBitMapRO *bm, ConcurrentMark *cm, BitMap* region_bm, BitMap* card_bm) : - _bm(bm), _cm(cm), _changed(false), _yield(true), - _words_done(0), _tot_live(0), _tot_used(0), - _region_bm(region_bm), _card_bm(card_bm),_final(final), - _regions_done(0), _start_vtime_sec(0.0) - { - _bottom_card_num = - intptr_t(uintptr_t(G1CollectedHeap::heap()->reserved_region().start()) >> - CardTableModRefBS::card_shift); - } + _bm(bm), _cm(cm), _region_bm(region_bm), _card_bm(card_bm), + _region_marked_bytes(0), _tot_words_done(0), + _tot_live(0), _tot_used(0), + _bottom_card_num(cm->heap_bottom_card_num()) { } // It takes a region that's not empty (i.e., it has at least one // live object in it and sets its corresponding bit on the region @@ -1319,29 +1471,16 @@ _region_bm->par_at_put((BitMap::idx_t) index, true); } else { // Starts humongous case: calculate how many regions are part of - // this humongous region and then set the bit range. It might - // have been a bit more efficient to look at the object that - // spans these humongous regions to calculate their number from - // the object's size. However, it's a good idea to calculate - // this based on the metadata itself, and not the region - // contents, so that this code is not aware of what goes into - // the humongous regions (in case this changes in the future). + // this humongous region and then set the bit range. G1CollectedHeap* g1h = G1CollectedHeap::heap(); - size_t end_index = index + 1; - while (end_index < g1h->n_regions()) { - HeapRegion* chr = g1h->region_at(end_index); - if (!chr->continuesHumongous()) break; - end_index += 1; - } + HeapRegion *last_hr = g1h->heap_region_containing_raw(hr->end() - 1); + size_t end_index = last_hr->hrs_index() + 1; _region_bm->par_at_put_range((BitMap::idx_t) index, (BitMap::idx_t) end_index, true); } } bool doHeapRegion(HeapRegion* hr) { - if (!_final && _regions_done == 0) { - _start_vtime_sec = os::elapsedVTime(); - } if (hr->continuesHumongous()) { // We will ignore these here and process them when their @@ -1355,48 +1494,41 @@ } HeapWord* nextTop = hr->next_top_at_mark_start(); - HeapWord* start = hr->top_at_conc_mark_count(); - assert(hr->bottom() <= start && start <= hr->end() && - hr->bottom() <= nextTop && nextTop <= hr->end() && - start <= nextTop, - "Preconditions."); - // Otherwise, record the number of word's we'll examine. + HeapWord* start = hr->bottom(); + + assert(start <= hr->end() && start <= nextTop && nextTop <= hr->end(), + err_msg("Preconditions not met - " + "start: "PTR_FORMAT", nextTop: "PTR_FORMAT", end: "PTR_FORMAT, + start, nextTop, hr->end())); + + // Record the number of word's we'll examine. size_t words_done = (nextTop - start); + // Find the first marked object at or after "start". start = _bm->getNextMarkedWordAddress(start, nextTop); + size_t marked_bytes = 0; // Below, the term "card num" means the result of shifting an address // by the card shift -- address 0 corresponds to card number 0. One // must subtract the card num of the bottom of the heap to obtain a // card table index. + // The first card num of the sequence of live cards currently being // constructed. -1 ==> no sequence. intptr_t start_card_num = -1; + // The last card num of the sequence of live cards currently being // constructed. -1 ==> no sequence. intptr_t last_card_num = -1; while (start < nextTop) { - if (_yield && _cm->do_yield_check()) { - // We yielded. It might be for a full collection, in which case - // all bets are off; terminate the traversal. - if (_cm->has_aborted()) { - _changed = false; - return true; - } else { - // Otherwise, it might be a collection pause, and the region - // we're looking at might be in the collection set. We'll - // abandon this region. - return false; - } - } oop obj = oop(start); int obj_sz = obj->size(); + // The card num of the start of the current object. intptr_t obj_card_num = intptr_t(uintptr_t(start) >> CardTableModRefBS::card_shift); - HeapWord* obj_last = start + obj_sz - 1; intptr_t obj_last_card_num = intptr_t(uintptr_t(obj_last) >> CardTableModRefBS::card_shift); @@ -1414,110 +1546,404 @@ start_card_num = obj_card_num; } } -#if CARD_BM_TEST_MODE - /* - gclog_or_tty->print_cr("Setting bits from %d/%d.", - obj_card_num - _bottom_card_num, - obj_last_card_num - _bottom_card_num); - */ - for (intptr_t j = obj_card_num; j <= obj_last_card_num; j++) { - _card_bm->par_at_put(j - _bottom_card_num, 1); - } -#endif } // In any case, we set the last card num. last_card_num = obj_last_card_num; marked_bytes += (size_t)obj_sz * HeapWordSize; + // Find the next marked object after this one. start = _bm->getNextMarkedWordAddress(start + 1, nextTop); - _changed = true; } + // Handle the last range, if any. if (start_card_num != -1) { mark_card_num_range(start_card_num, last_card_num); } - if (_final) { - // Mark the allocated-since-marking portion... - HeapWord* tp = hr->top(); - if (nextTop < tp) { - start_card_num = - intptr_t(uintptr_t(nextTop) >> CardTableModRefBS::card_shift); - last_card_num = - intptr_t(uintptr_t(tp) >> CardTableModRefBS::card_shift); - mark_card_num_range(start_card_num, last_card_num); - // This definitely means the region has live objects. - set_bit_for_region(hr); - } + + // Mark the allocated-since-marking portion... + HeapWord* top = hr->top(); + if (nextTop < top) { + start_card_num = intptr_t(uintptr_t(nextTop) >> CardTableModRefBS::card_shift); + last_card_num = intptr_t(uintptr_t(top) >> CardTableModRefBS::card_shift); + + mark_card_num_range(start_card_num, last_card_num); + + // This definitely means the region has live objects. + set_bit_for_region(hr); } - hr->add_to_marked_bytes(marked_bytes); // Update the live region bitmap. if (marked_bytes > 0) { set_bit_for_region(hr); } - hr->set_top_at_conc_mark_count(nextTop); + + // Set the marked bytes for the current region so that + // it can be queried by a calling verificiation routine + _region_marked_bytes = marked_bytes; + _tot_live += hr->next_live_bytes(); _tot_used += hr->used(); - _words_done = words_done; - - if (!_final) { - ++_regions_done; - if (_regions_done % 10 == 0) { - double end_vtime_sec = os::elapsedVTime(); - double elapsed_vtime_sec = end_vtime_sec - _start_vtime_sec; - if (elapsed_vtime_sec > (10.0 / 1000.0)) { - jlong sleep_time_ms = - (jlong) (elapsed_vtime_sec * _cm->cleanup_sleep_factor() * 1000.0); - os::sleep(Thread::current(), sleep_time_ms, false); - _start_vtime_sec = end_vtime_sec; - } - } - } + _tot_words_done = words_done; return false; } - bool changed() { return _changed; } - void reset() { _changed = false; _words_done = 0; } - void no_yield() { _yield = false; } - size_t words_done() { return _words_done; } - size_t tot_live() { return _tot_live; } - size_t tot_used() { return _tot_used; } + size_t region_marked_bytes() const { return _region_marked_bytes; } + + // Debugging + size_t tot_words_done() const { return _tot_words_done; } + size_t tot_live() const { return _tot_live; } + size_t tot_used() const { return _tot_used; } +}; + +// Heap region closure used for verifying the counting data +// that was accumulated concurrently and aggregated during +// the remark pause. This closure is applied to the heap +// regions during the STW cleanup pause. + +class VerifyLiveObjectDataHRClosure: public HeapRegionClosure { + ConcurrentMark* _cm; + CalcLiveObjectsClosure _calc_cl; + BitMap* _region_bm; // Region BM to be verified + BitMap* _card_bm; // Card BM to be verified + bool _verbose; // verbose output? + + BitMap* _exp_region_bm; // Expected Region BM values + BitMap* _exp_card_bm; // Expected card BM values + + int _failures; + +public: + VerifyLiveObjectDataHRClosure(ConcurrentMark* cm, + BitMap* region_bm, + BitMap* card_bm, + BitMap* exp_region_bm, + BitMap* exp_card_bm, + bool verbose) : + _cm(cm), + _calc_cl(_cm->nextMarkBitMap(), _cm, exp_region_bm, exp_card_bm), + _region_bm(region_bm), _card_bm(card_bm), _verbose(verbose), + _exp_region_bm(exp_region_bm), _exp_card_bm(exp_card_bm), + _failures(0) { } + + int failures() const { return _failures; } + + bool doHeapRegion(HeapRegion* hr) { + if (hr->continuesHumongous()) { + // We will ignore these here and process them when their + // associated "starts humongous" region is processed (see + // set_bit_for_heap_region()). Note that we cannot rely on their + // associated "starts humongous" region to have their bit set to + // 1 since, due to the region chunking in the parallel region + // iteration, a "continues humongous" region might be visited + // before its associated "starts humongous". + return false; + } + + int failures = 0; + + // Call the CalcLiveObjectsClosure to walk the marking bitmap for + // this region and set the corresponding bits in the expected region + // and card bitmaps. + bool res = _calc_cl.doHeapRegion(hr); + assert(res == false, "should be continuing"); + + MutexLockerEx x((_verbose ? ParGCRareEvent_lock : NULL), + Mutex::_no_safepoint_check_flag); + + // Verify that _top_at_conc_count == ntams + if (hr->top_at_conc_mark_count() != hr->next_top_at_mark_start()) { + if (_verbose) { + gclog_or_tty->print_cr("Region " SIZE_FORMAT ": top at conc count incorrect: " + "expected " PTR_FORMAT ", actual: " PTR_FORMAT, + hr->hrs_index(), hr->next_top_at_mark_start(), + hr->top_at_conc_mark_count()); + } + failures += 1; + } + + // Verify the marked bytes for this region. + size_t exp_marked_bytes = _calc_cl.region_marked_bytes(); + size_t act_marked_bytes = hr->next_marked_bytes(); + + // We're not OK if expected marked bytes > actual marked bytes. It means + // we have missed accounting some objects during the actual marking. + if (exp_marked_bytes > act_marked_bytes) { + if (_verbose) { + gclog_or_tty->print_cr("Region " SIZE_FORMAT ": marked bytes mismatch: " + "expected: " SIZE_FORMAT ", actual: " SIZE_FORMAT, + hr->hrs_index(), exp_marked_bytes, act_marked_bytes); + } + failures += 1; + } + + // Verify the bit, for this region, in the actual and expected + // (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(); + + bool expected = _exp_region_bm->at(index); + bool actual = _region_bm->at(index); + if (expected && !actual) { + if (_verbose) { + gclog_or_tty->print_cr("Region " SIZE_FORMAT ": region bitmap mismatch: " + "expected: %d, actual: %d", + hr->hrs_index(), expected, actual); + } + failures += 1; + } + + // Verify that the card bit maps for the cards spanned by the current + // region match. We have an error if we have a set bit in the expected + // bit map and the corresponding bit in the actual bitmap is not set. + + BitMap::idx_t start_idx = _cm->card_bitmap_index_for(hr->bottom()); + BitMap::idx_t end_idx = _cm->card_bitmap_index_for(hr->top()); + + for (BitMap::idx_t i = start_idx; i < end_idx; i+=1) { + expected = _exp_card_bm->at(i); + actual = _card_bm->at(i); + + if (expected && !actual) { + if (_verbose) { + gclog_or_tty->print_cr("Region " SIZE_FORMAT ": card bitmap mismatch at " SIZE_FORMAT ": " + "expected: %d, actual: %d", + hr->hrs_index(), i, expected, actual); + } + failures += 1; + } + } + + if (failures > 0 && _verbose) { + gclog_or_tty->print_cr("Region " HR_FORMAT ", ntams: " PTR_FORMAT ", " + "marked_bytes: calc/actual " SIZE_FORMAT "/" SIZE_FORMAT, + HR_FORMAT_PARAMS(hr), hr->next_top_at_mark_start(), + _calc_cl.region_marked_bytes(), hr->next_marked_bytes()); + } + + _failures += failures; + + // We could stop iteration over the heap when we + // find the first voilating region by returning true. + return false; + } }; -void ConcurrentMark::calcDesiredRegions() { - _region_bm.clear(); - _card_bm.clear(); - CalcLiveObjectsClosure calccl(false /*final*/, - nextMarkBitMap(), this, - &_region_bm, &_card_bm); - G1CollectedHeap *g1h = G1CollectedHeap::heap(); - g1h->heap_region_iterate(&calccl); - - do { - calccl.reset(); - g1h->heap_region_iterate(&calccl); - } while (calccl.changed()); -} +class G1ParVerifyFinalCountTask: public AbstractGangTask { +protected: + G1CollectedHeap* _g1h; + ConcurrentMark* _cm; + BitMap* _actual_region_bm; + BitMap* _actual_card_bm; + + uint _n_workers; + + BitMap* _expected_region_bm; + BitMap* _expected_card_bm; + + int _failures; + bool _verbose; + +public: + G1ParVerifyFinalCountTask(G1CollectedHeap* g1h, + BitMap* region_bm, BitMap* card_bm, + BitMap* expected_region_bm, BitMap* expected_card_bm) + : AbstractGangTask("G1 verify final counting"), + _g1h(g1h), _cm(_g1h->concurrent_mark()), + _actual_region_bm(region_bm), _actual_card_bm(card_bm), + _expected_region_bm(expected_region_bm), _expected_card_bm(expected_card_bm), + _failures(0), _verbose(false), + _n_workers(0) { + assert(VerifyDuringGC, "don't call this otherwise"); + + // Use the value already set as the number of active threads + // in the call to run_task(). + if (G1CollectedHeap::use_parallel_gc_threads()) { + assert( _g1h->workers()->active_workers() > 0, + "Should have been previously set"); + _n_workers = _g1h->workers()->active_workers(); + } else { + _n_workers = 1; + } + + assert(_expected_card_bm->size() == _actual_card_bm->size(), "sanity"); + assert(_expected_region_bm->size() == _actual_region_bm->size(), "sanity"); + + _verbose = _cm->verbose_medium(); + } + + void work(uint worker_id) { + assert(worker_id < _n_workers, "invariant"); + + VerifyLiveObjectDataHRClosure verify_cl(_cm, + _actual_region_bm, _actual_card_bm, + _expected_region_bm, + _expected_card_bm, + _verbose); + + if (G1CollectedHeap::use_parallel_gc_threads()) { + _g1h->heap_region_par_iterate_chunked(&verify_cl, + worker_id, + _n_workers, + HeapRegion::VerifyCountClaimValue); + } else { + _g1h->heap_region_iterate(&verify_cl); + } + + Atomic::add(verify_cl.failures(), &_failures); + } + + int failures() const { return _failures; } +}; + +// Final update of count data (during cleanup). +// Adds [top_at_count, NTAMS) to the marked bytes for each +// region. Sets the bits in the card bitmap corresponding +// to the interval [top_at_count, top], and sets the +// liveness bit for each region containing live data +// in the region bitmap. + +class FinalCountDataUpdateClosure: public HeapRegionClosure { + ConcurrentMark* _cm; + BitMap* _region_bm; + BitMap* _card_bm; + + size_t _total_live_bytes; + size_t _total_used_bytes; + size_t _total_words_done; + + void set_card_bitmap_range(BitMap::idx_t start_idx, BitMap::idx_t last_idx) { + assert(start_idx <= last_idx, "sanity"); + + // Set the inclusive bit range [start_idx, last_idx]. + // For small ranges (up to 8 cards) use a simple loop; otherwise + // use par_at_put_range. + if ((last_idx - start_idx) <= 8) { + for (BitMap::idx_t i = start_idx; i <= last_idx; i += 1) { + _card_bm->par_set_bit(i); + } + } else { + assert(last_idx < _card_bm->size(), "sanity"); + // Note BitMap::par_at_put_range() is exclusive. + _card_bm->par_at_put_range(start_idx, last_idx+1, true); + } + } + + // It takes a region that's not empty (i.e., it has at least one + // live object in it and sets its corresponding bit on the region + // bitmap to 1. If the region is "starts humongous" it will also set + // to 1 the bits on the region bitmap that correspond to its + // associated "continues humongous" regions. + void set_bit_for_region(HeapRegion* hr) { + assert(!hr->continuesHumongous(), "should have filtered those out"); + + size_t index = hr->hrs_index(); + if (!hr->startsHumongous()) { + // Normal (non-humongous) case: just set the bit. + _region_bm->par_set_bit((BitMap::idx_t) index); + } else { + // Starts humongous case: calculate how many regions are part of + // this humongous region and then set the bit range. + G1CollectedHeap* g1h = G1CollectedHeap::heap(); + HeapRegion *last_hr = g1h->heap_region_containing_raw(hr->end() - 1); + size_t end_index = last_hr->hrs_index() + 1; + _region_bm->par_at_put_range((BitMap::idx_t) index, + (BitMap::idx_t) end_index, true); + } + } + + public: + FinalCountDataUpdateClosure(ConcurrentMark* cm, + BitMap* region_bm, + BitMap* card_bm) : + _cm(cm), _region_bm(region_bm), _card_bm(card_bm), + _total_words_done(0), _total_live_bytes(0), _total_used_bytes(0) { } + + bool doHeapRegion(HeapRegion* hr) { + + if (hr->continuesHumongous()) { + // We will ignore these here and process them when their + // associated "starts humongous" region is processed (see + // set_bit_for_heap_region()). Note that we cannot rely on their + // associated "starts humongous" region to have their bit set to + // 1 since, due to the region chunking in the parallel region + // iteration, a "continues humongous" region might be visited + // before its associated "starts humongous". + return false; + } + + HeapWord* start = hr->top_at_conc_mark_count(); + HeapWord* ntams = hr->next_top_at_mark_start(); + HeapWord* top = hr->top(); + + assert(hr->bottom() <= start && start <= hr->end() && + hr->bottom() <= ntams && ntams <= hr->end(), "Preconditions."); + + size_t words_done = ntams - hr->bottom(); + + if (start < ntams) { + // Region was changed between remark and cleanup pauses + // We need to add (ntams - start) to the marked bytes + // for this region, and set bits for the range + // [ card_idx(start), card_idx(ntams) ) in the card bitmap. + size_t live_bytes = (ntams - start) * HeapWordSize; + hr->add_to_marked_bytes(live_bytes); + + // Record the new top at conc count + hr->set_top_at_conc_mark_count(ntams); + + // The setting of the bits in the card bitmap takes place below + } + + // Mark the allocated-since-marking portion... + if (ntams < top) { + // This definitely means the region has live objects. + set_bit_for_region(hr); + } + + // Now set the bits for [start, top] + BitMap::idx_t start_idx = _cm->card_bitmap_index_for(start); + BitMap::idx_t last_idx = _cm->card_bitmap_index_for(top); + set_card_bitmap_range(start_idx, last_idx); + + // Set the bit for the region if it contains live data + if (hr->next_marked_bytes() > 0) { + set_bit_for_region(hr); + } + + _total_words_done += words_done; + _total_used_bytes += hr->used(); + _total_live_bytes += hr->next_marked_bytes(); + + return false; + } + + size_t total_words_done() const { return _total_words_done; } + size_t total_live_bytes() const { return _total_live_bytes; } + size_t total_used_bytes() const { return _total_used_bytes; } +}; class G1ParFinalCountTask: public AbstractGangTask { protected: G1CollectedHeap* _g1h; - CMBitMap* _bm; + ConcurrentMark* _cm; + BitMap* _actual_region_bm; + BitMap* _actual_card_bm; + uint _n_workers; + size_t *_live_bytes; size_t *_used_bytes; - BitMap* _region_bm; - BitMap* _card_bm; + public: - G1ParFinalCountTask(G1CollectedHeap* g1h, CMBitMap* bm, - BitMap* region_bm, BitMap* card_bm) - : AbstractGangTask("G1 final counting"), _g1h(g1h), - _bm(bm), _region_bm(region_bm), _card_bm(card_bm), - _n_workers(0) - { + G1ParFinalCountTask(G1CollectedHeap* g1h, BitMap* region_bm, BitMap* card_bm) + : AbstractGangTask("G1 final counting"), + _g1h(g1h), _cm(_g1h->concurrent_mark()), + _actual_region_bm(region_bm), _actual_card_bm(card_bm), + _n_workers(0) { // Use the value already set as the number of active threads // in the call to run_task(). Needed for the allocation of // _live_bytes and _used_bytes. @@ -1539,29 +1965,32 @@ } void work(uint worker_id) { - CalcLiveObjectsClosure calccl(true /*final*/, - _bm, _g1h->concurrent_mark(), - _region_bm, _card_bm); - calccl.no_yield(); + assert(worker_id < _n_workers, "invariant"); + + FinalCountDataUpdateClosure final_update_cl(_cm, + _actual_region_bm, + _actual_card_bm); + if (G1CollectedHeap::use_parallel_gc_threads()) { - _g1h->heap_region_par_iterate_chunked(&calccl, worker_id, - (int) _n_workers, + _g1h->heap_region_par_iterate_chunked(&final_update_cl, + worker_id, + _n_workers, HeapRegion::FinalCountClaimValue); } else { - _g1h->heap_region_iterate(&calccl); + _g1h->heap_region_iterate(&final_update_cl); } - assert(calccl.complete(), "Shouldn't have yielded!"); - - assert(worker_id < _n_workers, "invariant"); - _live_bytes[worker_id] = calccl.tot_live(); - _used_bytes[worker_id] = calccl.tot_used(); - } + + _live_bytes[worker_id] = final_update_cl.total_live_bytes(); + _used_bytes[worker_id] = final_update_cl.total_used_bytes(); + } + size_t live_bytes() { size_t live_bytes = 0; for (uint i = 0; i < _n_workers; ++i) live_bytes += _live_bytes[i]; return live_bytes; } + size_t used_bytes() { size_t used_bytes = 0; for (uint i = 0; i < _n_workers; ++i) @@ -1724,8 +2153,7 @@ G1ParScrubRemSetTask(G1CollectedHeap* g1h, BitMap* region_bm, BitMap* card_bm) : AbstractGangTask("G1 ScrubRS"), _g1rs(g1h->g1_rem_set()), - _region_bm(region_bm), _card_bm(card_bm) - {} + _region_bm(region_bm), _card_bm(card_bm) { } void work(uint worker_id) { if (G1CollectedHeap::use_parallel_gc_threads()) { @@ -1772,11 +2200,10 @@ uint n_workers; // Do counting once more with the world stopped for good measure. - G1ParFinalCountTask g1_par_count_task(g1h, nextMarkBitMap(), - &_region_bm, &_card_bm); + G1ParFinalCountTask g1_par_count_task(g1h, &_region_bm, &_card_bm); + if (G1CollectedHeap::use_parallel_gc_threads()) { - assert(g1h->check_heap_region_claim_values( - HeapRegion::InitialClaimValue), + assert(g1h->check_heap_region_claim_values(HeapRegion::InitialClaimValue), "sanity check"); g1h->set_par_threads(); @@ -1787,14 +2214,42 @@ // Done with the parallel phase so reset to 0. g1h->set_par_threads(0); - assert(g1h->check_heap_region_claim_values( - HeapRegion::FinalCountClaimValue), + assert(g1h->check_heap_region_claim_values(HeapRegion::FinalCountClaimValue), "sanity check"); } else { n_workers = 1; g1_par_count_task.work(0); } + if (VerifyDuringGC) { + // Verify that the counting data accumulated during marking matches + // that calculated by walking the marking bitmap. + + // Bitmaps to hold expected values + BitMap expected_region_bm(_region_bm.size(), false); + BitMap expected_card_bm(_card_bm.size(), false); + + G1ParVerifyFinalCountTask g1_par_verify_task(g1h, + &_region_bm, + &_card_bm, + &expected_region_bm, + &expected_card_bm); + + if (G1CollectedHeap::use_parallel_gc_threads()) { + g1h->set_par_threads((int)n_workers); + g1h->workers()->run_task(&g1_par_verify_task); + // Done with the parallel phase so reset to 0. + g1h->set_par_threads(0); + + assert(g1h->check_heap_region_claim_values(HeapRegion::VerifyCountClaimValue), + "sanity check"); + } else { + g1_par_verify_task.work(0); + } + + guarantee(g1_par_verify_task.failures() == 0, "Unexpected accounting failures"); + } + size_t known_garbage_bytes = g1_par_count_task.used_bytes() - g1_par_count_task.live_bytes(); g1p->set_known_garbage_bytes(known_garbage_bytes); @@ -1905,6 +2360,10 @@ // races with it goes around and waits for completeCleanup to finish. g1h->increment_total_collections(); + // We reclaimed old regions so we should calculate the sizes to make + // sure we update the old gen/space data. + g1h->g1mm()->update_sizes(); + if (VerifyDuringGC) { HandleMark hm; // handle scope gclog_or_tty->print(" VerifyDuringGC:(after)"); @@ -1983,12 +2442,11 @@ class G1CMKeepAliveClosure: public OopClosure { G1CollectedHeap* _g1; ConcurrentMark* _cm; - CMBitMap* _bitMap; public: - G1CMKeepAliveClosure(G1CollectedHeap* g1, ConcurrentMark* cm, - CMBitMap* bitMap) : - _g1(g1), _cm(cm), - _bitMap(bitMap) {} + G1CMKeepAliveClosure(G1CollectedHeap* g1, ConcurrentMark* cm) : + _g1(g1), _cm(cm) { + assert(Thread::current()->is_VM_thread(), "otherwise fix worker id"); + } virtual void do_oop(narrowOop* p) { do_oop_work(p); } virtual void do_oop( oop* p) { do_oop_work(p); } @@ -2004,26 +2462,25 @@ } if (_g1->is_in_g1_reserved(addr) && _g1->is_obj_ill(obj)) { - _bitMap->mark(addr); + _cm->mark_and_count(obj); _cm->mark_stack_push(obj); } } }; class G1CMDrainMarkingStackClosure: public VoidClosure { + ConcurrentMark* _cm; CMMarkStack* _markStack; - CMBitMap* _bitMap; G1CMKeepAliveClosure* _oopClosure; public: - G1CMDrainMarkingStackClosure(CMBitMap* bitMap, CMMarkStack* markStack, + G1CMDrainMarkingStackClosure(ConcurrentMark* cm, CMMarkStack* markStack, G1CMKeepAliveClosure* oopClosure) : - _bitMap(bitMap), + _cm(cm), _markStack(markStack), - _oopClosure(oopClosure) - {} + _oopClosure(oopClosure) { } void do_void() { - _markStack->drain((OopClosure*)_oopClosure, _bitMap, false); + _markStack->drain((OopClosure*)_oopClosure, _cm->nextMarkBitMap(), false); } }; @@ -2102,8 +2559,7 @@ CMTask* _task; public: G1CMParDrainMarkingStackClosure(ConcurrentMark* cm, CMTask* task) : - _cm(cm), _task(task) - {} + _cm(cm), _task(task) { } void do_void() { do { @@ -2242,9 +2698,9 @@ rp->setup_policy(clear_all_soft_refs); assert(_markStack.isEmpty(), "mark stack should be empty"); - G1CMKeepAliveClosure g1_keep_alive(g1h, this, nextMarkBitMap()); + G1CMKeepAliveClosure g1_keep_alive(g1h, this); G1CMDrainMarkingStackClosure - g1_drain_mark_stack(nextMarkBitMap(), &_markStack, &g1_keep_alive); + g1_drain_mark_stack(this, &_markStack, &g1_keep_alive); // We use the work gang from the G1CollectedHeap and we utilize all // the worker threads. @@ -2616,18 +3072,6 @@ // during an evacuation pause). This was a late change to the code and // is currently not being taken advantage of. -class CMGlobalObjectClosure : public ObjectClosure { -private: - ConcurrentMark* _cm; - -public: - void do_object(oop obj) { - _cm->deal_with_reference(obj); - } - - CMGlobalObjectClosure(ConcurrentMark* cm) : _cm(cm) { } -}; - void ConcurrentMark::deal_with_reference(oop obj) { if (verbose_high()) { gclog_or_tty->print_cr("[global] we're dealing with reference "PTR_FORMAT, @@ -2672,6 +3116,18 @@ } } +class CMGlobalObjectClosure : public ObjectClosure { +private: + ConcurrentMark* _cm; + +public: + void do_object(oop obj) { + _cm->deal_with_reference(obj); + } + + CMGlobalObjectClosure(ConcurrentMark* cm) : _cm(cm) { } +}; + void ConcurrentMark::drainAllSATBBuffers() { guarantee(false, "drainAllSATBBuffers(): don't call this any more"); @@ -2693,15 +3149,6 @@ assert(satb_mq_set.completed_buffers_num() == 0, "invariant"); } -void ConcurrentMark::clear(oop p) { - assert(p != NULL && p->is_oop(), "expected an oop"); - HeapWord* addr = (HeapWord*)p; - assert(addr >= _nextMarkBitMap->startWord() || - addr < _nextMarkBitMap->endWord(), "in a region"); - - _nextMarkBitMap->clear(addr); -} - void ConcurrentMark::clearRangePrevBitmap(MemRegion mr) { // Note we are overriding the read-only view of the prev map here, via // the cast. @@ -3015,6 +3462,192 @@ } } +// Aggregate the counting data that was constructed concurrently +// with marking. +class AggregateCountDataHRClosure: public HeapRegionClosure { + ConcurrentMark* _cm; + BitMap* _cm_card_bm; + size_t _max_task_num; + + public: + AggregateCountDataHRClosure(ConcurrentMark *cm, + BitMap* cm_card_bm, + size_t max_task_num) : + _cm(cm), _cm_card_bm(cm_card_bm), + _max_task_num(max_task_num) { } + + bool is_card_aligned(HeapWord* p) { + return ((uintptr_t(p) & (CardTableModRefBS::card_size - 1)) == 0); + } + + bool doHeapRegion(HeapRegion* hr) { + if (hr->continuesHumongous()) { + // We will ignore these here and process them when their + // associated "starts humongous" region is processed. + // Note that we cannot rely on their associated + // "starts humongous" region to have their bit set to 1 + // since, due to the region chunking in the parallel region + // iteration, a "continues humongous" region might be visited + // before its associated "starts humongous". + return false; + } + + HeapWord* start = hr->bottom(); + HeapWord* limit = hr->next_top_at_mark_start(); + HeapWord* end = hr->end(); + + assert(start <= limit && limit <= hr->top() && hr->top() <= hr->end(), + err_msg("Preconditions not met - " + "start: "PTR_FORMAT", limit: "PTR_FORMAT", " + "top: "PTR_FORMAT", end: "PTR_FORMAT, + start, limit, hr->top(), hr->end())); + + assert(hr->next_marked_bytes() == 0, "Precondition"); + + if (start == limit) { + // NTAMS of this region has not been set so nothing to do. + return false; + } + + assert(is_card_aligned(start), "sanity"); + assert(is_card_aligned(end), "sanity"); + + BitMap::idx_t start_idx = _cm->card_bitmap_index_for(start); + BitMap::idx_t limit_idx = _cm->card_bitmap_index_for(limit); + BitMap::idx_t end_idx = _cm->card_bitmap_index_for(end); + + // If ntams is not card aligned then we bump the index for + // limit so that we get the card spanning ntams. + if (!is_card_aligned(limit)) { + limit_idx += 1; + } + + assert(limit_idx <= end_idx, "or else use atomics"); + + // Aggregate the "stripe" in the count data associated with hr. + size_t hrs_index = hr->hrs_index(); + size_t marked_bytes = 0; + + for (int i = 0; (size_t)i < _max_task_num; i += 1) { + size_t* marked_bytes_array = _cm->count_marked_bytes_array_for(i); + BitMap* task_card_bm = _cm->count_card_bitmap_for(i); + + // 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]; + + // Now union the bitmaps[0,max_task_num)[start_idx..limit_idx) + // into the global card bitmap. + BitMap::idx_t scan_idx = task_card_bm->get_next_one_offset(start_idx, limit_idx); + + while (scan_idx < limit_idx) { + assert(task_card_bm->at(scan_idx) == true, "should be"); + _cm_card_bm->set_bit(scan_idx); + assert(_cm_card_bm->at(scan_idx) == true, "should be"); + + // BitMap::get_next_one_offset() can handle the case when + // its left_offset parameter is greater than its right_offset + // parameter. If does, however, have an early exit if + // left_offset == right_offset. So let's limit the value + // passed in for left offset here. + BitMap::idx_t next_idx = MIN2(scan_idx + 1, limit_idx); + scan_idx = task_card_bm->get_next_one_offset(next_idx, limit_idx); + } + } + + // Update the marked bytes for this region. + hr->add_to_marked_bytes(marked_bytes); + + // Now set the top at count to NTAMS. + hr->set_top_at_conc_mark_count(limit); + + // Next heap region + return false; + } +}; + +class G1AggregateCountDataTask: public AbstractGangTask { +protected: + G1CollectedHeap* _g1h; + ConcurrentMark* _cm; + BitMap* _cm_card_bm; + size_t _max_task_num; + int _active_workers; + +public: + G1AggregateCountDataTask(G1CollectedHeap* g1h, + ConcurrentMark* cm, + BitMap* cm_card_bm, + size_t max_task_num, + int n_workers) : + AbstractGangTask("Count Aggregation"), + _g1h(g1h), _cm(cm), _cm_card_bm(cm_card_bm), + _max_task_num(max_task_num), + _active_workers(n_workers) { } + + void work(uint worker_id) { + AggregateCountDataHRClosure cl(_cm, _cm_card_bm, _max_task_num); + + if (G1CollectedHeap::use_parallel_gc_threads()) { + _g1h->heap_region_par_iterate_chunked(&cl, worker_id, + _active_workers, + HeapRegion::AggregateCountClaimValue); + } else { + _g1h->heap_region_iterate(&cl); + } + } +}; + + +void ConcurrentMark::aggregate_count_data() { + int n_workers = (G1CollectedHeap::use_parallel_gc_threads() ? + _g1h->workers()->active_workers() : + 1); + + G1AggregateCountDataTask g1_par_agg_task(_g1h, this, &_card_bm, + _max_task_num, n_workers); + + if (G1CollectedHeap::use_parallel_gc_threads()) { + assert(_g1h->check_heap_region_claim_values(HeapRegion::InitialClaimValue), + "sanity check"); + _g1h->set_par_threads(n_workers); + _g1h->workers()->run_task(&g1_par_agg_task); + _g1h->set_par_threads(0); + + assert(_g1h->check_heap_region_claim_values(HeapRegion::AggregateCountClaimValue), + "sanity check"); + _g1h->reset_heap_region_claim_values(); + } else { + g1_par_agg_task.work(0); + } +} + +// Clear the per-worker arrays used to store the per-region counting data +void ConcurrentMark::clear_all_count_data() { + // Clear the global card bitmap - it will be filled during + // liveness count aggregation (during remark) and the + // final counting task. + _card_bm.clear(); + + // Clear the global region bitmap - it will be filled as part + // of the final counting task. + _region_bm.clear(); + + size_t max_regions = _g1h->max_regions(); + assert(_max_task_num != 0, "unitialized"); + + for (int i = 0; (size_t) i < _max_task_num; i += 1) { + BitMap* task_card_bm = count_card_bitmap_for(i); + size_t* marked_bytes_array = count_marked_bytes_array_for(i); + + assert(task_card_bm->size() == _card_bm.size(), "size mismatch"); + assert(marked_bytes_array != NULL, "uninitialized"); + + memset(marked_bytes_array, 0, (max_regions * sizeof(size_t))); + task_card_bm->clear(); + } +} + void ConcurrentMark::print_stats() { if (verbose_stats()) { gclog_or_tty->print_cr("---------------------------------------------------------------------"); @@ -3350,6 +3983,8 @@ void ConcurrentMark::abort() { // Clear all marks to force marking thread to do nothing _nextMarkBitMap->clearAll(); + // Clear the liveness counting data + clear_all_count_data(); // Empty mark stack clear_marking_state(); for (int i = 0; i < (int)_max_task_num; ++i) { @@ -3402,23 +4037,15 @@ (_init_times.sum() + _remark_times.sum() + _cleanup_times.sum())/1000.0); gclog_or_tty->print_cr(" Total concurrent time = %8.2f s " - "(%8.2f s marking, %8.2f s counting).", + "(%8.2f s marking).", cmThread()->vtime_accum(), - cmThread()->vtime_mark_accum(), - cmThread()->vtime_count_accum()); + cmThread()->vtime_mark_accum()); } void ConcurrentMark::print_worker_threads_on(outputStream* st) const { _parallel_workers->print_worker_threads_on(st); } -// Closures -// XXX: there seems to be a lot of code duplication here; -// should refactor and consolidate the shared code. - -// This closure is used to mark refs into the CMS generation in -// the CMS bit map. Called at the first checkpoint. - // We take a break if someone is trying to stop the world. bool ConcurrentMark::do_yield_check(uint worker_id) { if (should_yield()) { @@ -4704,6 +5331,8 @@ CMTask::CMTask(int task_id, ConcurrentMark* cm, + size_t* marked_bytes, + BitMap* card_bm, CMTaskQueue* task_queue, CMTaskQueueSet* task_queues) : _g1h(G1CollectedHeap::heap()), @@ -4713,7 +5342,9 @@ _task_queue(task_queue), _task_queues(task_queues), _cm_oop_closure(NULL), - _aborted_region(MemRegion()) { + _aborted_region(MemRegion()), + _marked_bytes_array(marked_bytes), + _card_bm(card_bm) { guarantee(task_queue != NULL, "invariant"); guarantee(task_queues != NULL, "invariant"); diff -r f920d3637c9f -r 4c63e7c939aa hotspot/src/share/vm/gc_implementation/g1/concurrentMark.hpp --- a/hotspot/src/share/vm/gc_implementation/g1/concurrentMark.hpp Thu Jan 26 18:23:14 2012 -0800 +++ b/hotspot/src/share/vm/gc_implementation/g1/concurrentMark.hpp Wed Jul 05 18:01:11 2017 +0200 @@ -84,8 +84,8 @@ } // iteration - bool iterate(BitMapClosure* cl) { return _bm.iterate(cl); } - bool iterate(BitMapClosure* cl, MemRegion mr); + inline bool iterate(BitMapClosure* cl, MemRegion mr); + inline bool iterate(BitMapClosure* cl); // Return the address corresponding to the next marked bit at or after // "addr", and before "limit", if "limit" is non-NULL. If there is no @@ -349,10 +349,62 @@ high_verbose // per object verbose } CMVerboseLevel; +class YoungList; + +// Root Regions are regions that are not empty at the beginning of a +// marking cycle and which we might collect during an evacuation pause +// while the cycle is active. Given that, during evacuation pauses, we +// do not copy objects that are explicitly marked, what we have to do +// for the root regions is to scan them and mark all objects reachable +// from them. According to the SATB assumptions, we only need to visit +// each object once during marking. So, as long as we finish this scan +// before the next evacuation pause, we can copy the objects from the +// root regions without having to mark them or do anything else to them. +// +// Currently, we only support root region scanning once (at the start +// of the marking cycle) and the root regions are all the survivor +// regions populated during the initial-mark pause. +class CMRootRegions VALUE_OBJ_CLASS_SPEC { +private: + YoungList* _young_list; + ConcurrentMark* _cm; + + volatile bool _scan_in_progress; + volatile bool _should_abort; + HeapRegion* volatile _next_survivor; + +public: + CMRootRegions(); + // We actually do most of the initialization in this method. + void init(G1CollectedHeap* g1h, ConcurrentMark* cm); + + // Reset the claiming / scanning of the root regions. + void prepare_for_scan(); + + // Forces get_next() to return NULL so that the iteration aborts early. + void abort() { _should_abort = true; } + + // Return true if the CM thread are actively scanning root regions, + // false otherwise. + bool scan_in_progress() { return _scan_in_progress; } + + // Claim the next root region to scan atomically, or return NULL if + // all have been claimed. + HeapRegion* claim_next(); + + // Flag that we're done with root region scanning and notify anyone + // who's waiting on it. If aborted is false, assume that all regions + // have been claimed. + void scan_finished(); + + // If CM threads are still scanning root regions, wait until they + // are done. Return true if we had to wait, false otherwise. + bool wait_until_scan_finished(); +}; class ConcurrentMarkThread; -class ConcurrentMark: public CHeapObj { +class ConcurrentMark : public CHeapObj { friend class ConcurrentMarkThread; friend class CMTask; friend class CMBitMapClosure; @@ -386,7 +438,7 @@ FreeRegionList _cleanup_list; - // CMS marking support structures + // Concurrent marking support structures CMBitMap _markBitMap1; CMBitMap _markBitMap2; CMBitMapRO* _prevMarkBitMap; // completed mark bitmap @@ -400,6 +452,9 @@ HeapWord* _heap_start; HeapWord* _heap_end; + // Root region tracking and claiming. + CMRootRegions _root_regions; + // For gray objects CMMarkStack _markStack; // Grey objects behind global finger. CMRegionStack _regionStack; // Grey regions behind global finger. @@ -426,7 +481,6 @@ WorkGangBarrierSync _first_overflow_barrier_sync; WorkGangBarrierSync _second_overflow_barrier_sync; - // this is set by any task, when an overflow on the global data // structures is detected. volatile bool _has_overflown; @@ -554,9 +608,9 @@ bool has_overflown() { return _has_overflown; } void set_has_overflown() { _has_overflown = true; } void clear_has_overflown() { _has_overflown = false; } + bool restart_for_overflow() { return _restart_for_overflow; } bool has_aborted() { return _has_aborted; } - bool restart_for_overflow() { return _restart_for_overflow; } // Methods to enter the two overflow sync barriers void enter_first_sync_barrier(int task_num); @@ -578,6 +632,27 @@ } } + // Live Data Counting data structures... + // These data structures are initialized at the start of + // marking. They are written to while marking is active. + // They are aggregated during remark; the aggregated values + // are then used to populate the _region_bm, _card_bm, and + // the total live bytes, which are then subsequently updated + // during cleanup. + + // An array of bitmaps (one bit map per task). Each bitmap + // is used to record the cards spanned by the live objects + // marked by that task/worker. + BitMap* _count_card_bitmaps; + + // Used to record the number of marked live bytes + // (for each region, by worker thread). + size_t** _count_marked_bytes; + + // Card index of the bottom of the G1 heap. Used for biasing indices into + // the card bitmaps. + intptr_t _heap_bottom_card_num; + public: // Manipulation of the global mark stack. // Notice that the first mark_stack_push is CAS-based, whereas the @@ -671,6 +746,8 @@ // Returns true if there are any aborted memory regions. bool has_aborted_regions(); + CMRootRegions* root_regions() { return &_root_regions; } + bool concurrent_marking_in_progress() { return _concurrent_marking_in_progress; } @@ -703,6 +780,7 @@ ConcurrentMark(ReservedSpace rs, int max_regions); ~ConcurrentMark(); + ConcurrentMarkThread* cmThread() { return _cmThread; } CMBitMapRO* prevMarkBitMap() const { return _prevMarkBitMap; } @@ -720,8 +798,17 @@ // G1CollectedHeap // This notifies CM that a root during initial-mark needs to be - // grayed. It is MT-safe. - inline void grayRoot(oop obj, size_t word_size); + // grayed. It is MT-safe. word_size is the size of the object in + // words. It is passed explicitly as sometimes we cannot calculate + // it from the given object because it might be in an inconsistent + // state (e.g., in to-space and being copied). So the caller is + // responsible for dealing with this issue (e.g., get the size from + // the from-space image when the to-space image might be + // 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); // It's used during evacuation pauses to gray a region, if // necessary, and it's MT-safe. It assumes that the caller has @@ -772,6 +859,13 @@ void checkpointRootsInitialPre(); void checkpointRootsInitialPost(); + // Scan all the root regions and mark everything reachable from + // them. + void scanRootRegions(); + + // Scan a single root region and mark everything reachable from it. + void scanRootRegion(HeapRegion* hr, uint worker_id); + // Do concurrent phase of marking, to a tentative transitive closure. void markFromRoots(); @@ -781,15 +875,13 @@ void checkpointRootsFinal(bool clear_all_soft_refs); void checkpointRootsFinalWork(); - void calcDesiredRegions(); void cleanup(); void completeCleanup(); // Mark in the previous bitmap. NB: this is usually read-only, so use // this carefully! inline void markPrev(oop p); - inline void markNext(oop p); - void clear(oop p); + // Clears marks for all objects in the given range, for the prev, // next, or both bitmaps. NB: the previous bitmap is usually // read-only, so use this carefully! @@ -913,6 +1005,114 @@ bool verbose_high() { return _MARKING_VERBOSE_ && _verbose_level >= high_verbose; } + + // Counting data structure accessors + + // Returns the card number of the bottom of the G1 heap. + // Used in biasing indices into accounting card bitmaps. + intptr_t heap_bottom_card_num() const { + return _heap_bottom_card_num; + } + + // Returns the card bitmap for a given task or worker id. + BitMap* count_card_bitmap_for(uint worker_id) { + assert(0 <= worker_id && worker_id < _max_task_num, "oob"); + assert(_count_card_bitmaps != NULL, "uninitialized"); + BitMap* task_card_bm = &_count_card_bitmaps[worker_id]; + assert(task_card_bm->size() == _card_bm.size(), "size mismatch"); + return task_card_bm; + } + + // Returns the array containing the marked bytes for each region, + // for the given worker or task id. + size_t* count_marked_bytes_array_for(uint worker_id) { + assert(0 <= worker_id && worker_id < _max_task_num, "oob"); + assert(_count_marked_bytes != NULL, "uninitialized"); + size_t* marked_bytes_array = _count_marked_bytes[worker_id]; + assert(marked_bytes_array != NULL, "uninitialized"); + return marked_bytes_array; + } + + // Returns the index in the liveness accounting card table bitmap + // for the given address + inline BitMap::idx_t card_bitmap_index_for(HeapWord* addr); + + // Counts the size of the given memory region in the the given + // 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, + size_t* marked_bytes_array, + BitMap* task_card_bm); + + // Counts the given memory region in the task/worker counting + // 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, + 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, + 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 reponsibility + // 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 unconditinally 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); + +protected: + // Clear all the per-task bitmaps and arrays used to store the + // counting data. + void clear_all_count_data(); + + // Aggregates the counting data for each worker/task + // that was constructed while marking. Also sets + // the amount of marked bytes for each region and + // the top at concurrent mark count. + void aggregate_count_data(); + + // Verification routine + void verify_count_data(); }; // A class representing a marking task. @@ -1031,6 +1231,12 @@ TruncatedSeq _marking_step_diffs_ms; + // Counting data structures. Embedding the task's marked_bytes_array + // and card bitmap into the actual task saves having to go through + // the ConcurrentMark object. + size_t* _marked_bytes_array; + BitMap* _card_bm; + // LOTS of statistics related with this task #if _MARKING_STATS_ NumberSeq _all_clock_intervals_ms; @@ -1196,6 +1402,7 @@ } CMTask(int task_num, ConcurrentMark *cm, + size_t* marked_bytes, BitMap* card_bm, CMTaskQueue* task_queue, CMTaskQueueSet* task_queues); // it prints statistics associated with this task diff -r f920d3637c9f -r 4c63e7c939aa hotspot/src/share/vm/gc_implementation/g1/concurrentMark.inline.hpp --- a/hotspot/src/share/vm/gc_implementation/g1/concurrentMark.inline.hpp Thu Jan 26 18:23:14 2012 -0800 +++ b/hotspot/src/share/vm/gc_implementation/g1/concurrentMark.inline.hpp Wed Jul 05 18:01:11 2017 +0200 @@ -28,6 +28,214 @@ #include "gc_implementation/g1/concurrentMark.hpp" #include "gc_implementation/g1/g1CollectedHeap.inline.hpp" +// Returns the index in the liveness accounting card bitmap +// for the given address +inline BitMap::idx_t ConcurrentMark::card_bitmap_index_for(HeapWord* addr) { + // Below, the term "card num" means the result of shifting an address + // by the card shift -- address 0 corresponds to card number 0. One + // must subtract the card num of the bottom of the heap to obtain a + // card table index. + + intptr_t card_num = intptr_t(uintptr_t(addr) >> CardTableModRefBS::card_shift); + return card_num - heap_bottom_card_num(); +} + +// Counts the given memory region in the given task/worker +// counting data structures. +inline void ConcurrentMark::count_region(MemRegion mr, HeapRegion* hr, + size_t* marked_bytes_array, + BitMap* task_card_bm) { + G1CollectedHeap* g1h = _g1h; + HeapWord* start = mr.start(); + HeapWord* last = mr.last(); + size_t region_size_bytes = mr.byte_size(); + size_t index = hr->hrs_index(); + + assert(!hr->continuesHumongous(), "should not be HC region"); + assert(hr == g1h->heap_region_containing(start), "sanity"); + assert(hr == g1h->heap_region_containing(mr.last()), "sanity"); + assert(marked_bytes_array != NULL, "pre-condition"); + assert(task_card_bm != NULL, "pre-condition"); + + // Add to the task local marked bytes for this region. + marked_bytes_array[index] += region_size_bytes; + + BitMap::idx_t start_idx = card_bitmap_index_for(start); + BitMap::idx_t last_idx = card_bitmap_index_for(last); + + // The card bitmap is task/worker specific => no need to use 'par' routines. + // Set bits in the inclusive bit range [start_idx, last_idx]. + // + // For small ranges use a simple loop; otherwise use set_range + // The range are the cards that are spanned by the object/region + // so 8 cards will allow objects/regions up to 4K to be handled + // using the loop. + if ((last_idx - start_idx) <= 8) { + for (BitMap::idx_t i = start_idx; i <= last_idx; i += 1) { + task_card_bm->set_bit(i); + } + } else { + assert(last_idx < task_card_bm->size(), "sanity"); + // Note: BitMap::set_range() is exclusive. + task_card_bm->set_range(start_idx, last_idx+1); + } +} + +// Counts the given memory region in the task/worker counting +// data structures for the given worker id. +inline void ConcurrentMark::count_region(MemRegion mr, + 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); + 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, + size_t* marked_bytes_array, + BitMap* task_card_bm) { + MemRegion mr((HeapWord*)obj, obj->size()); + 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, + HeapRegion* hr, + size_t* marked_bytes_array, + BitMap* task_card_bm) { + HeapWord* addr = (HeapWord*)obj; + if (_nextMarkBitMap->parMark(addr)) { + // Update the task specific count data for the object. + count_object(obj, hr, marked_bytes_array, task_card_bm); + return true; + } + 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, + size_t word_size, + HeapRegion* hr, + uint worker_id) { + HeapWord* addr = (HeapWord*)obj; + if (_nextMarkBitMap->parMark(addr)) { + MemRegion mr(addr, word_size); + count_region(mr, hr, worker_id); + return true; + } + 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 unconditinally 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()); + + if (end_addr > start_addr) { + // Right-open interval [start-offset, end-offset). + BitMap::idx_t start_offset = heapWordToOffset(start_addr); + BitMap::idx_t end_offset = heapWordToOffset(end_addr); + + start_offset = _bm.get_next_one_offset(start_offset, end_offset); + while (start_offset < end_offset) { + HeapWord* obj_addr = offsetToHeapWord(start_offset); + oop obj = (oop) obj_addr; + if (!cl->do_bit(start_offset)) { + return false; + } + HeapWord* next_addr = MIN2(obj_addr + obj->size(), end_addr); + BitMap::idx_t next_offset = heapWordToOffset(next_addr); + start_offset = _bm.get_next_one_offset(next_offset, end_offset); + } + } + return true; +} + +inline bool CMBitMapRO::iterate(BitMapClosure* cl) { + MemRegion mr(startWord(), sizeInWords()); + return iterate(cl, mr); +} + inline void CMTask::push(oop obj) { HeapWord* objAddr = (HeapWord*) obj; assert(_g1h->is_in_g1_reserved(objAddr), "invariant"); @@ -84,7 +292,7 @@ HeapWord* objAddr = (HeapWord*) obj; assert(obj->is_oop_or_null(true /* ignore mark word */), "Error"); - if (_g1h->is_in_g1_reserved(objAddr)) { + if (_g1h->is_in_g1_reserved(objAddr)) { assert(obj != NULL, "null check is implicit"); if (!_nextMarkBitMap->isMarked(objAddr)) { // Only get the containing region if the object is not marked on the @@ -98,9 +306,9 @@ } // we need to mark it first - if (_nextMarkBitMap->parMark(objAddr)) { + if (_cm->par_mark_and_count(obj, hr, _marked_bytes_array, _card_bm)) { // No OrderAccess:store_load() is needed. It is implicit in the - // CAS done in parMark(objAddr) above + // CAS done in CMBitMap::parMark() call in the routine above. HeapWord* global_finger = _cm->finger(); #if _CHECK_BOTH_FINGERS_ @@ -160,25 +368,20 @@ ((CMBitMap*)_prevMarkBitMap)->mark((HeapWord*) p); } -inline void ConcurrentMark::markNext(oop p) { - assert(!_nextMarkBitMap->isMarked((HeapWord*) p), "sanity"); - _nextMarkBitMap->mark((HeapWord*) p); -} - -inline void ConcurrentMark::grayRoot(oop obj, size_t word_size) { +inline void ConcurrentMark::grayRoot(oop obj, size_t word_size, + uint worker_id, HeapRegion* hr) { + assert(obj != NULL, "pre-condition"); HeapWord* addr = (HeapWord*) obj; + if (hr == NULL) { + hr = _g1h->heap_region_containing_raw(addr); + } else { + assert(hr->is_in(addr), "pre-condition"); + } + assert(hr != NULL, "sanity"); + // Given that we're looking for a region that contains an object + // header it's impossible to get back a HC region. + assert(!hr->continuesHumongous(), "sanity"); - // Currently we don't do anything with word_size but we will use it - // in the very near future in the liveness calculation piggy-backing - // changes. - -#ifdef ASSERT - HeapRegion* hr = _g1h->heap_region_containing(addr); - assert(hr != NULL, "sanity"); - assert(!hr->is_survivor(), "should not allocate survivors during IM"); - assert(addr < hr->next_top_at_mark_start(), - err_msg("addr: "PTR_FORMAT" hr: "HR_FORMAT" NTAMS: "PTR_FORMAT, - addr, HR_FORMAT_PARAMS(hr), hr->next_top_at_mark_start())); // We cannot assert that word_size == obj->size() given that obj // might not be in a consistent state (another thread might be in // the process of copying it). So the best thing we can do is to @@ -188,10 +391,11 @@ err_msg("size: "SIZE_FORMAT" capacity: "SIZE_FORMAT" "HR_FORMAT, word_size * HeapWordSize, hr->capacity(), HR_FORMAT_PARAMS(hr))); -#endif // ASSERT - if (!_nextMarkBitMap->isMarked(addr)) { - _nextMarkBitMap->parMark(addr); + if (addr < hr->next_top_at_mark_start()) { + if (!_nextMarkBitMap->isMarked(addr)) { + par_mark_and_count(obj, word_size, hr, worker_id); + } } } diff -r f920d3637c9f -r 4c63e7c939aa hotspot/src/share/vm/gc_implementation/g1/concurrentMarkThread.cpp --- a/hotspot/src/share/vm/gc_implementation/g1/concurrentMarkThread.cpp Thu Jan 26 18:23:14 2012 -0800 +++ b/hotspot/src/share/vm/gc_implementation/g1/concurrentMarkThread.cpp Wed Jul 05 18:01:11 2017 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2012, 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 @@ -44,9 +44,7 @@ _started(false), _in_progress(false), _vtime_accum(0.0), - _vtime_mark_accum(0.0), - _vtime_count_accum(0.0) -{ + _vtime_mark_accum(0.0) { create_and_start(); } @@ -94,9 +92,36 @@ ResourceMark rm; HandleMark hm; double cycle_start = os::elapsedVTime(); - double mark_start_sec = os::elapsedTime(); char verbose_str[128]; + // We have to ensure that we finish scanning the root regions + // before the next GC takes place. To ensure this we have to + // make sure that we do not join the STS until the root regions + // have been scanned. If we did then it's possible that a + // subsequent GC could block us from joining the STS and proceed + // without the root regions have been scanned which would be a + // correctness issue. + + double scan_start = os::elapsedTime(); + if (!cm()->has_aborted()) { + if (PrintGC) { + gclog_or_tty->date_stamp(PrintGCDateStamps); + gclog_or_tty->stamp(PrintGCTimeStamps); + gclog_or_tty->print_cr("[GC concurrent-root-region-scan-start]"); + } + + _cm->scanRootRegions(); + + double scan_end = os::elapsedTime(); + if (PrintGC) { + gclog_or_tty->date_stamp(PrintGCDateStamps); + gclog_or_tty->stamp(PrintGCTimeStamps); + gclog_or_tty->print_cr("[GC concurrent-root-region-scan-end, %1.7lf]", + scan_end - scan_start); + } + } + + double mark_start_sec = os::elapsedTime(); if (PrintGC) { gclog_or_tty->date_stamp(PrintGCDateStamps); gclog_or_tty->stamp(PrintGCTimeStamps); @@ -148,36 +173,12 @@ } } while (cm()->restart_for_overflow()); - double counting_start_time = os::elapsedVTime(); - if (!cm()->has_aborted()) { - double count_start_sec = os::elapsedTime(); - if (PrintGC) { - gclog_or_tty->date_stamp(PrintGCDateStamps); - gclog_or_tty->stamp(PrintGCTimeStamps); - gclog_or_tty->print_cr("[GC concurrent-count-start]"); - } - - _sts.join(); - _cm->calcDesiredRegions(); - _sts.leave(); - - if (!cm()->has_aborted()) { - double count_end_sec = os::elapsedTime(); - if (PrintGC) { - gclog_or_tty->date_stamp(PrintGCDateStamps); - gclog_or_tty->stamp(PrintGCTimeStamps); - gclog_or_tty->print_cr("[GC concurrent-count-end, %1.7lf]", - count_end_sec - count_start_sec); - } - } - } - double end_time = os::elapsedVTime(); - _vtime_count_accum += (end_time - counting_start_time); // Update the total virtual time before doing this, since it will try // to measure it to get the vtime for this marking. We purposely // neglect the presumably-short "completeCleanup" phase here. _vtime_accum = (end_time - _vtime_start); + if (!cm()->has_aborted()) { if (g1_policy->adaptive_young_list_length()) { double now = os::elapsedTime(); diff -r f920d3637c9f -r 4c63e7c939aa hotspot/src/share/vm/gc_implementation/g1/concurrentMarkThread.hpp --- a/hotspot/src/share/vm/gc_implementation/g1/concurrentMarkThread.hpp Thu Jan 26 18:23:14 2012 -0800 +++ b/hotspot/src/share/vm/gc_implementation/g1/concurrentMarkThread.hpp Wed Jul 05 18:01:11 2017 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2012, 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 @@ -40,7 +40,6 @@ double _vtime_accum; // Accumulated virtual time. double _vtime_mark_accum; - double _vtime_count_accum; public: virtual void run(); @@ -69,8 +68,6 @@ double vtime_accum(); // Marking virtual time so far double vtime_mark_accum(); - // Counting virtual time so far. - double vtime_count_accum() { return _vtime_count_accum; } ConcurrentMark* cm() { return _cm; } diff -r f920d3637c9f -r 4c63e7c939aa hotspot/src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp --- a/hotspot/src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp Thu Jan 26 18:23:14 2012 -0800 +++ b/hotspot/src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp Wed Jul 05 18:01:11 2017 +0200 @@ -174,13 +174,10 @@ } }; -YoungList::YoungList(G1CollectedHeap* g1h) - : _g1h(g1h), _head(NULL), - _length(0), - _last_sampled_rs_lengths(0), - _survivor_head(NULL), _survivor_tail(NULL), _survivor_length(0) -{ - guarantee( check_list_empty(false), "just making sure..." ); +YoungList::YoungList(G1CollectedHeap* g1h) : + _g1h(g1h), _head(NULL), _length(0), _last_sampled_rs_lengths(0), + _survivor_head(NULL), _survivor_tail(NULL), _survivor_length(0) { + guarantee(check_list_empty(false), "just making sure..."); } void YoungList::push_region(HeapRegion *hr) { @@ -1029,6 +1026,15 @@ assert(isHumongous(word_size), "attempt_allocation_humongous() " "should only be called for humongous allocations"); + // Humongous objects can exhaust the heap quickly, so we should check if we + // need to start a marking cycle at each humongous object allocation. We do + // the check before we do the actual allocation. The reason for doing it + // before the allocation is that we avoid having to keep track of the newly + // allocated memory while we do a GC. + if (g1_policy()->need_to_start_conc_mark("concurrent humongous allocation", word_size)) { + collect(GCCause::_g1_humongous_allocation); + } + // We will loop until a) we manage to successfully perform the // allocation or b) we successfully schedule a collection which // fails to perform the allocation. b) is the only case when we'll @@ -1111,7 +1117,11 @@ return _mutator_alloc_region.attempt_allocation_locked(word_size, false /* bot_updates */); } else { - return humongous_obj_allocate(word_size); + HeapWord* result = humongous_obj_allocate(word_size); + if (result != NULL && g1_policy()->need_to_start_conc_mark("STW humongous allocation")) { + g1_policy()->set_initiate_conc_mark_if_possible(); + } + return result; } ShouldNotReachHere(); @@ -1257,7 +1267,18 @@ double start = os::elapsedTime(); g1_policy()->record_full_collection_start(); + // Note: When we have a more flexible GC logging framework that + // allows us to add optional attributes to a GC log record we + // could consider timing and reporting how long we wait in the + // following two methods. wait_while_free_regions_coming(); + // If we start the compaction before the CM threads finish + // scanning the root regions we might trip them over as we'll + // be moving objects / updating references. So let's wait until + // they are done. By telling them to abort, they should complete + // early. + _cm->root_regions()->abort(); + _cm->root_regions()->wait_until_scan_finished(); append_secondary_free_list_if_not_empty_with_lock(); gc_prologue(true); @@ -1286,7 +1307,8 @@ ref_processor_cm()->verify_no_references_recorded(); // Abandon current iterations of concurrent marking and concurrent - // refinement, if any are in progress. + // refinement, if any are in progress. We have to do this before + // wait_until_scan_finished() below. concurrent_mark()->abort(); // Make sure we'll choose a new allocation region afterwards. @@ -2295,7 +2317,8 @@ bool G1CollectedHeap::should_do_concurrent_full_gc(GCCause::Cause cause) { return ((cause == GCCause::_gc_locker && GCLockerInvokesConcurrent) || - (cause == GCCause::_java_lang_system_gc && ExplicitGCInvokesConcurrent)); + (cause == GCCause::_java_lang_system_gc && ExplicitGCInvokesConcurrent) || + cause == GCCause::_g1_humongous_allocation); } #ifndef PRODUCT @@ -3545,19 +3568,25 @@ verify_region_sets_optional(); verify_dirty_young_regions(); + // This call will decide whether this pause is an initial-mark + // pause. If it is, during_initial_mark_pause() will return true + // for the duration of this pause. + g1_policy()->decide_on_conc_mark_initiation(); + + // We do not allow initial-mark to be piggy-backed on a mixed GC. + assert(!g1_policy()->during_initial_mark_pause() || + g1_policy()->gcs_are_young(), "sanity"); + + // We also do not allow mixed GCs during marking. + assert(!mark_in_progress() || g1_policy()->gcs_are_young(), "sanity"); + + // Record whether this pause is an initial mark. When the current + // thread has completed its logging output and it's safe to signal + // the CM thread, the flag's value in the policy has been reset. + bool should_start_conc_mark = g1_policy()->during_initial_mark_pause(); + + // Inner scope for scope based logging, timers, and stats collection { - // This call will decide whether this pause is an initial-mark - // pause. If it is, during_initial_mark_pause() will return true - // for the duration of this pause. - g1_policy()->decide_on_conc_mark_initiation(); - - // We do not allow initial-mark to be piggy-backed on a mixed GC. - assert(!g1_policy()->during_initial_mark_pause() || - g1_policy()->gcs_are_young(), "sanity"); - - // We also do not allow mixed GCs during marking. - assert(!mark_in_progress() || g1_policy()->gcs_are_young(), "sanity"); - char verbose_str[128]; sprintf(verbose_str, "GC pause "); if (g1_policy()->gcs_are_young()) { @@ -3613,7 +3642,6 @@ Universe::verify(/* allow dirty */ false, /* silent */ false, /* option */ VerifyOption_G1UsePrevMarking); - } COMPILER2_PRESENT(DerivedPointerTable::clear()); @@ -3656,6 +3684,18 @@ g1_policy()->record_collection_pause_start(start_time_sec, start_used_bytes); + double scan_wait_start = os::elapsedTime(); + // We have to wait until the CM threads finish scanning the + // root regions as it's the only way to ensure that all the + // objects on them have been correctly scanned before we start + // moving them during the GC. + bool waited = _cm->root_regions()->wait_until_scan_finished(); + if (waited) { + double scan_wait_end = os::elapsedTime(); + double wait_time_ms = (scan_wait_end - scan_wait_start) * 1000.0; + g1_policy()->record_root_region_scan_wait_time(wait_time_ms); + } + #if YOUNG_LIST_VERBOSE gclog_or_tty->print_cr("\nAfter recording pause start.\nYoung_list:"); _young_list->print(); @@ -3765,16 +3805,14 @@ } if (g1_policy()->during_initial_mark_pause()) { + // We have to do this before we notify the CM threads that + // they can start working to make sure that all the + // appropriate initialization is done on the CM object. concurrent_mark()->checkpointRootsInitialPost(); set_marking_started(); - // CAUTION: after the doConcurrentMark() call below, - // the concurrent marking thread(s) could be running - // concurrently with us. Make sure that anything after - // this point does not assume that we are the only GC thread - // running. Note: of course, the actual marking work will - // not start until the safepoint itself is released in - // ConcurrentGCThread::safepoint_desynchronize(). - doConcurrentMark(); + // Note that we don't actually trigger the CM thread at + // this point. We do that later when we're sure that + // the current thread has completed its logging output. } allocate_dummy_regions(); @@ -3884,6 +3922,15 @@ } } + // The closing of the inner scope, immediately above, will complete + // the PrintGC logging output. The record_collection_pause_end() call + // above will complete the logging output of PrintGCDetails. + // + // It is not yet to safe, however, to tell the concurrent mark to + // start as we have some optional output below. We don't want the + // output from the concurrent mark thread interfering with this + // logging output either. + _hrs.verify_optional(); verify_region_sets_optional(); @@ -3901,6 +3948,21 @@ g1_rem_set()->print_summary_info(); } + // It should now be safe to tell the concurrent mark thread to start + // without its logging output interfering with the logging output + // that came from the pause. + + if (should_start_conc_mark) { + // CAUTION: after the doConcurrentMark() call below, + // the concurrent marking thread(s) could be running + // concurrently with us. Make sure that anything after + // this point does not assume that we are the only GC thread + // running. Note: of course, the actual marking work will + // not start until the safepoint itself is released in + // ConcurrentGCThread::safepoint_desynchronize(). + doConcurrentMark(); + } + return true; } @@ -4162,7 +4224,7 @@ G1ParGCAllocBuffer::G1ParGCAllocBuffer(size_t gclab_word_size) : ParGCAllocBuffer(gclab_word_size), _retired(false) { } -G1ParScanThreadState::G1ParScanThreadState(G1CollectedHeap* g1h, int queue_num) +G1ParScanThreadState::G1ParScanThreadState(G1CollectedHeap* g1h, uint queue_num) : _g1h(g1h), _refs(g1h->task_queue(queue_num)), _dcq(&g1h->dirty_card_queue_set()), @@ -4283,6 +4345,7 @@ G1ParScanThreadState* par_scan_state) : _g1(g1), _g1_rem(_g1->g1_rem_set()), _cm(_g1->concurrent_mark()), _par_scan_state(par_scan_state), + _worker_id(par_scan_state->queue_num()), _during_initial_mark(_g1->g1_policy()->during_initial_mark_pause()), _mark_in_progress(_g1->mark_in_progress()) { } @@ -4294,7 +4357,7 @@ #endif // ASSERT // We know that the object is not moving so it's safe to read its size. - _cm->grayRoot(obj, (size_t) obj->size()); + _cm->grayRoot(obj, (size_t) obj->size(), _worker_id); } void G1ParCopyHelper::mark_forwarded_object(oop from_obj, oop to_obj) { @@ -4316,7 +4379,7 @@ // worker so we cannot trust that its to-space image is // well-formed. So we have to read its size from its from-space // image which we know should not be changing. - _cm->grayRoot(to_obj, (size_t) from_obj->size()); + _cm->grayRoot(to_obj, (size_t) from_obj->size(), _worker_id); } oop G1ParCopyHelper::copy_to_survivor_space(oop old) { @@ -4406,6 +4469,8 @@ assert(barrier != G1BarrierRS || obj != NULL, "Precondition: G1BarrierRS implies obj is non-NULL"); + assert(_worker_id == _par_scan_state->queue_num(), "sanity"); + // here the null check is implicit in the cset_fast_test() test if (_g1->in_cset_fast_test(obj)) { oop forwardee; @@ -4424,7 +4489,7 @@ // When scanning the RS, we only care about objs in CS. if (barrier == G1BarrierRS) { - _par_scan_state->update_rs(_from, p, _par_scan_state->queue_num()); + _par_scan_state->update_rs(_from, p, _worker_id); } } else { // The object is not in collection set. If we're a root scanning @@ -4436,7 +4501,7 @@ } if (barrier == G1BarrierEvac && obj != NULL) { - _par_scan_state->update_rs(_from, p, _par_scan_state->queue_num()); + _par_scan_state->update_rs(_from, p, _worker_id); } if (do_gen_barrier && obj != NULL) { @@ -5666,16 +5731,6 @@ // And the region is empty. assert(!used_mr.is_empty(), "Should not have empty regions in a CS."); - - // If marking is in progress then clear any objects marked in - // the current region. Note mark_in_progress() returns false, - // even during an initial mark pause, until the set_marking_started() - // call which takes place later in the pause. - if (mark_in_progress()) { - assert(!g1_policy()->during_initial_mark_pause(), "sanity"); - _cm->nextMarkBitMap()->clearRange(used_mr); - } - free_region(cur, &pre_used, &local_free_list, false /* par */); } else { cur->uninstall_surv_rate_group(); @@ -5742,8 +5797,9 @@ } void G1CollectedHeap::reset_free_regions_coming() { + assert(free_regions_coming(), "pre-condition"); + { - assert(free_regions_coming(), "pre-condition"); MutexLockerEx x(SecondaryFreeList_lock, Mutex::_no_safepoint_check_flag); _free_regions_coming = false; SecondaryFreeList_lock->notify_all(); diff -r f920d3637c9f -r 4c63e7c939aa hotspot/src/share/vm/gc_implementation/g1/g1CollectedHeap.hpp --- a/hotspot/src/share/vm/gc_implementation/g1/g1CollectedHeap.hpp Thu Jan 26 18:23:14 2012 -0800 +++ b/hotspot/src/share/vm/gc_implementation/g1/g1CollectedHeap.hpp Wed Jul 05 18:01:11 2017 +0200 @@ -355,6 +355,7 @@ // explicitly started if: // (a) cause == _gc_locker and +GCLockerInvokesConcurrent, or // (b) cause == _java_lang_system_gc and +ExplicitGCInvokesConcurrent. + // (c) cause == _g1_humongous_allocation bool should_do_concurrent_full_gc(GCCause::Cause cause); // Keeps track of how many "full collections" (i.e., Full GCs or @@ -1172,6 +1173,10 @@ _old_set.remove(hr); } + size_t non_young_capacity_bytes() { + return _old_set.total_capacity_bytes() + _humongous_set.total_capacity_bytes(); + } + void set_free_regions_coming(); void reset_free_regions_coming(); bool free_regions_coming() { return _free_regions_coming; } @@ -1904,7 +1909,7 @@ G1ParScanPartialArrayClosure* _partial_scan_cl; int _hash_seed; - int _queue_num; + uint _queue_num; size_t _term_attempts; @@ -1948,7 +1953,7 @@ } public: - G1ParScanThreadState(G1CollectedHeap* g1h, int queue_num); + G1ParScanThreadState(G1CollectedHeap* g1h, uint queue_num); ~G1ParScanThreadState() { FREE_C_HEAP_ARRAY(size_t, _surviving_young_words_base); @@ -2040,7 +2045,7 @@ } int* hash_seed() { return &_hash_seed; } - int queue_num() { return _queue_num; } + uint queue_num() { return _queue_num; } size_t term_attempts() const { return _term_attempts; } void note_term_attempt() { _term_attempts++; } diff -r f920d3637c9f -r 4c63e7c939aa hotspot/src/share/vm/gc_implementation/g1/g1CollectorPolicy.cpp --- a/hotspot/src/share/vm/gc_implementation/g1/g1CollectorPolicy.cpp Thu Jan 26 18:23:14 2012 -0800 +++ b/hotspot/src/share/vm/gc_implementation/g1/g1CollectorPolicy.cpp Wed Jul 05 18:01:11 2017 +0200 @@ -141,6 +141,7 @@ _cur_clear_ct_time_ms(0.0), _mark_closure_time_ms(0.0), + _root_region_scan_wait_time_ms(0.0), _cur_ref_proc_time_ms(0.0), _cur_ref_enq_time_ms(0.0), @@ -213,8 +214,6 @@ _survivor_bytes_before_gc(0), _capacity_before_gc(0), - _prev_collection_pause_used_at_end_bytes(0), - _eden_cset_region_length(0), _survivor_cset_region_length(0), _old_cset_region_length(0), @@ -905,19 +904,10 @@ gclog_or_tty->print(" (%s)", gcs_are_young() ? "young" : "mixed"); } - if (!during_initial_mark_pause()) { - // We only need to do this here as the policy will only be applied - // to the GC we're about to start. so, no point is calculating this - // every time we calculate / recalculate the target young length. - update_survivors_policy(); - } else { - // The marking phase has a "we only copy implicitly live - // objects during marking" invariant. The easiest way to ensure it - // holds is not to allocate any survivor regions and tenure all - // objects. In the future we might change this and handle survivor - // regions specially during marking. - tenure_all_objects(); - } + // We only need to do this here as the policy will only be applied + // to the GC we're about to start. so, no point is calculating this + // every time we calculate / recalculate the target young length. + update_survivors_policy(); assert(_g1->used() == _g1->recalculate_used(), err_msg("sanity, used: "SIZE_FORMAT" recalculate_used: "SIZE_FORMAT, @@ -969,6 +959,9 @@ // This is initialized to zero here and is set during // the evacuation pause if marking is in progress. _cur_satb_drain_time_ms = 0.0; + // This is initialized to zero here and is set during the evacuation + // pause if we actually waited for the root region scanning to finish. + _root_region_scan_wait_time_ms = 0.0; _last_gc_was_young = false; @@ -1140,6 +1133,50 @@ return ret; } +bool G1CollectorPolicy::need_to_start_conc_mark(const char* source, size_t alloc_word_size) { + if (_g1->concurrent_mark()->cmThread()->during_cycle()) { + return false; + } + + size_t marking_initiating_used_threshold = + (_g1->capacity() / 100) * InitiatingHeapOccupancyPercent; + size_t cur_used_bytes = _g1->non_young_capacity_bytes(); + size_t alloc_byte_size = alloc_word_size * HeapWordSize; + + if ((cur_used_bytes + alloc_byte_size) > marking_initiating_used_threshold) { + if (gcs_are_young()) { + ergo_verbose5(ErgoConcCycles, + "request concurrent cycle initiation", + ergo_format_reason("occupancy higher than threshold") + ergo_format_byte("occupancy") + ergo_format_byte("allocation request") + ergo_format_byte_perc("threshold") + ergo_format_str("source"), + cur_used_bytes, + alloc_byte_size, + marking_initiating_used_threshold, + (double) InitiatingHeapOccupancyPercent, + source); + return true; + } else { + ergo_verbose5(ErgoConcCycles, + "do not request concurrent cycle initiation", + ergo_format_reason("still doing mixed collections") + ergo_format_byte("occupancy") + ergo_format_byte("allocation request") + ergo_format_byte_perc("threshold") + ergo_format_str("source"), + cur_used_bytes, + alloc_byte_size, + marking_initiating_used_threshold, + (double) InitiatingHeapOccupancyPercent, + source); + } + } + + return false; +} + // Anything below that is considered to be zero #define MIN_TIMER_GRANULARITY 0.0000001 @@ -1166,44 +1203,16 @@ #endif // PRODUCT last_pause_included_initial_mark = during_initial_mark_pause(); - if (last_pause_included_initial_mark) + if (last_pause_included_initial_mark) { record_concurrent_mark_init_end(0.0); - - size_t marking_initiating_used_threshold = - (_g1->capacity() / 100) * InitiatingHeapOccupancyPercent; - - if (!_g1->mark_in_progress() && !_last_young_gc) { - assert(!last_pause_included_initial_mark, "invariant"); - if (cur_used_bytes > marking_initiating_used_threshold) { - if (cur_used_bytes > _prev_collection_pause_used_at_end_bytes) { - assert(!during_initial_mark_pause(), "we should not see this here"); - - ergo_verbose3(ErgoConcCycles, - "request concurrent cycle initiation", - ergo_format_reason("occupancy higher than threshold") - ergo_format_byte("occupancy") - ergo_format_byte_perc("threshold"), - cur_used_bytes, - marking_initiating_used_threshold, - (double) InitiatingHeapOccupancyPercent); - - // Note: this might have already been set, if during the last - // pause we decided to start a cycle but at the beginning of - // this pause we decided to postpone it. That's OK. - set_initiate_conc_mark_if_possible(); - } else { - ergo_verbose2(ErgoConcCycles, - "do not request concurrent cycle initiation", - ergo_format_reason("occupancy lower than previous occupancy") - ergo_format_byte("occupancy") - ergo_format_byte("previous occupancy"), - cur_used_bytes, - _prev_collection_pause_used_at_end_bytes); - } - } } - _prev_collection_pause_used_at_end_bytes = cur_used_bytes; + if (!_last_young_gc && need_to_start_conc_mark("end of GC")) { + // Note: this might have already been set, if during the last + // pause we decided to start a cycle but at the beginning of + // this pause we decided to postpone it. That's OK. + set_initiate_conc_mark_if_possible(); + } _mmu_tracker->add_pause(end_time_sec - elapsed_ms/1000.0, end_time_sec, false); @@ -1257,6 +1266,10 @@ // is in progress. other_time_ms -= _cur_satb_drain_time_ms; + // Subtract the root region scanning wait time. It's initialized to + // zero at the start of the pause. + other_time_ms -= _root_region_scan_wait_time_ms; + if (parallel) { other_time_ms -= _cur_collection_par_time_ms; } else { @@ -1289,6 +1302,8 @@ // each other. Therefore we unconditionally record the SATB drain // time - even if it's zero. body_summary->record_satb_drain_time_ms(_cur_satb_drain_time_ms); + body_summary->record_root_region_scan_wait_time_ms( + _root_region_scan_wait_time_ms); body_summary->record_ext_root_scan_time_ms(ext_root_scan_time); body_summary->record_satb_filtering_time_ms(satb_filtering_time); @@ -1385,6 +1400,9 @@ (last_pause_included_initial_mark) ? " (initial-mark)" : "", elapsed_ms / 1000.0); + if (_root_region_scan_wait_time_ms > 0.0) { + print_stats(1, "Root Region Scan Waiting", _root_region_scan_wait_time_ms); + } if (parallel) { print_stats(1, "Parallel Time", _cur_collection_par_time_ms); print_par_stats(2, "GC Worker Start", _par_last_gc_worker_start_times_ms); @@ -1988,6 +2006,7 @@ if (summary->get_total_seq()->num() > 0) { print_summary_sd(0, "Evacuation Pauses", summary->get_total_seq()); if (body_summary != NULL) { + print_summary(1, "Root Region Scan Wait", body_summary->get_root_region_scan_wait_seq()); if (parallel) { print_summary(1, "Parallel Time", body_summary->get_parallel_seq()); print_summary(2, "Ext Root Scanning", body_summary->get_ext_root_scan_seq()); @@ -2029,15 +2048,17 @@ // parallel NumberSeq* other_parts[] = { body_summary->get_satb_drain_seq(), + body_summary->get_root_region_scan_wait_seq(), body_summary->get_parallel_seq(), body_summary->get_clear_ct_seq() }; calc_other_times_ms = NumberSeq(summary->get_total_seq(), - 3, other_parts); + 4, other_parts); } else { // serial NumberSeq* other_parts[] = { body_summary->get_satb_drain_seq(), + body_summary->get_root_region_scan_wait_seq(), body_summary->get_update_rs_seq(), body_summary->get_ext_root_scan_seq(), body_summary->get_satb_filtering_seq(), @@ -2045,7 +2066,7 @@ body_summary->get_obj_copy_seq() }; calc_other_times_ms = NumberSeq(summary->get_total_seq(), - 6, other_parts); + 7, other_parts); } check_other_times(1, summary->get_other_seq(), &calc_other_times_ms); } diff -r f920d3637c9f -r 4c63e7c939aa hotspot/src/share/vm/gc_implementation/g1/g1CollectorPolicy.hpp --- a/hotspot/src/share/vm/gc_implementation/g1/g1CollectorPolicy.hpp Thu Jan 26 18:23:14 2012 -0800 +++ b/hotspot/src/share/vm/gc_implementation/g1/g1CollectorPolicy.hpp Wed Jul 05 18:01:11 2017 +0200 @@ -65,6 +65,7 @@ class MainBodySummary: public CHeapObj { define_num_seq(satb_drain) // optional + define_num_seq(root_region_scan_wait) define_num_seq(parallel) // parallel only define_num_seq(ext_root_scan) define_num_seq(satb_filtering) @@ -177,7 +178,6 @@ double _cur_collection_start_sec; size_t _cur_collection_pause_used_at_start_bytes; size_t _cur_collection_pause_used_regions_at_start; - size_t _prev_collection_pause_used_at_end_bytes; double _cur_collection_par_time_ms; double _cur_satb_drain_time_ms; double _cur_clear_ct_time_ms; @@ -716,6 +716,7 @@ double _mark_remark_start_sec; double _mark_cleanup_start_sec; double _mark_closure_time_ms; + double _root_region_scan_wait_time_ms; // Update the young list target length either by setting it to the // desired fixed value or by calculating it using G1's pause @@ -800,6 +801,8 @@ GenRemSet::Name rem_set_name() { return GenRemSet::CardTable; } + bool need_to_start_conc_mark(const char* source, size_t alloc_word_size = 0); + // Update the heuristic info to record a collection pause of the given // start time, where the given number of bytes were used at the start. // This may involve changing the desired size of a collection set. @@ -816,6 +819,10 @@ _mark_closure_time_ms = mark_closure_time_ms; } + void record_root_region_scan_wait_time(double time_ms) { + _root_region_scan_wait_time_ms = time_ms; + } + void record_concurrent_mark_remark_start(); void record_concurrent_mark_remark_end(); @@ -1146,11 +1153,6 @@ _survivor_surv_rate_group->stop_adding_regions(); } - void tenure_all_objects() { - _max_survivor_regions = 0; - _tenuring_threshold = 0; - } - void record_survivor_regions(size_t regions, HeapRegion* head, HeapRegion* tail) { diff -r f920d3637c9f -r 4c63e7c939aa hotspot/src/share/vm/gc_implementation/g1/g1EvacFailure.hpp --- a/hotspot/src/share/vm/gc_implementation/g1/g1EvacFailure.hpp Thu Jan 26 18:23:14 2012 -0800 +++ b/hotspot/src/share/vm/gc_implementation/g1/g1EvacFailure.hpp Wed Jul 05 18:01:11 2017 +0200 @@ -70,16 +70,20 @@ OopsInHeapRegionClosure *_update_rset_cl; bool _during_initial_mark; bool _during_conc_mark; + uint _worker_id; + public: RemoveSelfForwardPtrObjClosure(G1CollectedHeap* g1, ConcurrentMark* cm, HeapRegion* hr, OopsInHeapRegionClosure* update_rset_cl, bool during_initial_mark, - bool during_conc_mark) : + bool during_conc_mark, + uint worker_id) : _g1(g1), _cm(cm), _hr(hr), _marked_bytes(0), _update_rset_cl(update_rset_cl), _during_initial_mark(during_initial_mark), - _during_conc_mark(during_conc_mark) { } + _during_conc_mark(during_conc_mark), + _worker_id(worker_id) { } size_t marked_bytes() { return _marked_bytes; } @@ -123,7 +127,7 @@ // explicitly and all objects in the CSet are considered // (implicitly) live. So, we won't mark them explicitly and // we'll leave them over NTAMS. - _cm->markNext(obj); + _cm->grayRoot(obj, obj_size, _worker_id, _hr); } _marked_bytes += (obj_size * HeapWordSize); obj->set_mark(markOopDesc::prototype()); @@ -155,12 +159,14 @@ G1CollectedHeap* _g1h; ConcurrentMark* _cm; OopsInHeapRegionClosure *_update_rset_cl; + uint _worker_id; public: RemoveSelfForwardPtrHRClosure(G1CollectedHeap* g1h, - OopsInHeapRegionClosure* update_rset_cl) : + OopsInHeapRegionClosure* update_rset_cl, + uint worker_id) : _g1h(g1h), _update_rset_cl(update_rset_cl), - _cm(_g1h->concurrent_mark()) { } + _worker_id(worker_id), _cm(_g1h->concurrent_mark()) { } bool doHeapRegion(HeapRegion *hr) { bool during_initial_mark = _g1h->g1_policy()->during_initial_mark_pause(); @@ -173,7 +179,8 @@ if (hr->evacuation_failed()) { RemoveSelfForwardPtrObjClosure rspc(_g1h, _cm, hr, _update_rset_cl, during_initial_mark, - during_conc_mark); + during_conc_mark, + _worker_id); MemRegion mr(hr->bottom(), hr->end()); // We'll recreate the prev marking info so we'll first clear @@ -226,7 +233,7 @@ update_rset_cl = &immediate_update; } - RemoveSelfForwardPtrHRClosure rsfp_cl(_g1h, update_rset_cl); + RemoveSelfForwardPtrHRClosure rsfp_cl(_g1h, update_rset_cl, worker_id); HeapRegion* hr = _g1h->start_cset_region_for_worker(worker_id); _g1h->collection_set_iterate_from(hr, &rsfp_cl); diff -r f920d3637c9f -r 4c63e7c939aa hotspot/src/share/vm/gc_implementation/g1/g1MonitoringSupport.hpp --- a/hotspot/src/share/vm/gc_implementation/g1/g1MonitoringSupport.hpp Thu Jan 26 18:23:14 2012 -0800 +++ b/hotspot/src/share/vm/gc_implementation/g1/g1MonitoringSupport.hpp Wed Jul 05 18:01:11 2017 +0200 @@ -89,16 +89,15 @@ // // * Min Capacity // -// We set this to 0 for all spaces. We could consider setting the old -// min capacity to the min capacity of the heap (see 7078465). +// We set this to 0 for all spaces. // // * Max Capacity // // For jstat, we set the max capacity of all spaces to heap_capacity, -// given that we don't always have a reasonably upper bound on how big -// each space can grow. For the memory pools, we actually make the max -// capacity undefined. We could consider setting the old max capacity -// to the max capacity of the heap (see 7078465). +// given that we don't always have a reasonable upper bound on how big +// each space can grow. For the memory pools, we make the max +// capacity undefined with the exception of the old memory pool for +// which we make the max capacity same as the max heap capacity. // // If we had more accurate occupancy / capacity information per // region set the above calculations would be greatly simplified and diff -r f920d3637c9f -r 4c63e7c939aa hotspot/src/share/vm/gc_implementation/g1/g1OopClosures.hpp --- a/hotspot/src/share/vm/gc_implementation/g1/g1OopClosures.hpp Thu Jan 26 18:23:14 2012 -0800 +++ b/hotspot/src/share/vm/gc_implementation/g1/g1OopClosures.hpp Wed Jul 05 18:01:11 2017 +0200 @@ -51,6 +51,7 @@ G1RemSet* _g1_rem; ConcurrentMark* _cm; G1ParScanThreadState* _par_scan_state; + uint _worker_id; bool _during_initial_mark; bool _mark_in_progress; public: @@ -219,6 +220,7 @@ // Closure for iterating over object fields during concurrent marking class G1CMOopClosure : public OopClosure { +private: G1CollectedHeap* _g1h; ConcurrentMark* _cm; CMTask* _task; @@ -229,4 +231,92 @@ virtual void do_oop(narrowOop* p) { do_oop_nv(p); } }; +// Closure to scan the root regions during concurrent marking +class G1RootRegionScanClosure : public OopClosure { +private: + G1CollectedHeap* _g1h; + ConcurrentMark* _cm; + uint _worker_id; +public: + G1RootRegionScanClosure(G1CollectedHeap* g1h, ConcurrentMark* cm, + uint worker_id) : + _g1h(g1h), _cm(cm), _worker_id(worker_id) { } + template void do_oop_nv(T* p); + virtual void do_oop( oop* p) { do_oop_nv(p); } + virtual void do_oop(narrowOop* p) { do_oop_nv(p); } +}; + +// Closure that applies the given two closures in sequence. +// Used by the RSet refinement code (when updating RSets +// during an evacuation pause) to record cards containing +// pointers into the collection set. + +class G1Mux2Closure : public OopClosure { + OopClosure* _c1; + OopClosure* _c2; +public: + G1Mux2Closure(OopClosure *c1, OopClosure *c2); + template void do_oop_nv(T* p); + virtual void do_oop(oop* p) { do_oop_nv(p); } + virtual void do_oop(narrowOop* p) { do_oop_nv(p); } +}; + +// A closure that returns true if it is actually applied +// to a reference + +class G1TriggerClosure : public OopClosure { + bool _triggered; +public: + G1TriggerClosure(); + bool triggered() const { return _triggered; } + template void do_oop_nv(T* p); + virtual void do_oop(oop* p) { do_oop_nv(p); } + virtual void do_oop(narrowOop* p) { do_oop_nv(p); } +}; + +// A closure which uses a triggering closure to determine +// whether to apply an oop closure. + +class G1InvokeIfNotTriggeredClosure: public OopClosure { + G1TriggerClosure* _trigger_cl; + OopClosure* _oop_cl; +public: + G1InvokeIfNotTriggeredClosure(G1TriggerClosure* t, OopClosure* oc); + template void do_oop_nv(T* p); + virtual void do_oop(oop* p) { do_oop_nv(p); } + virtual void do_oop(narrowOop* p) { do_oop_nv(p); } +}; + +class G1UpdateRSOrPushRefOopClosure: public OopClosure { + G1CollectedHeap* _g1; + G1RemSet* _g1_rem_set; + HeapRegion* _from; + OopsInHeapRegionClosure* _push_ref_cl; + bool _record_refs_into_cset; + int _worker_i; + +public: + G1UpdateRSOrPushRefOopClosure(G1CollectedHeap* g1h, + G1RemSet* rs, + OopsInHeapRegionClosure* push_ref_cl, + bool record_refs_into_cset, + int worker_i = 0); + + void set_from(HeapRegion* from) { + assert(from != NULL, "from region must be non-NULL"); + _from = from; + } + + bool self_forwarded(oop obj) { + bool result = (obj->is_forwarded() && (obj->forwardee()== obj)); + return result; + } + + bool apply_to_weak_ref_discovered_field() { return true; } + + template void do_oop_nv(T* p); + virtual void do_oop(narrowOop* p) { do_oop_nv(p); } + virtual void do_oop(oop* p) { do_oop_nv(p); } +}; + #endif // SHARE_VM_GC_IMPLEMENTATION_G1_G1OOPCLOSURES_HPP diff -r f920d3637c9f -r 4c63e7c939aa hotspot/src/share/vm/gc_implementation/g1/g1OopClosures.inline.hpp --- a/hotspot/src/share/vm/gc_implementation/g1/g1OopClosures.inline.hpp Thu Jan 26 18:23:14 2012 -0800 +++ b/hotspot/src/share/vm/gc_implementation/g1/g1OopClosures.inline.hpp Wed Jul 05 18:01:11 2017 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2012, 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 @@ -39,7 +39,8 @@ // perf-critical inner loop. #define FILTERINTOCSCLOSURE_DOHISTOGRAMCOUNT 0 -template inline void FilterIntoCSClosure::do_oop_nv(T* p) { +template +inline void FilterIntoCSClosure::do_oop_nv(T* p) { T heap_oop = oopDesc::load_heap_oop(p); if (!oopDesc::is_null(heap_oop) && _g1->obj_in_cs(oopDesc::decode_heap_oop_not_null(heap_oop))) { @@ -53,7 +54,8 @@ #define FILTEROUTOFREGIONCLOSURE_DOHISTOGRAMCOUNT 0 -template inline void FilterOutOfRegionClosure::do_oop_nv(T* p) { +template +inline void FilterOutOfRegionClosure::do_oop_nv(T* p) { T heap_oop = oopDesc::load_heap_oop(p); if (!oopDesc::is_null(heap_oop)) { HeapWord* obj_hw = (HeapWord*)oopDesc::decode_heap_oop_not_null(heap_oop); @@ -67,7 +69,8 @@ } // This closure is applied to the fields of the objects that have just been copied. -template inline void G1ParScanClosure::do_oop_nv(T* p) { +template +inline void G1ParScanClosure::do_oop_nv(T* p) { T heap_oop = oopDesc::load_heap_oop(p); if (!oopDesc::is_null(heap_oop)) { @@ -96,7 +99,8 @@ } } -template inline void G1ParPushHeapRSClosure::do_oop_nv(T* p) { +template +inline void G1ParPushHeapRSClosure::do_oop_nv(T* p) { T heap_oop = oopDesc::load_heap_oop(p); if (!oopDesc::is_null(heap_oop)) { @@ -111,7 +115,8 @@ } } -template inline void G1CMOopClosure::do_oop_nv(T* p) { +template +inline void G1CMOopClosure::do_oop_nv(T* p) { assert(_g1h->is_in_g1_reserved((HeapWord*) p), "invariant"); assert(!_g1h->is_on_master_free_list( _g1h->heap_region_containing((HeapWord*) p)), "invariant"); @@ -125,4 +130,97 @@ _task->deal_with_reference(obj); } +template +inline void G1RootRegionScanClosure::do_oop_nv(T* p) { + T heap_oop = oopDesc::load_heap_oop(p); + if (!oopDesc::is_null(heap_oop)) { + oop obj = oopDesc::decode_heap_oop_not_null(heap_oop); + HeapRegion* hr = _g1h->heap_region_containing((HeapWord*) obj); + if (hr != NULL) { + _cm->grayRoot(obj, obj->size(), _worker_id, hr); + } + } +} + +template +inline void G1Mux2Closure::do_oop_nv(T* p) { + // Apply first closure; then apply the second. + _c1->do_oop(p); + _c2->do_oop(p); +} + +template +inline void G1TriggerClosure::do_oop_nv(T* p) { + // Record that this closure was actually applied (triggered). + _triggered = true; +} + +template +inline void G1InvokeIfNotTriggeredClosure::do_oop_nv(T* p) { + if (!_trigger_cl->triggered()) { + _oop_cl->do_oop(p); + } +} + +template +inline void G1UpdateRSOrPushRefOopClosure::do_oop_nv(T* p) { + oop obj = oopDesc::load_decode_heap_oop(p); +#ifdef ASSERT + // can't do because of races + // assert(obj == NULL || obj->is_oop(), "expected an oop"); + + // Do the safe subset of is_oop + if (obj != NULL) { +#ifdef CHECK_UNHANDLED_OOPS + oopDesc* o = obj.obj(); +#else + oopDesc* o = obj; +#endif // CHECK_UNHANDLED_OOPS + assert((intptr_t)o % MinObjAlignmentInBytes == 0, "not oop aligned"); + assert(Universe::heap()->is_in_reserved(obj), "must be in heap"); + } +#endif // ASSERT + + assert(_from != NULL, "from region must be non-NULL"); + + HeapRegion* to = _g1->heap_region_containing(obj); + if (to != NULL && _from != to) { + // The _record_refs_into_cset flag is true during the RSet + // updating part of an evacuation pause. It is false at all + // other times: + // * rebuilding the rembered sets after a full GC + // * during concurrent refinement. + // * updating the remembered sets of regions in the collection + // set in the event of an evacuation failure (when deferred + // updates are enabled). + + if (_record_refs_into_cset && to->in_collection_set()) { + // We are recording references that point into the collection + // set and this particular reference does exactly that... + // If the referenced object has already been forwarded + // to itself, we are handling an evacuation failure and + // we have already visited/tried to copy this object + // there is no need to retry. + if (!self_forwarded(obj)) { + assert(_push_ref_cl != NULL, "should not be null"); + // Push the reference in the refs queue of the G1ParScanThreadState + // instance for this worker thread. + _push_ref_cl->do_oop(p); + } + + // Deferred updates to the CSet are either discarded (in the normal case), + // or processed (if an evacuation failure occurs) at the end + // of the collection. + // See G1RemSet::cleanup_after_oops_into_collection_set_do(). + } else { + // We either don't care about pushing references that point into the + // collection set (i.e. we're not during an evacuation pause) _or_ + // the reference doesn't point into the collection set. Either way + // we add the reference directly to the RSet of the region containing + // the referenced object. + _g1_rem_set->par_write_ref(_from, p, _worker_i); + } + } +} + #endif // SHARE_VM_GC_IMPLEMENTATION_G1_G1OOPCLOSURES_INLINE_HPP diff -r f920d3637c9f -r 4c63e7c939aa hotspot/src/share/vm/gc_implementation/g1/g1RemSet.cpp --- a/hotspot/src/share/vm/gc_implementation/g1/g1RemSet.cpp Thu Jan 26 18:23:14 2012 -0800 +++ b/hotspot/src/share/vm/gc_implementation/g1/g1RemSet.cpp Wed Jul 05 18:01:11 2017 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2012, 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 @@ -569,40 +569,26 @@ static IntHistogram out_of_histo(50, 50); -class TriggerClosure : public OopClosure { - bool _trigger; -public: - TriggerClosure() : _trigger(false) { } - bool value() const { return _trigger; } - template void do_oop_nv(T* p) { _trigger = true; } - virtual void do_oop(oop* p) { do_oop_nv(p); } - virtual void do_oop(narrowOop* p) { do_oop_nv(p); } -}; + +G1TriggerClosure::G1TriggerClosure() : + _triggered(false) { } + +G1InvokeIfNotTriggeredClosure::G1InvokeIfNotTriggeredClosure(G1TriggerClosure* t_cl, + OopClosure* oop_cl) : + _trigger_cl(t_cl), _oop_cl(oop_cl) { } -class InvokeIfNotTriggeredClosure: public OopClosure { - TriggerClosure* _t; - OopClosure* _oc; -public: - InvokeIfNotTriggeredClosure(TriggerClosure* t, OopClosure* oc): - _t(t), _oc(oc) { } - template void do_oop_nv(T* p) { - if (!_t->value()) _oc->do_oop(p); - } - virtual void do_oop(oop* p) { do_oop_nv(p); } - virtual void do_oop(narrowOop* p) { do_oop_nv(p); } -}; +G1Mux2Closure::G1Mux2Closure(OopClosure *c1, OopClosure *c2) : + _c1(c1), _c2(c2) { } -class Mux2Closure : public OopClosure { - OopClosure* _c1; - OopClosure* _c2; -public: - Mux2Closure(OopClosure *c1, OopClosure *c2) : _c1(c1), _c2(c2) { } - template void do_oop_nv(T* p) { - _c1->do_oop(p); _c2->do_oop(p); - } - virtual void do_oop(oop* p) { do_oop_nv(p); } - virtual void do_oop(narrowOop* p) { do_oop_nv(p); } -}; +G1UpdateRSOrPushRefOopClosure:: +G1UpdateRSOrPushRefOopClosure(G1CollectedHeap* g1h, + G1RemSet* rs, + OopsInHeapRegionClosure* push_ref_cl, + bool record_refs_into_cset, + int worker_i) : + _g1(g1h), _g1_rem_set(rs), _from(NULL), + _record_refs_into_cset(record_refs_into_cset), + _push_ref_cl(push_ref_cl), _worker_i(worker_i) { } bool G1RemSet::concurrentRefineOneCard_impl(jbyte* card_ptr, int worker_i, bool check_for_refs_into_cset) { @@ -629,17 +615,17 @@ assert((size_t)worker_i < n_workers(), "index of worker larger than _cset_rs_update_cl[].length"); oops_in_heap_closure = _cset_rs_update_cl[worker_i]; } - UpdateRSOrPushRefOopClosure update_rs_oop_cl(_g1, - _g1->g1_rem_set(), - oops_in_heap_closure, - check_for_refs_into_cset, - worker_i); + G1UpdateRSOrPushRefOopClosure update_rs_oop_cl(_g1, + _g1->g1_rem_set(), + oops_in_heap_closure, + check_for_refs_into_cset, + worker_i); update_rs_oop_cl.set_from(r); - TriggerClosure trigger_cl; + G1TriggerClosure trigger_cl; FilterIntoCSClosure into_cs_cl(NULL, _g1, &trigger_cl); - InvokeIfNotTriggeredClosure invoke_cl(&trigger_cl, &into_cs_cl); - Mux2Closure mux(&invoke_cl, &update_rs_oop_cl); + G1InvokeIfNotTriggeredClosure invoke_cl(&trigger_cl, &into_cs_cl); + G1Mux2Closure mux(&invoke_cl, &update_rs_oop_cl); FilterOutOfRegionClosure filter_then_update_rs_oop_cl(r, (check_for_refs_into_cset ? @@ -688,7 +674,7 @@ _conc_refine_cards++; } - return trigger_cl.value(); + return trigger_cl.triggered(); } bool G1RemSet::concurrentRefineOneCard(jbyte* card_ptr, int worker_i, diff -r f920d3637c9f -r 4c63e7c939aa hotspot/src/share/vm/gc_implementation/g1/g1RemSet.hpp --- a/hotspot/src/share/vm/gc_implementation/g1/g1RemSet.hpp Thu Jan 26 18:23:14 2012 -0800 +++ b/hotspot/src/share/vm/gc_implementation/g1/g1RemSet.hpp Wed Jul 05 18:01:11 2017 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2012, 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 @@ -191,44 +191,5 @@ virtual void do_oop( oop* p) { do_oop_work(p); } }; -class UpdateRSOrPushRefOopClosure: public OopClosure { - G1CollectedHeap* _g1; - G1RemSet* _g1_rem_set; - HeapRegion* _from; - OopsInHeapRegionClosure* _push_ref_cl; - bool _record_refs_into_cset; - int _worker_i; - - template void do_oop_work(T* p); - -public: - UpdateRSOrPushRefOopClosure(G1CollectedHeap* g1h, - G1RemSet* rs, - OopsInHeapRegionClosure* push_ref_cl, - bool record_refs_into_cset, - int worker_i = 0) : - _g1(g1h), - _g1_rem_set(rs), - _from(NULL), - _record_refs_into_cset(record_refs_into_cset), - _push_ref_cl(push_ref_cl), - _worker_i(worker_i) { } - - void set_from(HeapRegion* from) { - assert(from != NULL, "from region must be non-NULL"); - _from = from; - } - - bool self_forwarded(oop obj) { - bool result = (obj->is_forwarded() && (obj->forwardee()== obj)); - return result; - } - - virtual void do_oop(narrowOop* p) { do_oop_work(p); } - virtual void do_oop(oop* p) { do_oop_work(p); } - - bool apply_to_weak_ref_discovered_field() { return true; } -}; - #endif // SHARE_VM_GC_IMPLEMENTATION_G1_G1REMSET_HPP diff -r f920d3637c9f -r 4c63e7c939aa hotspot/src/share/vm/gc_implementation/g1/g1RemSet.inline.hpp --- a/hotspot/src/share/vm/gc_implementation/g1/g1RemSet.inline.hpp Thu Jan 26 18:23:14 2012 -0800 +++ b/hotspot/src/share/vm/gc_implementation/g1/g1RemSet.inline.hpp Wed Jul 05 18:01:11 2017 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2012, 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 @@ -85,66 +85,4 @@ } } -template -inline void UpdateRSOrPushRefOopClosure::do_oop_work(T* p) { - oop obj = oopDesc::load_decode_heap_oop(p); -#ifdef ASSERT - // can't do because of races - // assert(obj == NULL || obj->is_oop(), "expected an oop"); - - // Do the safe subset of is_oop - if (obj != NULL) { -#ifdef CHECK_UNHANDLED_OOPS - oopDesc* o = obj.obj(); -#else - oopDesc* o = obj; -#endif // CHECK_UNHANDLED_OOPS - assert((intptr_t)o % MinObjAlignmentInBytes == 0, "not oop aligned"); - assert(Universe::heap()->is_in_reserved(obj), "must be in heap"); - } -#endif // ASSERT - - assert(_from != NULL, "from region must be non-NULL"); - - HeapRegion* to = _g1->heap_region_containing(obj); - if (to != NULL && _from != to) { - // The _record_refs_into_cset flag is true during the RSet - // updating part of an evacuation pause. It is false at all - // other times: - // * rebuilding the rembered sets after a full GC - // * during concurrent refinement. - // * updating the remembered sets of regions in the collection - // set in the event of an evacuation failure (when deferred - // updates are enabled). - - if (_record_refs_into_cset && to->in_collection_set()) { - // We are recording references that point into the collection - // set and this particular reference does exactly that... - // If the referenced object has already been forwarded - // to itself, we are handling an evacuation failure and - // we have already visited/tried to copy this object - // there is no need to retry. - if (!self_forwarded(obj)) { - assert(_push_ref_cl != NULL, "should not be null"); - // Push the reference in the refs queue of the G1ParScanThreadState - // instance for this worker thread. - _push_ref_cl->do_oop(p); - } - - // Deferred updates to the CSet are either discarded (in the normal case), - // or processed (if an evacuation failure occurs) at the end - // of the collection. - // See G1RemSet::cleanup_after_oops_into_collection_set_do(). - } else { - // We either don't care about pushing references that point into the - // collection set (i.e. we're not during an evacuation pause) _or_ - // the reference doesn't point into the collection set. Either way - // we add the reference directly to the RSet of the region containing - // the referenced object. - _g1_rem_set->par_write_ref(_from, p, _worker_i); - } - } -} - - #endif // SHARE_VM_GC_IMPLEMENTATION_G1_G1REMSET_INLINE_HPP diff -r f920d3637c9f -r 4c63e7c939aa hotspot/src/share/vm/gc_implementation/g1/g1_specialized_oop_closures.hpp --- a/hotspot/src/share/vm/gc_implementation/g1/g1_specialized_oop_closures.hpp Thu Jan 26 18:23:14 2012 -0800 +++ b/hotspot/src/share/vm/gc_implementation/g1/g1_specialized_oop_closures.hpp Wed Jul 05 18:01:11 2017 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2012, 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 @@ -32,12 +32,14 @@ // Forward declarations. enum G1Barrier { - G1BarrierNone, G1BarrierRS, G1BarrierEvac + G1BarrierNone, + G1BarrierRS, + G1BarrierEvac }; -template +template class G1ParCopyClosure; + class G1ParScanClosure; class G1ParPushHeapRSClosure; @@ -46,6 +48,13 @@ class FilterIntoCSClosure; class FilterOutOfRegionClosure; class G1CMOopClosure; +class G1RootRegionScanClosure; + +// Specialized oop closures from g1RemSet.cpp +class G1Mux2Closure; +class G1TriggerClosure; +class G1InvokeIfNotTriggeredClosure; +class G1UpdateRSOrPushRefOopClosure; #ifdef FURTHER_SPECIALIZED_OOP_OOP_ITERATE_CLOSURES #error "FURTHER_SPECIALIZED_OOP_OOP_ITERATE_CLOSURES already defined." @@ -57,7 +66,12 @@ f(G1ParPushHeapRSClosure,_nv) \ f(FilterIntoCSClosure,_nv) \ f(FilterOutOfRegionClosure,_nv) \ - f(G1CMOopClosure,_nv) + f(G1CMOopClosure,_nv) \ + f(G1RootRegionScanClosure,_nv) \ + f(G1Mux2Closure,_nv) \ + f(G1TriggerClosure,_nv) \ + f(G1InvokeIfNotTriggeredClosure,_nv) \ + f(G1UpdateRSOrPushRefOopClosure,_nv) #ifdef FURTHER_SPECIALIZED_SINCE_SAVE_MARKS_CLOSURES #error "FURTHER_SPECIALIZED_SINCE_SAVE_MARKS_CLOSURES already defined." diff -r f920d3637c9f -r 4c63e7c939aa hotspot/src/share/vm/gc_implementation/g1/heapRegion.cpp --- a/hotspot/src/share/vm/gc_implementation/g1/heapRegion.cpp Thu Jan 26 18:23:14 2012 -0800 +++ b/hotspot/src/share/vm/gc_implementation/g1/heapRegion.cpp Wed Jul 05 18:01:11 2017 +0200 @@ -659,7 +659,7 @@ // If we're within a stop-world GC, then we might look at a card in a // GC alloc region that extends onto a GC LAB, which may not be // parseable. Stop such at the "saved_mark" of the region. - if (G1CollectedHeap::heap()->is_gc_active()) { + if (g1h->is_gc_active()) { mr = mr.intersection(used_region_at_save_marks()); } else { mr = mr.intersection(used_region()); @@ -688,53 +688,63 @@ OrderAccess::storeload(); } + // Cache the boundaries of the memory region in some const locals + HeapWord* const start = mr.start(); + HeapWord* const end = mr.end(); + // We used to use "block_start_careful" here. But we're actually happy // to update the BOT while we do this... - HeapWord* cur = block_start(mr.start()); - assert(cur <= mr.start(), "Postcondition"); + HeapWord* cur = block_start(start); + assert(cur <= start, "Postcondition"); + + oop obj; - while (cur <= mr.start()) { - if (oop(cur)->klass_or_null() == NULL) { + HeapWord* next = cur; + while (next <= start) { + cur = next; + obj = oop(cur); + if (obj->klass_or_null() == NULL) { // Ran into an unparseable point. return cur; } // Otherwise... - int sz = oop(cur)->size(); - if (cur + sz > mr.start()) break; - // Otherwise, go on. - cur = cur + sz; + next = (cur + obj->size()); } - oop obj; - obj = oop(cur); - // If we finish this loop... - assert(cur <= mr.start() - && obj->klass_or_null() != NULL - && cur + obj->size() > mr.start(), + + // If we finish the above loop...We have a parseable object that + // begins on or before the start of the memory region, and ends + // inside or spans the entire region. + + assert(obj == oop(cur), "sanity"); + assert(cur <= start && + obj->klass_or_null() != NULL && + (cur + obj->size()) > start, "Loop postcondition"); + if (!g1h->is_obj_dead(obj)) { obj->oop_iterate(cl, mr); } - HeapWord* next; - while (cur < mr.end()) { + while (cur < end) { obj = oop(cur); if (obj->klass_or_null() == NULL) { // Ran into an unparseable point. return cur; }; + // Otherwise: next = (cur + obj->size()); + if (!g1h->is_obj_dead(obj)) { - if (next < mr.end()) { + if (next < end || !obj->is_objArray()) { + // This object either does not span the MemRegion + // boundary, or if it does it's not an array. + // Apply closure to whole object. obj->oop_iterate(cl); } else { - // this obj spans the boundary. If it's an array, stop at the - // boundary. - if (obj->is_objArray()) { - obj->oop_iterate(cl, mr); - } else { - obj->oop_iterate(cl); - } + // This obj is an array that spans the boundary. + // Stop at the boundary. + obj->oop_iterate(cl, mr); } } cur = next; diff -r f920d3637c9f -r 4c63e7c939aa hotspot/src/share/vm/gc_implementation/g1/heapRegion.hpp --- a/hotspot/src/share/vm/gc_implementation/g1/heapRegion.hpp Thu Jan 26 18:23:14 2012 -0800 +++ b/hotspot/src/share/vm/gc_implementation/g1/heapRegion.hpp Wed Jul 05 18:01:11 2017 +0200 @@ -374,7 +374,9 @@ ParVerifyClaimValue = 4, RebuildRSClaimValue = 5, CompleteMarkCSetClaimValue = 6, - ParEvacFailureClaimValue = 7 + ParEvacFailureClaimValue = 7, + AggregateCountClaimValue = 8, + VerifyCountClaimValue = 9 }; inline HeapWord* par_allocate_no_bot_updates(size_t word_size) { diff -r f920d3637c9f -r 4c63e7c939aa hotspot/src/share/vm/gc_implementation/g1/heapRegion.inline.hpp --- a/hotspot/src/share/vm/gc_implementation/g1/heapRegion.inline.hpp Thu Jan 26 18:23:14 2012 -0800 +++ b/hotspot/src/share/vm/gc_implementation/g1/heapRegion.inline.hpp Wed Jul 05 18:01:11 2017 +0200 @@ -72,10 +72,11 @@ } inline void HeapRegion::note_start_of_copying(bool during_initial_mark) { - if (during_initial_mark) { - if (is_survivor()) { - assert(false, "should not allocate survivors during IM"); - } else { + if (is_survivor()) { + // This is how we always allocate survivors. + assert(_next_top_at_mark_start == bottom(), "invariant"); + } else { + if (during_initial_mark) { // During initial-mark we'll explicitly mark any objects on old // regions that are pointed to by roots. Given that explicit // marks only make sense under NTAMS it'd be nice if we could @@ -84,11 +85,6 @@ // NTAMS to the end of the region so all marks will be below // NTAMS. We'll set it to the actual top when we retire this region. _next_top_at_mark_start = end(); - } - } else { - if (is_survivor()) { - // This is how we always allocate survivors. - assert(_next_top_at_mark_start == bottom(), "invariant"); } else { // We could have re-used this old region as to-space over a // couple of GCs since the start of the concurrent marking @@ -101,19 +97,15 @@ } inline void HeapRegion::note_end_of_copying(bool during_initial_mark) { - if (during_initial_mark) { - if (is_survivor()) { - assert(false, "should not allocate survivors during IM"); - } else { + if (is_survivor()) { + // This is how we always allocate survivors. + assert(_next_top_at_mark_start == bottom(), "invariant"); + } else { + if (during_initial_mark) { // See the comment for note_start_of_copying() for the details // on this. assert(_next_top_at_mark_start == end(), "pre-condition"); _next_top_at_mark_start = top(); - } - } else { - if (is_survivor()) { - // This is how we always allocate survivors. - assert(_next_top_at_mark_start == bottom(), "invariant"); } else { // See the comment for note_start_of_copying() for the details // on this. diff -r f920d3637c9f -r 4c63e7c939aa hotspot/src/share/vm/gc_implementation/g1/heapRegionSet.hpp --- a/hotspot/src/share/vm/gc_implementation/g1/heapRegionSet.hpp Thu Jan 26 18:23:14 2012 -0800 +++ b/hotspot/src/share/vm/gc_implementation/g1/heapRegionSet.hpp Wed Jul 05 18:01:11 2017 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2012, 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 @@ -59,6 +59,7 @@ class HeapRegionSetBase VALUE_OBJ_CLASS_SPEC { friend class hrs_ext_msg; friend class HRSPhaseSetter; + friend class VMStructs; protected: static size_t calculate_region_num(HeapRegion* hr); diff -r f920d3637c9f -r 4c63e7c939aa hotspot/src/share/vm/gc_implementation/g1/vmStructs_g1.hpp --- a/hotspot/src/share/vm/gc_implementation/g1/vmStructs_g1.hpp Thu Jan 26 18:23:14 2012 -0800 +++ b/hotspot/src/share/vm/gc_implementation/g1/vmStructs_g1.hpp Wed Jul 05 18:01:11 2017 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2012, 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 @@ -40,6 +40,8 @@ nonstatic_field(G1CollectedHeap, _g1_committed, MemRegion) \ nonstatic_field(G1CollectedHeap, _summary_bytes_used, size_t) \ nonstatic_field(G1CollectedHeap, _g1mm, G1MonitoringSupport*) \ + nonstatic_field(G1CollectedHeap, _old_set, HeapRegionSetBase) \ + nonstatic_field(G1CollectedHeap, _humongous_set, HeapRegionSetBase) \ \ nonstatic_field(G1MonitoringSupport, _eden_committed, size_t) \ nonstatic_field(G1MonitoringSupport, _eden_used, size_t) \ @@ -47,6 +49,10 @@ nonstatic_field(G1MonitoringSupport, _survivor_used, size_t) \ nonstatic_field(G1MonitoringSupport, _old_committed, size_t) \ nonstatic_field(G1MonitoringSupport, _old_used, size_t) \ + \ + nonstatic_field(HeapRegionSetBase, _length, size_t) \ + nonstatic_field(HeapRegionSetBase, _region_num, size_t) \ + nonstatic_field(HeapRegionSetBase, _total_used_bytes, size_t) \ #define VM_TYPES_G1(declare_type, declare_toplevel_type) \ @@ -55,6 +61,7 @@ \ declare_type(HeapRegion, ContiguousSpace) \ declare_toplevel_type(HeapRegionSeq) \ + declare_toplevel_type(HeapRegionSetBase) \ declare_toplevel_type(G1MonitoringSupport) \ \ declare_toplevel_type(G1CollectedHeap*) \ diff -r f920d3637c9f -r 4c63e7c939aa hotspot/src/share/vm/gc_implementation/g1/vm_operations_g1.cpp --- a/hotspot/src/share/vm/gc_implementation/g1/vm_operations_g1.cpp Thu Jan 26 18:23:14 2012 -0800 +++ b/hotspot/src/share/vm/gc_implementation/g1/vm_operations_g1.cpp Wed Jul 05 18:01:11 2017 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2012, 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 @@ -74,8 +74,9 @@ G1CollectedHeap* g1h = G1CollectedHeap::heap(); assert(!_should_initiate_conc_mark || ((_gc_cause == GCCause::_gc_locker && GCLockerInvokesConcurrent) || - (_gc_cause == GCCause::_java_lang_system_gc && ExplicitGCInvokesConcurrent)), - "only a GC locker or a System.gc() induced GC should start a cycle"); + (_gc_cause == GCCause::_java_lang_system_gc && ExplicitGCInvokesConcurrent) || + _gc_cause == GCCause::_g1_humongous_allocation), + "only a GC locker, a System.gc() or a hum allocation induced GC should start a cycle"); if (_word_size > 0) { // An allocation has been requested. So, try to do that first. diff -r f920d3637c9f -r 4c63e7c939aa hotspot/src/share/vm/gc_interface/gcCause.cpp --- a/hotspot/src/share/vm/gc_interface/gcCause.cpp Thu Jan 26 18:23:14 2012 -0800 +++ b/hotspot/src/share/vm/gc_interface/gcCause.cpp Wed Jul 05 18:01:11 2017 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 2012, 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 @@ -84,6 +84,9 @@ case _g1_inc_collection_pause: return "G1 Evacuation Pause"; + case _g1_humongous_allocation: + return "G1 Humongous Allocation"; + case _last_ditch_collection: return "Last ditch collection"; diff -r f920d3637c9f -r 4c63e7c939aa hotspot/src/share/vm/gc_interface/gcCause.hpp --- a/hotspot/src/share/vm/gc_interface/gcCause.hpp Thu Jan 26 18:23:14 2012 -0800 +++ b/hotspot/src/share/vm/gc_interface/gcCause.hpp Wed Jul 05 18:01:11 2017 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 2012, 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 @@ -66,6 +66,7 @@ _adaptive_size_policy, _g1_inc_collection_pause, + _g1_humongous_allocation, _last_ditch_collection, _last_gc_cause diff -r f920d3637c9f -r 4c63e7c939aa hotspot/src/share/vm/interpreter/interpreterRuntime.cpp --- a/hotspot/src/share/vm/interpreter/interpreterRuntime.cpp Thu Jan 26 18:23:14 2012 -0800 +++ b/hotspot/src/share/vm/interpreter/interpreterRuntime.cpp Wed Jul 05 18:01:11 2017 +0200 @@ -859,7 +859,9 @@ const int branch_bci = branch_bcp != NULL ? method->bci_from(branch_bcp) : InvocationEntryBci; const int bci = branch_bcp != NULL ? method->bci_from(fr.interpreter_frame_bcp()) : InvocationEntryBci; + assert(!HAS_PENDING_EXCEPTION, "Should not have any exceptions pending"); nmethod* osr_nm = CompilationPolicy::policy()->event(method, method, branch_bci, bci, CompLevel_none, NULL, thread); + assert(!HAS_PENDING_EXCEPTION, "Event handler should not throw any exceptions"); if (osr_nm != NULL) { // We may need to do on-stack replacement which requires that no diff -r f920d3637c9f -r 4c63e7c939aa hotspot/src/share/vm/opto/block.hpp --- a/hotspot/src/share/vm/opto/block.hpp Thu Jan 26 18:23:14 2012 -0800 +++ b/hotspot/src/share/vm/opto/block.hpp Wed Jul 05 18:01:11 2017 +0200 @@ -284,13 +284,13 @@ // helper function that adds caller save registers to MachProjNode void add_call_kills(MachProjNode *proj, RegMask& regs, const char* save_policy, bool exclude_soe); // Schedule a call next in the block - uint sched_call(Matcher &matcher, Block_Array &bbs, uint node_cnt, Node_List &worklist, int *ready_cnt, MachCallNode *mcall, VectorSet &next_call); + uint sched_call(Matcher &matcher, Block_Array &bbs, uint node_cnt, Node_List &worklist, GrowableArray &ready_cnt, MachCallNode *mcall, VectorSet &next_call); // Perform basic-block local scheduling - Node *select(PhaseCFG *cfg, Node_List &worklist, int *ready_cnt, VectorSet &next_call, uint sched_slot); + Node *select(PhaseCFG *cfg, Node_List &worklist, GrowableArray &ready_cnt, VectorSet &next_call, uint sched_slot); void set_next_call( Node *n, VectorSet &next_call, Block_Array &bbs ); void needed_for_next_call(Node *this_call, VectorSet &next_call, Block_Array &bbs); - bool schedule_local(PhaseCFG *cfg, Matcher &m, int *ready_cnt, VectorSet &next_call); + bool schedule_local(PhaseCFG *cfg, Matcher &m, GrowableArray &ready_cnt, VectorSet &next_call); // Cleanup if any code lands between a Call and his Catch void call_catch_cleanup(Block_Array &bbs); // Detect implicit-null-check opportunities. Basically, find NULL checks diff -r f920d3637c9f -r 4c63e7c939aa hotspot/src/share/vm/opto/gcm.cpp --- a/hotspot/src/share/vm/opto/gcm.cpp Thu Jan 26 18:23:14 2012 -0800 +++ b/hotspot/src/share/vm/opto/gcm.cpp Wed Jul 05 18:01:11 2017 +0200 @@ -1344,8 +1344,8 @@ // Schedule locally. Right now a simple topological sort. // Later, do a real latency aware scheduler. - int *ready_cnt = NEW_RESOURCE_ARRAY(int,C->unique()); - memset( ready_cnt, -1, C->unique() * sizeof(int) ); + uint max_idx = C->unique(); + GrowableArray ready_cnt(max_idx, max_idx, -1); visited.Clear(); for (i = 0; i < _num_blocks; i++) { if (!_blocks[i]->schedule_local(this, matcher, ready_cnt, visited)) { diff -r f920d3637c9f -r 4c63e7c939aa hotspot/src/share/vm/opto/lcm.cpp --- a/hotspot/src/share/vm/opto/lcm.cpp Thu Jan 26 18:23:14 2012 -0800 +++ b/hotspot/src/share/vm/opto/lcm.cpp Wed Jul 05 18:01:11 2017 +0200 @@ -404,7 +404,7 @@ // remaining cases (most), choose the instruction with the greatest latency // (that is, the most number of pseudo-cycles required to the end of the // routine). If there is a tie, choose the instruction with the most inputs. -Node *Block::select(PhaseCFG *cfg, Node_List &worklist, int *ready_cnt, VectorSet &next_call, uint sched_slot) { +Node *Block::select(PhaseCFG *cfg, Node_List &worklist, GrowableArray &ready_cnt, VectorSet &next_call, uint sched_slot) { // If only a single entry on the stack, use it uint cnt = worklist.size(); @@ -465,7 +465,7 @@ // More than this instruction pending for successor to be ready, // don't choose this if other opportunities are ready - if (ready_cnt[use->_idx] > 1) + if (ready_cnt.at(use->_idx) > 1) n_choice = 1; } @@ -565,7 +565,7 @@ //------------------------------sched_call------------------------------------- -uint Block::sched_call( Matcher &matcher, Block_Array &bbs, uint node_cnt, Node_List &worklist, int *ready_cnt, MachCallNode *mcall, VectorSet &next_call ) { +uint Block::sched_call( Matcher &matcher, Block_Array &bbs, uint node_cnt, Node_List &worklist, GrowableArray &ready_cnt, MachCallNode *mcall, VectorSet &next_call ) { RegMask regs; // Schedule all the users of the call right now. All the users are @@ -574,8 +574,9 @@ for (DUIterator_Fast imax, i = mcall->fast_outs(imax); i < imax; i++) { Node* n = mcall->fast_out(i); assert( n->is_MachProj(), "" ); - --ready_cnt[n->_idx]; - assert( !ready_cnt[n->_idx], "" ); + int n_cnt = ready_cnt.at(n->_idx)-1; + ready_cnt.at_put(n->_idx, n_cnt); + assert( n_cnt == 0, "" ); // Schedule next to call _nodes.map(node_cnt++, n); // Collect defined registers @@ -590,7 +591,9 @@ Node* m = n->fast_out(j); // Get user if( bbs[m->_idx] != this ) continue; if( m->is_Phi() ) continue; - if( !--ready_cnt[m->_idx] ) + int m_cnt = ready_cnt.at(m->_idx)-1; + ready_cnt.at_put(m->_idx, m_cnt); + if( m_cnt == 0 ) worklist.push(m); } @@ -655,7 +658,7 @@ //------------------------------schedule_local--------------------------------- // Topological sort within a block. Someday become a real scheduler. -bool Block::schedule_local(PhaseCFG *cfg, Matcher &matcher, int *ready_cnt, VectorSet &next_call) { +bool Block::schedule_local(PhaseCFG *cfg, Matcher &matcher, GrowableArray &ready_cnt, VectorSet &next_call) { // Already "sorted" are the block start Node (as the first entry), and // the block-ending Node and any trailing control projections. We leave // these alone. PhiNodes and ParmNodes are made to follow the block start @@ -695,7 +698,7 @@ if( m && cfg->_bbs[m->_idx] == this && !m->is_top() ) local++; // One more block-local input } - ready_cnt[n->_idx] = local; // Count em up + ready_cnt.at_put(n->_idx, local); // Count em up #ifdef ASSERT if( UseConcMarkSweepGC || UseG1GC ) { @@ -729,7 +732,7 @@ } } for(uint i2=i; i2<_nodes.size(); i2++ ) // Trailing guys get zapped count - ready_cnt[_nodes[i2]->_idx] = 0; + ready_cnt.at_put(_nodes[i2]->_idx, 0); // All the prescheduled guys do not hold back internal nodes uint i3; @@ -737,8 +740,10 @@ Node *n = _nodes[i3]; // Get pre-scheduled for (DUIterator_Fast jmax, j = n->fast_outs(jmax); j < jmax; j++) { Node* m = n->fast_out(j); - if( cfg->_bbs[m->_idx] ==this ) // Local-block user - ready_cnt[m->_idx]--; // Fix ready count + if( cfg->_bbs[m->_idx] ==this ) { // Local-block user + int m_cnt = ready_cnt.at(m->_idx)-1; + ready_cnt.at_put(m->_idx, m_cnt); // Fix ready count + } } } @@ -747,7 +752,7 @@ Node_List worklist; for(uint i4=i3; i4_idx] ) { // Zero ready count? + if( !ready_cnt.at(m->_idx) ) { // Zero ready count? if (m->is_iteratively_computed()) { // Push induction variable increments last to allow other uses // of the phi to be scheduled first. The select() method breaks @@ -775,14 +780,14 @@ for (uint j=0; j<_nodes.size(); j++) { Node *n = _nodes[j]; int idx = n->_idx; - tty->print("# ready cnt:%3d ", ready_cnt[idx]); + tty->print("# ready cnt:%3d ", ready_cnt.at(idx)); tty->print("latency:%3d ", cfg->_node_latency->at_grow(idx)); tty->print("%4d: %s\n", idx, n->Name()); } } #endif - uint max_idx = matcher.C->unique(); + uint max_idx = (uint)ready_cnt.length(); // Pull from worklist and schedule while( worklist.size() ) { // Worklist is not ready @@ -840,11 +845,13 @@ Node* m = n->fast_out(i5); // Get user if( cfg->_bbs[m->_idx] != this ) continue; if( m->is_Phi() ) continue; - if (m->_idx > max_idx) { // new node, skip it + if (m->_idx >= max_idx) { // new node, skip it assert(m->is_MachProj() && n->is_Mach() && n->as_Mach()->has_call(), "unexpected node types"); continue; } - if( !--ready_cnt[m->_idx] ) + int m_cnt = ready_cnt.at(m->_idx)-1; + ready_cnt.at_put(m->_idx, m_cnt); + if( m_cnt == 0 ) worklist.push(m); } } diff -r f920d3637c9f -r 4c63e7c939aa hotspot/src/share/vm/opto/memnode.cpp --- a/hotspot/src/share/vm/opto/memnode.cpp Thu Jan 26 18:23:14 2012 -0800 +++ b/hotspot/src/share/vm/opto/memnode.cpp Wed Jul 05 18:01:11 2017 +0200 @@ -1718,8 +1718,10 @@ bool is_instance = (tinst != NULL) && tinst->is_known_instance_field(); if (ReduceFieldZeroing || is_instance) { Node* value = can_see_stored_value(mem,phase); - if (value != NULL && value->is_Con()) + if (value != NULL && value->is_Con()) { + assert(value->bottom_type()->higher_equal(_type),"sanity"); return value->bottom_type(); + } } if (is_instance) { @@ -1759,6 +1761,20 @@ return LoadNode::Ideal(phase, can_reshape); } +const Type* LoadBNode::Value(PhaseTransform *phase) const { + Node* mem = in(MemNode::Memory); + Node* value = can_see_stored_value(mem,phase); + if (value != NULL && value->is_Con() && + !value->bottom_type()->higher_equal(_type)) { + // If the input to the store does not fit with the load's result type, + // it must be truncated. We can't delay until Ideal call since + // a singleton Value is needed for split_thru_phi optimization. + int con = value->get_int(); + return TypeInt::make((con << 24) >> 24); + } + return LoadNode::Value(phase); +} + //--------------------------LoadUBNode::Ideal------------------------------------- // // If the previous store is to the same address as this load, @@ -1775,6 +1791,20 @@ return LoadNode::Ideal(phase, can_reshape); } +const Type* LoadUBNode::Value(PhaseTransform *phase) const { + Node* mem = in(MemNode::Memory); + Node* value = can_see_stored_value(mem,phase); + if (value != NULL && value->is_Con() && + !value->bottom_type()->higher_equal(_type)) { + // If the input to the store does not fit with the load's result type, + // it must be truncated. We can't delay until Ideal call since + // a singleton Value is needed for split_thru_phi optimization. + int con = value->get_int(); + return TypeInt::make(con & 0xFF); + } + return LoadNode::Value(phase); +} + //--------------------------LoadUSNode::Ideal------------------------------------- // // If the previous store is to the same address as this load, @@ -1791,6 +1821,20 @@ return LoadNode::Ideal(phase, can_reshape); } +const Type* LoadUSNode::Value(PhaseTransform *phase) const { + Node* mem = in(MemNode::Memory); + Node* value = can_see_stored_value(mem,phase); + if (value != NULL && value->is_Con() && + !value->bottom_type()->higher_equal(_type)) { + // If the input to the store does not fit with the load's result type, + // it must be truncated. We can't delay until Ideal call since + // a singleton Value is needed for split_thru_phi optimization. + int con = value->get_int(); + return TypeInt::make(con & 0xFFFF); + } + return LoadNode::Value(phase); +} + //--------------------------LoadSNode::Ideal-------------------------------------- // // If the previous store is to the same address as this load, @@ -1809,6 +1853,20 @@ return LoadNode::Ideal(phase, can_reshape); } +const Type* LoadSNode::Value(PhaseTransform *phase) const { + Node* mem = in(MemNode::Memory); + Node* value = can_see_stored_value(mem,phase); + if (value != NULL && value->is_Con() && + !value->bottom_type()->higher_equal(_type)) { + // If the input to the store does not fit with the load's result type, + // it must be truncated. We can't delay until Ideal call since + // a singleton Value is needed for split_thru_phi optimization. + int con = value->get_int(); + return TypeInt::make((con << 16) >> 16); + } + return LoadNode::Value(phase); +} + //============================================================================= //----------------------------LoadKlassNode::make------------------------------ // Polymorphic factory method: diff -r f920d3637c9f -r 4c63e7c939aa hotspot/src/share/vm/opto/memnode.hpp --- a/hotspot/src/share/vm/opto/memnode.hpp Thu Jan 26 18:23:14 2012 -0800 +++ b/hotspot/src/share/vm/opto/memnode.hpp Wed Jul 05 18:01:11 2017 +0200 @@ -215,6 +215,7 @@ virtual int Opcode() const; virtual uint ideal_reg() const { return Op_RegI; } virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); + virtual const Type *Value(PhaseTransform *phase) const; virtual int store_Opcode() const { return Op_StoreB; } virtual BasicType memory_type() const { return T_BYTE; } }; @@ -228,6 +229,7 @@ virtual int Opcode() const; virtual uint ideal_reg() const { return Op_RegI; } virtual Node* Ideal(PhaseGVN *phase, bool can_reshape); + virtual const Type *Value(PhaseTransform *phase) const; virtual int store_Opcode() const { return Op_StoreB; } virtual BasicType memory_type() const { return T_BYTE; } }; @@ -241,10 +243,25 @@ virtual int Opcode() const; virtual uint ideal_reg() const { return Op_RegI; } virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); + virtual const Type *Value(PhaseTransform *phase) const; virtual int store_Opcode() const { return Op_StoreC; } virtual BasicType memory_type() const { return T_CHAR; } }; +//------------------------------LoadSNode-------------------------------------- +// Load a short (16bits signed) from memory +class LoadSNode : public LoadNode { +public: + LoadSNode( Node *c, Node *mem, Node *adr, const TypePtr* at, const TypeInt *ti = TypeInt::SHORT ) + : LoadNode(c,mem,adr,at,ti) {} + virtual int Opcode() const; + virtual uint ideal_reg() const { return Op_RegI; } + virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); + virtual const Type *Value(PhaseTransform *phase) const; + virtual int store_Opcode() const { return Op_StoreC; } + virtual BasicType memory_type() const { return T_SHORT; } +}; + //------------------------------LoadINode-------------------------------------- // Load an integer from memory class LoadINode : public LoadNode { @@ -433,19 +450,6 @@ }; -//------------------------------LoadSNode-------------------------------------- -// Load a short (16bits signed) from memory -class LoadSNode : public LoadNode { -public: - LoadSNode( Node *c, Node *mem, Node *adr, const TypePtr* at, const TypeInt *ti = TypeInt::SHORT ) - : LoadNode(c,mem,adr,at,ti) {} - virtual int Opcode() const; - virtual uint ideal_reg() const { return Op_RegI; } - virtual Node *Ideal(PhaseGVN *phase, bool can_reshape); - virtual int store_Opcode() const { return Op_StoreC; } - virtual BasicType memory_type() const { return T_SHORT; } -}; - //------------------------------StoreNode-------------------------------------- // Store value; requires Store, Address and Value class StoreNode : public MemNode { diff -r f920d3637c9f -r 4c63e7c939aa hotspot/src/share/vm/opto/parseHelper.cpp --- a/hotspot/src/share/vm/opto/parseHelper.cpp Thu Jan 26 18:23:14 2012 -0800 +++ b/hotspot/src/share/vm/opto/parseHelper.cpp Wed Jul 05 18:01:11 2017 +0200 @@ -71,14 +71,14 @@ // Throw uncommon trap if class is not loaded or the value we are casting // _from_ is not loaded, and value is not null. If the value _is_ NULL, // then the checkcast does nothing. - const TypeInstPtr *tp = _gvn.type(obj)->isa_instptr(); - if (!will_link || (tp && !tp->is_loaded())) { + const TypeOopPtr *tp = _gvn.type(obj)->isa_oopptr(); + if (!will_link || (tp && tp->klass() && !tp->klass()->is_loaded())) { if (C->log() != NULL) { if (!will_link) { C->log()->elem("assert_null reason='checkcast' klass='%d'", C->log()->identify(klass)); } - if (tp && !tp->is_loaded()) { + if (tp && tp->klass() && !tp->klass()->is_loaded()) { // %%% Cannot happen? C->log()->elem("assert_null reason='checkcast source' klass='%d'", C->log()->identify(tp->klass())); diff -r f920d3637c9f -r 4c63e7c939aa hotspot/src/share/vm/prims/jvmtiThreadState.cpp --- a/hotspot/src/share/vm/prims/jvmtiThreadState.cpp Thu Jan 26 18:23:14 2012 -0800 +++ b/hotspot/src/share/vm/prims/jvmtiThreadState.cpp Wed Jul 05 18:01:11 2017 +0200 @@ -319,6 +319,15 @@ // clearing the flag indicates we are done with the PopFrame() dance clr_pending_step_for_popframe(); + // If exception was thrown in this frame, need to reset jvmti thread state. + // Single stepping may not get enabled correctly by the agent since + // exception state is passed in MethodExit event which may be sent at some + // time in the future. JDWP agent ignores MethodExit events if caused by + // an exception. + // + if (is_exception_detected()) { + clear_exception_detected(); + } // If step is pending for popframe then it may not be // a repeat step. The new_bci and method_id is same as current_bci // and current method_id after pop and step for recursive calls. @@ -385,6 +394,15 @@ // the ForceEarlyReturn() dance clr_pending_step_for_earlyret(); + // If exception was thrown in this frame, need to reset jvmti thread state. + // Single stepping may not get enabled correctly by the agent since + // exception state is passed in MethodExit event which may be sent at some + // time in the future. JDWP agent ignores MethodExit events if caused by + // an exception. + // + if (is_exception_detected()) { + clear_exception_detected(); + } // If step is pending for earlyret then it may not be a repeat step. // The new_bci and method_id is same as current_bci and current // method_id after earlyret and step for recursive calls. diff -r f920d3637c9f -r 4c63e7c939aa hotspot/src/share/vm/prims/jvmtiThreadState.hpp --- a/hotspot/src/share/vm/prims/jvmtiThreadState.hpp Thu Jan 26 18:23:14 2012 -0800 +++ b/hotspot/src/share/vm/prims/jvmtiThreadState.hpp Wed Jul 05 18:01:11 2017 +0200 @@ -165,6 +165,10 @@ inline bool is_exception_caught() { return _exception_caught; } inline void set_exception_detected() { _exception_detected = true; _exception_caught = false; } + inline void clear_exception_detected() { + _exception_detected = false; + assert(_exception_caught == false, "_exception_caught is out of phase"); + } inline void set_exception_caught() { _exception_caught = true; _exception_detected = false; } diff -r f920d3637c9f -r 4c63e7c939aa hotspot/src/share/vm/runtime/advancedThresholdPolicy.cpp --- a/hotspot/src/share/vm/runtime/advancedThresholdPolicy.cpp Thu Jan 26 18:23:14 2012 -0800 +++ b/hotspot/src/share/vm/runtime/advancedThresholdPolicy.cpp Wed Jul 05 18:01:11 2017 +0200 @@ -271,13 +271,10 @@ } // Create MDO if necessary. -void AdvancedThresholdPolicy::create_mdo(methodHandle mh, TRAPS) { +void AdvancedThresholdPolicy::create_mdo(methodHandle mh, JavaThread* THREAD) { if (mh->is_native() || mh->is_abstract() || mh->is_accessor()) return; if (mh->method_data() == NULL) { - methodOopDesc::build_interpreter_method_data(mh, THREAD); - if (HAS_PENDING_EXCEPTION) { - CLEAR_PENDING_EXCEPTION; - } + methodOopDesc::build_interpreter_method_data(mh, CHECK_AND_CLEAR); } } @@ -426,22 +423,22 @@ } // Update the rate and submit compile -void AdvancedThresholdPolicy::submit_compile(methodHandle mh, int bci, CompLevel level, TRAPS) { +void AdvancedThresholdPolicy::submit_compile(methodHandle mh, int bci, CompLevel level, JavaThread* thread) { int hot_count = (bci == InvocationEntryBci) ? mh->invocation_count() : mh->backedge_count(); update_rate(os::javaTimeMillis(), mh()); - CompileBroker::compile_method(mh, bci, level, mh, hot_count, "tiered", THREAD); + CompileBroker::compile_method(mh, bci, level, mh, hot_count, "tiered", thread); } // Handle the invocation event. void AdvancedThresholdPolicy::method_invocation_event(methodHandle mh, methodHandle imh, - CompLevel level, nmethod* nm, TRAPS) { + CompLevel level, nmethod* nm, JavaThread* thread) { if (should_create_mdo(mh(), level)) { - create_mdo(mh, THREAD); + create_mdo(mh, thread); } if (is_compilation_enabled() && !CompileBroker::compilation_is_in_queue(mh, InvocationEntryBci)) { CompLevel next_level = call_event(mh(), level); if (next_level != level) { - compile(mh, InvocationEntryBci, next_level, THREAD); + compile(mh, InvocationEntryBci, next_level, thread); } } } @@ -449,13 +446,13 @@ // Handle the back branch event. Notice that we can compile the method // with a regular entry from here. void AdvancedThresholdPolicy::method_back_branch_event(methodHandle mh, methodHandle imh, - int bci, CompLevel level, nmethod* nm, TRAPS) { + int bci, CompLevel level, nmethod* nm, JavaThread* thread) { if (should_create_mdo(mh(), level)) { - create_mdo(mh, THREAD); + create_mdo(mh, thread); } // Check if MDO should be created for the inlined method if (should_create_mdo(imh(), level)) { - create_mdo(imh, THREAD); + create_mdo(imh, thread); } if (is_compilation_enabled()) { @@ -463,7 +460,7 @@ CompLevel max_osr_level = (CompLevel)imh->highest_osr_comp_level(); // At the very least compile the OSR version if (!CompileBroker::compilation_is_in_queue(imh, bci) && next_osr_level != level) { - compile(imh, bci, next_osr_level, THREAD); + compile(imh, bci, next_osr_level, thread); } // Use loop event as an opportunity to also check if there's been @@ -502,14 +499,14 @@ next_level = CompLevel_full_profile; } if (cur_level != next_level) { - compile(mh, InvocationEntryBci, next_level, THREAD); + compile(mh, InvocationEntryBci, next_level, thread); } } } else { cur_level = comp_level(imh()); next_level = call_event(imh(), cur_level); if (!CompileBroker::compilation_is_in_queue(imh, bci) && next_level != cur_level) { - compile(imh, InvocationEntryBci, next_level, THREAD); + compile(imh, InvocationEntryBci, next_level, thread); } } } diff -r f920d3637c9f -r 4c63e7c939aa hotspot/src/share/vm/runtime/advancedThresholdPolicy.hpp --- a/hotspot/src/share/vm/runtime/advancedThresholdPolicy.hpp Thu Jan 26 18:23:14 2012 -0800 +++ b/hotspot/src/share/vm/runtime/advancedThresholdPolicy.hpp Wed Jul 05 18:01:11 2017 +0200 @@ -197,7 +197,7 @@ // determines whether we should do that. inline bool should_create_mdo(methodOop method, CompLevel cur_level); // Create MDO if necessary. - void create_mdo(methodHandle mh, TRAPS); + void create_mdo(methodHandle mh, JavaThread* thread); // Is method profiled enough? bool is_method_profiled(methodOop method); @@ -208,12 +208,12 @@ jlong start_time() const { return _start_time; } // Submit a given method for compilation (and update the rate). - virtual void submit_compile(methodHandle mh, int bci, CompLevel level, TRAPS); + virtual void submit_compile(methodHandle mh, int bci, CompLevel level, JavaThread* thread); // event() from SimpleThresholdPolicy would call these. virtual void method_invocation_event(methodHandle method, methodHandle inlinee, - CompLevel level, nmethod* nm, TRAPS); + CompLevel level, nmethod* nm, JavaThread* thread); virtual void method_back_branch_event(methodHandle method, methodHandle inlinee, - int bci, CompLevel level, nmethod* nm, TRAPS); + int bci, CompLevel level, nmethod* nm, JavaThread* thread); public: AdvancedThresholdPolicy() : _start_time(0) { } // Select task is called by CompileBroker. We should return a task or NULL. diff -r f920d3637c9f -r 4c63e7c939aa hotspot/src/share/vm/runtime/arguments.cpp --- a/hotspot/src/share/vm/runtime/arguments.cpp Thu Jan 26 18:23:14 2012 -0800 +++ b/hotspot/src/share/vm/runtime/arguments.cpp Wed Jul 05 18:01:11 2017 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2012, 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 @@ -1040,6 +1040,16 @@ } #ifndef KERNEL +static void disable_adaptive_size_policy(const char* collector_name) { + if (UseAdaptiveSizePolicy) { + if (FLAG_IS_CMDLINE(UseAdaptiveSizePolicy)) { + warning("disabling UseAdaptiveSizePolicy; it is incompatible with %s.", + collector_name); + } + FLAG_SET_DEFAULT(UseAdaptiveSizePolicy, false); + } +} + // If the user has chosen ParallelGCThreads > 0, we set UseParNewGC // if it's not explictly set or unset. If the user has chosen // UseParNewGC and not explicitly set ParallelGCThreads we @@ -1049,11 +1059,8 @@ "control point invariant"); assert(UseParNewGC, "Error"); - // Turn off AdaptiveSizePolicy by default for parnew until it is - // complete. - if (FLAG_IS_DEFAULT(UseAdaptiveSizePolicy)) { - FLAG_SET_DEFAULT(UseAdaptiveSizePolicy, false); - } + // Turn off AdaptiveSizePolicy for parnew until it is complete. + disable_adaptive_size_policy("UseParNewGC"); if (ParallelGCThreads == 0) { FLAG_SET_DEFAULT(ParallelGCThreads, @@ -1110,11 +1117,8 @@ FLAG_SET_ERGO(bool, UseParNewGC, true); } - // Turn off AdaptiveSizePolicy by default for cms until it is - // complete. - if (FLAG_IS_DEFAULT(UseAdaptiveSizePolicy)) { - FLAG_SET_DEFAULT(UseAdaptiveSizePolicy, false); - } + // Turn off AdaptiveSizePolicy for CMS until it is complete. + disable_adaptive_size_policy("UseConcMarkSweepGC"); // In either case, adjust ParallelGCThreads and/or UseParNewGC // as needed. diff -r f920d3637c9f -r 4c63e7c939aa hotspot/src/share/vm/runtime/compilationPolicy.cpp --- a/hotspot/src/share/vm/runtime/compilationPolicy.cpp Thu Jan 26 18:23:14 2012 -0800 +++ b/hotspot/src/share/vm/runtime/compilationPolicy.cpp Wed Jul 05 18:01:11 2017 +0200 @@ -306,29 +306,27 @@ return (current >= initial + target); } -nmethod* NonTieredCompPolicy::event(methodHandle method, methodHandle inlinee, int branch_bci, int bci, CompLevel comp_level, nmethod* nm, TRAPS) { +nmethod* NonTieredCompPolicy::event(methodHandle method, methodHandle inlinee, int branch_bci, + int bci, CompLevel comp_level, nmethod* nm, JavaThread* thread) { assert(comp_level == CompLevel_none, "This should be only called from the interpreter"); NOT_PRODUCT(trace_frequency_counter_overflow(method, branch_bci, bci)); - if (JvmtiExport::can_post_interpreter_events()) { - assert(THREAD->is_Java_thread(), "Wrong type of thread"); - if (((JavaThread*)THREAD)->is_interp_only_mode()) { - // If certain JVMTI events (e.g. frame pop event) are requested then the - // thread is forced to remain in interpreted code. This is - // implemented partly by a check in the run_compiled_code - // section of the interpreter whether we should skip running - // compiled code, and partly by skipping OSR compiles for - // interpreted-only threads. - if (bci != InvocationEntryBci) { - reset_counter_for_back_branch_event(method); - return NULL; - } + if (JvmtiExport::can_post_interpreter_events() && thread->is_interp_only_mode()) { + // If certain JVMTI events (e.g. frame pop event) are requested then the + // thread is forced to remain in interpreted code. This is + // implemented partly by a check in the run_compiled_code + // section of the interpreter whether we should skip running + // compiled code, and partly by skipping OSR compiles for + // interpreted-only threads. + if (bci != InvocationEntryBci) { + reset_counter_for_back_branch_event(method); + return NULL; } } if (bci == InvocationEntryBci) { // when code cache is full, compilation gets switched off, UseCompiler // is set to false if (!method->has_compiled_code() && UseCompiler) { - method_invocation_event(method, CHECK_NULL); + method_invocation_event(method, thread); } else { // Force counter overflow on method entry, even if no compilation // happened. (The method_invocation_event call does this also.) @@ -344,7 +342,7 @@ NOT_PRODUCT(trace_osr_request(method, osr_nm, bci)); // when code cache is full, we should not compile any more... if (osr_nm == NULL && UseCompiler) { - method_back_branch_event(method, bci, CHECK_NULL); + method_back_branch_event(method, bci, thread); osr_nm = method->lookup_osr_nmethod_for(bci, CompLevel_highest_tier, true); } if (osr_nm == NULL) { @@ -395,7 +393,7 @@ // SimpleCompPolicy - compile current method -void SimpleCompPolicy::method_invocation_event( methodHandle m, TRAPS) { +void SimpleCompPolicy::method_invocation_event(methodHandle m, JavaThread* thread) { int hot_count = m->invocation_count(); reset_counter_for_invocation_event(m); const char* comment = "count"; @@ -405,18 +403,18 @@ if (nm == NULL ) { const char* comment = "count"; CompileBroker::compile_method(m, InvocationEntryBci, CompLevel_highest_tier, - m, hot_count, comment, CHECK); + m, hot_count, comment, thread); } } } -void SimpleCompPolicy::method_back_branch_event(methodHandle m, int bci, TRAPS) { +void SimpleCompPolicy::method_back_branch_event(methodHandle m, int bci, JavaThread* thread) { int hot_count = m->backedge_count(); const char* comment = "backedge_count"; if (is_compilation_enabled() && !m->is_not_osr_compilable() && can_be_compiled(m)) { CompileBroker::compile_method(m, bci, CompLevel_highest_tier, - m, hot_count, comment, CHECK); + m, hot_count, comment, thread); NOT_PRODUCT(trace_osr_completion(m->lookup_osr_nmethod_for(bci, CompLevel_highest_tier, true));) } } @@ -427,14 +425,13 @@ // Consider m for compilation -void StackWalkCompPolicy::method_invocation_event(methodHandle m, TRAPS) { +void StackWalkCompPolicy::method_invocation_event(methodHandle m, JavaThread* thread) { int hot_count = m->invocation_count(); reset_counter_for_invocation_event(m); const char* comment = "count"; if (is_compilation_enabled() && m->code() == NULL && can_be_compiled(m)) { - ResourceMark rm(THREAD); - JavaThread *thread = (JavaThread*)THREAD; + ResourceMark rm(thread); frame fr = thread->last_frame(); assert(fr.is_interpreted_frame(), "must be interpreted"); assert(fr.interpreter_frame_method() == m(), "bad method"); @@ -461,17 +458,17 @@ assert(top != NULL, "findTopInlinableFrame returned null"); if (TraceCompilationPolicy) top->print(); CompileBroker::compile_method(top->top_method(), InvocationEntryBci, CompLevel_highest_tier, - m, hot_count, comment, CHECK); + m, hot_count, comment, thread); } } } -void StackWalkCompPolicy::method_back_branch_event(methodHandle m, int bci, TRAPS) { +void StackWalkCompPolicy::method_back_branch_event(methodHandle m, int bci, JavaThread* thread) { int hot_count = m->backedge_count(); const char* comment = "backedge_count"; if (is_compilation_enabled() && !m->is_not_osr_compilable() && can_be_compiled(m)) { - CompileBroker::compile_method(m, bci, CompLevel_highest_tier, m, hot_count, comment, CHECK); + CompileBroker::compile_method(m, bci, CompLevel_highest_tier, m, hot_count, comment, thread); NOT_PRODUCT(trace_osr_completion(m->lookup_osr_nmethod_for(bci, CompLevel_highest_tier, true));) } diff -r f920d3637c9f -r 4c63e7c939aa hotspot/src/share/vm/runtime/compilationPolicy.hpp --- a/hotspot/src/share/vm/runtime/compilationPolicy.hpp Thu Jan 26 18:23:14 2012 -0800 +++ b/hotspot/src/share/vm/runtime/compilationPolicy.hpp Wed Jul 05 18:01:11 2017 +0200 @@ -64,7 +64,7 @@ virtual int compiler_count(CompLevel comp_level) = 0; // main notification entry, return a pointer to an nmethod if the OSR is required, // returns NULL otherwise. - virtual nmethod* event(methodHandle method, methodHandle inlinee, int branch_bci, int bci, CompLevel comp_level, nmethod* nm, TRAPS) = 0; + virtual nmethod* event(methodHandle method, methodHandle inlinee, int branch_bci, int bci, CompLevel comp_level, nmethod* nm, JavaThread* thread) = 0; // safepoint() is called at the end of the safepoint virtual void do_safepoint_work() = 0; // reprofile request @@ -105,15 +105,15 @@ virtual bool is_mature(methodOop method); virtual void initialize(); virtual CompileTask* select_task(CompileQueue* compile_queue); - virtual nmethod* event(methodHandle method, methodHandle inlinee, int branch_bci, int bci, CompLevel comp_level, nmethod* nm, TRAPS); - virtual void method_invocation_event(methodHandle m, TRAPS) = 0; - virtual void method_back_branch_event(methodHandle m, int bci, TRAPS) = 0; + virtual nmethod* event(methodHandle method, methodHandle inlinee, int branch_bci, int bci, CompLevel comp_level, nmethod* nm, JavaThread* thread); + virtual void method_invocation_event(methodHandle m, JavaThread* thread) = 0; + virtual void method_back_branch_event(methodHandle m, int bci, JavaThread* thread) = 0; }; class SimpleCompPolicy : public NonTieredCompPolicy { public: - virtual void method_invocation_event(methodHandle m, TRAPS); - virtual void method_back_branch_event(methodHandle m, int bci, TRAPS); + virtual void method_invocation_event(methodHandle m, JavaThread* thread); + virtual void method_back_branch_event(methodHandle m, int bci, JavaThread* thread); }; // StackWalkCompPolicy - existing C2 policy @@ -121,8 +121,8 @@ #ifdef COMPILER2 class StackWalkCompPolicy : public NonTieredCompPolicy { public: - virtual void method_invocation_event(methodHandle m, TRAPS); - virtual void method_back_branch_event(methodHandle m, int bci, TRAPS); + virtual void method_invocation_event(methodHandle m, JavaThread* thread); + virtual void method_back_branch_event(methodHandle m, int bci, JavaThread* thread); private: RFrame* findTopInlinableFrame(GrowableArray* stack); diff -r f920d3637c9f -r 4c63e7c939aa hotspot/src/share/vm/runtime/frame.cpp --- a/hotspot/src/share/vm/runtime/frame.cpp Thu Jan 26 18:23:14 2012 -0800 +++ b/hotspot/src/share/vm/runtime/frame.cpp Wed Jul 05 18:01:11 2017 +0200 @@ -1315,7 +1315,6 @@ } #endif - #ifdef ASSERT void frame::interpreter_frame_verify_monitor(BasicObjectLock* value) const { assert(is_interpreted_frame(), "Not an interpreted frame"); @@ -1331,24 +1330,35 @@ guarantee((current - low_mark) % monitor_size == 0 , "Misaligned bottom of BasicObjectLock*"); guarantee( current >= low_mark , "Current BasicObjectLock* below than low_mark"); } +#endif +#ifndef PRODUCT +void frame::describe(FrameValues& values, int frame_no) { + // boundaries: sp and the 'real' frame pointer + values.describe(-1, sp(), err_msg("sp for #%d", frame_no), 1); + intptr_t* frame_pointer = real_fp(); // Note: may differ from fp() -void frame::describe(FrameValues& values, int frame_no) { - intptr_t* frame_pointer = real_fp(); + // print frame info at the highest boundary + intptr_t* info_address = MAX2(sp(), frame_pointer); + + if (info_address != frame_pointer) { + // print frame_pointer explicitly if not marked by the frame info + values.describe(-1, frame_pointer, err_msg("frame pointer for #%d", frame_no), 1); + } + if (is_entry_frame() || is_compiled_frame() || is_interpreted_frame() || is_native_frame()) { // Label values common to most frames values.describe(-1, unextended_sp(), err_msg("unextended_sp for #%d", frame_no)); - values.describe(-1, sp(), err_msg("sp for #%d", frame_no)); - values.describe(-1, frame_pointer, err_msg("frame pointer for #%d", frame_no)); } + if (is_interpreted_frame()) { methodOop m = interpreter_frame_method(); int bci = interpreter_frame_bci(); // Label the method and current bci - values.describe(-1, MAX2(sp(), frame_pointer), + values.describe(-1, info_address, FormatBuffer<1024>("#%d method %s @ %d", frame_no, m->name_and_sig_as_C_string(), bci), 2); - values.describe(-1, MAX2(sp(), frame_pointer), + values.describe(-1, info_address, err_msg("- %d locals %d max stack", m->max_locals(), m->max_stack()), 1); if (m->max_locals() > 0) { intptr_t* l0 = interpreter_frame_local_at(0); @@ -1380,21 +1390,36 @@ } } else if (is_entry_frame()) { // For now just label the frame - values.describe(-1, MAX2(sp(), frame_pointer), err_msg("#%d entry frame", frame_no), 2); + values.describe(-1, info_address, err_msg("#%d entry frame", frame_no), 2); } else if (is_compiled_frame()) { // For now just label the frame nmethod* nm = cb()->as_nmethod_or_null(); - values.describe(-1, MAX2(sp(), frame_pointer), + values.describe(-1, info_address, FormatBuffer<1024>("#%d nmethod " INTPTR_FORMAT " for method %s%s", frame_no, nm, nm->method()->name_and_sig_as_C_string(), - is_deoptimized_frame() ? " (deoptimized" : ""), 2); + (_deopt_state == is_deoptimized) ? + " (deoptimized)" : + ((_deopt_state == unknown) ? " (state unknown)" : "")), + 2); } else if (is_native_frame()) { // For now just label the frame nmethod* nm = cb()->as_nmethod_or_null(); - values.describe(-1, MAX2(sp(), frame_pointer), + values.describe(-1, info_address, FormatBuffer<1024>("#%d nmethod " INTPTR_FORMAT " for native method %s", frame_no, nm, nm->method()->name_and_sig_as_C_string()), 2); + } else if (is_ricochet_frame()) { + values.describe(-1, info_address, err_msg("#%d ricochet frame", frame_no), 2); + } else { + // provide default info if not handled before + char *info = (char *) "special frame"; + if ((_cb != NULL) && + (_cb->name() != NULL)) { + info = (char *)_cb->name(); + } + values.describe(-1, info_address, err_msg("#%d <%s>", frame_no, info), 2); } + + // platform dependent additional data describe_pd(values, frame_no); } @@ -1411,7 +1436,7 @@ } -#ifdef ASSERT +#ifndef PRODUCT void FrameValues::describe(int owner, intptr_t* location, const char* description, int priority) { FrameValue fv; @@ -1424,6 +1449,7 @@ } +#ifdef ASSERT void FrameValues::validate() { _values.sort(compare); bool error = false; @@ -1449,7 +1475,7 @@ } assert(!error, "invalid layout"); } - +#endif // ASSERT void FrameValues::print(JavaThread* thread) { _values.sort(compare); @@ -1498,4 +1524,4 @@ } } -#endif +#endif // ndef PRODUCT diff -r f920d3637c9f -r 4c63e7c939aa hotspot/src/share/vm/runtime/frame.hpp --- a/hotspot/src/share/vm/runtime/frame.hpp Thu Jan 26 18:23:14 2012 -0800 +++ b/hotspot/src/share/vm/runtime/frame.hpp Wed Jul 05 18:01:11 2017 +0200 @@ -494,7 +494,7 @@ }; -#ifdef ASSERT +#ifndef PRODUCT // A simple class to describe a location on the stack class FrameValue VALUE_OBJ_CLASS_SPEC { public: @@ -524,7 +524,9 @@ // Used by frame functions to describe locations. void describe(int owner, intptr_t* location, const char* description, int priority = 0); +#ifdef ASSERT void validate(); +#endif void print(JavaThread* thread); }; diff -r f920d3637c9f -r 4c63e7c939aa hotspot/src/share/vm/runtime/globals.hpp --- a/hotspot/src/share/vm/runtime/globals.hpp Thu Jan 26 18:23:14 2012 -0800 +++ b/hotspot/src/share/vm/runtime/globals.hpp Wed Jul 05 18:01:11 2017 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2012, 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 @@ -3477,16 +3477,19 @@ " Linux this policy requires root privilege.") \ \ product(bool, ThreadPriorityVerbose, false, \ - "print priority changes") \ + "Print priority changes") \ \ product(intx, DefaultThreadPriority, -1, \ - "what native priority threads run at if not specified elsewhere (-1 means no change)") \ + "The native priority at which threads run if not elsewhere " \ + "specified (-1 means no change)") \ \ product(intx, CompilerThreadPriority, -1, \ - "what priority should compiler threads run at (-1 means no change)") \ + "The native priority at which compiler threads should run " \ + "(-1 means no change)") \ \ product(intx, VMThreadPriority, -1, \ - "what priority should VM threads run at (-1 means no change)") \ + "The native priority at which the VM thread should run " \ + "(-1 means no change)") \ \ product(bool, CompilerThreadHintNoPreempt, true, \ "(Solaris only) Give compiler threads an extra quanta") \ @@ -3505,6 +3508,15 @@ product(intx, JavaPriority9_To_OSPriority, -1, "Map Java priorities to OS priorities") \ product(intx, JavaPriority10_To_OSPriority,-1, "Map Java priorities to OS priorities") \ \ + experimental(bool, UseCriticalJavaThreadPriority, false, \ + "Java thread priority 10 maps to critical scheduling priority") \ + \ + experimental(bool, UseCriticalCompilerThreadPriority, false, \ + "Compiler thread(s) run at critical scheduling priority") \ + \ + experimental(bool, UseCriticalCMSThreadPriority, false, \ + "ConcurrentMarkSweep thread runs at critical scheduling priority")\ + \ /* compiler debugging */ \ notproduct(intx, CompileTheWorldStartAt, 1, \ "First class to consider when using +CompileTheWorld") \ @@ -3574,7 +3586,7 @@ "Threshold at which tier 3 compilation is invoked (invocation " \ "minimum must be satisfied.") \ \ - product(intx, Tier3BackEdgeThreshold, 7000, \ + product(intx, Tier3BackEdgeThreshold, 60000, \ "Back edge threshold at which tier 3 OSR compilation is invoked") \ \ product(intx, Tier4InvocationThreshold, 5000, \ @@ -3871,7 +3883,7 @@ product(bool, UseVMInterruptibleIO, false, \ "(Unstable, Solaris-specific) Thread interrupt before or with " \ "EINTR for I/O operations results in OS_INTRPT. The default value"\ - " of this flag is true for JDK 6 and earliers") + " of this flag is true for JDK 6 and earlier") /* * Macros for factoring of globals diff -r f920d3637c9f -r 4c63e7c939aa hotspot/src/share/vm/runtime/mutexLocker.cpp --- a/hotspot/src/share/vm/runtime/mutexLocker.cpp Thu Jan 26 18:23:14 2012 -0800 +++ b/hotspot/src/share/vm/runtime/mutexLocker.cpp Wed Jul 05 18:01:11 2017 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2012, 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 @@ -126,6 +126,7 @@ Mutex* FreeList_lock = NULL; Monitor* SecondaryFreeList_lock = NULL; Mutex* OldSets_lock = NULL; +Monitor* RootRegionScan_lock = NULL; Mutex* MMUTracker_lock = NULL; Mutex* HotCardCache_lock = NULL; @@ -199,6 +200,7 @@ def(FreeList_lock , Mutex, leaf , true ); def(SecondaryFreeList_lock , Monitor, leaf , true ); def(OldSets_lock , Mutex , leaf , true ); + def(RootRegionScan_lock , Monitor, leaf , true ); def(MMUTracker_lock , Mutex , leaf , true ); def(HotCardCache_lock , Mutex , special , true ); def(EvacFailureStack_lock , Mutex , nonleaf , true ); diff -r f920d3637c9f -r 4c63e7c939aa hotspot/src/share/vm/runtime/mutexLocker.hpp --- a/hotspot/src/share/vm/runtime/mutexLocker.hpp Thu Jan 26 18:23:14 2012 -0800 +++ b/hotspot/src/share/vm/runtime/mutexLocker.hpp Wed Jul 05 18:01:11 2017 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2012, 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 @@ -115,7 +115,7 @@ #ifndef PRODUCT extern Mutex* FullGCALot_lock; // a lock to make FullGCALot MT safe -#endif +#endif // PRODUCT extern Mutex* Debug1_lock; // A bunch of pre-allocated locks that can be used for tracing extern Mutex* Debug2_lock; // down synchronization related bugs! extern Mutex* Debug3_lock; @@ -129,6 +129,7 @@ extern Mutex* FreeList_lock; // protects the free region list during safepoints extern Monitor* SecondaryFreeList_lock; // protects the secondary free region list extern Mutex* OldSets_lock; // protects the old region sets +extern Monitor* RootRegionScan_lock; // used to notify that the CM threads have finished scanning the IM snapshot regions extern Mutex* MMUTracker_lock; // protects the MMU // tracker data structures extern Mutex* HotCardCache_lock; // protects the hot card cache diff -r f920d3637c9f -r 4c63e7c939aa hotspot/src/share/vm/runtime/os.hpp --- a/hotspot/src/share/vm/runtime/os.hpp Thu Jan 26 18:23:14 2012 -0800 +++ b/hotspot/src/share/vm/runtime/os.hpp Wed Jul 05 18:01:11 2017 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2012, 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 @@ -73,8 +73,9 @@ MinPriority = 1, // Minimum priority NormPriority = 5, // Normal (non-daemon) priority NearMaxPriority = 9, // High priority, used for VMThread - MaxPriority = 10 // Highest priority, used for WatcherThread + MaxPriority = 10, // Highest priority, used for WatcherThread // ensures that VMThread doesn't starve profiler + CriticalPriority = 11 // Critical thread priority }; // Typedef for structured exception handling support @@ -733,7 +734,7 @@ // Thread priority helpers (implemented in OS-specific part) static OSReturn set_native_priority(Thread* thread, int native_prio); static OSReturn get_native_priority(const Thread* const thread, int* priority_ptr); - static int java_to_os_priority[MaxPriority + 1]; + static int java_to_os_priority[CriticalPriority + 1]; // Hint to the underlying OS that a task switch would not be good. // Void return because it's a hint and can fail. static void hint_no_preempt(); diff -r f920d3637c9f -r 4c63e7c939aa hotspot/src/share/vm/runtime/simpleThresholdPolicy.cpp --- a/hotspot/src/share/vm/runtime/simpleThresholdPolicy.cpp Thu Jan 26 18:23:14 2012 -0800 +++ b/hotspot/src/share/vm/runtime/simpleThresholdPolicy.cpp Wed Jul 05 18:01:11 2017 +0200 @@ -177,13 +177,11 @@ } nmethod* SimpleThresholdPolicy::event(methodHandle method, methodHandle inlinee, - int branch_bci, int bci, CompLevel comp_level, nmethod* nm, TRAPS) { + int branch_bci, int bci, CompLevel comp_level, nmethod* nm, JavaThread* thread) { if (comp_level == CompLevel_none && - JvmtiExport::can_post_interpreter_events()) { - assert(THREAD->is_Java_thread(), "Should be java thread"); - if (((JavaThread*)THREAD)->is_interp_only_mode()) { - return NULL; - } + JvmtiExport::can_post_interpreter_events() && + thread->is_interp_only_mode()) { + return NULL; } nmethod *osr_nm = NULL; @@ -197,9 +195,9 @@ } if (bci == InvocationEntryBci) { - method_invocation_event(method, inlinee, comp_level, nm, THREAD); + method_invocation_event(method, inlinee, comp_level, nm, thread); } else { - method_back_branch_event(method, inlinee, bci, comp_level, nm, THREAD); + method_back_branch_event(method, inlinee, bci, comp_level, nm, thread); // method == inlinee if the event originated in the main method int highest_level = inlinee->highest_osr_comp_level(); if (highest_level > comp_level) { @@ -210,7 +208,7 @@ } // Check if the method can be compiled, change level if necessary -void SimpleThresholdPolicy::compile(methodHandle mh, int bci, CompLevel level, TRAPS) { +void SimpleThresholdPolicy::compile(methodHandle mh, int bci, CompLevel level, JavaThread* thread) { assert(level <= TieredStopAtLevel, "Invalid compilation level"); if (level == CompLevel_none) { return; @@ -221,7 +219,7 @@ // pure C1. if (!can_be_compiled(mh, level)) { if (level == CompLevel_full_optimization && can_be_compiled(mh, CompLevel_simple)) { - compile(mh, bci, CompLevel_simple, THREAD); + compile(mh, bci, CompLevel_simple, thread); } return; } @@ -232,14 +230,14 @@ if (PrintTieredEvents) { print_event(COMPILE, mh, mh, bci, level); } - submit_compile(mh, bci, level, THREAD); + submit_compile(mh, bci, level, thread); } } // Tell the broker to compile the method -void SimpleThresholdPolicy::submit_compile(methodHandle mh, int bci, CompLevel level, TRAPS) { +void SimpleThresholdPolicy::submit_compile(methodHandle mh, int bci, CompLevel level, JavaThread* thread) { int hot_count = (bci == InvocationEntryBci) ? mh->invocation_count() : mh->backedge_count(); - CompileBroker::compile_method(mh, bci, level, mh, hot_count, "tiered", THREAD); + CompileBroker::compile_method(mh, bci, level, mh, hot_count, "tiered", thread); } // Call and loop predicates determine whether a transition to a higher @@ -366,11 +364,11 @@ // Handle the invocation event. void SimpleThresholdPolicy::method_invocation_event(methodHandle mh, methodHandle imh, - CompLevel level, nmethod* nm, TRAPS) { + CompLevel level, nmethod* nm, JavaThread* thread) { if (is_compilation_enabled() && !CompileBroker::compilation_is_in_queue(mh, InvocationEntryBci)) { CompLevel next_level = call_event(mh(), level); if (next_level != level) { - compile(mh, InvocationEntryBci, next_level, THREAD); + compile(mh, InvocationEntryBci, next_level, thread); } } } @@ -378,7 +376,7 @@ // Handle the back branch event. Notice that we can compile the method // with a regular entry from here. void SimpleThresholdPolicy::method_back_branch_event(methodHandle mh, methodHandle imh, - int bci, CompLevel level, nmethod* nm, TRAPS) { + int bci, CompLevel level, nmethod* nm, JavaThread* thread) { // If the method is already compiling, quickly bail out. if (is_compilation_enabled() && !CompileBroker::compilation_is_in_queue(mh, bci)) { // Use loop event as an opportinity to also check there's been @@ -391,13 +389,13 @@ next_osr_level < CompLevel_full_optimization ? next_osr_level : cur_level); bool is_compiling = false; if (next_level != cur_level) { - compile(mh, InvocationEntryBci, next_level, THREAD); + compile(mh, InvocationEntryBci, next_level, thread); is_compiling = true; } // Do the OSR version if (!is_compiling && next_osr_level != level) { - compile(mh, bci, next_osr_level, THREAD); + compile(mh, bci, next_osr_level, thread); } } } diff -r f920d3637c9f -r 4c63e7c939aa hotspot/src/share/vm/runtime/simpleThresholdPolicy.hpp --- a/hotspot/src/share/vm/runtime/simpleThresholdPolicy.hpp Thu Jan 26 18:23:14 2012 -0800 +++ b/hotspot/src/share/vm/runtime/simpleThresholdPolicy.hpp Wed Jul 05 18:01:11 2017 +0200 @@ -67,9 +67,9 @@ // Print policy-specific information if necessary virtual void print_specific(EventType type, methodHandle mh, methodHandle imh, int bci, CompLevel level) { } // Check if the method can be compiled, change level if necessary - void compile(methodHandle mh, int bci, CompLevel level, TRAPS); + void compile(methodHandle mh, int bci, CompLevel level, JavaThread* thread); // Submit a given method for compilation - virtual void submit_compile(methodHandle mh, int bci, CompLevel level, TRAPS); + virtual void submit_compile(methodHandle mh, int bci, CompLevel level, JavaThread* thread); // Simple methods are as good being compiled with C1 as C2. // This function tells if it's such a function. inline bool is_trivial(methodOop method); @@ -88,9 +88,9 @@ return CompLevel_none; } virtual void method_invocation_event(methodHandle method, methodHandle inlinee, - CompLevel level, nmethod* nm, TRAPS); + CompLevel level, nmethod* nm, JavaThread* thread); virtual void method_back_branch_event(methodHandle method, methodHandle inlinee, - int bci, CompLevel level, nmethod* nm, TRAPS); + int bci, CompLevel level, nmethod* nm, JavaThread* thread); public: SimpleThresholdPolicy() : _c1_count(0), _c2_count(0) { } virtual int compiler_count(CompLevel comp_level) { @@ -104,7 +104,7 @@ virtual void disable_compilation(methodOop method) { } virtual void reprofile(ScopeDesc* trap_scope, bool is_osr); virtual nmethod* event(methodHandle method, methodHandle inlinee, - int branch_bci, int bci, CompLevel comp_level, nmethod* nm, TRAPS); + int branch_bci, int bci, CompLevel comp_level, nmethod* nm, JavaThread* thread); // Select task is called by CompileBroker. We should return a task or NULL. virtual CompileTask* select_task(CompileQueue* compile_queue); // Tell the runtime if we think a given method is adequately profiled. diff -r f920d3637c9f -r 4c63e7c939aa hotspot/src/share/vm/services/diagnosticArgument.cpp --- a/hotspot/src/share/vm/services/diagnosticArgument.cpp Thu Jan 26 18:23:14 2012 -0800 +++ b/hotspot/src/share/vm/services/diagnosticArgument.cpp Wed Jul 05 18:01:11 2017 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2012 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 @@ -59,12 +59,13 @@ template <> void DCmdArgument::parse_value(const char* str, size_t len, TRAPS) { + // len is the length of the current token starting at str if (len == 0) { set_value(true); } else { - if (strcasecmp(str, "true") == 0) { + if (len == strlen("true") && strncasecmp(str, "true", len) == 0) { set_value(true); - } else if (strcasecmp(str, "false") == 0) { + } else if (len == strlen("false") && strncasecmp(str, "false", len) == 0) { set_value(false); } else { THROW_MSG(vmSymbols::java_lang_IllegalArgumentException(), diff -r f920d3637c9f -r 4c63e7c939aa hotspot/src/share/vm/services/diagnosticCommand.cpp --- a/hotspot/src/share/vm/services/diagnosticCommand.cpp Thu Jan 26 18:23:14 2012 -0800 +++ b/hotspot/src/share/vm/services/diagnosticCommand.cpp Wed Jul 05 18:01:11 2017 +0200 @@ -31,6 +31,33 @@ #include "services/heapDumper.hpp" #include "services/management.hpp" +void DCmdRegistrant::register_dcmds(){ + // Registration of the diagnostic commands + // First boolean argument specifies if the command is enabled + // Second boolean argument specifies if the command is hidden + DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl(true, false)); + DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl(true, false)); + DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl(true, false)); + DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl(true, false)); + DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl(true, false)); + DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl(true, false)); + DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl(true, false)); + DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl(true, false)); +#ifndef SERVICES_KERNEL // Heap dumping not supported + DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl(true, false)); +#endif // SERVICES_KERNEL + DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl(true, false)); + DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl(true, false)); + +} + +#ifndef HAVE_EXTRA_DCMD +void DCmdRegistrant::register_dcmds_ext(){ + // Do nothing here +} +#endif + + HelpDCmd::HelpDCmd(outputStream* output, bool heap) : DCmdWithParser(output, heap), _all("-all", "Show help for all commands", "BOOLEAN", false, "false"), _cmd("command name", "The name of the command for which we want help", diff -r f920d3637c9f -r 4c63e7c939aa hotspot/src/share/vm/services/diagnosticCommand.hpp --- a/hotspot/src/share/vm/services/diagnosticCommand.hpp Thu Jan 26 18:23:14 2012 -0800 +++ b/hotspot/src/share/vm/services/diagnosticCommand.hpp Wed Jul 05 18:01:11 2017 +0200 @@ -34,6 +34,7 @@ #include "services/diagnosticArgument.hpp" #include "services/diagnosticCommand.hpp" #include "services/diagnosticFramework.hpp" +#include "services/diagnosticCommand_ext.hpp" class HelpDCmd : public DCmdWithParser { protected: diff -r f920d3637c9f -r 4c63e7c939aa hotspot/src/share/vm/services/diagnosticCommand_ext.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hotspot/src/share/vm/services/diagnosticCommand_ext.hpp Wed Jul 05 18:01:11 2017 +0200 @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2012, 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_SERVICES_DIAGNOSTICCOMMAND_EXT_HPP +#define SHARE_VM_SERVICES_DIAGNOSTICCOMMAND_EXT_HPP + +#undef HAVE_EXTRA_DCMD + +#endif // SHARE_VM_SERVICES_DIAGNOSTICCOMMAND_HPP diff -r f920d3637c9f -r 4c63e7c939aa hotspot/src/share/vm/services/diagnosticFramework.hpp --- a/hotspot/src/share/vm/services/diagnosticFramework.hpp Thu Jan 26 18:23:14 2012 -0800 +++ b/hotspot/src/share/vm/services/diagnosticFramework.hpp Wed Jul 05 18:01:11 2017 +0200 @@ -387,4 +387,17 @@ } }; +// This class provides a convenient way to register Dcmds, without a need to change +// management.cpp every time. Body of these two methods resides in +// diagnosticCommand.cpp + +class DCmdRegistrant : public AllStatic { + +private: + static void register_dcmds(); + static void register_dcmds_ext(); + + friend class Management; +}; + #endif // SHARE_VM_SERVICES_DIAGNOSTICFRAMEWORK_HPP diff -r f920d3637c9f -r 4c63e7c939aa hotspot/src/share/vm/services/g1MemoryPool.cpp --- a/hotspot/src/share/vm/services/g1MemoryPool.cpp Thu Jan 26 18:23:14 2012 -0800 +++ b/hotspot/src/share/vm/services/g1MemoryPool.cpp Wed Jul 05 18:01:11 2017 +0200 @@ -78,7 +78,7 @@ G1MemoryPoolSuper(g1h, "G1 Old Gen", g1h->g1mm()->old_space_committed(), /* init_size */ - _undefined_max, + g1h->g1mm()->old_gen_max(), true /* support_usage_threshold */) { } MemoryUsage G1OldGenPool::get_memory_usage() { diff -r f920d3637c9f -r 4c63e7c939aa hotspot/src/share/vm/services/g1MemoryPool.hpp --- a/hotspot/src/share/vm/services/g1MemoryPool.hpp Thu Jan 26 18:23:14 2012 -0800 +++ b/hotspot/src/share/vm/services/g1MemoryPool.hpp Wed Jul 05 18:01:11 2017 +0200 @@ -101,7 +101,7 @@ return _g1mm->old_space_used(); } size_t max_size() const { - return _undefined_max; + return _g1mm->old_gen_max(); } MemoryUsage get_memory_usage(); }; diff -r f920d3637c9f -r 4c63e7c939aa hotspot/src/share/vm/services/gcNotifier.cpp --- a/hotspot/src/share/vm/services/gcNotifier.cpp Thu Jan 26 18:23:14 2012 -0800 +++ b/hotspot/src/share/vm/services/gcNotifier.cpp Wed Jul 05 18:01:11 2017 +0200 @@ -44,7 +44,8 @@ // Make a copy of the last GC statistics // GC may occur between now and the creation of the notification int num_pools = MemoryService::num_memory_pools(); - GCStatInfo* stat = new GCStatInfo(num_pools); + // stat is deallocated inside GCNotificationRequest + GCStatInfo* stat = new(ResourceObj::C_HEAP) GCStatInfo(num_pools); mgr->get_last_gc_stat(stat); GCNotificationRequest *request = new GCNotificationRequest(os::javaTimeMillis(),mgr,action,cause,stat); addRequest(request); diff -r f920d3637c9f -r 4c63e7c939aa hotspot/src/share/vm/services/management.cpp --- a/hotspot/src/share/vm/services/management.cpp Thu Jan 26 18:23:14 2012 -0800 +++ b/hotspot/src/share/vm/services/management.cpp Wed Jul 05 18:01:11 2017 +0200 @@ -119,21 +119,8 @@ _optional_support.isThreadAllocatedMemorySupported = 1; // Registration of the diagnostic commands - // First boolean argument specifies if the command is enabled - // Second boolean argument specifies if the command is hidden - DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl(true, false)); - DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl(true, false)); - DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl(true, false)); - DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl(true, false)); - DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl(true, false)); - DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl(true, false)); - DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl(true, false)); - DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl(true, false)); -#ifndef SERVICES_KERNEL // Heap dumping not supported - DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl(true, false)); -#endif // SERVICES_KERNEL - DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl(true, false)); - DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl(true, false)); + DCmdRegistrant::register_dcmds(); + DCmdRegistrant::register_dcmds_ext(); } void Management::initialize(TRAPS) { @@ -2047,15 +2034,15 @@ // Make a copy of the last GC statistics // GC may occur while constructing the last GC information int num_pools = MemoryService::num_memory_pools(); - GCStatInfo* stat = new GCStatInfo(num_pools); - if (mgr->get_last_gc_stat(stat) == 0) { + GCStatInfo stat(num_pools); + if (mgr->get_last_gc_stat(&stat) == 0) { gc_stat->gc_index = 0; return; } - gc_stat->gc_index = stat->gc_index(); - gc_stat->start_time = Management::ticks_to_ms(stat->start_time()); - gc_stat->end_time = Management::ticks_to_ms(stat->end_time()); + gc_stat->gc_index = stat.gc_index(); + gc_stat->start_time = Management::ticks_to_ms(stat.start_time()); + gc_stat->end_time = Management::ticks_to_ms(stat.end_time()); // Current implementation does not have GC extension attributes gc_stat->num_gc_ext_attributes = 0; @@ -2073,17 +2060,17 @@ objArrayHandle usage_after_gc_ah(THREAD, au); for (int i = 0; i < num_pools; i++) { - Handle before_usage = MemoryService::create_MemoryUsage_obj(stat->before_gc_usage_for_pool(i), CHECK); + Handle before_usage = MemoryService::create_MemoryUsage_obj(stat.before_gc_usage_for_pool(i), CHECK); Handle after_usage; - MemoryUsage u = stat->after_gc_usage_for_pool(i); + MemoryUsage u = stat.after_gc_usage_for_pool(i); if (u.max_size() == 0 && u.used() > 0) { // If max size == 0, this pool is a survivor space. // Set max size = -1 since the pools will be swapped after GC. MemoryUsage usage(u.init_size(), u.used(), u.committed(), (size_t)-1); after_usage = MemoryService::create_MemoryUsage_obj(usage, CHECK); } else { - after_usage = MemoryService::create_MemoryUsage_obj(stat->after_gc_usage_for_pool(i), CHECK); + after_usage = MemoryService::create_MemoryUsage_obj(stat.after_gc_usage_for_pool(i), CHECK); } usage_before_gc_ah->obj_at_put(i, before_usage()); usage_after_gc_ah->obj_at_put(i, after_usage()); diff -r f920d3637c9f -r 4c63e7c939aa hotspot/src/share/vm/services/memoryManager.cpp --- a/hotspot/src/share/vm/services/memoryManager.cpp Thu Jan 26 18:23:14 2012 -0800 +++ b/hotspot/src/share/vm/services/memoryManager.cpp Wed Jul 05 18:01:11 2017 +0200 @@ -214,8 +214,8 @@ void GCMemoryManager::initialize_gc_stat_info() { assert(MemoryService::num_memory_pools() > 0, "should have one or more memory pools"); - _last_gc_stat = new GCStatInfo(MemoryService::num_memory_pools()); - _current_gc_stat = new GCStatInfo(MemoryService::num_memory_pools()); + _last_gc_stat = new(ResourceObj::C_HEAP) GCStatInfo(MemoryService::num_memory_pools()); + _current_gc_stat = new(ResourceObj::C_HEAP) GCStatInfo(MemoryService::num_memory_pools()); // tracking concurrent collections we need two objects: one to update, and one to // hold the publicly available "last (completed) gc" information. } diff -r f920d3637c9f -r 4c63e7c939aa hotspot/src/share/vm/services/memoryManager.hpp --- a/hotspot/src/share/vm/services/memoryManager.hpp Thu Jan 26 18:23:14 2012 -0800 +++ b/hotspot/src/share/vm/services/memoryManager.hpp Wed Jul 05 18:01:11 2017 +0200 @@ -108,7 +108,7 @@ const char* name() { return "CodeCacheManager"; } }; -class GCStatInfo : public CHeapObj { +class GCStatInfo : public ResourceObj { private: size_t _index; jlong _start_time; diff -r f920d3637c9f -r 4c63e7c939aa hotspot/src/share/vm/utilities/bitMap.inline.hpp --- a/hotspot/src/share/vm/utilities/bitMap.inline.hpp Thu Jan 26 18:23:14 2012 -0800 +++ b/hotspot/src/share/vm/utilities/bitMap.inline.hpp Wed Jul 05 18:01:11 2017 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2012, 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 @@ -178,8 +178,30 @@ for (; !(res & 1); res_offset++) { res = res >> 1; } - assert(res_offset >= l_offset && - res_offset < r_offset, "just checking"); + +#ifdef ASSERT + // In the following assert, if r_offset is not bitamp word aligned, + // checking that res_offset is strictly less than r_offset is too + // strong and will trip the assert. + // + // Consider the case where l_offset is bit 15 and r_offset is bit 17 + // of the same map word, and where bits [15:16:17:18] == [00:00:00:01]. + // All the bits in the range [l_offset:r_offset) are 0. + // The loop that calculates res_offset, above, would yield the offset + // of bit 18 because it's in the same map word as l_offset and there + // is a set bit in that map word above l_offset (i.e. res != NoBits). + // + // In this case, however, we can assert is that res_offset is strictly + // less than size() since we know that there is at least one set bit + // at an offset above, but in the same map word as, r_offset. + // Otherwise, if r_offset is word aligned then it will not be in the + // same map word as l_offset (unless it equals l_offset). So either + // there won't be a set bit between l_offset and the end of it's map + // word (i.e. res == NoBits), or res_offset will be less than r_offset. + + idx_t limit = is_word_aligned(r_offset) ? r_offset : size(); + assert(res_offset >= l_offset && res_offset < limit, "just checking"); +#endif // ASSERT return MIN2(res_offset, r_offset); } // skip over all word length 0-bit runs diff -r f920d3637c9f -r 4c63e7c939aa hotspot/src/share/vm/utilities/exceptions.hpp --- a/hotspot/src/share/vm/utilities/exceptions.hpp Thu Jan 26 18:23:14 2012 -0800 +++ b/hotspot/src/share/vm/utilities/exceptions.hpp Wed Jul 05 18:01:11 2017 +0200 @@ -189,6 +189,13 @@ #define CHECK_NULL CHECK_(NULL) #define CHECK_false CHECK_(false) +#define CHECK_AND_CLEAR THREAD); if (HAS_PENDING_EXCEPTION) { CLEAR_PENDING_EXCEPTION; return; } (0 +#define CHECK_AND_CLEAR_(result) THREAD); if (HAS_PENDING_EXCEPTION) { CLEAR_PENDING_EXCEPTION; return result; } (0 +#define CHECK_AND_CLEAR_0 CHECK_AND_CLEAR_(0) +#define CHECK_AND_CLEAR_NH CHECK_AND_CLEAR_(Handle()) +#define CHECK_AND_CLEAR_NULL CHECK_AND_CLEAR_(NULL) +#define CHECK_AND_CLEAR_false CHECK_AND_CLEAR_(false) + // The THROW... macros should be used to throw an exception. They require a THREAD variable to be // visible within the scope containing the THROW. Usually this is achieved by declaring the function // with a TRAPS argument. @@ -258,7 +265,6 @@ ShouldNotReachHere(); \ } (0 - // ExceptionMark is a stack-allocated helper class for local exception handling. // It is used with the EXCEPTION_MARK macro. diff -r f920d3637c9f -r 4c63e7c939aa hotspot/src/share/vm/utilities/globalDefinitions_visCPP.hpp --- a/hotspot/src/share/vm/utilities/globalDefinitions_visCPP.hpp Thu Jan 26 18:23:14 2012 -0800 +++ b/hotspot/src/share/vm/utilities/globalDefinitions_visCPP.hpp Wed Jul 05 18:01:11 2017 +0200 @@ -130,6 +130,9 @@ //---------------------------------------------------------------------------------------------------- // Non-standard stdlib-like stuff: inline int strcasecmp(const char *s1, const char *s2) { return _stricmp(s1,s2); } +inline int strncasecmp(const char *s1, const char *s2, size_t n) { + return _strnicmp(s1,s2,n); +} //---------------------------------------------------------------------------------------------------- diff -r f920d3637c9f -r 4c63e7c939aa jdk/.hgtags --- a/jdk/.hgtags Thu Jan 26 18:23:14 2012 -0800 +++ b/jdk/.hgtags Wed Jul 05 18:01:11 2017 +0200 @@ -144,3 +144,4 @@ 39e938cd1b82ec3aab0a9aa66fd8a0457cd0c9c2 jdk8-b20 664fa4fb0ee411ef048903c479f8b962fcdb2f4b jdk8-b21 dda27c73d8db4a9c7a23872b6f0c5106edcb2021 jdk8-b22 +54202e0148ec7d4570cab5bc9b00d216a7677569 jdk8-b23