diff --git a/.hgtags b/.hgtags
index 93926ee960b..0a1b78991e9 100644
--- a/.hgtags
+++ b/.hgtags
@@ -459,3 +459,4 @@ e6278add9ff28fab70fe1cc4c1d65f7363dc9445 jdk-10+31
 a2008587c13fa05fa2dbfcb09fe987576fbedfd1 jdk-10+32
 bbd692ad4fa300ecca7939ffbe3b1d5e52a28cc6 jdk-10+33
 89deac44e51517841491ba86ff44aa82a5ca96b3 jdk-10+34
+d8c634b016c628622c9abbdc6bf50509e5dedbec jdk-10+35
diff --git a/doc/testing.html b/doc/testing.html
index d71f7e569e8..be4c23302bd 100644
--- a/doc/testing.html
+++ b/doc/testing.html
@@ -57,6 +57,7 @@ $ make exploded-run-test TEST=hotspot_tier1</code></pre>
 <h3 id="gtest">Gtest</h3>
 <p>Since the Hotspot Gtest suite is so quick, the default is to run all tests. This is specified by just <code>gtest</code>, or as a fully qualified test descriptor <code>gtest:all</code>.</p>
 <p>If you want, you can single out an individual test or a group of tests, for instance <code>gtest:LogDecorations</code> or <code>gtest:LogDecorations.level_test_vm</code>. This can be particularly useful if you want to run a shaky test repeatedly.</p>
+<p>For Gtest, there is a separate test suite for each JVM variant. The JVM variant is defined by adding <code>/&lt;variant&gt;</code> to the test descriptor, e.g. <code>gtest:Log/client</code>. If you specify no variant, gtest will run once for each JVM variant present (e.g. server, client). So if you only have the server JVM present, then <code>gtest:all</code> will be equivalent to <code>gtest:all/server</code>.</p>
 <h2 id="test-results-and-summary">Test results and summary</h2>
 <p>At the end of the test run, a summary of all tests run will be presented. This will have a consistent look, regardless of what test suites were used. This is a sample summary:</p>
 <pre><code>==============================
diff --git a/doc/testing.md b/doc/testing.md
index ffd685ff516..da9b8ca7624 100644
--- a/doc/testing.md
+++ b/doc/testing.md
@@ -81,6 +81,12 @@ If you want, you can single out an individual test or a group of tests, for
 instance `gtest:LogDecorations` or `gtest:LogDecorations.level_test_vm`. This
 can be particularly useful if you want to run a shaky test repeatedly.
 
+For Gtest, there is a separate test suite for each JVM variant. The JVM variant
+is defined by adding `/<variant>` to the test descriptor, e.g.
+`gtest:Log/client`. If you specify no variant, gtest will run once for each JVM
+variant present (e.g. server, client). So if you only have the server JVM
+present, then `gtest:all` will be equivalent to `gtest:all/server`.
+
 ## Test results and summary
 
 At the end of the test run, a summary of all tests run will be presented. This
diff --git a/make/CompileInterimLangtools.gmk b/make/CompileInterimLangtools.gmk
index afdb14a936a..b8ff3af48ad 100644
--- a/make/CompileInterimLangtools.gmk
+++ b/make/CompileInterimLangtools.gmk
@@ -69,8 +69,8 @@ define SetupInterimModule
           Standard.java, \
       EXTRA_FILES := $(BUILDTOOLS_OUTPUTDIR)/gensrc/$1.interim/module-info.java, \
       COPY := .gif .png .xml .css .js javax.tools.JavaCompilerTool, \
-      BIN := $(BUILDTOOLS_OUTPUTDIR)/interim_modules/$1.interim, \
-      ADD_JAVAC_FLAGS := --module-path $(BUILDTOOLS_OUTPUTDIR)/interim_modules \
+      BIN := $(BUILDTOOLS_OUTPUTDIR)/interim_langtools_modules/$1.interim, \
+      ADD_JAVAC_FLAGS := --module-path $(BUILDTOOLS_OUTPUTDIR)/interim_langtools_modules \
           $$(INTERIM_LANGTOOLS_ADD_EXPORTS) \
           -Xlint:-module, \
   ))
diff --git a/make/CompileInterimRmic.gmk b/make/CompileInterimRmic.gmk
index acc02c34bc8..6127e930c09 100644
--- a/make/CompileInterimRmic.gmk
+++ b/make/CompileInterimRmic.gmk
@@ -65,10 +65,10 @@ $(eval $(call SetupJavaCompilation, BUILD_jdk.rmic.interim, \
     EXCLUDE_FILES := $(TOPDIR)/src/jdk.rmic/share/classes/module-info.java, \
     EXTRA_FILES := $(BUILDTOOLS_OUTPUTDIR)/gensrc/jdk.rmic.interim/module-info.java, \
     INCLUDES := $(RMIC_PKGS), \
-    BIN := $(BUILDTOOLS_OUTPUTDIR)/interim_modules/jdk.rmic.interim, \
+    BIN := $(BUILDTOOLS_OUTPUTDIR)/interim_rmic_modules/jdk.rmic.interim, \
     COPY := .properties, \
     ADD_JAVAC_FLAGS := \
-        --module-path $(BUILDTOOLS_OUTPUTDIR)/interim_modules \
+        --module-path $(BUILDTOOLS_OUTPUTDIR)/interim_rmic_modules \
         --add-modules java.corba \
         --add-exports java.corba/com.sun.corba.se.impl.util=jdk.rmic.interim \
         $(INTERIM_RMIC_ADD_EXPORTS), \
diff --git a/make/CreateJmods.gmk b/make/CreateJmods.gmk
index d1e05718dcf..0b530bb8b6b 100644
--- a/make/CreateJmods.gmk
+++ b/make/CreateJmods.gmk
@@ -1,5 +1,4 @@
-
-# Copyright (c) 2014, 2016, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2014, 2017, 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
@@ -33,6 +32,8 @@ ifeq ($(MODULE), )
   $(error MODULE must be set when calling CreateJmods.gmk)
 endif
 
+$(eval $(call IncludeCustomExtension, CreateJmods.gmk))
+
 ################################################################################
 
 JMODS_DIR := $(IMAGES_OUTPUTDIR)/jmods
diff --git a/make/Help.gmk b/make/Help.gmk
index e7f0bfa29de..b6974c71de4 100644
--- a/make/Help.gmk
+++ b/make/Help.gmk
@@ -115,6 +115,13 @@ print-configurations:
         # We need a dummy rule otherwise make will complain
 	@true
 
-ALL_GLOBAL_TARGETS := help print-configurations
+# This is not really a "help" target, but it is a global target, and those are
+# all contained in this file.
+run-test-prebuilt:
+	@( cd $(topdir) && \
+	    $(MAKE) --no-print-directory -r -R -I make/common/ -f make/RunTestsPrebuilt.gmk \
+	    run-test-prebuilt TEST="$(TEST)" )
+
+ALL_GLOBAL_TARGETS := help print-configurations run-test-prebuilt
 
 .PHONY: $(ALL_GLOBAL_TARGETS)
diff --git a/make/InitSupport.gmk b/make/InitSupport.gmk
index f4605766ccf..48af7838450 100644
--- a/make/InitSupport.gmk
+++ b/make/InitSupport.gmk
@@ -279,7 +279,9 @@ ifeq ($(HAS_SPEC),)
           # generated files.
           ifeq ($$(MAKE_RESTARTS),)
             ifeq ($$(words $$(matching_confs)), 1)
-              $$(info Building configuration '$$(matching_confs)' (matching CONF=$$(CONF)))
+              ifneq ($$(findstring $$(LOG_LEVEL), info debug trace),)
+                $$(info Building configuration '$$(matching_confs)' (matching CONF=$$(CONF)))
+              endif
             else
               $$(info Building these configurations (matching CONF=$$(CONF)):)
               $$(foreach var, $$(matching_confs), $$(info * $$(var)))
diff --git a/make/RunTests.gmk b/make/RunTests.gmk
index 1153bae8f02..81cb8ff2ba8 100644
--- a/make/RunTests.gmk
+++ b/make/RunTests.gmk
@@ -88,6 +88,9 @@ ifneq ($(wildcard $(JTREG_FAILURE_HANDLER)), )
       -timeoutHandlerTimeout:0
 endif
 
+GTEST_LAUNCHER_DIRS := $(patsubst %/gtestLauncher, %, $(wildcard $(TEST_IMAGE_DIR)/hotspot/gtest/*/gtestLauncher))
+GTEST_VARIANTS := $(strip $(patsubst $(TEST_IMAGE_DIR)/hotspot/gtest/%, %, $(GTEST_LAUNCHER_DIRS)))
+
 ################################################################################
 # Parse control variables
 ################################################################################
@@ -166,16 +169,23 @@ hotspot_JTREG_PROBLEM_LIST += $(TOPDIR)/test/hotspot/jtreg/ProblemList.txt
 # Helper function to determine if a test specification is a Gtest test
 #
 # It is a Gtest test if it is either "gtest", or "gtest:" followed by an optional
-# test filter string.
+# test filter string, and an optional "/<variant>" to select a specific JVM
+# variant. If no variant is specified, all found variants are tested.
 define ParseGtestTestSelection
   $(if $(filter gtest%, $1), \
     $(if $(filter gtest, $1), \
-      gtest:all \
+      $(addprefix gtest:all/, $(GTEST_VARIANTS)) \
     , \
-      $(if $(filter gtest:, $1), \
-        gtest:all \
+      $(if $(strip $(or $(filter gtest/%, $1) $(filter gtest:/%, $1))), \
+        $(patsubst gtest:/%, gtest:all/%, $(patsubst gtest/%, gtest:/%, $1)) \
       , \
-        $1 \
+        $(if $(filter gtest:%, $1), \
+          $(if $(findstring /, $1), \
+            $1 \
+          , \
+            $(addprefix $1/, $(GTEST_VARIANTS)) \
+          ) \
+        ) \
       ) \
     ) \
   )
@@ -253,6 +263,15 @@ define ParseJtregTestSelection
   )
 endef
 
+# Helper function to determine if a test specification is a special test
+#
+# It is a special test if it is "special:" followed by a test name.
+define ParseSpecialTestSelection
+  $(if $(filter special:%, $1), \
+    $1 \
+  )
+endef
+
 ifeq ($(TEST), )
   $(info No test selection given in TEST!)
   $(info Please use e.g. 'run-test TEST=tier1' or 'run-test-tier1')
@@ -271,6 +290,9 @@ $(foreach test, $(TEST), \
   $(if $(strip $(PARSED_TESTS)), , \
     $(eval PARSED_TESTS += $(call ParseJtregTestSelection, $(test))) \
   ) \
+  $(if $(strip $(PARSED_TESTS)), , \
+    $(eval PARSED_TESTS += $(call ParseSpecialTestSelection, $(test))) \
+  ) \
   $(if $(strip $(PARSED_TESTS)), , \
     $(eval UNKNOWN_TEST := $(test)) \
   ) \
@@ -320,7 +342,12 @@ define SetupRunGtestTestBody
   $1_TEST_SUPPORT_DIR := $$(TEST_SUPPORT_DIR)/$1
   $1_EXITCODE := $$($1_TEST_RESULTS_DIR)/exitcode.txt
 
-  $1_TEST_NAME := $$(strip $$(patsubst gtest:%, %, $$($1_TEST)))
+  $1_VARIANT :=  $$(lastword $$(subst /, , $$($1_TEST)))
+  ifeq ($$(filter $$($1_VARIANT), $$(GTEST_VARIANTS)), )
+    $$(error Invalid gtest variant '$$($1_VARIANT)'. Valid variants: $$(GTEST_VARIANTS))
+  endif
+  $1_TEST_NAME := $$(strip $$(patsubst %/$$($1_VARIANT), %, \
+      $$(patsubst gtest:%, %, $$($1_TEST))))
   ifneq ($$($1_TEST_NAME), all)
     $1_GTEST_FILTER := --gtest_filter=$$($1_TEST_NAME)*
   endif
@@ -334,7 +361,7 @@ define SetupRunGtestTestBody
 	$$(call LogWarn, Running test '$$($1_TEST)')
 	$$(call MakeDir, $$($1_TEST_RESULTS_DIR) $$($1_TEST_SUPPORT_DIR))
 	$$(call ExecuteWithLog, $$($1_TEST_SUPPORT_DIR)/gtest, \
-	    $$(FIXPATH) $$(TEST_IMAGE_DIR)/hotspot/gtest/server/gtestLauncher \
+	    $$(FIXPATH) $$(TEST_IMAGE_DIR)/hotspot/gtest/$$($1_VARIANT)/gtestLauncher \
 	         -jdk $(JDK_IMAGE_DIR) $$($1_GTEST_FILTER) \
 	         --gtest_output=xml:$$($1_TEST_RESULTS_DIR)/gtest.xml \
 	         $$($1_GTEST_REPEAT) $$(GTEST_OPTIONS) $$(GTEST_VM_OPTIONS) \
@@ -550,6 +577,69 @@ define SetupRunJtregTestBody
   TARGETS += $1
 endef
 
+################################################################################
+
+### Rules for special tests
+
+SetupRunSpecialTest = $(NamedParamsMacroTemplate)
+define SetupRunSpecialTestBody
+  $1_TEST_RESULTS_DIR := $$(TEST_RESULTS_DIR)/$1
+  $1_TEST_SUPPORT_DIR := $$(TEST_SUPPORT_DIR)/$1
+  $1_EXITCODE := $$($1_TEST_RESULTS_DIR)/exitcode.txt
+
+  $1_FULL_TEST_NAME := $$(strip $$(patsubst special:%, %, $$($1_TEST)))
+  ifneq ($$(findstring :, $$($1_FULL_TEST_NAME)), )
+    $1_TEST_NAME := $$(firstword $$(subst :, ,$$($1_FULL_TEST_NAME)))
+    $1_TEST_ARGS := $$(strip $$(patsubst special:$$($1_TEST_NAME):%, %, $$($1_TEST)))
+  else
+    $1_TEST_NAME := $$($1_FULL_TEST_NAME)
+    $1_TEST_ARGS :=
+  endif
+
+  ifeq ($$($1_TEST_NAME), hotspot-internal)
+    $1_TEST_COMMAND_LINE := \
+        $$(JDK_IMAGE_DIR)/bin/java -XX:+ExecuteInternalVMTests \
+        -XX:+ShowMessageBoxOnError -version
+  else ifeq ($$($1_TEST_NAME), failure-handler)
+    $1_TEST_COMMAND_LINE := \
+        ($(CD) $(TOPDIR)/make/test && $(MAKE) $(MAKE_ARGS) -f \
+        BuildFailureHandler.gmk test)
+  else ifeq ($$($1_TEST_NAME), make)
+    $1_TEST_COMMAND_LINE := \
+        ($(CD) $(TOPDIR)/test/make && $(MAKE) $(MAKE_ARGS) -f \
+        TestMake.gmk $$($1_TEST_ARGS))
+  else
+    $$(error Invalid special test specification: $$($1_TEST_NAME))
+  endif
+
+  run-test-$1:
+	$$(call LogWarn)
+	$$(call LogWarn, Running test '$$($1_TEST)')
+	$$(call MakeDir, $$($1_TEST_RESULTS_DIR) $$($1_TEST_SUPPORT_DIR))
+	$$(call ExecuteWithLog, $$($1_TEST_SUPPORT_DIR)/test-execution, \
+	    $$($1_TEST_COMMAND_LINE) \
+	        > >($(TEE) $$($1_TEST_RESULTS_DIR)/test-output.txt) \
+	    && $$(ECHO) $$$$? > $$($1_EXITCODE) \
+	    || $$(ECHO) $$$$? > $$($1_EXITCODE) \
+	)
+
+  $1_RESULT_FILE := $$($1_TEST_RESULTS_DIR)/gtest.txt
+
+  # We can not parse the various "special" tests.
+  parse-test-$1: run-test-$1
+	$$(call LogWarn, Finished running test '$$($1_TEST)')
+	$$(call LogWarn, Test report is stored in $$(strip \
+	    $$(subst $$(TOPDIR)/, , $$($1_TEST_RESULTS_DIR))))
+	$$(call LogWarn, Warning: Special test results are not properly parsed!)
+	$$(eval $1_PASSED := 0)
+	$$(eval $1_FAILED := 0)
+	$$(eval $1_ERROR := 0)
+	$$(eval $1_TOTAL := 0)
+
+  $1: run-test-$1 parse-test-$1
+
+  TARGETS += $1
+endef
 
 ################################################################################
 # Setup and execute make rules for all selected tests
@@ -562,6 +652,9 @@ UseGtestTestHandler = \
 UseJtregTestHandler = \
   $(if $(filter jtreg:%, $1), true)
 
+UseSpecialTestHandler = \
+  $(if $(filter special:%, $1), true)
+
 # Now process each test to run and setup a proper make rule
 $(foreach test, $(TESTS_TO_RUN), \
   $(eval TEST_ID := $(shell $(ECHO) $(strip $(test)) | \
@@ -582,6 +675,11 @@ $(foreach test, $(TESTS_TO_RUN), \
         TEST := $(test), \
     )) \
   ) \
+  $(if $(call UseSpecialTestHandler, $(test)), \
+    $(eval $(call SetupRunSpecialTest, $(TEST_ID), \
+        TEST := $(test), \
+    )) \
+  ) \
 )
 
 # Sort also removes duplicates, so if there is any we'll get fewer words.
diff --git a/make/RunTestsPrebuilt.gmk b/make/RunTestsPrebuilt.gmk
new file mode 100644
index 00000000000..ca5bc6f49e7
--- /dev/null
+++ b/make/RunTestsPrebuilt.gmk
@@ -0,0 +1,283 @@
+#
+# Copyright (c) 2017, 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.  Oracle designates this
+# particular file as subject to the "Classpath" exception as provided
+# by Oracle in the LICENSE file that accompanied this code.
+#
+# 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.
+#
+
+################################################################################
+# Initial bootstrapping, copied and stripped down from Makefile and Init.gmk
+################################################################################
+
+# In Cygwin, the MAKE variable gets prepended with the current directory if the
+# make executable is called using a Windows mixed path (c:/cygwin/bin/make.exe).
+ifneq ($(findstring :, $(MAKE)), )
+  export MAKE := $(patsubst $(CURDIR)%, %, $(patsubst $(CURDIR)/%, %, $(MAKE)))
+endif
+
+# Locate this Makefile
+ifeq ($(filter /%, $(lastword $(MAKEFILE_LIST))),)
+  makefile_path := $(CURDIR)/$(strip $(lastword $(MAKEFILE_LIST)))
+else
+  makefile_path := $(lastword $(MAKEFILE_LIST))
+endif
+TOPDIR := $(strip $(patsubst %/make/, %, $(dir $(makefile_path))))
+
+################################################################################
+# Functions
+################################################################################
+
+# Setup a required or optional variable, and/or check that it is properly
+# given.
+# Note: No spaces are allowed around the arguments.
+#
+# $1: The name of the argument
+# $2: The default value, if any, or OPTIONAL (do not provide a default but
+#     do not exit if it is missing)
+# $3: If NO_CHECK, disable checking for target file/directory existence
+define SetupVariable
+  ifeq ($$($1), )
+    ifeq ($2, )
+      $$(info Error: Prebuilt variable $1 is missing, needed for run-tests-prebuilt)
+      $$(error Cannot continue.)
+    else ifeq ($2, OPTIONAL)
+      ifneq ($$(findstring $$(LOG), info debug trace), )
+        $$(info Prebuilt variable $1 is not provided)
+      endif
+    else
+      ifneq ($$(findstring $$(LOG), info debug trace), )
+        $$(info Prebuilt variable $1=$2 (default value))
+      endif
+      $1:=$2
+    endif
+  else
+    ifneq ($$(findstring $$(LOG), info debug trace), )
+      $$(info Prebuilt variable $1=$$($1))
+    endif
+  endif
+  # If $1 has a value (is not optional), and $3 is not set (to NO_CHECK),
+  # and if wildcard is empty, then complain that the file is missing.
+  ifeq ($$(strip $$(if $$($1), , OPTIONAL) $$(wildcard $$($1)) $3), )
+    $$(info Error: Prebuilt variable $1 points to missing file/directory:)
+    $$(info '$$($1)')
+    $$(error Cannot continue.)
+  endif
+endef
+
+# Create an ephemeral spec file
+#
+# $1: The output file name
+# $2..$N: The lines to output to the file
+define CreateNewSpec
+  $(if $(strip $(26)), \
+    $(error Internal makefile error: \
+      Too many arguments to macro, please update CreateNewSpec in RunTestsPrebuilt.gmk) \
+  ) \
+  $(shell $(RM) $1) \
+  $(foreach i, $(call sequence, 2, 25), \
+    $(if $(strip $($i)), \
+      $(call AppendFile, $(strip $($i)), $1) \
+    ) \
+  )
+endef
+
+################################################################################
+# Check input and setup basic buildsystem support
+################################################################################
+
+# Verify that user has given correct additional input.
+
+# These variables are absolutely necessary
+$(eval $(call SetupVariable,OUTPUTDIR))
+$(eval $(call SetupVariable,BOOT_JDK))
+$(eval $(call SetupVariable,JT_HOME))
+
+# These can have default values based on the ones above
+$(eval $(call SetupVariable,JDK_IMAGE_DIR,$(OUTPUTDIR)/images/jdk))
+$(eval $(call SetupVariable,TEST_IMAGE_DIR,$(OUTPUTDIR)/images/test))
+
+# Provide default values for tools that we need
+$(eval $(call SetupVariable,MAKE,make,NO_CHECK))
+$(eval $(call SetupVariable,BASH,bash,NO_CHECK))
+
+# Check optional variables
+$(eval $(call SetupVariable,JIB_JAR,OPTIONAL))
+
+# Now that we have verified that we have the required variables available, we
+# can include the prebuilt spec file ourselves, without an ephemeral spec
+# wrapper. This is required so we can include MakeBase which is needed for
+# CreateNewSpec.
+HAS_SPEC :=
+include $(TOPDIR)/make/InitSupport.gmk
+
+$(eval $(call CheckDeprecatedEnvironment))
+$(eval $(call CheckInvalidMakeFlags))
+$(eval $(call ParseLogLevel))
+
+SPEC := $(TOPDIR)/make/RunTestsPrebuiltSpec.gmk
+include $(SPEC)
+include $(TOPDIR)/make/common/MakeBase.gmk
+
+################################################################################
+# Determine what platform we're running on
+################################################################################
+UNAME := uname
+
+# Get OS name from uname (Cygwin inexplicably adds _NT-x.x)
+UNAME_OS := $(shell $(UNAME) -s | $(CUT) -f1 -d_)
+
+ifeq ($(UNAME_OS), CYGWIN)
+  OPENJDK_TARGET_OS := windows
+  OPENJDK_TARGET_OS_TYPE := windows
+  OPENJDK_TARGET_OS_ENV := windows.cygwin
+else
+  OPENJDK_TARGET_OS_TYPE:=unix
+  ifeq ($(UNAME_OS), Linux)
+    OPENJDK_TARGET_OS := linux
+  else ifeq ($(UNAME_OS), Darwin)
+    OPENJDK_TARGET_OS := macosx
+  else ifeq ($(UNAME_OS), SunOS)
+    OPENJDK_TARGET_OS := solaris
+  else
+    OPENJDK_TARGET_OS := $(UNAME_OS)
+  endif
+  OPENJDK_TARGET_OS_ENV := $(OPENJDK_TARGET_OS)
+endif
+
+# Assume little endian unless otherwise specified
+OPENJDK_TARGET_CPU_ENDIAN := little
+
+ifeq ($(OPENJDK_TARGET_OS), solaris)
+  # On solaris, use uname -p
+  UNAME_CPU := $(shell $(UNAME) -p)
+  # Assume 64-bit platform
+  OPENJDK_TARGET_CPU_BITS := 64
+  ifeq ($(UNAME_CPU), i386)
+    OPENJDK_TARGET_CPU := x86_64
+  else ifeq ($(UNAME_CPU), sparc)
+    OPENJDK_TARGET_CPU := sparcv9
+    OPENJDK_TARGET_CPU_ENDIAN := big
+  else
+    OPENJDK_TARGET_CPU := $(UNAME_CPU)
+  endif
+else
+  # ... all others use uname -m
+  UNAME_CPU := $(shell $(UNAME) -m)
+  ifeq ($(UNAME_CPU), i686)
+    OPENJDK_TARGET_CPU := x86
+    OPENJDK_TARGET_CPU_BITS := 32
+  else
+    # Assume all others are 64-bit. We use the same CPU name as uname for
+    # at least x86_64 and aarch64.
+    OPENJDK_TARGET_CPU := $(UNAME_CPU)
+    OPENJDK_TARGET_CPU_BITS := 64
+  endif
+endif
+
+OPENJDK_TARGET_CPU_ARCH := $(OPENJDK_TARGET_CPU)
+ifeq ($(OPENJDK_TARGET_CPU), x86_64)
+  OPENJDK_TARGET_CPU_ARCH := x86
+else ifeq ($(OPENJDK_TARGET_CPU), sparcv9)
+  OPENJDK_TARGET_CPU_ARCH := sparc
+endif
+
+ifeq ($(OPENJDK_TARGET_OS), windows)
+  ifeq ($(wildcard $(TEST_IMAGE_DIR)/bin/fixpath.exe), )
+    $$(info Error: fixpath is missing from test image '$(TEST_IMAGE_DIR)')
+    $$(error Cannot continue.)
+  endif
+  FIXPATH := $(TEST_IMAGE_DIR)/bin/fixpath.exe -c
+  PATH_SEP:=;
+else
+  FIXPATH :=
+  PATH_SEP:=:
+endif
+
+# Check number of cores
+ifeq ($(OPENJDK_TARGET_OS), linux)
+    NUM_CORES := $(shell $(CAT) /proc/cpuinfo  | $(GREP) -c processor)
+else ifeq ($(OPENJDK_TARGET_OS), macosx)
+    NUM_CORES := $(shell /usr/sbin/sysctl -n hw.ncpu)
+else ifeq ($(OPENJDK_TARGET_OS), solaris)
+    NUM_CORES := $(shell LC_MESSAGES=C /usr/sbin/psrinfo -v | $(GREP) -c on-line)
+else ifeq ($(OPENJDK_TARGET_OS), windows)
+    NUM_CORES := $(NUMBER_OF_PROCESSORS)
+else
+    NUM_CORES := 1
+endif
+
+################################################################################
+# Generate the ephemeral spec file
+################################################################################
+
+# Now we can include additional custom support.
+# This might define CUSTOM_NEW_SPEC_LINE
+ifneq ($(CUSTOM_MAKE_DIR), )
+  include $(CUSTOM_MAKE_DIR)/RunTestsPrebuilt.gmk
+endif
+
+NEW_SPEC := $(OUTPUTDIR)/run-test-spec.gmk
+
+$(call CreateNewSpec, $(NEW_SPEC), \
+    # Generated file -- do not edit!, \
+    SPEC := $(NEW_SPEC), \
+    TOPDIR := $(TOPDIR), \
+    OUTPUTDIR := $(OUTPUTDIR), \
+    BOOT_JDK := $(BOOT_JDK), \
+    JT_HOME := $(JT_HOME), \
+    JDK_IMAGE_DIR := $(JDK_IMAGE_DIR), \
+    TEST_IMAGE_DIR := $(TEST_IMAGE_DIR), \
+    MAKE := $(MAKE), \
+    BASH := $(BASH), \
+    JIB_JAR := $(JIB_JAR), \
+    FIXPATH := $(FIXPATH), \
+    PATH_SEP := $(PATH_SEP), \
+    OPENJDK_TARGET_OS := $(OPENJDK_TARGET_OS), \
+    OPENJDK_TARGET_OS_TYPE := $(OPENJDK_TARGET_OS_TYPE), \
+    OPENJDK_TARGET_OS_ENV := $(OPENJDK_TARGET_OS_ENV), \
+    OPENJDK_TARGET_CPU := $(OPENJDK_TARGET_CPU), \
+    OPENJDK_TARGET_CPU_ARCH := $(OPENJDK_TARGET_CPU_ARCH), \
+    OPENJDK_TARGET_CPU_BITS := $(OPENJDK_TARGET_CPU_BITS), \
+    OPENJDK_TARGET_CPU_ENDIAN := $(OPENJDK_TARGET_CPU_ENDIAN), \
+    NUM_CORES := $(NUM_CORES), \
+    include $(TOPDIR)/make/RunTestsPrebuiltSpec.gmk, \
+    $(CUSTOM_NEW_SPEC_LINE), \
+)
+
+################################################################################
+# The run-test-prebuilt target
+################################################################################
+
+SPEC := $(NEW_SPEC)
+
+default: all
+
+run-test-prebuilt:
+	@$(RM) -f $(MAKESUPPORT_OUTPUTDIR)/exit-with-error
+	@cd $(TOPDIR) && $(MAKE) $(MAKE_ARGS) -f make/RunTests.gmk run-test \
+	    TEST="$(TEST)"
+	@if test -f $(MAKESUPPORT_OUTPUTDIR)/exit-with-error ; then \
+	  exit 1 ; \
+	fi
+
+all: run-test-prebuilt
+
+.PHONY: default all
diff --git a/make/RunTestsPrebuiltSpec.gmk b/make/RunTestsPrebuiltSpec.gmk
new file mode 100644
index 00000000000..5194099fd62
--- /dev/null
+++ b/make/RunTestsPrebuiltSpec.gmk
@@ -0,0 +1,175 @@
+#
+# Copyright (c) 2017, 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.  Oracle designates this
+# particular file as subject to the "Classpath" exception as provided
+# by Oracle in the LICENSE file that accompanied this code.
+#
+# 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.
+#
+
+################################################################################
+# Fake minimalistic spec file for RunTestsPrebuilt.gmk.
+################################################################################
+
+define VerifyVariable
+  ifeq ($$($1), )
+    $$(info Error: Variable $1 is missing, needed by RunTestPrebuiltSpec.gmk)
+    $$(error Cannot continue.)
+  else
+    ifneq ($$(findstring $$(LOG_LEVEL), debug trace), )
+      $$(info Prebuilt variable $1=$$($1))
+    endif
+  endif
+endef
+
+# It is the responsibility of the file including us to have set these up.
+# Verify that this is correct.
+$(eval $(call VerifyVariable,SPEC))
+$(eval $(call VerifyVariable,TOPDIR))
+$(eval $(call VerifyVariable,OUTPUTDIR))
+$(eval $(call VerifyVariable,BOOT_JDK))
+$(eval $(call VerifyVariable,JT_HOME))
+$(eval $(call VerifyVariable,JDK_IMAGE_DIR))
+$(eval $(call VerifyVariable,TEST_IMAGE_DIR))
+$(eval $(call VerifyVariable,MAKE))
+$(eval $(call VerifyVariable,BASH))
+
+################################################################################
+# The "human readable" name of this configuration
+CONF_NAME := run-test-prebuilt
+
+# Number of parallel jobs to use for compilation
+JOBS ?= $(NUM_CORES)
+TEST_JOBS ?= 0
+
+# Use hard-coded values for java flags (one size, fits all!)
+JAVA_FLAGS := -Duser.language=en -Duser.country=US
+JAVA_FLAGS_BIG:= -Xms64M -Xmx1600M -XX:ThreadStackSize=1536
+JAVA_FLAGS_SMALL:= -XX:+UseSerialGC -Xms32M -Xmx512M -XX:TieredStopAtLevel=1
+BUILD_JAVA_FLAGS := $(JAVA_FLAGS_BIG)
+
+################################################################################
+# Hard-coded values copied from spec.gmk.in.
+X:=
+SPACE:=$(X) $(X)
+COMMA:=,
+MAKE_ARGS = $(MAKE_LOG_FLAGS) -r -R -I $(TOPDIR)/make/common SPEC=$(SPEC) \
+    MAKE_LOG_FLAGS="$(MAKE_LOG_FLAGS)" LOG_LEVEL=$(LOG_LEVEL)
+BASH_ARGS := -o pipefail -e
+SHELL := $(BASH) $(BASH_ARGS)
+
+################################################################################
+# Set some reasonable defaults for features
+DEBUG_LEVEL := release
+HOTSPOT_DEBUG_LEVEL := release
+BUILD_GTEST := true
+BUILD_FAILURE_HANDLER := true
+
+################################################################################
+# Alias some paths (that should not really be used) to our JDK image under test.
+SUPPORT_OUTPUTDIR := $(OUTPUTDIR)/support
+BUILDTOOLS_OUTPUTDIR := $(OUTPUTDIR)/buildtools
+HOTSPOT_OUTPUTDIR := $(OUTPUTDIR)/hotspot
+JDK_OUTPUTDIR := $(OUTPUTDIR)/jdk
+IMAGES_OUTPUTDIR := $(OUTPUTDIR)/images
+BUNDLES_OUTPUTDIR := $(OUTPUTDIR)/bundles
+TESTMAKE_OUTPUTDIR := $(OUTPUTDIR)/test-make
+MAKESUPPORT_OUTPUTDIR := $(OUTPUTDIR)/make-support
+BUILDJDK_OUTPUTDIR := $(OUTPUTDIR)/buildjdk
+
+JRE_IMAGE_DIR := $(JDK_IMAGE_DIR)
+
+################################################################################
+# Assume build platform is same as target platform
+OPENJDK_BUILD_OS := $(OPENJDK_TARGET_OS)
+OPENJDK_BUILD_OS_TYPE := $(OPENJDK_TARGET_OS_TYPE)
+OPENJDK_BUILD_OS_ENV := $(OPENJDK_TARGET_OS_ENV)
+
+OPENJDK_BUILD_CPU := $(OPENJDK_TARGET_CPU)
+OPENJDK_BUILD_CPU_ARCH := $(OPENJDK_TARGET_CPU_ARCH)
+OPENJDK_BUILD_CPU_BITS := $(OPENJDK_TARGET_CPU_BITS)
+OPENJDK_BUILD_CPU_ENDIAN := $(OPENJDK_TARGET_CPU_ENDIAN)
+
+################################################################################
+# Java executable definitions
+JAVA_CMD := $(BOOT_JDK)/bin/java
+JAVAC_CMD := $(BOOT_JDK)/bin/javac
+JAVAH_CMD := $(BOOT_JDK)/bin/javah
+JAR_CMD := $(BOOT_JDK)/bin/jar
+JLINK_CMD := $(JDK_OUTPUTDIR)/bin/jlink
+JMOD_CMD := $(JDK_OUTPUTDIR)/bin/jmod
+JARSIGNER_CMD := $(BOOT_JDK)/bin/jarsigner
+
+JAVA := $(FIXPATH) $(JAVA_CMD) $(JAVA_FLAGS_BIG) $(JAVA_FLAGS)
+JAVA_SMALL := $(FIXPATH) $(JAVA_CMD) $(JAVA_FLAGS_SMALL) $(JAVA_FLAGS)
+JAVA_JAVAC := $(FIXPATH) $(JAVA_CMD) $(JAVA_FLAGS_SMALL) $(JAVA_FLAGS)
+JAVAC := $(FIXPATH) $(JAVAC_CMD)
+JAVAH := $(FIXPATH) $(JAVAH_CMD)
+JAR := $(FIXPATH) $(JAR_CMD)
+JLINK := $(FIXPATH) $(JLINK_CMD)
+JMOD := $(FIXPATH) $(JMOD_CMD)
+JARSIGNER := $(FIXPATH) $(JARSIGNER_CMD)
+
+BUILD_JAVA := $(JAVA)
+################################################################################
+# Some common tools. Assume most common name and no path.
+AWK := awk
+BASENAME := basename
+CAT := cat
+CD := cd
+CHMOD := chmod
+CP := cp
+CUT := cut
+DATE := date
+DIFF := diff
+DIRNAME := dirname
+FIND := find
+FIND_DELETE := -delete
+ECHO := echo
+EGREP := grep -E
+FGREP := grep -F
+GREP := grep
+GZIP := gzip
+HEAD := head
+LS := ls
+LN := ln
+MKDIR := mkdir
+MV := mv
+NAWK := nawk
+NICE := nice
+PATCH := patch
+PRINTF := printf
+RM := rm -f
+RMDIR := rmdir
+SED := sed
+SH := sh
+SORT := sort
+TAR := tar
+TAIL := tail
+TEE := tee
+TR := tr
+TOUCH := touch
+UNIQ := uniq
+WC := wc
+XARGS := xargs
+ZIPEXE := zip
+UNZIP := unzip
+EXPR := expr
+FILE := file
+HG := hg
diff --git a/make/autoconf/boot-jdk.m4 b/make/autoconf/boot-jdk.m4
index 0c0abd61fa6..161b0f18f2a 100644
--- a/make/autoconf/boot-jdk.m4
+++ b/make/autoconf/boot-jdk.m4
@@ -353,9 +353,6 @@ AC_DEFUN_ONCE([BOOTJDK_SETUP_BOOT_JDK_ARGUMENTS],
 
   AC_MSG_CHECKING([flags for boot jdk java command] )
 
-  # Disable special log output when a debug build is used as Boot JDK...
-  ADD_JVM_ARG_IF_OK([-XX:-PrintVMOptions -XX:-UnlockDiagnosticVMOptions -XX:-LogVMOutput],boot_jdk_jvmargs,[$JAVA])
-
   # Force en-US environment
   ADD_JVM_ARG_IF_OK([-Duser.language=en -Duser.country=US],boot_jdk_jvmargs,[$JAVA])
 
diff --git a/make/autoconf/generated-configure.sh b/make/autoconf/generated-configure.sh
index d3cb5f606c6..e856469fd0b 100644
--- a/make/autoconf/generated-configure.sh
+++ b/make/autoconf/generated-configure.sh
@@ -5159,7 +5159,7 @@ VS_SDK_PLATFORM_NAME_2013=
 #CUSTOM_AUTOCONF_INCLUDE
 
 # Do not change or remove the following line, it is needed for consistency checks:
-DATE_WHEN_GENERATED=1512410983
+DATE_WHEN_GENERATED=1512479382
 
 ###############################################################################
 #
@@ -67379,23 +67379,6 @@ fi
   { $as_echo "$as_me:${as_lineno-$LINENO}: checking flags for boot jdk java command " >&5
 $as_echo_n "checking flags for boot jdk java command ... " >&6; }
 
-  # Disable special log output when a debug build is used as Boot JDK...
-
-  $ECHO "Check if jvm arg is ok: -XX:-PrintVMOptions -XX:-UnlockDiagnosticVMOptions -XX:-LogVMOutput" >&5
-  $ECHO "Command: $JAVA -XX:-PrintVMOptions -XX:-UnlockDiagnosticVMOptions -XX:-LogVMOutput -version" >&5
-  OUTPUT=`$JAVA -XX:-PrintVMOptions -XX:-UnlockDiagnosticVMOptions -XX:-LogVMOutput -version 2>&1`
-  FOUND_WARN=`$ECHO "$OUTPUT" | $GREP -i warn`
-  FOUND_VERSION=`$ECHO $OUTPUT | $GREP " version \""`
-  if test "x$FOUND_VERSION" != x && test "x$FOUND_WARN" = x; then
-    boot_jdk_jvmargs="$boot_jdk_jvmargs -XX:-PrintVMOptions -XX:-UnlockDiagnosticVMOptions -XX:-LogVMOutput"
-    JVM_ARG_OK=true
-  else
-    $ECHO "Arg failed:" >&5
-    $ECHO "$OUTPUT" >&5
-    JVM_ARG_OK=false
-  fi
-
-
   # Force en-US environment
 
   $ECHO "Check if jvm arg is ok: -Duser.language=en -Duser.country=US" >&5
diff --git a/make/autoconf/spec.gmk.in b/make/autoconf/spec.gmk.in
index b8449f78048..dda3fbebaca 100644
--- a/make/autoconf/spec.gmk.in
+++ b/make/autoconf/spec.gmk.in
@@ -565,6 +565,7 @@ JAVAC_FLAGS?=@JAVAC_FLAGS@
 
 BUILD_JAVA_FLAGS := @BOOTCYCLE_JVM_ARGS_BIG@
 BUILD_JAVA=@FIXPATH@ $(BUILD_JDK)/bin/java $(BUILD_JAVA_FLAGS)
+BUILD_JAR=@FIXPATH@ $(BUILD_JDK)/bin/jar
 
 # Interim langtools and rmic modules and arguments
 INTERIM_LANGTOOLS_BASE_MODULES := java.compiler jdk.compiler jdk.javadoc
@@ -577,7 +578,7 @@ INTERIM_LANGTOOLS_MODULES_COMMA := $(strip $(subst $(SPACE),$(COMMA),$(strip \
 INTERIM_LANGTOOLS_ARGS := \
     --limit-modules java.base,jdk.zipfs,$(INTERIM_LANGTOOLS_MODULES_COMMA) \
     --add-modules $(INTERIM_LANGTOOLS_MODULES_COMMA) \
-    --module-path $(BUILDTOOLS_OUTPUTDIR)/interim_modules \
+    --module-path $(BUILDTOOLS_OUTPUTDIR)/interim_langtools_modules \
     $(INTERIM_LANGTOOLS_ADD_EXPORTS) \
     #
 JAVAC_MAIN_CLASS = -m jdk.compiler.interim/com.sun.tools.javac.Main
@@ -588,8 +589,10 @@ INTERIM_RMIC_MODULES := $(addsuffix .interim, $(INTERIM_RMIC_BASE_MODULES))
 INTERIM_RMIC_ADD_EXPORTS := \
     --add-exports java.corba/com.sun.corba.se.impl.util=jdk.rmic.interim \
     #
-INTERIM_RMIC_ARGS := --limit-modules java.base,jdk.compiler,jdk.javadoc,java.corba \
-    --module-path $(BUILDTOOLS_OUTPUTDIR)/interim_modules \
+# Use = to delay expansion of PathList since it's not available in this file.
+INTERIM_RMIC_ARGS = --limit-modules java.base,jdk.compiler,jdk.javadoc,java.corba \
+    --module-path $(call PathList, $(BUILDTOOLS_OUTPUTDIR)/interim_rmic_modules \
+        $(BUILDTOOLS_OUTPUTDIR)/interim_langtools_modules) \
     $(INTERIM_RMIC_ADD_EXPORTS) \
     #
 
diff --git a/make/common/JarArchive.gmk b/make/common/JarArchive.gmk
index 1e1c017ab61..b6487913dd4 100644
--- a/make/common/JarArchive.gmk
+++ b/make/common/JarArchive.gmk
@@ -56,6 +56,7 @@ FALSE_FIND_PATTERN:=-name FILE_NAME_THAT_DOESNT_EXIST
 #       added to the archive.
 #   EXTRA_MANIFEST_ATTR:=Extra attribute to add to manifest.
 #   CHECK_COMPRESS_JAR Check the COMPRESS_JAR variable
+#   JAR_CMD:=Optionally override the jar command to use when creating the archive.
 SetupJarArchive = $(NamedParamsMacroTemplate)
 define SetupJarArchiveBody
 
@@ -65,6 +66,7 @@ define SetupJarArchiveBody
   $1_DELETESS_FILE:=$$(dir $$($1_JAR))_the.$$($1_JARNAME)_deletess
   $1_DELETES_FILE:=$$(dir $$($1_JAR))_the.$$($1_JARNAME)_deletes
   $1_BIN:=$$(dir $$($1_JAR))
+  $$(call SetIfEmpty, $1_JAR_CMD, $$(JAR))
 
   ifeq (,$$($1_SUFFIXES))
     # No suffix was set, default to classes.
@@ -109,7 +111,7 @@ define SetupJarArchiveBody
 
   # Check if this jar needs to have its index generated.
   ifneq (,$$($1_JARINDEX))
-    $1_JARINDEX = (cd $$(dir $$@) && $(JAR) -i $$(notdir $$@))
+    $1_JARINDEX = (cd $$(dir $$@) && $$($1_JAR_CMD) -i $$(notdir $$@))
   else
     $1_JARINDEX = true
   endif
@@ -189,7 +191,7 @@ define SetupJarArchiveBody
   $1_UPDATE_CONTENTS=\
       if [ "`$(WC) -l $$($1_BIN)/_the.$$($1_JARNAME)_contents | $(AWK) '{ print $$$$1 }'`" -gt "0" ]; then \
         $(ECHO) "  updating" `$(WC) -l $$($1_BIN)/_the.$$($1_JARNAME)_contents | $(AWK) '{ print $$$$1 }'` files && \
-        $(JAR) $$($1_JAR_UPDATE_OPTIONS) $$@ @$$($1_BIN)/_the.$$($1_JARNAME)_contents; \
+        $$($1_JAR_CMD) $$($1_JAR_UPDATE_OPTIONS) $$@ @$$($1_BIN)/_the.$$($1_JARNAME)_contents; \
       fi $$(NEWLINE)
   # The s-variants of the above macros are used when the jar is created from scratch.
   # NOTICE: please leave the parentheses space separated otherwise the AIX build will break!
@@ -208,7 +210,7 @@ define SetupJarArchiveBody
             | $(SED) 's|$$(src)/|-C $$(src) |g' >> \
         $$($1_BIN)/_the.$$($1_JARNAME)_contents) $$(NEWLINE) )
   endif
-  $1_SUPDATE_CONTENTS=$(JAR) $$($1_JAR_UPDATE_OPTIONS) $$@ @$$($1_BIN)/_the.$$($1_JARNAME)_contents $$(NEWLINE)
+  $1_SUPDATE_CONTENTS=$$($1_JAR_CMD) $$($1_JAR_UPDATE_OPTIONS) $$@ @$$($1_BIN)/_the.$$($1_JARNAME)_contents $$(NEWLINE)
 
   # Use a slightly shorter name for logging, but with enough path to identify this jar.
   $1_NAME:=$$(subst $$(OUTPUTDIR)/,,$$($1_JAR))
@@ -226,7 +228,7 @@ define SetupJarArchiveBody
   endif
 
   # Include all variables of significance in the vardeps file
-  $1_VARDEPS := $(JAR) $$($1_JAR_CREATE_OPTIONS) $$($1_MANIFEST) \
+  $1_VARDEPS := $$($1_JAR_CMD) $$($1_JAR_CREATE_OPTIONS) $$($1_MANIFEST) \
       $$($1_JARMAIN) $$($1_EXTRA_MANIFEST_ATTR) $$($1_ORIG_DEPS) $$($1_SRCS) \
       $$($1_INCLUDES) $$($1_EXCLUDES) $$($1_EXCLUDE_FILES) $$($1_EXTRA_FILES)
   $1_VARDEPS_FILE := $$(call DependOnVariable, $1_VARDEPS, $$(dir $$($1_JAR))_the.$$($1_JARNAME).vardeps)
@@ -250,7 +252,7 @@ define SetupJarArchiveBody
 	  $$(if $$($1_EXTRA_MANIFEST_ATTR), \
 	    $(PRINTF) "$$($1_EXTRA_MANIFEST_ATTR)\n" >> $$($1_MANIFEST_FILE) $$(NEWLINE)) \
 	  $(ECHO) Creating $$($1_NAME) $$(NEWLINE) \
-	  $(JAR) $$($1_JAR_CREATE_OPTIONS) $$@ $$($1_MANIFEST_FILE) $$(NEWLINE) \
+	  $$($1_JAR_CMD) $$($1_JAR_CREATE_OPTIONS) $$@ $$($1_MANIFEST_FILE) $$(NEWLINE) \
 	  $$($1_SCAPTURE_CONTENTS) \
 	  $$($1_SCAPTURE_METAINF) \
 	  $$($1_SUPDATE_CONTENTS) \
diff --git a/make/common/MakeBase.gmk b/make/common/MakeBase.gmk
index 69dc9fbffe1..61a27155a84 100644
--- a/make/common/MakeBase.gmk
+++ b/make/common/MakeBase.gmk
@@ -912,6 +912,17 @@ else
       $(shell $(PRINTF) "%s" $(call ShellQuote, $1) > $2)
 endif
 
+# Param 1 - Text to write
+# Param 2 - File to write to
+ifeq ($(HAS_FILE_FUNCTION), true)
+  AppendFile = \
+      $(file >>$2,$(strip $1))
+else
+  # Use printf to get consistent behavior on all platforms.
+  AppendFile = \
+      $(shell $(PRINTF) "%s" $(call ShellQuote, $1) >> $2)
+endif
+
 ################################################################################
 # DependOnVariable
 #
diff --git a/make/conf/jib-profiles.js b/make/conf/jib-profiles.js
index 9bf032e0e8e..172aabb7fe4 100644
--- a/make/conf/jib-profiles.js
+++ b/make/conf/jib-profiles.js
@@ -662,6 +662,16 @@ var getJibProfilesProfiles = function (input, common, data) {
         }
     });
 
+    // For open profiles, the non-debug jdk bundles, need an "open" prefix on the
+    // remote bundle names, forming the word "openjdk". See JDK-8188789.
+    common.main_profile_names.forEach(function (name) {
+        var openName = name + common.open_suffix;
+        profiles[openName].artifacts["jdk"].remote = replaceAll(
+            "\/jdk-", "/openjdk-",
+            replaceAll("\/\\1", "/open\\1",
+                       profiles[openName].artifacts["jdk"].remote));
+    });
+
     // Profiles used to run tests. Used in JPRT and Mach 5.
     var testOnlyProfiles = {
         "run-test-jprt": {
@@ -779,6 +789,10 @@ var getJibProfilesDependencies = function (input, common) {
         macosx_x64: "2.7.1-Xcode6.3-MacOSX10.9+1.0"
     }[input.target_platform];
 
+    var makeBinDir = (input.build_os == "windows"
+        ? input.get("gnumake", "install_path") + "/cygwin/bin"
+        : input.get("gnumake", "install_path") + "/bin");
+
     var dependencies = {
 
         boot_jdk: {
@@ -831,13 +845,13 @@ var getJibProfilesDependencies = function (input, common) {
                 ? "gnumake-" + input.build_osenv_platform
                 : "gnumake-" + input.build_platform),
 
-            configure_args: (input.build_os == "windows"
-                ? "MAKE=" + input.get("gnumake", "install_path") + "/cygwin/bin/make"
-                : "MAKE=" + input.get("gnumake", "install_path") + "/bin/make"),
+            configure_args: "MAKE=" + makeBinDir + "/make",
 
-            environment_path: (input.build_os == "windows"
-                ? input.get("gnumake", "install_path") + "/cygwin/bin"
-                : input.get("gnumake", "install_path") + "/bin")
+            environment: {
+                "MAKE": makeBinDir + "/make"
+            },
+
+            environment_path: makeBinDir
         },
 
         freetype: {
diff --git a/make/hotspot/lib/CompileJvm.gmk b/make/hotspot/lib/CompileJvm.gmk
index 136efbd756e..66ba5033562 100644
--- a/make/hotspot/lib/CompileJvm.gmk
+++ b/make/hotspot/lib/CompileJvm.gmk
@@ -59,6 +59,7 @@ JVM_CFLAGS_INCLUDES += \
     -I$(TOPDIR)/src/hotspot/share/precompiled \
     -I$(TOPDIR)/src/java.base/share/native/include \
     -I$(TOPDIR)/src/java.base/$(OPENJDK_TARGET_OS_TYPE)/native/include \
+    -I$(TOPDIR)/src/java.management/share/native/include \
     -I$(TOPDIR)/src/java.base/share/native/libjimage \
     #
 
diff --git a/make/jdk/src/classes/build/tools/jdwpgen/RootNode.java b/make/jdk/src/classes/build/tools/jdwpgen/RootNode.java
index 8fbd71036b8..2361f335949 100644
--- a/make/jdk/src/classes/build/tools/jdwpgen/RootNode.java
+++ b/make/jdk/src/classes/build/tools/jdwpgen/RootNode.java
@@ -40,6 +40,7 @@ class RootNode extends AbstractNamedNode {
     }
 
     void document(PrintWriter writer) {
+        writer.println("<!DOCTYPE html>");
         writer.println("<html><head><title>" + comment() + "</title></head>");
         writer.println("<body bgcolor=\"white\">");
         for (Node node : components) {
diff --git a/make/mapfiles/libjava/mapfile-vers b/make/mapfiles/libjava/mapfile-vers
index a2f7303f06e..ce5100c539e 100644
--- a/make/mapfiles/libjava/mapfile-vers
+++ b/make/mapfiles/libjava/mapfile-vers
@@ -74,7 +74,7 @@ SUNWprivate_1.1 {
 		JNU_ThrowStringIndexOutOfBoundsException;
 		JNU_ToString;
 
-		Java_java_io_FileDescriptor_close;
+		Java_java_io_FileDescriptor_close0;
 		Java_java_io_FileDescriptor_initIDs;
 		Java_java_io_FileDescriptor_sync;
 		Java_java_io_FileDescriptor_getAppend;
diff --git a/make/test/JtregNativeHotspot.gmk b/make/test/JtregNativeHotspot.gmk
index 9c621f53410..8cf3b0ed6f3 100644
--- a/make/test/JtregNativeHotspot.gmk
+++ b/make/test/JtregNativeHotspot.gmk
@@ -79,6 +79,7 @@ BUILD_HOTSPOT_JTREG_NATIVE_SRC += \
     $(TOPDIR)/test/hotspot/jtreg/serviceability/jvmti/ModuleAwareAgents/ClassLoadPrepare \
     $(TOPDIR)/test/hotspot/jtreg/serviceability/jvmti/ModuleAwareAgents/ThreadStart \
     $(TOPDIR)/test/hotspot/jtreg/serviceability/jvmti/StartPhase/AllowedFunctions \
+    $(TOPDIR)/test/hotspot/jtreg/serviceability/dcmd/jvmti/AttachFailed \
     #
 
 # Add conditional directories here when needed.
@@ -110,6 +111,8 @@ ifeq ($(TOOLCHAIN_TYPE), solstudio)
     BUILD_HOTSPOT_JTREG_LIBRARIES_LIBS_libAllowedFunctions := -lc
     BUILD_HOTSPOT_JTREG_LIBRARIES_LIBS_libRedefineDoubleDelete := -lc
     BUILD_HOTSPOT_JTREG_LIBRARIES_LIBS_libHandshakeTransitionTest := -lc
+    BUILD_HOTSPOT_JTREG_LIBRARIES_LIBS_libHasNoEntryPoint := -lc
+    BUILD_HOTSPOT_JTREG_LIBRARIES_LIBS_libReturnError := -lc
 endif
 
 ifeq ($(OPENJDK_TARGET_OS), linux)
diff --git a/src/hotspot/cpu/aarch64/assembler_aarch64.hpp b/src/hotspot/cpu/aarch64/assembler_aarch64.hpp
index ab1bcd55817..2a9766f5143 100644
--- a/src/hotspot/cpu/aarch64/assembler_aarch64.hpp
+++ b/src/hotspot/cpu/aarch64/assembler_aarch64.hpp
@@ -985,12 +985,33 @@ public:
   }
 
   void hint(int imm) {
-    system(0b00, 0b011, 0b0010, imm, 0b000);
+    system(0b00, 0b011, 0b0010, 0b0000, imm);
   }
 
   void nop() {
     hint(0);
   }
+
+  void yield() {
+    hint(1);
+  }
+
+  void wfe() {
+    hint(2);
+  }
+
+  void wfi() {
+    hint(3);
+  }
+
+  void sev() {
+    hint(4);
+  }
+
+  void sevl() {
+    hint(5);
+  }
+
   // we only provide mrs and msr for the special purpose system
   // registers where op1 (instr[20:19]) == 11 and, (currently) only
   // use it for FPSR n.b msr has L (instr[21]) == 0 mrs has L == 1
diff --git a/src/hotspot/cpu/aarch64/c1_LIRAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/c1_LIRAssembler_aarch64.cpp
index 36fefc866aa..ecd4a8e19f2 100644
--- a/src/hotspot/cpu/aarch64/c1_LIRAssembler_aarch64.cpp
+++ b/src/hotspot/cpu/aarch64/c1_LIRAssembler_aarch64.cpp
@@ -494,42 +494,6 @@ void LIR_Assembler::add_debug_info_for_branch(address adr, CodeEmitInfo* info) {
   }
 }
 
-// Rather than take a segfault when the polling page is protected,
-// explicitly check for a safepoint in progress and if there is one,
-// fake a call to the handler as if a segfault had been caught.
-void LIR_Assembler::poll_for_safepoint(relocInfo::relocType rtype, CodeEmitInfo* info) {
-  __ mov(rscratch1, SafepointSynchronize::address_of_state());
-  __ ldrb(rscratch1, Address(rscratch1));
-  Label nope, poll;
-  __ cbz(rscratch1, nope);
-  __ block_comment("safepoint");
-  __ enter();
-  __ push(0x3, sp);                // r0 & r1
-  __ push(0x3ffffffc, sp);         // integer registers except lr & sp & r0 & r1
-  __ adr(r0, poll);
-  __ str(r0, Address(rthread, JavaThread::saved_exception_pc_offset()));
-  __ mov(rscratch1, CAST_FROM_FN_PTR(address, SharedRuntime::get_poll_stub));
-  __ blrt(rscratch1, 1, 0, 1);
-  __ maybe_isb();
-  __ pop(0x3ffffffc, sp);          // integer registers except lr & sp & r0 & r1
-  __ mov(rscratch1, r0);
-  __ pop(0x3, sp);                 // r0 & r1
-  __ leave();
-  __ br(rscratch1);
-  address polling_page(os::get_polling_page());
-  assert(os::is_poll_address(polling_page), "should be");
-  unsigned long off;
-  __ adrp(rscratch1, Address(polling_page, rtype), off);
-  __ bind(poll);
-  if (info)
-    add_debug_info_for_branch(info);  // This isn't just debug info:
-                                      // it's the oop map
-  else
-    __ code_section()->relocate(pc(), rtype);
-  __ ldrw(zr, Address(rscratch1, off));
-  __ bind(nope);
-}
-
 void LIR_Assembler::return_op(LIR_Opr result) {
   assert(result->is_illegal() || !result->is_single_cpu() || result->as_register() == r0, "word returns are in r0,");
 
@@ -549,11 +513,9 @@ int LIR_Assembler::safepoint_poll(LIR_Opr tmp, CodeEmitInfo* info) {
   address polling_page(os::get_polling_page());
   guarantee(info != NULL, "Shouldn't be NULL");
   assert(os::is_poll_address(polling_page), "should be");
-  unsigned long off;
-  __ adrp(rscratch1, Address(polling_page, relocInfo::poll_type), off);
-  assert(off == 0, "must be");
+  __ get_polling_page(rscratch1, polling_page, relocInfo::poll_type);
   add_debug_info_for_branch(info);  // This isn't just debug info:
-  // it's the oop map
+                                    // it's the oop map
   __ read_polling_page(rscratch1, relocInfo::poll_type);
   return __ offset();
 }
diff --git a/src/hotspot/cpu/aarch64/globalDefinitions_aarch64.hpp b/src/hotspot/cpu/aarch64/globalDefinitions_aarch64.hpp
index ad6b12de22d..5977764c2d2 100644
--- a/src/hotspot/cpu/aarch64/globalDefinitions_aarch64.hpp
+++ b/src/hotspot/cpu/aarch64/globalDefinitions_aarch64.hpp
@@ -51,4 +51,6 @@ const bool CCallingConventionRequiresIntsAsLongs = false;
 
 #define SUPPORT_RESERVED_STACK_AREA
 
+#define THREAD_LOCAL_POLL
+
 #endif // CPU_AARCH64_VM_GLOBALDEFINITIONS_AARCH64_HPP
diff --git a/src/hotspot/cpu/aarch64/globals_aarch64.hpp b/src/hotspot/cpu/aarch64/globals_aarch64.hpp
index bf34c157c9e..dfd984e4fb0 100644
--- a/src/hotspot/cpu/aarch64/globals_aarch64.hpp
+++ b/src/hotspot/cpu/aarch64/globals_aarch64.hpp
@@ -79,7 +79,7 @@ define_pd_global(bool, CompactStrings, true);
 // Clear short arrays bigger than one word in an arch-specific way
 define_pd_global(intx, InitArrayShortSize, BytesPerLong);
 
-define_pd_global(bool, ThreadLocalHandshakes, false);
+define_pd_global(bool, ThreadLocalHandshakes, true);
 
 #if defined(COMPILER1) || defined(COMPILER2)
 define_pd_global(intx, InlineSmallCode,          1000);
diff --git a/src/hotspot/cpu/aarch64/interp_masm_aarch64.cpp b/src/hotspot/cpu/aarch64/interp_masm_aarch64.cpp
index b1b3c5e5273..550bf100764 100644
--- a/src/hotspot/cpu/aarch64/interp_masm_aarch64.cpp
+++ b/src/hotspot/cpu/aarch64/interp_masm_aarch64.cpp
@@ -30,12 +30,13 @@
 #include "logging/log.hpp"
 #include "oops/arrayOop.hpp"
 #include "oops/markOop.hpp"
-#include "oops/methodData.hpp"
 #include "oops/method.hpp"
+#include "oops/methodData.hpp"
 #include "prims/jvmtiExport.hpp"
 #include "prims/jvmtiThreadState.hpp"
 #include "runtime/basicLock.hpp"
 #include "runtime/biasedLocking.hpp"
+#include "runtime/safepointMechanism.hpp"
 #include "runtime/sharedRuntime.hpp"
 #include "runtime/thread.inline.hpp"
 
@@ -438,13 +439,26 @@ void InterpreterMacroAssembler::dispatch_epilog(TosState state, int step) {
 
 void InterpreterMacroAssembler::dispatch_base(TosState state,
                                               address* table,
-                                              bool verifyoop) {
+                                              bool verifyoop,
+                                              bool generate_poll) {
   if (VerifyActivationFrameSize) {
     Unimplemented();
   }
   if (verifyoop) {
     verify_oop(r0, state);
   }
+
+  Label safepoint;
+  address* const safepoint_table = Interpreter::safept_table(state);
+  bool needs_thread_local_poll = generate_poll &&
+    SafepointMechanism::uses_thread_local_poll() && table != safepoint_table;
+
+  if (needs_thread_local_poll) {
+    NOT_PRODUCT(block_comment("Thread-local Safepoint poll"));
+    ldr(rscratch2, Address(rthread, Thread::polling_page_offset()));
+    tbnz(rscratch2, exact_log2(SafepointMechanism::poll_bit()), safepoint);
+  }
+
   if (table == Interpreter::dispatch_table(state)) {
     addw(rscratch2, rscratch1, Interpreter::distance_from_dispatch_table(state));
     ldr(rscratch2, Address(rdispatch, rscratch2, Address::uxtw(3)));
@@ -453,10 +467,17 @@ void InterpreterMacroAssembler::dispatch_base(TosState state,
     ldr(rscratch2, Address(rscratch2, rscratch1, Address::uxtw(3)));
   }
   br(rscratch2);
+
+  if (needs_thread_local_poll) {
+    bind(safepoint);
+    lea(rscratch2, ExternalAddress((address)safepoint_table));
+    ldr(rscratch2, Address(rscratch2, rscratch1, Address::uxtw(3)));
+    br(rscratch2);
+  }
 }
 
-void InterpreterMacroAssembler::dispatch_only(TosState state) {
-  dispatch_base(state, Interpreter::dispatch_table(state));
+void InterpreterMacroAssembler::dispatch_only(TosState state, bool generate_poll) {
+  dispatch_base(state, Interpreter::dispatch_table(state), true, generate_poll);
 }
 
 void InterpreterMacroAssembler::dispatch_only_normal(TosState state) {
@@ -468,10 +489,10 @@ void InterpreterMacroAssembler::dispatch_only_noverify(TosState state) {
 }
 
 
-void InterpreterMacroAssembler::dispatch_next(TosState state, int step) {
+void InterpreterMacroAssembler::dispatch_next(TosState state, int step, bool generate_poll) {
   // load next bytecode
   ldrb(rscratch1, Address(pre(rbcp, step)));
-  dispatch_base(state, Interpreter::dispatch_table(state));
+  dispatch_base(state, Interpreter::dispatch_table(state), generate_poll);
 }
 
 void InterpreterMacroAssembler::dispatch_via(TosState state, address* table) {
@@ -1585,6 +1606,7 @@ void InterpreterMacroAssembler::call_VM_base(Register oop_result,
 }
 
 void InterpreterMacroAssembler::profile_obj_type(Register obj, const Address& mdo_addr) {
+  assert_different_registers(obj, rscratch1);
   Label update, next, none;
 
   verify_oop(obj);
@@ -1745,6 +1767,7 @@ void InterpreterMacroAssembler::profile_return_type(Register mdp, Register ret,
 }
 
 void InterpreterMacroAssembler::profile_parameters_type(Register mdp, Register tmp1, Register tmp2) {
+  assert_different_registers(rscratch1, rscratch2, mdp, tmp1, tmp2);
   if (ProfileInterpreter && MethodData::profile_parameters()) {
     Label profile_continue, done;
 
@@ -1752,8 +1775,8 @@ void InterpreterMacroAssembler::profile_parameters_type(Register mdp, Register t
 
     // Load the offset of the area within the MDO used for
     // parameters. If it's negative we're not profiling any parameters
-    ldr(tmp1, Address(mdp, in_bytes(MethodData::parameters_type_data_di_offset()) - in_bytes(MethodData::data_offset())));
-    tbnz(tmp1, 63, profile_continue);  // i.e. sign bit set
+    ldrw(tmp1, Address(mdp, in_bytes(MethodData::parameters_type_data_di_offset()) - in_bytes(MethodData::data_offset())));
+    tbnz(tmp1, 31, profile_continue);  // i.e. sign bit set
 
     // Compute a pointer to the area for parameters from the offset
     // and move the pointer to the slot for the last
diff --git a/src/hotspot/cpu/aarch64/interp_masm_aarch64.hpp b/src/hotspot/cpu/aarch64/interp_masm_aarch64.hpp
index 637ae481f5b..59b33a17d22 100644
--- a/src/hotspot/cpu/aarch64/interp_masm_aarch64.hpp
+++ b/src/hotspot/cpu/aarch64/interp_masm_aarch64.hpp
@@ -55,7 +55,8 @@ class InterpreterMacroAssembler: public MacroAssembler {
                             bool check_exceptions);
 
   // base routine for all dispatches
-  void dispatch_base(TosState state, address* table, bool verifyoop = true);
+  void dispatch_base(TosState state, address* table,
+                     bool verifyoop = true, bool generate_poll = false);
 
  public:
   InterpreterMacroAssembler(CodeBuffer* code) : MacroAssembler(code) {}
@@ -165,12 +166,12 @@ class InterpreterMacroAssembler: public MacroAssembler {
   void dispatch_prolog(TosState state, int step = 0);
   void dispatch_epilog(TosState state, int step = 0);
   // dispatch via rscratch1
-  void dispatch_only(TosState state);
+  void dispatch_only(TosState state, bool generate_poll = false);
   // dispatch normal table via rscratch1 (assume rscratch1 is loaded already)
   void dispatch_only_normal(TosState state);
   void dispatch_only_noverify(TosState state);
   // load rscratch1 from [rbcp + step] and dispatch via rscratch1
-  void dispatch_next(TosState state, int step = 0);
+  void dispatch_next(TosState state, int step = 0, bool generate_poll = false);
   // load rscratch1 from [esi] and dispatch via rscratch1 and table
   void dispatch_via (TosState state, address* table);
 
diff --git a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp
index 81c00ffd106..bef4fd4e573 100644
--- a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp
+++ b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp
@@ -287,6 +287,40 @@ void MacroAssembler::serialize_memory(Register thread, Register tmp) {
   dsb(Assembler::SY);
 }
 
+void MacroAssembler::safepoint_poll(Label& slow_path) {
+  if (SafepointMechanism::uses_thread_local_poll()) {
+    ldr(rscratch1, Address(rthread, Thread::polling_page_offset()));
+    tbnz(rscratch1, exact_log2(SafepointMechanism::poll_bit()), slow_path);
+  } else {
+    unsigned long offset;
+    adrp(rscratch1, ExternalAddress(SafepointSynchronize::address_of_state()), offset);
+    ldrw(rscratch1, Address(rscratch1, offset));
+    assert(SafepointSynchronize::_not_synchronized == 0, "rewrite this code");
+    cbnz(rscratch1, slow_path);
+  }
+}
+
+// Just like safepoint_poll, but use an acquiring load for thread-
+// local polling.
+//
+// We need an acquire here to ensure that any subsequent load of the
+// global SafepointSynchronize::_state flag is ordered after this load
+// of the local Thread::_polling page.  We don't want this poll to
+// return false (i.e. not safepointing) and a later poll of the global
+// SafepointSynchronize::_state spuriously to return true.
+//
+// This is to avoid a race when we're in a native->Java transition
+// racing the code which wakes up from a safepoint.
+//
+void MacroAssembler::safepoint_poll_acquire(Label& slow_path) {
+  if (SafepointMechanism::uses_thread_local_poll()) {
+    lea(rscratch1, Address(rthread, Thread::polling_page_offset()));
+    ldar(rscratch1, rscratch1);
+    tbnz(rscratch1, exact_log2(SafepointMechanism::poll_bit()), slow_path);
+  } else {
+    safepoint_poll(slow_path);
+  }
+}
 
 void MacroAssembler::reset_last_Java_frame(bool clear_fp) {
   // we must set sp to zero to clear frame
@@ -4336,15 +4370,26 @@ void MacroAssembler::bang_stack_size(Register size, Register tmp) {
 }
 
 
-address MacroAssembler::read_polling_page(Register r, address page, relocInfo::relocType rtype) {
-  unsigned long off;
-  adrp(r, Address(page, rtype), off);
-  InstructionMark im(this);
-  code_section()->relocate(inst_mark(), rtype);
-  ldrw(zr, Address(r, off));
-  return inst_mark();
+// Move the address of the polling page into dest.
+void MacroAssembler::get_polling_page(Register dest, address page, relocInfo::relocType rtype) {
+  if (SafepointMechanism::uses_thread_local_poll()) {
+    ldr(dest, Address(rthread, Thread::polling_page_offset()));
+  } else {
+    unsigned long off;
+    adrp(dest, Address(page, rtype), off);
+    assert(off == 0, "polling page must be page aligned");
+  }
 }
 
+// Move the address of the polling page into r, then read the polling
+// page.
+address MacroAssembler::read_polling_page(Register r, address page, relocInfo::relocType rtype) {
+  get_polling_page(r, page, rtype);
+  return read_polling_page(r, rtype);
+}
+
+// Read the polling page.  The address of the polling page must
+// already be in r.
 address MacroAssembler::read_polling_page(Register r, relocInfo::relocType rtype) {
   InstructionMark im(this);
   code_section()->relocate(inst_mark(), rtype);
diff --git a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.hpp b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.hpp
index f5cab401535..94c8c037164 100644
--- a/src/hotspot/cpu/aarch64/macroAssembler_aarch64.hpp
+++ b/src/hotspot/cpu/aarch64/macroAssembler_aarch64.hpp
@@ -97,6 +97,9 @@ class MacroAssembler: public Assembler {
  virtual void check_and_handle_popframe(Register java_thread);
  virtual void check_and_handle_earlyret(Register java_thread);
 
+  void safepoint_poll(Label& slow_path);
+  void safepoint_poll_acquire(Label& slow_path);
+
   // Biased locking support
   // lock_reg and obj_reg must be loaded up with the appropriate values.
   // swap_reg is killed.
@@ -995,12 +998,12 @@ public:
   void atomic_xchgalw(Register prev, Register newv, Register addr);
 
   void orptr(Address adr, RegisterOrConstant src) {
-    ldr(rscratch2, adr);
+    ldr(rscratch1, adr);
     if (src.is_register())
-      orr(rscratch2, rscratch2, src.as_register());
+      orr(rscratch1, rscratch1, src.as_register());
     else
-      orr(rscratch2, rscratch2, src.as_constant());
-    str(rscratch2, adr);
+      orr(rscratch1, rscratch1, src.as_constant());
+    str(rscratch1, adr);
   }
 
   // A generic CAS; success or failure is in the EQ flag.
@@ -1199,6 +1202,7 @@ public:
 
   address read_polling_page(Register r, address page, relocInfo::relocType rtype);
   address read_polling_page(Register r, relocInfo::relocType rtype);
+  void get_polling_page(Register dest, address page, relocInfo::relocType rtype);
 
   // CRC32 code for java.util.zip.CRC32::updateBytes() instrinsic.
   void update_byte_crc32(Register crc, Register val, Register table);
diff --git a/src/hotspot/cpu/aarch64/nativeInst_aarch64.cpp b/src/hotspot/cpu/aarch64/nativeInst_aarch64.cpp
index 88e1d3d1460..6a313273ea6 100644
--- a/src/hotspot/cpu/aarch64/nativeInst_aarch64.cpp
+++ b/src/hotspot/cpu/aarch64/nativeInst_aarch64.cpp
@@ -245,6 +245,11 @@ bool NativeInstruction::is_safepoint_poll() {
   // mov(reg, polling_page);
   // ldr(zr, [reg, #offset]);
   //
+  // or
+  //
+  // ldr(reg, [rthread, #offset]);
+  // ldr(zr, [reg, #offset]);
+  //
   // however, we cannot rely on the polling page address load always
   // directly preceding the read from the page. C1 does that but C2
   // has to do the load and read as two independent instruction
diff --git a/src/hotspot/cpu/aarch64/sharedRuntime_aarch64.cpp b/src/hotspot/cpu/aarch64/sharedRuntime_aarch64.cpp
index 9b67fe7a5f2..0a5b696b575 100644
--- a/src/hotspot/cpu/aarch64/sharedRuntime_aarch64.cpp
+++ b/src/hotspot/cpu/aarch64/sharedRuntime_aarch64.cpp
@@ -1664,7 +1664,7 @@ nmethod* SharedRuntime::generate_native_wrapper(MacroAssembler* masm,
   // critical natives they are offset down.
   GrowableArray<int> arg_order(2 * total_in_args);
   VMRegPair tmp_vmreg;
-  tmp_vmreg.set1(r19->as_VMReg());
+  tmp_vmreg.set2(r19->as_VMReg());
 
   if (!is_critical_native) {
     for (int i = total_in_args - 1, c_arg = total_c_args - 1; i >= 0; i--, c_arg--) {
@@ -1952,7 +1952,7 @@ nmethod* SharedRuntime::generate_native_wrapper(MacroAssembler* masm,
       __ strw(rscratch1, Address(rthread, JavaThread::thread_state_offset()));
 
       // Force this write out before the read below
-      __ dmb(Assembler::SY);
+      __ dmb(Assembler::ISH);
     } else {
       __ lea(rscratch2, Address(rthread, JavaThread::thread_state_offset()));
       __ stlrw(rscratch1, rscratch2);
@@ -1970,13 +1970,7 @@ nmethod* SharedRuntime::generate_native_wrapper(MacroAssembler* masm,
   // check for safepoint operation in progress and/or pending suspend requests
   Label safepoint_in_progress, safepoint_in_progress_done;
   {
-    assert(SafepointSynchronize::_not_synchronized == 0, "fix this code");
-    unsigned long offset;
-    __ adrp(rscratch1,
-            ExternalAddress((address)SafepointSynchronize::address_of_state()),
-            offset);
-    __ ldrw(rscratch1, Address(rscratch1, offset));
-    __ cbnzw(rscratch1, safepoint_in_progress);
+    __ safepoint_poll_acquire(safepoint_in_progress);
     __ ldrw(rscratch1, Address(rthread, JavaThread::suspend_flags_offset()));
     __ cbnzw(rscratch1, safepoint_in_progress);
     __ bind(safepoint_in_progress_done);
@@ -2932,8 +2926,11 @@ SafepointBlob* SharedRuntime::generate_handler_blob(address call_ptr, int poll_t
 
   if (!cause_return) {
     // overwrite the return address pushed by save_live_registers
-    __ ldr(c_rarg0, Address(rthread, JavaThread::saved_exception_pc_offset()));
-    __ str(c_rarg0, Address(rfp, wordSize));
+    // Additionally, r20 is a callee-saved register so we can look at
+    // it later to determine if someone changed the return address for
+    // us!
+    __ ldr(r20, Address(rthread, JavaThread::saved_exception_pc_offset()));
+    __ str(r20, Address(rfp, wordSize));
   }
 
   // Do the call
@@ -2968,11 +2965,40 @@ SafepointBlob* SharedRuntime::generate_handler_blob(address call_ptr, int poll_t
   // No exception case
   __ bind(noException);
 
+  Label no_adjust, bail;
+  if (SafepointMechanism::uses_thread_local_poll() && !cause_return) {
+    // If our stashed return pc was modified by the runtime we avoid touching it
+    __ ldr(rscratch1, Address(rfp, wordSize));
+    __ cmp(r20, rscratch1);
+    __ br(Assembler::NE, no_adjust);
+
+#ifdef ASSERT
+    // Verify the correct encoding of the poll we're about to skip.
+    // See NativeInstruction::is_ldrw_to_zr()
+    __ ldrw(rscratch1, Address(r20));
+    __ ubfx(rscratch2, rscratch1, 22, 10);
+    __ cmpw(rscratch2, 0b1011100101);
+    __ br(Assembler::NE, bail);
+    __ ubfx(rscratch2, rscratch1, 0, 5);
+    __ cmpw(rscratch2, 0b11111);
+    __ br(Assembler::NE, bail);
+#endif
+    // Adjust return pc forward to step over the safepoint poll instruction
+    __ add(r20, r20, NativeInstruction::instruction_size);
+    __ str(r20, Address(rfp, wordSize));
+  }
+
+  __ bind(no_adjust);
   // Normal exit, restore registers and exit.
   RegisterSaver::restore_live_registers(masm, save_vectors);
 
   __ ret(lr);
 
+#ifdef ASSERT
+  __ bind(bail);
+  __ stop("Attempting to adjust pc to skip safepoint poll but the return point is not what we expected");
+#endif
+
   // Make sure all code is generated
   masm->flush();
 
diff --git a/src/hotspot/cpu/aarch64/templateInterpreterGenerator_aarch64.cpp b/src/hotspot/cpu/aarch64/templateInterpreterGenerator_aarch64.cpp
index 9174c554bcc..f33c6e513a8 100644
--- a/src/hotspot/cpu/aarch64/templateInterpreterGenerator_aarch64.cpp
+++ b/src/hotspot/cpu/aarch64/templateInterpreterGenerator_aarch64.cpp
@@ -414,6 +414,14 @@ address TemplateInterpreterGenerator::generate_return_entry_for(TosState state,
   __ restore_constant_pool_cache();
   __ get_method(rmethod);
 
+  if (state == atos) {
+    Register obj = r0;
+    Register mdp = r1;
+    Register tmp = r2;
+    __ ldr(mdp, Address(rmethod, Method::method_data_offset()));
+    __ profile_return_type(mdp, obj, tmp);
+  }
+
   // Pop N words from the stack
   __ get_cache_and_index_at_bcp(r1, r2, 1, index_size);
   __ ldr(r1, Address(r1, ConstantPoolCache::base_offset() + ConstantPoolCacheEntry::flags_offset()));
@@ -967,12 +975,7 @@ address TemplateInterpreterGenerator::generate_CRC32_update_entry() {
 
     Label slow_path;
     // If we need a safepoint check, generate full interpreter entry.
-    ExternalAddress state(SafepointSynchronize::address_of_state());
-    unsigned long offset;
-    __ adrp(rscratch1, ExternalAddress(SafepointSynchronize::address_of_state()), offset);
-    __ ldrw(rscratch1, Address(rscratch1, offset));
-    assert(SafepointSynchronize::_not_synchronized == 0, "rewrite this code");
-    __ cbnz(rscratch1, slow_path);
+    __ safepoint_poll(slow_path);
 
     // We don't generate local frame and don't align stack because
     // we call stub code and there is no safepoint on this path.
@@ -986,6 +989,7 @@ address TemplateInterpreterGenerator::generate_CRC32_update_entry() {
     __ ldrw(val, Address(esp, 0));              // byte value
     __ ldrw(crc, Address(esp, wordSize));       // Initial CRC
 
+    unsigned long offset;
     __ adrp(tbl, ExternalAddress(StubRoutines::crc_table_addr()), offset);
     __ add(tbl, tbl, offset);
 
@@ -1020,12 +1024,7 @@ address TemplateInterpreterGenerator::generate_CRC32_updateBytes_entry(AbstractI
 
     Label slow_path;
     // If we need a safepoint check, generate full interpreter entry.
-    ExternalAddress state(SafepointSynchronize::address_of_state());
-    unsigned long offset;
-    __ adrp(rscratch1, ExternalAddress(SafepointSynchronize::address_of_state()), offset);
-    __ ldrw(rscratch1, Address(rscratch1, offset));
-    assert(SafepointSynchronize::_not_synchronized == 0, "rewrite this code");
-    __ cbnz(rscratch1, slow_path);
+    __ safepoint_poll(slow_path);
 
     // We don't generate local frame and don't align stack because
     // we call stub code and there is no safepoint on this path.
@@ -1375,7 +1374,7 @@ address TemplateInterpreterGenerator::generate_native_entry(bool synchronized) {
   if (os::is_MP()) {
     if (UseMembar) {
       // Force this write out before the read below
-      __ dsb(Assembler::SY);
+      __ dmb(Assembler::ISH);
     } else {
       // Write serialization page so VM thread can do a pseudo remote membar.
       // We use the current thread pointer to calculate a thread specific
@@ -1387,16 +1386,8 @@ address TemplateInterpreterGenerator::generate_native_entry(bool synchronized) {
 
   // check for safepoint operation in progress and/or pending suspend requests
   {
-    Label Continue;
-    {
-      unsigned long offset;
-      __ adrp(rscratch2, SafepointSynchronize::address_of_state(), offset);
-      __ ldrw(rscratch2, Address(rscratch2, offset));
-    }
-    assert(SafepointSynchronize::_not_synchronized == 0,
-           "SafepointSynchronize::_not_synchronized");
-    Label L;
-    __ cbnz(rscratch2, L);
+    Label L, Continue;
+    __ safepoint_poll_acquire(L);
     __ ldrw(rscratch2, Address(rthread, JavaThread::suspend_flags_offset()));
     __ cbz(rscratch2, Continue);
     __ bind(L);
@@ -1671,6 +1662,14 @@ address TemplateInterpreterGenerator::generate_normal_entry(bool synchronized) {
   __ mov(rscratch2, true);
   __ strb(rscratch2, do_not_unlock_if_synchronized);
 
+  Label no_mdp;
+  Register mdp = r3;
+  __ ldr(mdp, Address(rmethod, Method::method_data_offset()));
+  __ cbz(mdp, no_mdp);
+  __ add(mdp, mdp, in_bytes(MethodData::data_offset()));
+  __ profile_parameters_type(mdp, r1, r2);
+  __ bind(no_mdp);
+
   // increment invocation count & check for overflow
   Label invocation_counter_overflow;
   Label profile_method;
diff --git a/src/hotspot/cpu/aarch64/templateTable_aarch64.cpp b/src/hotspot/cpu/aarch64/templateTable_aarch64.cpp
index 2ba42035e7b..9390d38b1b8 100644
--- a/src/hotspot/cpu/aarch64/templateTable_aarch64.cpp
+++ b/src/hotspot/cpu/aarch64/templateTable_aarch64.cpp
@@ -1717,7 +1717,7 @@ void TemplateTable::branch(bool is_jsr, bool is_wide)
     __ push_i(r1);
     // Adjust the bcp by the 16-bit displacement in r2
     __ add(rbcp, rbcp, r2);
-    __ dispatch_only(vtos);
+    __ dispatch_only(vtos, /*generate_poll*/true);
     return;
   }
 
@@ -1833,7 +1833,7 @@ void TemplateTable::branch(bool is_jsr, bool is_wide)
   // continue with the bytecode @ target
   // rscratch1: target bytecode
   // rbcp: target bcp
-  __ dispatch_only(vtos);
+  __ dispatch_only(vtos, /*generate_poll*/true);
 
   if (UseLoopCounter) {
     if (ProfileInterpreter) {
@@ -1973,7 +1973,7 @@ void TemplateTable::ret() {
   __ ldr(rbcp, Address(rmethod, Method::const_offset()));
   __ lea(rbcp, Address(rbcp, r1));
   __ add(rbcp, rbcp, in_bytes(ConstMethod::codes_offset()));
-  __ dispatch_next(vtos);
+  __ dispatch_next(vtos, 0, /*generate_poll*/true);
 }
 
 void TemplateTable::wide_ret() {
@@ -1984,7 +1984,7 @@ void TemplateTable::wide_ret() {
   __ ldr(rbcp, Address(rmethod, Method::const_offset()));
   __ lea(rbcp, Address(rbcp, r1));
   __ add(rbcp, rbcp, in_bytes(ConstMethod::codes_offset()));
-  __ dispatch_next(vtos);
+  __ dispatch_next(vtos, 0, /*generate_poll*/true);
 }
 
 
@@ -2014,7 +2014,7 @@ void TemplateTable::tableswitch() {
   __ rev32(r3, r3);
   __ load_unsigned_byte(rscratch1, Address(rbcp, r3, Address::sxtw(0)));
   __ add(rbcp, rbcp, r3, ext::sxtw);
-  __ dispatch_only(vtos);
+  __ dispatch_only(vtos, /*generate_poll*/true);
   // handle default
   __ bind(default_case);
   __ profile_switch_default(r0);
@@ -2064,7 +2064,7 @@ void TemplateTable::fast_linearswitch() {
   __ rev32(r3, r3);
   __ add(rbcp, rbcp, r3, ext::sxtw);
   __ ldrb(rscratch1, Address(rbcp, 0));
-  __ dispatch_only(vtos);
+  __ dispatch_only(vtos, /*generate_poll*/true);
 }
 
 void TemplateTable::fast_binaryswitch() {
@@ -2162,7 +2162,7 @@ void TemplateTable::fast_binaryswitch() {
   __ rev32(j, j);
   __ load_unsigned_byte(rscratch1, Address(rbcp, j, Address::sxtw(0)));
   __ lea(rbcp, Address(rbcp, j, Address::sxtw(0)));
-  __ dispatch_only(vtos);
+  __ dispatch_only(vtos, /*generate_poll*/true);
 
   // default case -> j = default offset
   __ bind(default_case);
@@ -2171,7 +2171,7 @@ void TemplateTable::fast_binaryswitch() {
   __ rev32(j, j);
   __ load_unsigned_byte(rscratch1, Address(rbcp, j, Address::sxtw(0)));
   __ lea(rbcp, Address(rbcp, j, Address::sxtw(0)));
-  __ dispatch_only(vtos);
+  __ dispatch_only(vtos, /*generate_poll*/true);
 }
 
 
diff --git a/src/hotspot/cpu/aarch64/vm_version_aarch64.cpp b/src/hotspot/cpu/aarch64/vm_version_aarch64.cpp
index 76d750a6942..33e78cd8914 100644
--- a/src/hotspot/cpu/aarch64/vm_version_aarch64.cpp
+++ b/src/hotspot/cpu/aarch64/vm_version_aarch64.cpp
@@ -394,4 +394,6 @@ void VM_Version::initialize() {
                                    g.generate_getPsrInfo());
 
   get_processor_features();
+
+  UNSUPPORTED_OPTION(CriticalJNINatives);
 }
diff --git a/src/hotspot/cpu/arm/stubGenerator_arm.cpp b/src/hotspot/cpu/arm/stubGenerator_arm.cpp
index 97ef93c2e4f..ec1a6d8279d 100644
--- a/src/hotspot/cpu/arm/stubGenerator_arm.cpp
+++ b/src/hotspot/cpu/arm/stubGenerator_arm.cpp
@@ -2968,7 +2968,9 @@ class StubGenerator: public StubCodeGenerator {
         CardTableModRefBS* ct = barrier_set_cast<CardTableModRefBS>(bs);
         assert(sizeof(*ct->byte_map_base) == sizeof(jbyte), "adjust this code");
 
-        Label L_cardtable_loop;
+        Label L_cardtable_loop, L_done;
+
+        __ cbz_32(count, L_done); // zero count - nothing to do
 
         __ add_ptr_scaled_int32(count, addr, count, LogBytesPerHeapOop);
         __ sub(count, count, BytesPerHeapOop);                            // last addr
@@ -2987,6 +2989,7 @@ class StubGenerator: public StubCodeGenerator {
         __ strb(zero, Address(addr, 1, post_indexed));
         __ subs(count, count, 1);
         __ b(L_cardtable_loop, ge);
+        __ BIND(L_done);
       }
       break;
     case BarrierSet::ModRef:
diff --git a/src/hotspot/cpu/ppc/c1_MacroAssembler_ppc.cpp b/src/hotspot/cpu/ppc/c1_MacroAssembler_ppc.cpp
index 631b20b81a5..875c5dfdfdf 100644
--- a/src/hotspot/cpu/ppc/c1_MacroAssembler_ppc.cpp
+++ b/src/hotspot/cpu/ppc/c1_MacroAssembler_ppc.cpp
@@ -41,20 +41,25 @@
 
 void C1_MacroAssembler::inline_cache_check(Register receiver, Register iCache) {
   const Register temp_reg = R12_scratch2;
+  Label Lmiss;
+
   verify_oop(receiver);
+  MacroAssembler::null_check(receiver, oopDesc::klass_offset_in_bytes(), &Lmiss);
   load_klass(temp_reg, receiver);
-  if (TrapBasedICMissChecks) {
+
+  if (TrapBasedICMissChecks && TrapBasedNullChecks) {
     trap_ic_miss_check(temp_reg, iCache);
   } else {
-    Label L;
+    Label Lok;
     cmpd(CCR0, temp_reg, iCache);
-    beq(CCR0, L);
+    beq(CCR0, Lok);
+    bind(Lmiss);
     //load_const_optimized(temp_reg, SharedRuntime::get_ic_miss_stub(), R0);
     calculate_address_from_global_toc(temp_reg, SharedRuntime::get_ic_miss_stub(), true, true, false);
     mtctr(temp_reg);
     bctr();
     align(32, 12);
-    bind(L);
+    bind(Lok);
   }
 }
 
diff --git a/src/hotspot/cpu/ppc/templateTable_ppc_64.cpp b/src/hotspot/cpu/ppc/templateTable_ppc_64.cpp
index 282ffeb218b..c7fdc9d7e23 100644
--- a/src/hotspot/cpu/ppc/templateTable_ppc_64.cpp
+++ b/src/hotspot/cpu/ppc/templateTable_ppc_64.cpp
@@ -3371,7 +3371,7 @@ void TemplateTable::invokevirtual(int byte_no) {
   __ testbitdi(CCR0, R0, Rflags, ConstantPoolCacheEntry::is_vfinal_shift);
   __ bfalse(CCR0, LnotFinal);
 
-  if (RewriteBytecodes && !UseSharedSpaces) {
+  if (RewriteBytecodes && !UseSharedSpaces && !DumpSharedSpaces) {
     patch_bytecode(Bytecodes::_fast_invokevfinal, Rnew_bc, R12_scratch2);
   }
   invokevfinal_helper(Rvtableindex_or_method, Rflags, R11_scratch1, R12_scratch2);
diff --git a/src/hotspot/cpu/s390/assembler_s390.hpp b/src/hotspot/cpu/s390/assembler_s390.hpp
index a839700259c..9900adbfebc 100644
--- a/src/hotspot/cpu/s390/assembler_s390.hpp
+++ b/src/hotspot/cpu/s390/assembler_s390.hpp
@@ -582,7 +582,11 @@ class Assembler : public AbstractAssembler {
 #define LOC_ZOPC    (unsigned long)(0xebL << 40 | 0xf2L)        // z196
 #define LOCG_ZOPC   (unsigned long)(0xebL << 40 | 0xe2L)        // z196
 
-#define LMG_ZOPC    (unsigned long)(235L << 40 | 4L)
+
+// LOAD multiple registers at once
+#define LM_ZOPC     (unsigned  int)(0x98  << 24)
+#define LMY_ZOPC    (unsigned long)(0xebL << 40 | 0x98L)
+#define LMG_ZOPC    (unsigned long)(0xebL << 40 | 0x04L)
 
 #define LE_ZOPC     (unsigned  int)(0x78 << 24)
 #define LEY_ZOPC    (unsigned long)(237L << 40 | 100L)
@@ -613,7 +617,10 @@ class Assembler : public AbstractAssembler {
 #define STOC_ZOPC   (unsigned long)(0xebL << 40 | 0xf3L)        // z196
 #define STOCG_ZOPC  (unsigned long)(0xebL << 40 | 0xe3L)        // z196
 
-#define STMG_ZOPC   (unsigned long)(235L << 40 | 36L)
+// STORE multiple registers at once
+#define STM_ZOPC    (unsigned  int)(0x90  << 24)
+#define STMY_ZOPC   (unsigned long)(0xebL << 40 | 0x90L)
+#define STMG_ZOPC   (unsigned long)(0xebL << 40 | 0x24L)
 
 #define STE_ZOPC    (unsigned  int)(0x70 << 24)
 #define STEY_ZOPC   (unsigned long)(237L << 40 | 102L)
@@ -874,15 +881,19 @@ class Assembler : public AbstractAssembler {
 
 // Shift
 // arithmetic
-#define SLA_ZOPC    (unsigned  int)(139 << 24)
-#define SLAG_ZOPC   (unsigned long)(235L << 40 | 11L)
-#define SRA_ZOPC    (unsigned  int)(138 << 24)
-#define SRAG_ZOPC   (unsigned long)(235L << 40 | 10L)
+#define SLA_ZOPC    (unsigned  int)(0x8b  << 24)
+#define SLAK_ZOPC   (unsigned long)(0xebL << 40 | 0xddL)
+#define SLAG_ZOPC   (unsigned long)(0xebL << 40 | 0x0bL)
+#define SRA_ZOPC    (unsigned  int)(0x8a  << 24)
+#define SRAK_ZOPC   (unsigned long)(0xebL << 40 | 0xdcL)
+#define SRAG_ZOPC   (unsigned long)(0xebL << 40 | 0x0aL)
 // logical
-#define SLL_ZOPC    (unsigned  int)(137 << 24)
-#define SLLG_ZOPC   (unsigned long)(235L << 40 | 13L)
-#define SRL_ZOPC    (unsigned  int)(136 << 24)
-#define SRLG_ZOPC   (unsigned long)(235L << 40 | 12L)
+#define SLL_ZOPC    (unsigned  int)(0x89  << 24)
+#define SLLK_ZOPC   (unsigned long)(0xebL << 40 | 0xdfL)
+#define SLLG_ZOPC   (unsigned long)(0xebL << 40 | 0x0dL)
+#define SRL_ZOPC    (unsigned  int)(0x88  << 24)
+#define SRLK_ZOPC   (unsigned long)(0xebL << 40 | 0xdeL)
+#define SRLG_ZOPC   (unsigned long)(0xebL << 40 | 0x0cL)
 
 // Rotate, then AND/XOR/OR/insert
 // rotate
@@ -2262,12 +2273,16 @@ class Assembler : public AbstractAssembler {
 
   // shift
   inline void z_sla( Register r1,              int64_t d2, Register b2=Z_R0); // shift left  r1 = r1 << ((d2+b2)&0x3f) ; int32, only 31 bits shifted, sign preserved!
+  inline void z_slak(Register r1, Register r3, int64_t d2, Register b2=Z_R0); // shift left  r1 = r3 << ((d2+b2)&0x3f) ; int32, only 31 bits shifted, sign preserved!
   inline void z_slag(Register r1, Register r3, int64_t d2, Register b2=Z_R0); // shift left  r1 = r3 << ((d2+b2)&0x3f) ; int64, only 63 bits shifted, sign preserved!
   inline void z_sra( Register r1,              int64_t d2, Register b2=Z_R0); // shift right r1 = r1 >> ((d2+b2)&0x3f) ; int32, sign extended
+  inline void z_srak(Register r1, Register r3, int64_t d2, Register b2=Z_R0); // shift right r1 = r3 >> ((d2+b2)&0x3f) ; int32, sign extended
   inline void z_srag(Register r1, Register r3, int64_t d2, Register b2=Z_R0); // shift right r1 = r3 >> ((d2+b2)&0x3f) ; int64, sign extended
   inline void z_sll( Register r1,              int64_t d2, Register b2=Z_R0); // shift left  r1 = r1 << ((d2+b2)&0x3f) ; int32, zeros added
+  inline void z_sllk(Register r1, Register r3, int64_t d2, Register b2=Z_R0); // shift left  r1 = r3 << ((d2+b2)&0x3f) ; int32, zeros added
   inline void z_sllg(Register r1, Register r3, int64_t d2, Register b2=Z_R0); // shift left  r1 = r3 << ((d2+b2)&0x3f) ; int64, zeros added
   inline void z_srl( Register r1,              int64_t d2, Register b2=Z_R0); // shift right r1 = r1 >> ((d2+b2)&0x3f) ; int32, zero extended
+  inline void z_srlk(Register r1, Register r3, int64_t d2, Register b2=Z_R0); // shift right r1 = r3 >> ((d2+b2)&0x3f) ; int32, zero extended
   inline void z_srlg(Register r1, Register r3, int64_t d2, Register b2=Z_R0); // shift right r1 = r3 >> ((d2+b2)&0x3f) ; int64, zero extended
 
   // rotate
@@ -3035,7 +3050,11 @@ class Assembler : public AbstractAssembler {
 
   inline void z_tam();
   inline void z_stckf(int64_t d2, Register b2);
+  inline void z_stm( Register r1, Register r3, int64_t d2, Register b2);
+  inline void z_stmy(Register r1, Register r3, int64_t d2, Register b2);
   inline void z_stmg(Register r1, Register r3, int64_t d2, Register b2);
+  inline void z_lm( Register r1, Register r3, int64_t d2, Register b2);
+  inline void z_lmy(Register r1, Register r3, int64_t d2, Register b2);
   inline void z_lmg(Register r1, Register r3, int64_t d2, Register b2);
 
   inline void z_cs( Register r1, Register r3, int64_t d2, Register b2);
diff --git a/src/hotspot/cpu/s390/assembler_s390.inline.hpp b/src/hotspot/cpu/s390/assembler_s390.inline.hpp
index 19c472787c5..583d86e18b0 100644
--- a/src/hotspot/cpu/s390/assembler_s390.inline.hpp
+++ b/src/hotspot/cpu/s390/assembler_s390.inline.hpp
@@ -334,12 +334,16 @@ inline void Assembler::z_stfle(int64_t d2, Register b2) { emit_32(STFLE_ZOPC | u
 // SHIFT/RORATE OPERATIONS
 //-----------------------------------
 inline void Assembler::z_sla( Register r1,              int64_t d2, Register b2) { emit_32( SLA_ZOPC  | regt(r1, 8, 32) | uimm12(d2, 20, 32) | reg(b2, 16, 32)); }
+inline void Assembler::z_slak(Register r1, Register r3, int64_t d2, Register b2) { emit_48( SLAK_ZOPC | regt(r1, 8, 48) | simm20(d2)         | reg(b2, 16, 48) | reg(r3, 12, 48)); }
 inline void Assembler::z_slag(Register r1, Register r3, int64_t d2, Register b2) { emit_48( SLAG_ZOPC | regt(r1, 8, 48) | simm20(d2)         | reg(b2, 16, 48) | reg(r3, 12, 48)); }
 inline void Assembler::z_sra( Register r1,              int64_t d2, Register b2) { emit_32( SRA_ZOPC  | regt(r1, 8, 32) | uimm12(d2, 20, 32) | reg(b2, 16, 32)); }
+inline void Assembler::z_srak(Register r1, Register r3, int64_t d2, Register b2) { emit_48( SRAK_ZOPC | regt(r1, 8, 48) | simm20(d2)         | reg(b2, 16, 48) | reg(r3, 12, 48)); }
 inline void Assembler::z_srag(Register r1, Register r3, int64_t d2, Register b2) { emit_48( SRAG_ZOPC | regt(r1, 8, 48) | simm20(d2)         | reg(b2, 16, 48) | reg(r3, 12, 48)); }
 inline void Assembler::z_sll( Register r1,              int64_t d2, Register b2) { emit_32( SLL_ZOPC  | regt(r1, 8, 32) | uimm12(d2, 20, 32) | reg(b2, 16, 32)); }
+inline void Assembler::z_sllk(Register r1, Register r3, int64_t d2, Register b2) { emit_48( SLLK_ZOPC | regt(r1, 8, 48) | simm20(d2)         | reg(b2, 16, 48) | reg(r3, 12, 48)); }
 inline void Assembler::z_sllg(Register r1, Register r3, int64_t d2, Register b2) { emit_48( SLLG_ZOPC | regt(r1, 8, 48) | simm20(d2)         | reg(b2, 16, 48) | reg(r3, 12, 48)); }
 inline void Assembler::z_srl( Register r1,              int64_t d2, Register b2) { emit_32( SRL_ZOPC  | regt(r1, 8, 32) | uimm12(d2, 20, 32) | reg(b2, 16, 32)); }
+inline void Assembler::z_srlk(Register r1, Register r3, int64_t d2, Register b2) { emit_48( SRLK_ZOPC | regt(r1, 8, 48) | simm20(d2)         | reg(b2, 16, 48) | reg(r3, 12, 48)); }
 inline void Assembler::z_srlg(Register r1, Register r3, int64_t d2, Register b2) { emit_48( SRLG_ZOPC | regt(r1, 8, 48) | simm20(d2)         | reg(b2, 16, 48) | reg(r3, 12, 48)); }
 
 // rotate left
@@ -690,10 +694,14 @@ inline void Assembler::z_ahhlr(Register r1, Register r2, Register r3) { emit_32(
 
 inline void Assembler::z_tam() { emit_16( TAM_ZOPC); }
 inline void Assembler::z_stckf(int64_t d2, Register b2) { emit_32( STCKF_ZOPC | uimm12(d2, 20, 32) | regz(b2, 16, 32)); }
-inline void Assembler::z_stmg(Register r1, Register r3, int64_t d2, Register b2) { emit_48( STMG_ZOPC | simm20(d2) | reg(r1, 8, 48) | reg(r3,12,48)| reg(b2,16,48) ); }
-inline void Assembler::z_lmg(Register r1, Register r3, int64_t d2, Register b2)  { emit_48( LMG_ZOPC  | simm20(d2) | reg(r1, 8, 48) | reg(r3,12,48)| reg(b2,16,48) ); }
+inline void Assembler::z_stm( Register r1, Register r3, int64_t d2, Register b2) { emit_32( STM_ZOPC  | reg(r1, 8, 32) | reg(r3,12,32)| reg(b2,16,32) | uimm12(d2, 20,32)); }
+inline void Assembler::z_stmy(Register r1, Register r3, int64_t d2, Register b2) { emit_48( STMY_ZOPC | reg(r1, 8, 48) | reg(r3,12,48)| reg(b2,16,48) | simm20(d2) ); }
+inline void Assembler::z_stmg(Register r1, Register r3, int64_t d2, Register b2) { emit_48( STMG_ZOPC | reg(r1, 8, 48) | reg(r3,12,48)| reg(b2,16,48) | simm20(d2) ); }
+inline void Assembler::z_lm(  Register r1, Register r3, int64_t d2, Register b2) { emit_32( LM_ZOPC   | reg(r1, 8, 32) | reg(r3,12,32)| reg(b2,16,32) | uimm12(d2, 20,32)); }
+inline void Assembler::z_lmy( Register r1, Register r3, int64_t d2, Register b2) { emit_48( LMY_ZOPC  | reg(r1, 8, 48) | reg(r3,12,48)| reg(b2,16,48) | simm20(d2) ); }
+inline void Assembler::z_lmg( Register r1, Register r3, int64_t d2, Register b2) { emit_48( LMG_ZOPC  | reg(r1, 8, 48) | reg(r3,12,48)| reg(b2,16,48) | simm20(d2) ); }
 
-inline void Assembler::z_cs(Register r1, Register r3, int64_t d2, Register b2)  { emit_32( CS_ZOPC  | regt(r1, 8, 32) | reg(r3, 12, 32) | reg(b2, 16, 32) | uimm12(d2, 20, 32)); }
+inline void Assembler::z_cs( Register r1, Register r3, int64_t d2, Register b2) { emit_32( CS_ZOPC  | regt(r1, 8, 32) | reg(r3, 12, 32) | reg(b2, 16, 32) | uimm12(d2, 20, 32)); }
 inline void Assembler::z_csy(Register r1, Register r3, int64_t d2, Register b2) { emit_48( CSY_ZOPC | regt(r1, 8, 48) | reg(r3, 12, 48) | reg(b2, 16, 48) | simm20(d2)); }
 inline void Assembler::z_csg(Register r1, Register r3, int64_t d2, Register b2) { emit_48( CSG_ZOPC | regt(r1, 8, 48) | reg(r3, 12, 48) | reg(b2, 16, 48) | simm20(d2)); }
 inline void Assembler::z_cs( Register r1, Register r3, const Address& a) { assert(!a.has_index(), "Cannot encode index"); z_cs( r1, r3, a.disp(), a.baseOrR0()); }
diff --git a/src/hotspot/cpu/s390/macroAssembler_s390.cpp b/src/hotspot/cpu/s390/macroAssembler_s390.cpp
index af2c02934ff..afc7a7667e2 100644
--- a/src/hotspot/cpu/s390/macroAssembler_s390.cpp
+++ b/src/hotspot/cpu/s390/macroAssembler_s390.cpp
@@ -936,7 +936,7 @@ void MacroAssembler::load_long_pcrelative(Register Rdst, address dataLocation) {
 
   // Some extra safety net.
   if (!RelAddr::is_in_range_of_RelAddr32(total_distance)) {
-    guarantee(RelAddr::is_in_range_of_RelAddr32(total_distance), "too far away");
+    guarantee(RelAddr::is_in_range_of_RelAddr32(total_distance), "load_long_pcrelative can't handle distance " INTPTR_FORMAT, total_distance);
   }
 
   (this)->relocate(rspec, relocInfo::pcrel_addr_format);
@@ -956,7 +956,7 @@ void MacroAssembler::load_addr_pcrelative(Register Rdst, address addrLocation) {
 
   // Some extra safety net.
   if (!RelAddr::is_in_range_of_RelAddr32(total_distance)) {
-    guarantee(RelAddr::is_in_range_of_RelAddr32(total_distance), "too far away");
+    guarantee(RelAddr::is_in_range_of_RelAddr32(total_distance), "load_long_pcrelative can't handle distance " INTPTR_FORMAT, total_distance);
   }
 
   (this)->relocate(rspec, relocInfo::pcrel_addr_format);
@@ -1025,6 +1025,13 @@ void MacroAssembler::testbit(Register r, unsigned int bitPos) {
   }
 }
 
+void MacroAssembler::prefetch_read(Address a) {
+  z_pfd(1, a.disp20(), a.indexOrR0(), a.base());
+}
+void MacroAssembler::prefetch_update(Address a) {
+  z_pfd(2, a.disp20(), a.indexOrR0(), a.base());
+}
+
 // Clear a register, i.e. load const zero into reg.
 // Return len (in bytes) of generated instruction(s).
 // whole_reg: Clear 64 bits if true, 32 bits otherwise.
@@ -4896,77 +4903,296 @@ unsigned int MacroAssembler::CopyRawMemory_AlignedDisjoint(Register src_reg, Reg
 
 // Intrinsics for CompactStrings
 
-// Compress char[] to byte[]. odd_reg contains cnt. Kills dst. Early clobber: result
+// Compress char[] to byte[].
+//   Restores: src, dst
+//   Uses:     cnt
+//   Kills:    tmp, Z_R0, Z_R1.
+//   Early clobber: result.
+// Note:
+//   cnt is signed int. Do not rely on high word!
+//       counts # characters, not bytes.
 // The result is the number of characters copied before the first incompatible character was found.
-// If tmp2 is provided and the compression fails, the compression stops exactly at this point and the result is precise.
+// If precise is true, the processing stops exactly at this point. Otherwise, the result may be off
+// by a few bytes. The result always indicates the number of copied characters.
 //
 // Note: Does not behave exactly like package private StringUTF16 compress java implementation in case of failure:
-// - Different number of characters may have been written to dead array (if tmp2 not provided).
+// - Different number of characters may have been written to dead array (if precise is false).
 // - Returns a number <cnt instead of 0. (Result gets compared with cnt.)
-unsigned int MacroAssembler::string_compress(Register result, Register src, Register dst, Register odd_reg,
-                                             Register even_reg, Register tmp, Register tmp2) {
-  int block_start = offset();
-  Label Lloop1, Lloop2, Lslow, Ldone;
-  const Register addr2 = dst, ind1 = result, mask = tmp;
-  const bool precise = (tmp2 != noreg);
+unsigned int MacroAssembler::string_compress(Register result, Register src, Register dst, Register cnt,
+                                             Register tmp,    bool precise) {
+  assert_different_registers(Z_R0, Z_R1, src, dst, cnt, tmp);
 
-  BLOCK_COMMENT("string_compress {");
-
-  z_sll(odd_reg, 1);       // Number of bytes to read. (Must be a positive simm32.)
-  clear_reg(ind1);         // Index to read.
-  z_llilf(mask, 0xFF00FF00);
-  z_ahi(odd_reg, -16);     // Last possible index for fast loop.
-  z_brl(Lslow);
-
-  // ind1: index, even_reg: index increment, odd_reg: index limit
-  z_iihf(mask, 0xFF00FF00);
-  z_lhi(even_reg, 16);
-
-  bind(Lloop1); // 8 Characters per iteration.
-  z_lg(Z_R0, Address(src, ind1));
-  z_lg(Z_R1, Address(src, ind1, 8));
   if (precise) {
+    BLOCK_COMMENT("encode_iso_array {");
+  } else {
+    BLOCK_COMMENT("string_compress {");
+  }
+  int  block_start = offset();
+
+  Register       Rsrc  = src;
+  Register       Rdst  = dst;
+  Register       Rix   = tmp;
+  Register       Rcnt  = cnt;
+  Register       Rmask = result;  // holds incompatibility check mask until result value is stored.
+  Label          ScalarShortcut, AllDone;
+
+  z_iilf(Rmask, 0xFF00FF00);
+  z_iihf(Rmask, 0xFF00FF00);
+
+#if 0  // Sacrifice shortcuts for code compactness
+  {
+    //---<  shortcuts for short strings (very frequent)   >---
+    //   Strings with 4 and 8 characters were fond to occur very frequently.
+    //   Therefore, we handle them right away with minimal overhead.
+    Label     skipShortcut, skip4Shortcut, skip8Shortcut;
+    Register  Rout = Z_R0;
+    z_chi(Rcnt, 4);
+    z_brne(skip4Shortcut);                 // 4 characters are very frequent
+      z_lg(Z_R0, 0, Rsrc);                 // Treat exactly 4 characters specially.
+      if (VM_Version::has_DistinctOpnds()) {
+        Rout = Z_R0;
+        z_ngrk(Rix, Z_R0, Rmask);
+      } else {
+        Rout = Rix;
+        z_lgr(Rix, Z_R0);
+        z_ngr(Z_R0, Rmask);
+      }
+      z_brnz(skipShortcut);
+      z_stcmh(Rout, 5, 0, Rdst);
+      z_stcm(Rout,  5, 2, Rdst);
+      z_lgfr(result, Rcnt);
+      z_bru(AllDone);
+    bind(skip4Shortcut);
+
+    z_chi(Rcnt, 8);
+    z_brne(skip8Shortcut);                 // There's more to do...
+      z_lmg(Z_R0, Z_R1, 0, Rsrc);          // Treat exactly 8 characters specially.
+      if (VM_Version::has_DistinctOpnds()) {
+        Rout = Z_R0;
+        z_ogrk(Rix, Z_R0, Z_R1);
+        z_ngr(Rix, Rmask);
+      } else {
+        Rout = Rix;
+        z_lgr(Rix, Z_R0);
+        z_ogr(Z_R0, Z_R1);
+        z_ngr(Z_R0, Rmask);
+      }
+      z_brnz(skipShortcut);
+      z_stcmh(Rout, 5, 0, Rdst);
+      z_stcm(Rout,  5, 2, Rdst);
+      z_stcmh(Z_R1, 5, 4, Rdst);
+      z_stcm(Z_R1,  5, 6, Rdst);
+      z_lgfr(result, Rcnt);
+      z_bru(AllDone);
+
+    bind(skip8Shortcut);
+    clear_reg(Z_R0, true, false);          // #characters already processed (none). Precond for scalar loop.
+    z_brl(ScalarShortcut);                 // Just a few characters
+
+    bind(skipShortcut);
+  }
+#endif
+  clear_reg(Z_R0);                         // make sure register is properly initialized.
+
+  if (VM_Version::has_VectorFacility()) {
+    const int  min_vcnt     = 32;          // Minimum #characters required to use vector instructions.
+                                           // Otherwise just do nothing in vector mode.
+                                           // Must be multiple of 2*(vector register length in chars (8 HW = 128 bits)).
+    const int  log_min_vcnt = exact_log2(min_vcnt);
+    Label      VectorLoop, VectorDone, VectorBreak;
+
+    VectorRegister Vtmp1      = Z_V16;
+    VectorRegister Vtmp2      = Z_V17;
+    VectorRegister Vmask      = Z_V18;
+    VectorRegister Vzero      = Z_V19;
+    VectorRegister Vsrc_first = Z_V20;
+    VectorRegister Vsrc_last  = Z_V23;
+
+    assert((Vsrc_last->encoding() - Vsrc_first->encoding() + 1) == min_vcnt/8, "logic error");
+    assert(VM_Version::has_DistinctOpnds(), "Assumption when has_VectorFacility()");
+    z_srak(Rix, Rcnt, log_min_vcnt);       // # vector loop iterations
+    z_brz(VectorDone);                     // not enough data for vector loop
+
+    z_vzero(Vzero);                        // all zeroes
+    z_vgmh(Vmask, 0, 7);                   // generate 0xff00 mask for all 2-byte elements
+    z_sllg(Z_R0, Rix, log_min_vcnt);       // remember #chars that will be processed by vector loop
+
+    bind(VectorLoop);
+      z_vlm(Vsrc_first, Vsrc_last, 0, Rsrc);
+      add2reg(Rsrc, min_vcnt*2);
+
+      //---<  check for incompatible character  >---
+      z_vo(Vtmp1, Z_V20, Z_V21);
+      z_vo(Vtmp2, Z_V22, Z_V23);
+      z_vo(Vtmp1, Vtmp1, Vtmp2);
+      z_vn(Vtmp1, Vtmp1, Vmask);
+      z_vceqhs(Vtmp1, Vtmp1, Vzero);       // high half of all chars must be zero for successful compress.
+      z_brne(VectorBreak);                 // break vector loop, incompatible character found.
+                                           // re-process data from current iteration in break handler.
+
+      //---<  pack & store characters  >---
+      z_vpkh(Vtmp1, Z_V20, Z_V21);         // pack (src1, src2) -> tmp1
+      z_vpkh(Vtmp2, Z_V22, Z_V23);         // pack (src3, src4) -> tmp2
+      z_vstm(Vtmp1, Vtmp2, 0, Rdst);       // store packed string
+      add2reg(Rdst, min_vcnt);
+
+      z_brct(Rix, VectorLoop);
+
+    z_bru(VectorDone);
+
+    bind(VectorBreak);
+      add2reg(Rsrc, -min_vcnt*2);          // Fix Rsrc. Rsrc was already updated, but Rdst and Rix are not.
+      z_sll(Rix, log_min_vcnt);            // # chars processed so far in VectorLoop, excl. current iteration.
+      z_sr(Z_R0, Rix);                     // correct # chars processed in total.
+
+    bind(VectorDone);
+  }
+
+  {
+    const int  min_cnt     =  8;           // Minimum #characters required to use unrolled loop.
+                                           // Otherwise just do nothing in unrolled loop.
+                                           // Must be multiple of 8.
+    const int  log_min_cnt = exact_log2(min_cnt);
+    Label      UnrolledLoop, UnrolledDone, UnrolledBreak;
+
     if (VM_Version::has_DistinctOpnds()) {
-      z_ogrk(tmp2, Z_R0, Z_R1);
+      z_srk(Rix, Rcnt, Z_R0);              // remaining # chars to compress in unrolled loop
     } else {
-      z_lgr(tmp2, Z_R0);
-      z_ogr(tmp2, Z_R1);
+      z_lr(Rix, Rcnt);
+      z_sr(Rix, Z_R0);
     }
-    z_ngr(tmp2, mask);
-    z_brne(Lslow);         // Failed fast case, retry slowly.
+    z_sra(Rix, log_min_cnt);             // unrolled loop count
+    z_brz(UnrolledDone);
+
+    bind(UnrolledLoop);
+      z_lmg(Z_R0, Z_R1, 0, Rsrc);
+      if (precise) {
+        z_ogr(Z_R1, Z_R0);                 // check all 8 chars for incompatibility
+        z_ngr(Z_R1, Rmask);
+        z_brnz(UnrolledBreak);
+
+        z_lg(Z_R1, 8, Rsrc);               // reload destroyed register
+        z_stcmh(Z_R0, 5, 0, Rdst);
+        z_stcm(Z_R0,  5, 2, Rdst);
+      } else {
+        z_stcmh(Z_R0, 5, 0, Rdst);
+        z_stcm(Z_R0,  5, 2, Rdst);
+
+        z_ogr(Z_R0, Z_R1);
+        z_ngr(Z_R0, Rmask);
+        z_brnz(UnrolledBreak);
+      }
+      z_stcmh(Z_R1, 5, 4, Rdst);
+      z_stcm(Z_R1,  5, 6, Rdst);
+
+      add2reg(Rsrc, min_cnt*2);
+      add2reg(Rdst, min_cnt);
+      z_brct(Rix, UnrolledLoop);
+
+    z_lgfr(Z_R0, Rcnt);                    // # chars processed in total after unrolled loop.
+    z_nilf(Z_R0, ~(min_cnt-1));
+    z_tmll(Rcnt, min_cnt-1);
+    z_brnaz(ScalarShortcut);               // if all bits zero, there is nothing left to do for scalar loop.
+                                           // Rix == 0 in all cases.
+    z_lgfr(result, Rcnt);                  // all characters processed.
+    z_sgfr(Rdst, Rcnt);                    // restore ptr
+    z_sgfr(Rsrc, Rcnt);                    // restore ptr, double the element count for Rsrc restore
+    z_sgfr(Rsrc, Rcnt);
+    z_bru(AllDone);
+
+    bind(UnrolledBreak);
+    z_lgfr(Z_R0, Rcnt);                    // # chars processed in total after unrolled loop
+    z_nilf(Z_R0, ~(min_cnt-1));
+    z_sll(Rix, log_min_cnt);               // # chars processed so far in UnrolledLoop, excl. current iteration.
+    z_sr(Z_R0, Rix);                       // correct # chars processed in total.
+    if (!precise) {
+      z_lgfr(result, Z_R0);
+      z_aghi(result, min_cnt/2);           // min_cnt/2 characters have already been written
+                                           // but ptrs were not updated yet.
+      z_sgfr(Rdst, Z_R0);                  // restore ptr
+      z_sgfr(Rsrc, Z_R0);                  // restore ptr, double the element count for Rsrc restore
+      z_sgfr(Rsrc, Z_R0);
+      z_bru(AllDone);
+    }
+    bind(UnrolledDone);
   }
-  z_stcmh(Z_R0, 5, 0, addr2);
-  z_stcm(Z_R0, 5, 2, addr2);
-  if (!precise) { z_ogr(Z_R0, Z_R1); }
-  z_stcmh(Z_R1, 5, 4, addr2);
-  z_stcm(Z_R1, 5, 6, addr2);
-  if (!precise) {
-    z_ngr(Z_R0, mask);
-    z_brne(Ldone);         // Failed (more than needed was written).
+
+  {
+    Label     ScalarLoop, ScalarDone, ScalarBreak;
+
+    bind(ScalarShortcut);
+    z_ltgfr(result, Rcnt);
+    z_brz(AllDone);
+
+#if 0  // Sacrifice shortcuts for code compactness
+    {
+      //---<  Special treatment for very short strings (one or two characters)  >---
+      //   For these strings, we are sure that the above code was skipped.
+      //   Thus, no registers were modified, register restore is not required.
+      Label     ScalarDoit, Scalar2Char;
+      z_chi(Rcnt, 2);
+      z_brh(ScalarDoit);
+      z_llh(Z_R1,  0, Z_R0, Rsrc);
+      z_bre(Scalar2Char);
+      z_tmll(Z_R1, 0xff00);
+      z_lghi(result, 0);                   // cnt == 1, first char invalid, no chars successfully processed
+      z_brnaz(AllDone);
+      z_stc(Z_R1,  0, Z_R0, Rdst);
+      z_lghi(result, 1);
+      z_bru(AllDone);
+
+      bind(Scalar2Char);
+      z_llh(Z_R0,  2, Z_R0, Rsrc);
+      z_tmll(Z_R1, 0xff00);
+      z_lghi(result, 0);                   // cnt == 2, first char invalid, no chars successfully processed
+      z_brnaz(AllDone);
+      z_stc(Z_R1,  0, Z_R0, Rdst);
+      z_tmll(Z_R0, 0xff00);
+      z_lghi(result, 1);                   // cnt == 2, second char invalid, one char successfully processed
+      z_brnaz(AllDone);
+      z_stc(Z_R0,  1, Z_R0, Rdst);
+      z_lghi(result, 2);
+      z_bru(AllDone);
+
+      bind(ScalarDoit);
+    }
+#endif
+
+    if (VM_Version::has_DistinctOpnds()) {
+      z_srk(Rix, Rcnt, Z_R0);              // remaining # chars to compress in unrolled loop
+    } else {
+      z_lr(Rix, Rcnt);
+      z_sr(Rix, Z_R0);
+    }
+    z_lgfr(result, Rcnt);                  // # processed characters (if all runs ok).
+    z_brz(ScalarDone);
+
+    bind(ScalarLoop);
+      z_llh(Z_R1, 0, Z_R0, Rsrc);
+      z_tmll(Z_R1, 0xff00);
+      z_brnaz(ScalarBreak);
+      z_stc(Z_R1, 0, Z_R0, Rdst);
+      add2reg(Rsrc, 2);
+      add2reg(Rdst, 1);
+      z_brct(Rix, ScalarLoop);
+
+    z_bru(ScalarDone);
+
+    bind(ScalarBreak);
+    z_sr(result, Rix);
+
+    bind(ScalarDone);
+    z_sgfr(Rdst, result);                  // restore ptr
+    z_sgfr(Rsrc, result);                  // restore ptr, double the element count for Rsrc restore
+    z_sgfr(Rsrc, result);
   }
-  z_aghi(addr2, 8);
-  z_brxle(ind1, even_reg, Lloop1);
-
-  bind(Lslow);
-  // Compute index limit and skip if negative.
-  z_ahi(odd_reg, 16-2);    // Last possible index for slow loop.
-  z_lhi(even_reg, 2);
-  z_cr(ind1, odd_reg);
-  z_brh(Ldone);
-
-  bind(Lloop2); // 1 Character per iteration.
-  z_llh(Z_R0, Address(src, ind1));
-  z_tmll(Z_R0, 0xFF00);
-  z_brnaz(Ldone);          // Failed slow case: Return number of written characters.
-  z_stc(Z_R0, Address(addr2));
-  z_aghi(addr2, 1);
-  z_brxle(ind1, even_reg, Lloop2);
-
-  bind(Ldone);             // result = ind1 = 2*cnt
-  z_srl(ind1, 1);
-
-  BLOCK_COMMENT("} string_compress");
+  bind(AllDone);
 
+  if (precise) {
+    BLOCK_COMMENT("} encode_iso_array");
+  } else {
+    BLOCK_COMMENT("} string_compress");
+  }
   return offset() - block_start;
 }
 
@@ -4997,53 +5223,432 @@ unsigned int MacroAssembler::string_inflate_trot(Register src, Register dst, Reg
   return offset() - block_start;
 }
 
-// Inflate byte[] to char[]. odd_reg contains cnt. Kills src.
-unsigned int MacroAssembler::string_inflate(Register src, Register dst, Register odd_reg,
-                                            Register even_reg, Register tmp) {
-  int block_start = offset();
+// Inflate byte[] to char[].
+//   Restores: src, dst
+//   Uses:     cnt
+//   Kills:    tmp, Z_R0, Z_R1.
+// Note:
+//   cnt is signed int. Do not rely on high word!
+//       counts # characters, not bytes.
+unsigned int MacroAssembler::string_inflate(Register src, Register dst, Register cnt, Register tmp) {
+  assert_different_registers(Z_R0, Z_R1, src, dst, cnt, tmp);
 
   BLOCK_COMMENT("string_inflate {");
+  int block_start = offset();
 
-  Label Lloop1, Lloop2, Lslow, Ldone;
-  const Register addr1 = src, ind2 = tmp;
+  Register   Rcnt = cnt;   // # characters (src: bytes, dst: char (2-byte)), remaining after current loop.
+  Register   Rix  = tmp;   // loop index
+  Register   Rsrc = src;   // addr(src array)
+  Register   Rdst = dst;   // addr(dst array)
+  Label      ScalarShortcut, AllDone;
 
-  z_sll(odd_reg, 1);       // Number of bytes to write. (Must be a positive simm32.)
-  clear_reg(ind2);         // Index to write.
-  z_ahi(odd_reg, -16);     // Last possible index for fast loop.
-  z_brl(Lslow);
+#if 0  // Sacrifice shortcuts for code compactness
+  {
+    //---<  shortcuts for short strings (very frequent)   >---
+    Label   skipShortcut, skip4Shortcut;
+    z_ltr(Rcnt, Rcnt);                     // absolutely nothing to do for strings of len == 0.
+    z_brz(AllDone);
+    clear_reg(Z_R0);                       // make sure registers are properly initialized.
+    clear_reg(Z_R1);
+    z_chi(Rcnt, 4);
+    z_brne(skip4Shortcut);                 // 4 characters are very frequent
+      z_icm(Z_R0, 5,    0, Rsrc);          // Treat exactly 4 characters specially.
+      z_icm(Z_R1, 5,    2, Rsrc);
+      z_stm(Z_R0, Z_R1, 0, Rdst);
+      z_bru(AllDone);
+    bind(skip4Shortcut);
 
-  // ind2: index, even_reg: index increment, odd_reg: index limit
-  clear_reg(Z_R0);
-  clear_reg(Z_R1);
-  z_lhi(even_reg, 16);
+    z_chi(Rcnt, 8);
+    z_brh(skipShortcut);                   // There's a lot to do...
+    z_lgfr(Z_R0, Rcnt);                    // remaining #characters (<= 8). Precond for scalar loop.
+                                           // This does not destroy the "register cleared" state of Z_R0.
+    z_brl(ScalarShortcut);                 // Just a few characters
+      z_icmh(Z_R0, 5, 0, Rsrc);            // Treat exactly 8 characters specially.
+      z_icmh(Z_R1, 5, 4, Rsrc);
+      z_icm(Z_R0,  5, 2, Rsrc);
+      z_icm(Z_R1,  5, 6, Rsrc);
+      z_stmg(Z_R0, Z_R1, 0, Rdst);
+      z_bru(AllDone);
+    bind(skipShortcut);
+  }
+#endif
+  clear_reg(Z_R0);                         // make sure register is properly initialized.
 
-  bind(Lloop1); // 8 Characters per iteration.
-  z_icmh(Z_R0, 5, 0, addr1);
-  z_icmh(Z_R1, 5, 4, addr1);
-  z_icm(Z_R0, 5, 2, addr1);
-  z_icm(Z_R1, 5, 6, addr1);
-  z_aghi(addr1, 8);
-  z_stg(Z_R0, Address(dst, ind2));
-  z_stg(Z_R1, Address(dst, ind2, 8));
-  z_brxle(ind2, even_reg, Lloop1);
+  if (VM_Version::has_VectorFacility()) {
+    const int  min_vcnt     = 32;          // Minimum #characters required to use vector instructions.
+                                           // Otherwise just do nothing in vector mode.
+                                           // Must be multiple of vector register length (16 bytes = 128 bits).
+    const int  log_min_vcnt = exact_log2(min_vcnt);
+    Label      VectorLoop, VectorDone;
 
-  bind(Lslow);
-  // Compute index limit and skip if negative.
-  z_ahi(odd_reg, 16-2);    // Last possible index for slow loop.
-  z_lhi(even_reg, 2);
-  z_cr(ind2, odd_reg);
-  z_brh(Ldone);
+    assert(VM_Version::has_DistinctOpnds(), "Assumption when has_VectorFacility()");
+    z_srak(Rix, Rcnt, log_min_vcnt);       // calculate # vector loop iterations
+    z_brz(VectorDone);                     // skip if none
 
-  bind(Lloop2); // 1 Character per iteration.
-  z_llc(Z_R0, Address(addr1));
-  z_sth(Z_R0, Address(dst, ind2));
-  z_aghi(addr1, 1);
-  z_brxle(ind2, even_reg, Lloop2);
+    z_sllg(Z_R0, Rix, log_min_vcnt);       // remember #chars that will be processed by vector loop
 
-  bind(Ldone);
+    bind(VectorLoop);
+      z_vlm(Z_V20, Z_V21, 0, Rsrc);        // get next 32 characters (single-byte)
+      add2reg(Rsrc, min_vcnt);
+
+      z_vuplhb(Z_V22, Z_V20);              // V2 <- (expand) V0(high)
+      z_vupllb(Z_V23, Z_V20);              // V3 <- (expand) V0(low)
+      z_vuplhb(Z_V24, Z_V21);              // V4 <- (expand) V1(high)
+      z_vupllb(Z_V25, Z_V21);              // V5 <- (expand) V1(low)
+      z_vstm(Z_V22, Z_V25, 0, Rdst);       // store next 32 bytes
+      add2reg(Rdst, min_vcnt*2);
+
+      z_brct(Rix, VectorLoop);
+
+    bind(VectorDone);
+  }
+
+  const int  min_cnt     =  8;             // Minimum #characters required to use unrolled scalar loop.
+                                           // Otherwise just do nothing in unrolled scalar mode.
+                                           // Must be multiple of 8.
+  {
+    const int  log_min_cnt = exact_log2(min_cnt);
+    Label      UnrolledLoop, UnrolledDone;
+
+
+    if (VM_Version::has_DistinctOpnds()) {
+      z_srk(Rix, Rcnt, Z_R0);              // remaining # chars to process in unrolled loop
+    } else {
+      z_lr(Rix, Rcnt);
+      z_sr(Rix, Z_R0);
+    }
+    z_sra(Rix, log_min_cnt);               // unrolled loop count
+    z_brz(UnrolledDone);
+
+    clear_reg(Z_R0);
+    clear_reg(Z_R1);
+
+    bind(UnrolledLoop);
+      z_icmh(Z_R0, 5, 0, Rsrc);
+      z_icmh(Z_R1, 5, 4, Rsrc);
+      z_icm(Z_R0,  5, 2, Rsrc);
+      z_icm(Z_R1,  5, 6, Rsrc);
+      add2reg(Rsrc, min_cnt);
+
+      z_stmg(Z_R0, Z_R1, 0, Rdst);
+
+      add2reg(Rdst, min_cnt*2);
+      z_brct(Rix, UnrolledLoop);
+
+    bind(UnrolledDone);
+    z_lgfr(Z_R0, Rcnt);                    // # chars left over after unrolled loop.
+    z_nilf(Z_R0, min_cnt-1);
+    z_brnz(ScalarShortcut);                // if zero, there is nothing left to do for scalar loop.
+                                           // Rix == 0 in all cases.
+    z_sgfr(Z_R0, Rcnt);                    // negative # characters the ptrs have been advanced previously.
+    z_agr(Rdst, Z_R0);                     // restore ptr, double the element count for Rdst restore.
+    z_agr(Rdst, Z_R0);
+    z_agr(Rsrc, Z_R0);                     // restore ptr.
+    z_bru(AllDone);
+  }
+
+  {
+    bind(ScalarShortcut);
+    // Z_R0 must contain remaining # characters as 64-bit signed int here.
+    //      register contents is preserved over scalar processing (for register fixup).
+
+#if 0  // Sacrifice shortcuts for code compactness
+    {
+      Label      ScalarDefault;
+      z_chi(Rcnt, 2);
+      z_brh(ScalarDefault);
+      z_llc(Z_R0,  0, Z_R0, Rsrc);     // 6 bytes
+      z_sth(Z_R0,  0, Z_R0, Rdst);     // 4 bytes
+      z_brl(AllDone);
+      z_llc(Z_R0,  1, Z_R0, Rsrc);     // 6 bytes
+      z_sth(Z_R0,  2, Z_R0, Rdst);     // 4 bytes
+      z_bru(AllDone);
+      bind(ScalarDefault);
+    }
+#endif
+
+    Label   CodeTable;
+    // Some comments on Rix calculation:
+    //  - Rcnt is small, therefore no bits shifted out of low word (sll(g) instructions).
+    //  - high word of both Rix and Rcnt may contain garbage
+    //  - the final lngfr takes care of that garbage, extending the sign to high word
+    z_sllg(Rix, Z_R0, 2);                // calculate 10*Rix = (4*Rix + Rix)*2
+    z_ar(Rix, Z_R0);
+    z_larl(Z_R1, CodeTable);
+    z_sll(Rix, 1);
+    z_lngfr(Rix, Rix);      // ix range: [0..7], after inversion & mult: [-(7*12)..(0*12)].
+    z_bc(Assembler::bcondAlways, 0, Rix, Z_R1);
+
+    z_llc(Z_R1,  6, Z_R0, Rsrc);  // 6 bytes
+    z_sth(Z_R1, 12, Z_R0, Rdst);  // 4 bytes
+
+    z_llc(Z_R1,  5, Z_R0, Rsrc);
+    z_sth(Z_R1, 10, Z_R0, Rdst);
+
+    z_llc(Z_R1,  4, Z_R0, Rsrc);
+    z_sth(Z_R1,  8, Z_R0, Rdst);
+
+    z_llc(Z_R1,  3, Z_R0, Rsrc);
+    z_sth(Z_R1,  6, Z_R0, Rdst);
+
+    z_llc(Z_R1,  2, Z_R0, Rsrc);
+    z_sth(Z_R1,  4, Z_R0, Rdst);
+
+    z_llc(Z_R1,  1, Z_R0, Rsrc);
+    z_sth(Z_R1,  2, Z_R0, Rdst);
+
+    z_llc(Z_R1,  0, Z_R0, Rsrc);
+    z_sth(Z_R1,  0, Z_R0, Rdst);
+    bind(CodeTable);
+
+    z_chi(Rcnt, 8);                        // no fixup for small strings. Rdst, Rsrc were not modified.
+    z_brl(AllDone);
+
+    z_sgfr(Z_R0, Rcnt);                    // # characters the ptrs have been advanced previously.
+    z_agr(Rdst, Z_R0);                     // restore ptr, double the element count for Rdst restore.
+    z_agr(Rdst, Z_R0);
+    z_agr(Rsrc, Z_R0);                     // restore ptr.
+  }
+  bind(AllDone);
 
   BLOCK_COMMENT("} string_inflate");
+  return offset() - block_start;
+}
 
+// Inflate byte[] to char[], length known at compile time.
+//   Restores: src, dst
+//   Kills:    tmp, Z_R0, Z_R1.
+// Note:
+//   len is signed int. Counts # characters, not bytes.
+unsigned int MacroAssembler::string_inflate_const(Register src, Register dst, Register tmp, int len) {
+  assert_different_registers(Z_R0, Z_R1, src, dst, tmp);
+
+  BLOCK_COMMENT("string_inflate_const {");
+  int block_start = offset();
+
+  Register   Rix  = tmp;   // loop index
+  Register   Rsrc = src;   // addr(src array)
+  Register   Rdst = dst;   // addr(dst array)
+  Label      ScalarShortcut, AllDone;
+  int        nprocessed = 0;
+  int        src_off    = 0;  // compensate for saved (optimized away) ptr advancement.
+  int        dst_off    = 0;  // compensate for saved (optimized away) ptr advancement.
+  bool       restore_inputs = false;
+  bool       workreg_clear  = false;
+
+  if ((len >= 32) && VM_Version::has_VectorFacility()) {
+    const int  min_vcnt     = 32;          // Minimum #characters required to use vector instructions.
+                                           // Otherwise just do nothing in vector mode.
+                                           // Must be multiple of vector register length (16 bytes = 128 bits).
+    const int  log_min_vcnt = exact_log2(min_vcnt);
+    const int  iterations   = (len - nprocessed) >> log_min_vcnt;
+    nprocessed             += iterations << log_min_vcnt;
+    Label      VectorLoop;
+
+    if (iterations == 1) {
+      z_vlm(Z_V20, Z_V21, 0+src_off, Rsrc);  // get next 32 characters (single-byte)
+      z_vuplhb(Z_V22, Z_V20);                // V2 <- (expand) V0(high)
+      z_vupllb(Z_V23, Z_V20);                // V3 <- (expand) V0(low)
+      z_vuplhb(Z_V24, Z_V21);                // V4 <- (expand) V1(high)
+      z_vupllb(Z_V25, Z_V21);                // V5 <- (expand) V1(low)
+      z_vstm(Z_V22, Z_V25, 0+dst_off, Rdst); // store next 32 bytes
+
+      src_off += min_vcnt;
+      dst_off += min_vcnt*2;
+    } else {
+      restore_inputs = true;
+
+      z_lgfi(Rix, len>>log_min_vcnt);
+      bind(VectorLoop);
+        z_vlm(Z_V20, Z_V21, 0, Rsrc);        // get next 32 characters (single-byte)
+        add2reg(Rsrc, min_vcnt);
+
+        z_vuplhb(Z_V22, Z_V20);              // V2 <- (expand) V0(high)
+        z_vupllb(Z_V23, Z_V20);              // V3 <- (expand) V0(low)
+        z_vuplhb(Z_V24, Z_V21);              // V4 <- (expand) V1(high)
+        z_vupllb(Z_V25, Z_V21);              // V5 <- (expand) V1(low)
+        z_vstm(Z_V22, Z_V25, 0, Rdst);       // store next 32 bytes
+        add2reg(Rdst, min_vcnt*2);
+
+        z_brct(Rix, VectorLoop);
+    }
+  }
+
+  if (((len-nprocessed) >= 16) && VM_Version::has_VectorFacility()) {
+    const int  min_vcnt     = 16;          // Minimum #characters required to use vector instructions.
+                                           // Otherwise just do nothing in vector mode.
+                                           // Must be multiple of vector register length (16 bytes = 128 bits).
+    const int  log_min_vcnt = exact_log2(min_vcnt);
+    const int  iterations   = (len - nprocessed) >> log_min_vcnt;
+    nprocessed             += iterations << log_min_vcnt;
+    assert(iterations == 1, "must be!");
+
+    z_vl(Z_V20, 0+src_off, Z_R0, Rsrc);    // get next 16 characters (single-byte)
+    z_vuplhb(Z_V22, Z_V20);                // V2 <- (expand) V0(high)
+    z_vupllb(Z_V23, Z_V20);                // V3 <- (expand) V0(low)
+    z_vstm(Z_V22, Z_V23, 0+dst_off, Rdst); // store next 32 bytes
+
+    src_off += min_vcnt;
+    dst_off += min_vcnt*2;
+  }
+
+  if ((len-nprocessed) > 8) {
+    const int  min_cnt     =  8;           // Minimum #characters required to use unrolled scalar loop.
+                                           // Otherwise just do nothing in unrolled scalar mode.
+                                           // Must be multiple of 8.
+    const int  log_min_cnt = exact_log2(min_cnt);
+    const int  iterations  = (len - nprocessed) >> log_min_cnt;
+    nprocessed     += iterations << log_min_cnt;
+
+    //---<  avoid loop overhead/ptr increment for small # iterations  >---
+    if (iterations <= 2) {
+      clear_reg(Z_R0);
+      clear_reg(Z_R1);
+      workreg_clear = true;
+
+      z_icmh(Z_R0, 5, 0+src_off, Rsrc);
+      z_icmh(Z_R1, 5, 4+src_off, Rsrc);
+      z_icm(Z_R0,  5, 2+src_off, Rsrc);
+      z_icm(Z_R1,  5, 6+src_off, Rsrc);
+      z_stmg(Z_R0, Z_R1, 0+dst_off, Rdst);
+
+      src_off += min_cnt;
+      dst_off += min_cnt*2;
+    }
+
+    if (iterations == 2) {
+      z_icmh(Z_R0, 5, 0+src_off, Rsrc);
+      z_icmh(Z_R1, 5, 4+src_off, Rsrc);
+      z_icm(Z_R0,  5, 2+src_off, Rsrc);
+      z_icm(Z_R1,  5, 6+src_off, Rsrc);
+      z_stmg(Z_R0, Z_R1, 0+dst_off, Rdst);
+
+      src_off += min_cnt;
+      dst_off += min_cnt*2;
+    }
+
+    if (iterations > 2) {
+      Label      UnrolledLoop;
+      restore_inputs  = true;
+
+      clear_reg(Z_R0);
+      clear_reg(Z_R1);
+      workreg_clear = true;
+
+      z_lgfi(Rix, iterations);
+      bind(UnrolledLoop);
+        z_icmh(Z_R0, 5, 0, Rsrc);
+        z_icmh(Z_R1, 5, 4, Rsrc);
+        z_icm(Z_R0,  5, 2, Rsrc);
+        z_icm(Z_R1,  5, 6, Rsrc);
+        add2reg(Rsrc, min_cnt);
+
+        z_stmg(Z_R0, Z_R1, 0, Rdst);
+        add2reg(Rdst, min_cnt*2);
+
+        z_brct(Rix, UnrolledLoop);
+    }
+  }
+
+  if ((len-nprocessed) > 0) {
+    switch (len-nprocessed) {
+      case 8:
+        if (!workreg_clear) {
+          clear_reg(Z_R0);
+          clear_reg(Z_R1);
+        }
+        z_icmh(Z_R0, 5, 0+src_off, Rsrc);
+        z_icmh(Z_R1, 5, 4+src_off, Rsrc);
+        z_icm(Z_R0,  5, 2+src_off, Rsrc);
+        z_icm(Z_R1,  5, 6+src_off, Rsrc);
+        z_stmg(Z_R0, Z_R1, 0+dst_off, Rdst);
+        break;
+      case 7:
+        if (!workreg_clear) {
+          clear_reg(Z_R0);
+          clear_reg(Z_R1);
+        }
+        clear_reg(Rix);
+        z_icm(Z_R0,  5, 0+src_off, Rsrc);
+        z_icm(Z_R1,  5, 2+src_off, Rsrc);
+        z_icm(Rix,   5, 4+src_off, Rsrc);
+        z_stm(Z_R0,  Z_R1, 0+dst_off, Rdst);
+        z_llc(Z_R0,  6+src_off, Z_R0, Rsrc);
+        z_st(Rix,    8+dst_off, Z_R0, Rdst);
+        z_sth(Z_R0, 12+dst_off, Z_R0, Rdst);
+        break;
+      case 6:
+        if (!workreg_clear) {
+          clear_reg(Z_R0);
+          clear_reg(Z_R1);
+        }
+        clear_reg(Rix);
+        z_icm(Z_R0, 5, 0+src_off, Rsrc);
+        z_icm(Z_R1, 5, 2+src_off, Rsrc);
+        z_icm(Rix,  5, 4+src_off, Rsrc);
+        z_stm(Z_R0, Z_R1, 0+dst_off, Rdst);
+        z_st(Rix,   8+dst_off, Z_R0, Rdst);
+        break;
+      case 5:
+        if (!workreg_clear) {
+          clear_reg(Z_R0);
+          clear_reg(Z_R1);
+        }
+        z_icm(Z_R0, 5, 0+src_off, Rsrc);
+        z_icm(Z_R1, 5, 2+src_off, Rsrc);
+        z_llc(Rix,  4+src_off, Z_R0, Rsrc);
+        z_stm(Z_R0, Z_R1, 0+dst_off, Rdst);
+        z_sth(Rix,  8+dst_off, Z_R0, Rdst);
+        break;
+      case 4:
+        if (!workreg_clear) {
+          clear_reg(Z_R0);
+          clear_reg(Z_R1);
+        }
+        z_icm(Z_R0, 5, 0+src_off, Rsrc);
+        z_icm(Z_R1, 5, 2+src_off, Rsrc);
+        z_stm(Z_R0, Z_R1, 0+dst_off, Rdst);
+        break;
+      case 3:
+        if (!workreg_clear) {
+          clear_reg(Z_R0);
+        }
+        z_llc(Z_R1, 2+src_off, Z_R0, Rsrc);
+        z_icm(Z_R0, 5, 0+src_off, Rsrc);
+        z_sth(Z_R1, 4+dst_off, Z_R0, Rdst);
+        z_st(Z_R0,  0+dst_off, Rdst);
+        break;
+      case 2:
+        z_llc(Z_R0, 0+src_off, Z_R0, Rsrc);
+        z_llc(Z_R1, 1+src_off, Z_R0, Rsrc);
+        z_sth(Z_R0, 0+dst_off, Z_R0, Rdst);
+        z_sth(Z_R1, 2+dst_off, Z_R0, Rdst);
+        break;
+      case 1:
+        z_llc(Z_R0, 0+src_off, Z_R0, Rsrc);
+        z_sth(Z_R0, 0+dst_off, Z_R0, Rdst);
+        break;
+      default:
+        guarantee(false, "Impossible");
+        break;
+    }
+    src_off   +=  len-nprocessed;
+    dst_off   += (len-nprocessed)*2;
+    nprocessed = len;
+  }
+
+  //---< restore modified input registers  >---
+  if ((nprocessed > 0) && restore_inputs) {
+    z_agfi(Rsrc, -(nprocessed-src_off));
+    if (nprocessed < 1000000000) { // avoid int overflow
+      z_agfi(Rdst, -(nprocessed*2-dst_off));
+    } else {
+      z_agfi(Rdst, -(nprocessed-dst_off));
+      z_agfi(Rdst, -nprocessed);
+    }
+  }
+
+  BLOCK_COMMENT("} string_inflate_const");
   return offset() - block_start;
 }
 
diff --git a/src/hotspot/cpu/s390/macroAssembler_s390.hpp b/src/hotspot/cpu/s390/macroAssembler_s390.hpp
index 908ce8d98aa..8fb0731747d 100644
--- a/src/hotspot/cpu/s390/macroAssembler_s390.hpp
+++ b/src/hotspot/cpu/s390/macroAssembler_s390.hpp
@@ -198,6 +198,9 @@ class MacroAssembler: public Assembler {
   // Test a bit in a register. Result is reflected in CC.
   void testbit(Register r, unsigned int bitPos);
 
+  void prefetch_read(Address a);
+  void prefetch_update(Address a);
+
   // Clear a register, i.e. load const zero into reg. Return len (in bytes) of
   // generated instruction(s).
   //   whole_reg: Clear 64 bits if true, 32 bits otherwise.
@@ -836,7 +839,7 @@ class MacroAssembler: public Assembler {
   void load_mirror(Register mirror, Register method);
 
   //--------------------------
-  //---  perations on arrays.
+  //---  Operations on arrays.
   //--------------------------
   unsigned int Clear_Array(Register cnt_arg, Register base_pointer_arg, Register src_addr, Register src_len);
   unsigned int Clear_Array_Const(long cnt, Register base);
@@ -849,20 +852,34 @@ class MacroAssembler: public Assembler {
   // Special String Intrinsics Implementation.
   //-------------------------------------------
   // Intrinsics for CompactStrings
-  // Compress char[] to byte[]. odd_reg contains cnt. tmp3 is only needed for precise behavior in failure case. Kills dst.
-  unsigned int string_compress(Register result, Register src, Register dst, Register odd_reg,
-                               Register even_reg, Register tmp, Register tmp2 = noreg);
+  //   Restores: src, dst
+  //   Uses:     cnt
+  //   Kills:    tmp, Z_R0, Z_R1.
+  //   Early clobber: result.
+  //   Boolean precise controls accuracy of result value.
+  unsigned int string_compress(Register result, Register src, Register dst, Register cnt,
+                               Register tmp,    bool precise);
+
+  // Inflate byte[] to char[].
+  unsigned int string_inflate_trot(Register src, Register dst, Register cnt, Register tmp);
+
+  // Inflate byte[] to char[].
+  //   Restores: src, dst
+  //   Uses:     cnt
+  //   Kills:    tmp, Z_R0, Z_R1.
+  unsigned int string_inflate(Register src, Register dst, Register cnt, Register tmp);
+
+  // Inflate byte[] to char[], length known at compile time.
+  //   Restores: src, dst
+  //   Kills:    tmp, Z_R0, Z_R1.
+  // Note:
+  //   len is signed int. Counts # characters, not bytes.
+  unsigned int string_inflate_const(Register src, Register dst, Register tmp, int len);
 
   // Kills src.
   unsigned int has_negatives(Register result, Register src, Register cnt,
                              Register odd_reg, Register even_reg, Register tmp);
 
-  // Inflate byte[] to char[].
-  unsigned int string_inflate_trot(Register src, Register dst, Register cnt, Register tmp);
-  // Odd_reg contains cnt. Kills src.
-  unsigned int string_inflate(Register src, Register dst, Register odd_reg,
-                              Register even_reg, Register tmp);
-
   unsigned int string_compare(Register str1, Register str2, Register cnt1, Register cnt2,
                               Register odd_reg, Register even_reg, Register result, int ae);
 
diff --git a/src/hotspot/cpu/s390/s390.ad b/src/hotspot/cpu/s390/s390.ad
index 15902d9f7aa..fb876ba6180 100644
--- a/src/hotspot/cpu/s390/s390.ad
+++ b/src/hotspot/cpu/s390/s390.ad
@@ -10267,14 +10267,14 @@ instruct indexOf_UL(iRegP haystack, rarg2RegI haycnt, iRegP needle, rarg5RegI ne
 %}
 
 // char[] to byte[] compression
-instruct string_compress(iRegP src, rarg5RegP dst, iRegI result, roddRegI len, revenRegI evenReg, iRegI tmp, flagsReg cr) %{
+instruct string_compress(iRegP src, iRegP dst, iRegI result, iRegI len, iRegI tmp, flagsReg cr) %{
   match(Set result (StrCompressedCopy src (Binary dst len)));
-  effect(TEMP_DEF result, USE_KILL dst, USE_KILL len, TEMP evenReg, TEMP tmp, KILL cr); // R0, R1 are killed, too.
+  effect(TEMP_DEF result, TEMP tmp, KILL cr); // R0, R1 are killed, too.
   ins_cost(300);
   format %{ "String Compress $src->$dst($len) -> $result" %}
   ins_encode %{
     __ string_compress($result$$Register, $src$$Register, $dst$$Register, $len$$Register,
-                       $evenReg$$Register, $tmp$$Register);
+                       $tmp$$Register, false);
   %}
   ins_pipe(pipe_class_dummy);
 %}
@@ -10293,13 +10293,25 @@ instruct string_compress(iRegP src, rarg5RegP dst, iRegI result, roddRegI len, r
 //%}
 
 // byte[] to char[] inflation
-instruct string_inflate(Universe dummy, rarg5RegP src, iRegP dst, roddRegI len, revenRegI evenReg, iRegI tmp, flagsReg cr) %{
+instruct string_inflate(Universe dummy, iRegP src, iRegP dst, iRegI len, iRegI tmp, flagsReg cr) %{
   match(Set dummy (StrInflatedCopy src (Binary dst len)));
-  effect(USE_KILL src, USE_KILL len, TEMP evenReg, TEMP tmp, KILL cr); // R0, R1 are killed, too.
+  effect(TEMP tmp, KILL cr); // R0, R1 are killed, too.
   ins_cost(300);
   format %{ "String Inflate $src->$dst($len)" %}
   ins_encode %{
-    __ string_inflate($src$$Register, $dst$$Register, $len$$Register, $evenReg$$Register, $tmp$$Register);
+    __ string_inflate($src$$Register, $dst$$Register, $len$$Register, $tmp$$Register);
+  %}
+  ins_pipe(pipe_class_dummy);
+%}
+
+// byte[] to char[] inflation
+instruct string_inflate_const(Universe dummy, iRegP src, iRegP dst, iRegI tmp, immI len, flagsReg cr) %{
+  match(Set dummy (StrInflatedCopy src (Binary dst len)));
+  effect(TEMP tmp, KILL cr); // R0, R1 are killed, too.
+  ins_cost(300);
+  format %{ "String Inflate (constLen) $src->$dst($len)" %}
+  ins_encode %{
+    __ string_inflate_const($src$$Register, $dst$$Register, $tmp$$Register, $len$$constant);
   %}
   ins_pipe(pipe_class_dummy);
 %}
@@ -10318,14 +10330,14 @@ instruct has_negatives(rarg5RegP ary1, iRegI len, iRegI result, roddRegI oddReg,
 %}
 
 // encode char[] to byte[] in ISO_8859_1
-instruct encode_iso_array(rarg5RegP src, iRegP dst, iRegI result, roddRegI len, revenRegI evenReg, iRegI tmp, iRegI tmp2, flagsReg cr) %{
+instruct encode_iso_array(iRegP src, iRegP dst, iRegI result, iRegI len, iRegI tmp, flagsReg cr) %{
   match(Set result (EncodeISOArray src (Binary dst len)));
-  effect(TEMP_DEF result, USE_KILL src, USE_KILL len, TEMP evenReg, TEMP tmp, TEMP tmp2, KILL cr); // R0, R1 are killed, too.
+  effect(TEMP_DEF result, TEMP tmp, KILL cr); // R0, R1 are killed, too.
   ins_cost(300);
   format %{ "Encode array $src->$dst($len) -> $result" %}
   ins_encode %{
     __ string_compress($result$$Register, $src$$Register, $dst$$Register, $len$$Register,
-                       $evenReg$$Register, $tmp$$Register, $tmp2$$Register);
+                       $tmp$$Register, true);
   %}
   ins_pipe(pipe_class_dummy);
 %}
diff --git a/src/hotspot/cpu/s390/templateTable_s390.cpp b/src/hotspot/cpu/s390/templateTable_s390.cpp
index 7c1e458b9d6..972bb7d9c59 100644
--- a/src/hotspot/cpu/s390/templateTable_s390.cpp
+++ b/src/hotspot/cpu/s390/templateTable_s390.cpp
@@ -2884,12 +2884,12 @@ void TemplateTable::putfield_or_static(int byte_no, bool is_static, RewriteContr
   // ztos
   BTB_BEGIN(is_Bool, bsize, "putfield_or_static:is_Bool");
   __ pop(ztos);
-  if (do_rewrite) {
+  if (!is_static) {
     pop_and_check_object(obj);
   }
   __ z_nilf(Z_tos, 0x1);
   __ z_stc(Z_tos, field);
-  if (!is_static) {
+  if (do_rewrite) {
     patch_bytecode(Bytecodes::_fast_zputfield, bc, Z_ARG5, true, byte_no);
   }
   __ z_bru(Done);
diff --git a/src/hotspot/cpu/sparc/c1_LIRAssembler_sparc.cpp b/src/hotspot/cpu/sparc/c1_LIRAssembler_sparc.cpp
index 1f3dc374cce..c9b250eb06f 100644
--- a/src/hotspot/cpu/sparc/c1_LIRAssembler_sparc.cpp
+++ b/src/hotspot/cpu/sparc/c1_LIRAssembler_sparc.cpp
@@ -398,8 +398,13 @@ void LIR_Assembler::jobject2reg(jobject o, Register reg) {
   if (o == NULL) {
     __ set(NULL_WORD, reg);
   } else {
+#ifdef ASSERT
+    {
+      ThreadInVMfromNative tiv(JavaThread::current());
+      assert(Universe::heap()->is_in_reserved(JNIHandles::resolve(o)), "should be real oop");
+    }
+#endif
     int oop_index = __ oop_recorder()->find_index(o);
-    assert(Universe::heap()->is_in_reserved(JNIHandles::resolve(o)), "should be real oop");
     RelocationHolder rspec = oop_Relocation::spec(oop_index);
     __ set(NULL_WORD, reg, rspec); // Will be set when the nmethod is created
   }
diff --git a/src/hotspot/cpu/sparc/stubGenerator_sparc.cpp b/src/hotspot/cpu/sparc/stubGenerator_sparc.cpp
index 351555dbe51..d93b294574b 100644
--- a/src/hotspot/cpu/sparc/stubGenerator_sparc.cpp
+++ b/src/hotspot/cpu/sparc/stubGenerator_sparc.cpp
@@ -898,7 +898,9 @@ class StubGenerator: public StubCodeGenerator {
           assert(sizeof(*ct->byte_map_base) == sizeof(jbyte), "adjust this code");
           assert_different_registers(addr, count, tmp);
 
-          Label L_loop;
+          Label L_loop, L_done;
+
+          __ cmp_and_br_short(count, 0, Assembler::equal, Assembler::pt, L_done); // zero count - nothing to do
 
           __ sll_ptr(count, LogBytesPerHeapOop, count);
           __ sub(count, BytesPerHeapOop, count);
@@ -914,6 +916,7 @@ class StubGenerator: public StubCodeGenerator {
           __ subcc(count, 1, count);
           __ brx(Assembler::greaterEqual, false, Assembler::pt, L_loop);
           __ delayed()->add(addr, 1, addr);
+        __ BIND(L_done);
         }
         break;
       case BarrierSet::ModRef:
diff --git a/src/hotspot/cpu/x86/assembler_x86.cpp b/src/hotspot/cpu/x86/assembler_x86.cpp
index 3bdc0ed3c30..961ebb531be 100644
--- a/src/hotspot/cpu/x86/assembler_x86.cpp
+++ b/src/hotspot/cpu/x86/assembler_x86.cpp
@@ -1256,7 +1256,7 @@ void Assembler::addr_nop_8() {
 
 void Assembler::addsd(XMMRegister dst, XMMRegister src) {
   NOT_LP64(assert(VM_Version::supports_sse2(), ""));
-  InstructionAttr attributes(AVX_128bit, /* rex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ false);
+  InstructionAttr attributes(AVX_128bit, /* rex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false);
   attributes.set_rex_vex_w_reverted();
   int encode = simd_prefix_and_encode(dst, dst, src, VEX_SIMD_F2, VEX_OPCODE_0F, &attributes);
   emit_int8(0x58);
@@ -1266,7 +1266,7 @@ void Assembler::addsd(XMMRegister dst, XMMRegister src) {
 void Assembler::addsd(XMMRegister dst, Address src) {
   NOT_LP64(assert(VM_Version::supports_sse2(), ""));
   InstructionMark im(this);
-  InstructionAttr attributes(AVX_128bit, /* rex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ false);
+  InstructionAttr attributes(AVX_128bit, /* rex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false);
   attributes.set_address_attributes(/* tuple_type */ EVEX_T1S, /* input_size_in_bits */ EVEX_64bit);
   attributes.set_rex_vex_w_reverted();
   simd_prefix(dst, dst, src, VEX_SIMD_F2, VEX_OPCODE_0F, &attributes);
@@ -1276,7 +1276,7 @@ void Assembler::addsd(XMMRegister dst, Address src) {
 
 void Assembler::addss(XMMRegister dst, XMMRegister src) {
   NOT_LP64(assert(VM_Version::supports_sse(), ""));
-  InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ false);
+  InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false);
   int encode = simd_prefix_and_encode(dst, dst, src, VEX_SIMD_F3, VEX_OPCODE_0F, &attributes);
   emit_int8(0x58);
   emit_int8((unsigned char)(0xC0 | encode));
@@ -1285,7 +1285,7 @@ void Assembler::addss(XMMRegister dst, XMMRegister src) {
 void Assembler::addss(XMMRegister dst, Address src) {
   NOT_LP64(assert(VM_Version::supports_sse(), ""));
   InstructionMark im(this);
-  InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ false);
+  InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false);
   attributes.set_address_attributes(/* tuple_type */ EVEX_T1S, /* input_size_in_bits */ EVEX_32bit);
   simd_prefix(dst, dst, src, VEX_SIMD_F3, VEX_OPCODE_0F, &attributes);
   emit_int8(0x58);
@@ -1295,7 +1295,7 @@ void Assembler::addss(XMMRegister dst, Address src) {
 void Assembler::aesdec(XMMRegister dst, Address src) {
   assert(VM_Version::supports_aes(), "");
   InstructionMark im(this);
-  InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ true, /* no_mask_reg */ false, /* uses_vl */ false);
+  InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ true, /* no_mask_reg */ true, /* uses_vl */ false);
   simd_prefix(dst, dst, src, VEX_SIMD_66, VEX_OPCODE_0F_38, &attributes);
   emit_int8((unsigned char)0xDE);
   emit_operand(dst, src);
@@ -1303,7 +1303,7 @@ void Assembler::aesdec(XMMRegister dst, Address src) {
 
 void Assembler::aesdec(XMMRegister dst, XMMRegister src) {
   assert(VM_Version::supports_aes(), "");
-  InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ true, /* no_mask_reg */ false, /* uses_vl */ false);
+  InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ true, /* no_mask_reg */ true, /* uses_vl */ false);
   int encode = simd_prefix_and_encode(dst, dst, src, VEX_SIMD_66, VEX_OPCODE_0F_38, &attributes);
   emit_int8((unsigned char)0xDE);
   emit_int8(0xC0 | encode);
@@ -1312,7 +1312,7 @@ void Assembler::aesdec(XMMRegister dst, XMMRegister src) {
 void Assembler::aesdeclast(XMMRegister dst, Address src) {
   assert(VM_Version::supports_aes(), "");
   InstructionMark im(this);
-  InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ true, /* no_mask_reg */ false, /* uses_vl */ false);
+  InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ true, /* no_mask_reg */ true, /* uses_vl */ false);
   simd_prefix(dst, dst, src, VEX_SIMD_66, VEX_OPCODE_0F_38, &attributes);
   emit_int8((unsigned char)0xDF);
   emit_operand(dst, src);
@@ -1320,7 +1320,7 @@ void Assembler::aesdeclast(XMMRegister dst, Address src) {
 
 void Assembler::aesdeclast(XMMRegister dst, XMMRegister src) {
   assert(VM_Version::supports_aes(), "");
-  InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ true, /* no_mask_reg */ false, /* uses_vl */ false);
+  InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ true, /* no_mask_reg */ true, /* uses_vl */ false);
   int encode = simd_prefix_and_encode(dst, dst, src, VEX_SIMD_66, VEX_OPCODE_0F_38, &attributes);
   emit_int8((unsigned char)0xDF);
   emit_int8((unsigned char)(0xC0 | encode));
@@ -1329,7 +1329,7 @@ void Assembler::aesdeclast(XMMRegister dst, XMMRegister src) {
 void Assembler::aesenc(XMMRegister dst, Address src) {
   assert(VM_Version::supports_aes(), "");
   InstructionMark im(this);
-  InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ true, /* no_mask_reg */ false, /* uses_vl */ false);
+  InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ true, /* no_mask_reg */ true, /* uses_vl */ false);
   simd_prefix(dst, dst, src, VEX_SIMD_66, VEX_OPCODE_0F_38, &attributes);
   emit_int8((unsigned char)0xDC);
   emit_operand(dst, src);
@@ -1337,7 +1337,7 @@ void Assembler::aesenc(XMMRegister dst, Address src) {
 
 void Assembler::aesenc(XMMRegister dst, XMMRegister src) {
   assert(VM_Version::supports_aes(), "");
-  InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ true, /* no_mask_reg */ false, /* uses_vl */ false);
+  InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ true, /* no_mask_reg */ true, /* uses_vl */ false);
   int encode = simd_prefix_and_encode(dst, dst, src, VEX_SIMD_66, VEX_OPCODE_0F_38, &attributes);
   emit_int8((unsigned char)0xDC);
   emit_int8(0xC0 | encode);
@@ -1346,7 +1346,7 @@ void Assembler::aesenc(XMMRegister dst, XMMRegister src) {
 void Assembler::aesenclast(XMMRegister dst, Address src) {
   assert(VM_Version::supports_aes(), "");
   InstructionMark im(this);
-  InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ true, /* no_mask_reg */ false, /* uses_vl */ false);
+  InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ true, /* no_mask_reg */ true, /* uses_vl */ false);
   simd_prefix(dst, dst, src, VEX_SIMD_66, VEX_OPCODE_0F_38, &attributes);
   emit_int8((unsigned char)0xDD);
   emit_operand(dst, src);
@@ -1354,7 +1354,7 @@ void Assembler::aesenclast(XMMRegister dst, Address src) {
 
 void Assembler::aesenclast(XMMRegister dst, XMMRegister src) {
   assert(VM_Version::supports_aes(), "");
-  InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ true, /* no_mask_reg */ false, /* uses_vl */ false);
+  InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ true, /* no_mask_reg */ true, /* uses_vl */ false);
   int encode = simd_prefix_and_encode(dst, dst, src, VEX_SIMD_66, VEX_OPCODE_0F_38, &attributes);
   emit_int8((unsigned char)0xDD);
   emit_int8((unsigned char)(0xC0 | encode));
@@ -1387,7 +1387,7 @@ void Assembler::andl(Register dst, Register src) {
 
 void Assembler::andnl(Register dst, Register src1, Register src2) {
   assert(VM_Version::supports_bmi1(), "bit manipulation instructions not supported");
-  InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ true, /* no_mask_reg */ false, /* uses_vl */ false);
+  InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ true, /* no_mask_reg */ true, /* uses_vl */ false);
   int encode = vex_prefix_and_encode(dst->encoding(), src1->encoding(), src2->encoding(), VEX_SIMD_NONE, VEX_OPCODE_0F_38, &attributes);
   emit_int8((unsigned char)0xF2);
   emit_int8((unsigned char)(0xC0 | encode));
@@ -1396,7 +1396,7 @@ void Assembler::andnl(Register dst, Register src1, Register src2) {
 void Assembler::andnl(Register dst, Register src1, Address src2) {
   assert(VM_Version::supports_bmi1(), "bit manipulation instructions not supported");
   InstructionMark im(this);
-  InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ true, /* no_mask_reg */ false, /* uses_vl */ false);
+  InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ true, /* no_mask_reg */ true, /* uses_vl */ false);
   vex_prefix(src2, src1->encoding(), dst->encoding(), VEX_SIMD_NONE, VEX_OPCODE_0F_38, &attributes);
   emit_int8((unsigned char)0xF2);
   emit_operand(dst, src2);
@@ -1424,7 +1424,7 @@ void Assembler::bswapl(Register reg) { // bswap
 
 void Assembler::blsil(Register dst, Register src) {
   assert(VM_Version::supports_bmi1(), "bit manipulation instructions not supported");
-  InstructionAttr attributes(AVX_128bit, /* vex_w */ false, /* legacy_mode */ true, /* no_mask_reg */ false, /* uses_vl */ false);
+  InstructionAttr attributes(AVX_128bit, /* vex_w */ false, /* legacy_mode */ true, /* no_mask_reg */ true, /* uses_vl */ false);
   int encode = vex_prefix_and_encode(rbx->encoding(), dst->encoding(), src->encoding(), VEX_SIMD_NONE, VEX_OPCODE_0F_38, &attributes);
   emit_int8((unsigned char)0xF3);
   emit_int8((unsigned char)(0xC0 | encode));
@@ -1433,7 +1433,7 @@ void Assembler::blsil(Register dst, Register src) {
 void Assembler::blsil(Register dst, Address src) {
   assert(VM_Version::supports_bmi1(), "bit manipulation instructions not supported");
   InstructionMark im(this);
-  InstructionAttr attributes(AVX_128bit, /* vex_w */ false, /* legacy_mode */ true, /* no_mask_reg */ false, /* uses_vl */ false);
+  InstructionAttr attributes(AVX_128bit, /* vex_w */ false, /* legacy_mode */ true, /* no_mask_reg */ true, /* uses_vl */ false);
   vex_prefix(src, dst->encoding(), rbx->encoding(), VEX_SIMD_NONE, VEX_OPCODE_0F_38, &attributes);
   emit_int8((unsigned char)0xF3);
   emit_operand(rbx, src);
@@ -1441,7 +1441,7 @@ void Assembler::blsil(Register dst, Address src) {
 
 void Assembler::blsmskl(Register dst, Register src) {
   assert(VM_Version::supports_bmi1(), "bit manipulation instructions not supported");
-  InstructionAttr attributes(AVX_128bit, /* vex_w */ false, /* legacy_mode */ true, /* no_mask_reg */ false, /* uses_vl */ false);
+  InstructionAttr attributes(AVX_128bit, /* vex_w */ false, /* legacy_mode */ true, /* no_mask_reg */ true, /* uses_vl */ false);
   int encode = vex_prefix_and_encode(rdx->encoding(), dst->encoding(), src->encoding(), VEX_SIMD_NONE, VEX_OPCODE_0F_38, &attributes);
   emit_int8((unsigned char)0xF3);
   emit_int8((unsigned char)(0xC0 | encode));
@@ -1450,7 +1450,7 @@ void Assembler::blsmskl(Register dst, Register src) {
 void Assembler::blsmskl(Register dst, Address src) {
   assert(VM_Version::supports_bmi1(), "bit manipulation instructions not supported");
   InstructionMark im(this);
-  InstructionAttr attributes(AVX_128bit, /* vex_w */ false, /* legacy_mode */ true, /* no_mask_reg */ false, /* uses_vl */ false);
+  InstructionAttr attributes(AVX_128bit, /* vex_w */ false, /* legacy_mode */ true, /* no_mask_reg */ true, /* uses_vl */ false);
   vex_prefix(src, dst->encoding(), rdx->encoding(), VEX_SIMD_NONE, VEX_OPCODE_0F_38, &attributes);
   emit_int8((unsigned char)0xF3);
   emit_operand(rdx, src);
@@ -1458,7 +1458,7 @@ void Assembler::blsmskl(Register dst, Address src) {
 
 void Assembler::blsrl(Register dst, Register src) {
   assert(VM_Version::supports_bmi1(), "bit manipulation instructions not supported");
-  InstructionAttr attributes(AVX_128bit, /* vex_w */ false, /* legacy_mode */ true, /* no_mask_reg */ false, /* uses_vl */ false);
+  InstructionAttr attributes(AVX_128bit, /* vex_w */ false, /* legacy_mode */ true, /* no_mask_reg */ true, /* uses_vl */ false);
   int encode = vex_prefix_and_encode(rcx->encoding(), dst->encoding(), src->encoding(), VEX_SIMD_NONE, VEX_OPCODE_0F_38, &attributes);
   emit_int8((unsigned char)0xF3);
   emit_int8((unsigned char)(0xC0 | encode));
@@ -1467,7 +1467,7 @@ void Assembler::blsrl(Register dst, Register src) {
 void Assembler::blsrl(Register dst, Address src) {
   assert(VM_Version::supports_bmi1(), "bit manipulation instructions not supported");
   InstructionMark im(this);
-  InstructionAttr attributes(AVX_128bit, /* vex_w */ false, /* legacy_mode */ true, /* no_mask_reg */ false, /* uses_vl */ false);
+  InstructionAttr attributes(AVX_128bit, /* vex_w */ false, /* legacy_mode */ true, /* no_mask_reg */ true, /* uses_vl */ false);
   vex_prefix(src, dst->encoding(), rcx->encoding(), VEX_SIMD_NONE, VEX_OPCODE_0F_38, &attributes);
   emit_int8((unsigned char)0xF3);
   emit_operand(rcx, src);
@@ -1753,7 +1753,7 @@ void Assembler::cvtdq2ps(XMMRegister dst, XMMRegister src) {
 
 void Assembler::cvtsd2ss(XMMRegister dst, XMMRegister src) {
   NOT_LP64(assert(VM_Version::supports_sse2(), ""));
-  InstructionAttr attributes(AVX_128bit, /* rex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ false);
+  InstructionAttr attributes(AVX_128bit, /* rex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false);
   attributes.set_rex_vex_w_reverted();
   int encode = simd_prefix_and_encode(dst, dst, src, VEX_SIMD_F2, VEX_OPCODE_0F, &attributes);
   emit_int8(0x5A);
@@ -1763,7 +1763,7 @@ void Assembler::cvtsd2ss(XMMRegister dst, XMMRegister src) {
 void Assembler::cvtsd2ss(XMMRegister dst, Address src) {
   NOT_LP64(assert(VM_Version::supports_sse2(), ""));
   InstructionMark im(this);
-  InstructionAttr attributes(AVX_128bit, /* rex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ false);
+  InstructionAttr attributes(AVX_128bit, /* rex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false);
   attributes.set_address_attributes(/* tuple_type */ EVEX_T1S, /* input_size_in_bits */ EVEX_64bit);
   attributes.set_rex_vex_w_reverted();
   simd_prefix(dst, dst, src, VEX_SIMD_F2, VEX_OPCODE_0F, &attributes);
@@ -1817,7 +1817,7 @@ void Assembler::cvtsi2ssq(XMMRegister dst, Register src) {
 
 void Assembler::cvtss2sd(XMMRegister dst, XMMRegister src) {
   NOT_LP64(assert(VM_Version::supports_sse2(), ""));
-  InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ false);
+  InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false);
   int encode = simd_prefix_and_encode(dst, dst, src, VEX_SIMD_F3, VEX_OPCODE_0F, &attributes);
   emit_int8(0x5A);
   emit_int8((unsigned char)(0xC0 | encode));
@@ -1826,7 +1826,7 @@ void Assembler::cvtss2sd(XMMRegister dst, XMMRegister src) {
 void Assembler::cvtss2sd(XMMRegister dst, Address src) {
   NOT_LP64(assert(VM_Version::supports_sse2(), ""));
   InstructionMark im(this);
-  InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ false);
+  InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false);
   attributes.set_address_attributes(/* tuple_type */ EVEX_T1S, /* input_size_in_bits */ EVEX_32bit);
   simd_prefix(dst, dst, src, VEX_SIMD_F3, VEX_OPCODE_0F, &attributes);
   emit_int8(0x5A);
@@ -1870,7 +1870,7 @@ void Assembler::decl(Address dst) {
 void Assembler::divsd(XMMRegister dst, Address src) {
   NOT_LP64(assert(VM_Version::supports_sse2(), ""));
   InstructionMark im(this);
-  InstructionAttr attributes(AVX_128bit, /* rex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ false);
+  InstructionAttr attributes(AVX_128bit, /* rex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false);
   attributes.set_address_attributes(/* tuple_type */ EVEX_T1S, /* input_size_in_bits */ EVEX_64bit);
   attributes.set_rex_vex_w_reverted();
   simd_prefix(dst, dst, src, VEX_SIMD_F2, VEX_OPCODE_0F, &attributes);
@@ -1880,7 +1880,7 @@ void Assembler::divsd(XMMRegister dst, Address src) {
 
 void Assembler::divsd(XMMRegister dst, XMMRegister src) {
   NOT_LP64(assert(VM_Version::supports_sse2(), ""));
-  InstructionAttr attributes(AVX_128bit, /* rex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ false);
+  InstructionAttr attributes(AVX_128bit, /* rex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false);
   attributes.set_rex_vex_w_reverted();
   int encode = simd_prefix_and_encode(dst, dst, src, VEX_SIMD_F2, VEX_OPCODE_0F, &attributes);
   emit_int8(0x5E);
@@ -1890,7 +1890,7 @@ void Assembler::divsd(XMMRegister dst, XMMRegister src) {
 void Assembler::divss(XMMRegister dst, Address src) {
   NOT_LP64(assert(VM_Version::supports_sse(), ""));
   InstructionMark im(this);
-  InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ false);
+  InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false);
   attributes.set_address_attributes(/* tuple_type */ EVEX_T1S, /* input_size_in_bits */ EVEX_32bit);
   simd_prefix(dst, dst, src, VEX_SIMD_F3, VEX_OPCODE_0F, &attributes);
   emit_int8(0x5E);
@@ -1899,7 +1899,7 @@ void Assembler::divss(XMMRegister dst, Address src) {
 
 void Assembler::divss(XMMRegister dst, XMMRegister src) {
   NOT_LP64(assert(VM_Version::supports_sse(), ""));
-  InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ false);
+  InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false);
   int encode = simd_prefix_and_encode(dst, dst, src, VEX_SIMD_F3, VEX_OPCODE_0F, &attributes);
   emit_int8(0x5E);
   emit_int8((unsigned char)(0xC0 | encode));
@@ -2105,7 +2105,7 @@ void Assembler::jmpb(Label& L) {
 void Assembler::ldmxcsr( Address src) {
   if (UseAVX > 0 ) {
     InstructionMark im(this);
-    InstructionAttr attributes(AVX_128bit, /* vex_w */ false, /* legacy_mode */ true, /* no_mask_reg */ false, /* uses_vl */ false);
+    InstructionAttr attributes(AVX_128bit, /* vex_w */ false, /* legacy_mode */ true, /* no_mask_reg */ true, /* uses_vl */ false);
     vex_prefix(src, 0, 0, VEX_SIMD_NONE, VEX_OPCODE_0F, &attributes);
     emit_int8((unsigned char)0xAE);
     emit_operand(as_Register(2), src);
@@ -2784,7 +2784,7 @@ void Assembler::movsbl(Register dst, Register src) { // movsxb
 
 void Assembler::movsd(XMMRegister dst, XMMRegister src) {
   NOT_LP64(assert(VM_Version::supports_sse2(), ""));
-  InstructionAttr attributes(AVX_128bit, /* rex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ false);
+  InstructionAttr attributes(AVX_128bit, /* rex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false);
   attributes.set_rex_vex_w_reverted();
   int encode = simd_prefix_and_encode(dst, dst, src, VEX_SIMD_F2, VEX_OPCODE_0F, &attributes);
   emit_int8(0x10);
@@ -2794,7 +2794,7 @@ void Assembler::movsd(XMMRegister dst, XMMRegister src) {
 void Assembler::movsd(XMMRegister dst, Address src) {
   NOT_LP64(assert(VM_Version::supports_sse2(), ""));
   InstructionMark im(this);
-  InstructionAttr attributes(AVX_128bit, /* rex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ false);
+  InstructionAttr attributes(AVX_128bit, /* rex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false);
   attributes.set_address_attributes(/* tuple_type */ EVEX_T1S, /* input_size_in_bits */ EVEX_64bit);
   attributes.set_rex_vex_w_reverted();
   simd_prefix(dst, xnoreg, src, VEX_SIMD_F2, VEX_OPCODE_0F, &attributes);
@@ -2805,7 +2805,7 @@ void Assembler::movsd(XMMRegister dst, Address src) {
 void Assembler::movsd(Address dst, XMMRegister src) {
   NOT_LP64(assert(VM_Version::supports_sse2(), ""));
   InstructionMark im(this);
-  InstructionAttr attributes(AVX_128bit, /* rex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ false);
+  InstructionAttr attributes(AVX_128bit, /* rex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false);
   attributes.set_address_attributes(/* tuple_type */ EVEX_T1S, /* input_size_in_bits */ EVEX_64bit);
   attributes.reset_is_clear_context();
   attributes.set_rex_vex_w_reverted();
@@ -2816,7 +2816,7 @@ void Assembler::movsd(Address dst, XMMRegister src) {
 
 void Assembler::movss(XMMRegister dst, XMMRegister src) {
   NOT_LP64(assert(VM_Version::supports_sse(), ""));
-  InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ false);
+  InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false);
   int encode = simd_prefix_and_encode(dst, dst, src, VEX_SIMD_F3, VEX_OPCODE_0F, &attributes);
   emit_int8(0x10);
   emit_int8((unsigned char)(0xC0 | encode));
@@ -2825,7 +2825,7 @@ void Assembler::movss(XMMRegister dst, XMMRegister src) {
 void Assembler::movss(XMMRegister dst, Address src) {
   NOT_LP64(assert(VM_Version::supports_sse(), ""));
   InstructionMark im(this);
-  InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ false);
+  InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false);
   attributes.set_address_attributes(/* tuple_type */ EVEX_T1S, /* input_size_in_bits */ EVEX_32bit);
   simd_prefix(dst, xnoreg, src, VEX_SIMD_F3, VEX_OPCODE_0F, &attributes);
   emit_int8(0x10);
@@ -2835,7 +2835,7 @@ void Assembler::movss(XMMRegister dst, Address src) {
 void Assembler::movss(Address dst, XMMRegister src) {
   NOT_LP64(assert(VM_Version::supports_sse(), ""));
   InstructionMark im(this);
-  InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ false);
+  InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false);
   attributes.set_address_attributes(/* tuple_type */ EVEX_T1S, /* input_size_in_bits */ EVEX_32bit);
   attributes.reset_is_clear_context();
   simd_prefix(src, xnoreg, dst, VEX_SIMD_F3, VEX_OPCODE_0F, &attributes);
@@ -2931,7 +2931,7 @@ void Assembler::mull(Register src) {
 void Assembler::mulsd(XMMRegister dst, Address src) {
   NOT_LP64(assert(VM_Version::supports_sse2(), ""));
   InstructionMark im(this);
-  InstructionAttr attributes(AVX_128bit, /* rex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ false);
+  InstructionAttr attributes(AVX_128bit, /* rex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false);
   attributes.set_address_attributes(/* tuple_type */ EVEX_T1S, /* input_size_in_bits */ EVEX_64bit);
   attributes.set_rex_vex_w_reverted();
   simd_prefix(dst, dst, src, VEX_SIMD_F2, VEX_OPCODE_0F, &attributes);
@@ -2941,7 +2941,7 @@ void Assembler::mulsd(XMMRegister dst, Address src) {
 
 void Assembler::mulsd(XMMRegister dst, XMMRegister src) {
   NOT_LP64(assert(VM_Version::supports_sse2(), ""));
-  InstructionAttr attributes(AVX_128bit, /* rex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ false);
+  InstructionAttr attributes(AVX_128bit, /* rex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false);
   attributes.set_rex_vex_w_reverted();
   int encode = simd_prefix_and_encode(dst, dst, src, VEX_SIMD_F2, VEX_OPCODE_0F, &attributes);
   emit_int8(0x59);
@@ -2951,7 +2951,7 @@ void Assembler::mulsd(XMMRegister dst, XMMRegister src) {
 void Assembler::mulss(XMMRegister dst, Address src) {
   NOT_LP64(assert(VM_Version::supports_sse(), ""));
   InstructionMark im(this);
-  InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ false);
+  InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false);
   attributes.set_address_attributes(/* tuple_type */ EVEX_T1S, /* input_size_in_bits */ EVEX_32bit);
   simd_prefix(dst, dst, src, VEX_SIMD_F3, VEX_OPCODE_0F, &attributes);
   emit_int8(0x59);
@@ -2960,7 +2960,7 @@ void Assembler::mulss(XMMRegister dst, Address src) {
 
 void Assembler::mulss(XMMRegister dst, XMMRegister src) {
   NOT_LP64(assert(VM_Version::supports_sse(), ""));
-  InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ false);
+  InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false);
   int encode = simd_prefix_and_encode(dst, dst, src, VEX_SIMD_F3, VEX_OPCODE_0F, &attributes);
   emit_int8(0x59);
   emit_int8((unsigned char)(0xC0 | encode));
@@ -4289,7 +4289,7 @@ void Assembler::vpalignr(XMMRegister dst, XMMRegister nds, XMMRegister src, int
 
 void Assembler::pblendw(XMMRegister dst, XMMRegister src, int imm8) {
   assert(VM_Version::supports_sse4_1(), "");
-  InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ true, /* no_mask_reg */ false, /* uses_vl */ false);
+  InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ true, /* no_mask_reg */ true, /* uses_vl */ false);
   int encode = simd_prefix_and_encode(dst, dst, src, VEX_SIMD_66, VEX_OPCODE_0F_3A, &attributes);
   emit_int8((unsigned char)0x0E);
   emit_int8((unsigned char)(0xC0 | encode));
@@ -4388,7 +4388,7 @@ void Assembler::smovl() {
 
 void Assembler::sqrtsd(XMMRegister dst, XMMRegister src) {
   NOT_LP64(assert(VM_Version::supports_sse2(), ""));
-  InstructionAttr attributes(AVX_128bit, /* rex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ false);
+  InstructionAttr attributes(AVX_128bit, /* rex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false);
   attributes.set_rex_vex_w_reverted();
   int encode = simd_prefix_and_encode(dst, dst, src, VEX_SIMD_F2, VEX_OPCODE_0F, &attributes);
   emit_int8(0x51);
@@ -4398,7 +4398,7 @@ void Assembler::sqrtsd(XMMRegister dst, XMMRegister src) {
 void Assembler::sqrtsd(XMMRegister dst, Address src) {
   NOT_LP64(assert(VM_Version::supports_sse2(), ""));
   InstructionMark im(this);
-  InstructionAttr attributes(AVX_128bit, /* rex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ false);
+  InstructionAttr attributes(AVX_128bit, /* rex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false);
   attributes.set_address_attributes(/* tuple_type */ EVEX_T1S, /* input_size_in_bits */ EVEX_64bit);
   attributes.set_rex_vex_w_reverted();
   simd_prefix(dst, dst, src, VEX_SIMD_F2, VEX_OPCODE_0F, &attributes);
@@ -4408,7 +4408,7 @@ void Assembler::sqrtsd(XMMRegister dst, Address src) {
 
 void Assembler::sqrtss(XMMRegister dst, XMMRegister src) {
   NOT_LP64(assert(VM_Version::supports_sse(), ""));
-  InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ false);
+  InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false);
   int encode = simd_prefix_and_encode(dst, dst, src, VEX_SIMD_F3, VEX_OPCODE_0F, &attributes);
   emit_int8(0x51);
   emit_int8((unsigned char)(0xC0 | encode));
@@ -4421,7 +4421,7 @@ void Assembler::std() {
 void Assembler::sqrtss(XMMRegister dst, Address src) {
   NOT_LP64(assert(VM_Version::supports_sse(), ""));
   InstructionMark im(this);
-  InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ false);
+  InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false);
   attributes.set_address_attributes(/* tuple_type */ EVEX_T1S, /* input_size_in_bits */ EVEX_32bit);
   simd_prefix(dst, dst, src, VEX_SIMD_F3, VEX_OPCODE_0F, &attributes);
   emit_int8(0x51);
@@ -4484,7 +4484,7 @@ void Assembler::subl(Register dst, Register src) {
 
 void Assembler::subsd(XMMRegister dst, XMMRegister src) {
   NOT_LP64(assert(VM_Version::supports_sse2(), ""));
-  InstructionAttr attributes(AVX_128bit, /* rex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ false);
+  InstructionAttr attributes(AVX_128bit, /* rex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false);
   attributes.set_rex_vex_w_reverted();
   int encode = simd_prefix_and_encode(dst, dst, src, VEX_SIMD_F2, VEX_OPCODE_0F, &attributes);
   emit_int8(0x5C);
@@ -4494,7 +4494,7 @@ void Assembler::subsd(XMMRegister dst, XMMRegister src) {
 void Assembler::subsd(XMMRegister dst, Address src) {
   NOT_LP64(assert(VM_Version::supports_sse2(), ""));
   InstructionMark im(this);
-  InstructionAttr attributes(AVX_128bit, /* rex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ false);
+  InstructionAttr attributes(AVX_128bit, /* rex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false);
   attributes.set_address_attributes(/* tuple_type */ EVEX_T1S, /* input_size_in_bits */ EVEX_64bit);
   attributes.set_rex_vex_w_reverted();
   simd_prefix(dst, dst, src, VEX_SIMD_F2, VEX_OPCODE_0F, &attributes);
@@ -4504,7 +4504,7 @@ void Assembler::subsd(XMMRegister dst, Address src) {
 
 void Assembler::subss(XMMRegister dst, XMMRegister src) {
   NOT_LP64(assert(VM_Version::supports_sse(), ""));
-  InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false , /* uses_vl */ false);
+  InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true , /* uses_vl */ false);
   int encode = simd_prefix_and_encode(dst, dst, src, VEX_SIMD_F3, VEX_OPCODE_0F, &attributes);
   emit_int8(0x5C);
   emit_int8((unsigned char)(0xC0 | encode));
@@ -4513,7 +4513,7 @@ void Assembler::subss(XMMRegister dst, XMMRegister src) {
 void Assembler::subss(XMMRegister dst, Address src) {
   NOT_LP64(assert(VM_Version::supports_sse(), ""));
   InstructionMark im(this);
-  InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ false);
+  InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false);
   attributes.set_address_attributes(/* tuple_type */ EVEX_T1S, /* input_size_in_bits */ EVEX_32bit);
   simd_prefix(dst, dst, src, VEX_SIMD_F3, VEX_OPCODE_0F, &attributes);
   emit_int8(0x5C);
@@ -4735,7 +4735,7 @@ void Assembler::xorb(Register dst, Address src) {
 void Assembler::vaddsd(XMMRegister dst, XMMRegister nds, Address src) {
   assert(VM_Version::supports_avx(), "");
   InstructionMark im(this);
-  InstructionAttr attributes(AVX_128bit, /* vex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ false);
+  InstructionAttr attributes(AVX_128bit, /* vex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false);
   attributes.set_address_attributes(/* tuple_type */ EVEX_T1S, /* input_size_in_bits */ EVEX_64bit);
   attributes.set_rex_vex_w_reverted();
   vex_prefix(src, nds->encoding(), dst->encoding(), VEX_SIMD_F2, VEX_OPCODE_0F, &attributes);
@@ -4745,7 +4745,7 @@ void Assembler::vaddsd(XMMRegister dst, XMMRegister nds, Address src) {
 
 void Assembler::vaddsd(XMMRegister dst, XMMRegister nds, XMMRegister src) {
   assert(VM_Version::supports_avx(), "");
-  InstructionAttr attributes(AVX_128bit, /* vex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ false);
+  InstructionAttr attributes(AVX_128bit, /* vex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false);
   attributes.set_rex_vex_w_reverted();
   int encode = vex_prefix_and_encode(dst->encoding(), nds->encoding(), src->encoding(), VEX_SIMD_F2, VEX_OPCODE_0F, &attributes);
   emit_int8(0x58);
@@ -4755,7 +4755,7 @@ void Assembler::vaddsd(XMMRegister dst, XMMRegister nds, XMMRegister src) {
 void Assembler::vaddss(XMMRegister dst, XMMRegister nds, Address src) {
   assert(VM_Version::supports_avx(), "");
   InstructionMark im(this);
-  InstructionAttr attributes(AVX_128bit, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ false);
+  InstructionAttr attributes(AVX_128bit, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false);
   attributes.set_address_attributes(/* tuple_type */ EVEX_T1S, /* input_size_in_bits */ EVEX_32bit);
   vex_prefix(src, nds->encoding(), dst->encoding(), VEX_SIMD_F3, VEX_OPCODE_0F, &attributes);
   emit_int8(0x58);
@@ -4764,7 +4764,7 @@ void Assembler::vaddss(XMMRegister dst, XMMRegister nds, Address src) {
 
 void Assembler::vaddss(XMMRegister dst, XMMRegister nds, XMMRegister src) {
   assert(VM_Version::supports_avx(), "");
-  InstructionAttr attributes(AVX_128bit, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ false);
+  InstructionAttr attributes(AVX_128bit, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false);
   int encode = vex_prefix_and_encode(dst->encoding(), nds->encoding(), src->encoding(), VEX_SIMD_F3, VEX_OPCODE_0F, &attributes);
   emit_int8(0x58);
   emit_int8((unsigned char)(0xC0 | encode));
@@ -4773,7 +4773,7 @@ void Assembler::vaddss(XMMRegister dst, XMMRegister nds, XMMRegister src) {
 void Assembler::vdivsd(XMMRegister dst, XMMRegister nds, Address src) {
   assert(VM_Version::supports_avx(), "");
   InstructionMark im(this);
-  InstructionAttr attributes(AVX_128bit, /* vex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ false);
+  InstructionAttr attributes(AVX_128bit, /* vex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false);
   attributes.set_address_attributes(/* tuple_type */ EVEX_T1S, /* input_size_in_bits */ EVEX_64bit);
   attributes.set_rex_vex_w_reverted();
   vex_prefix(src, nds->encoding(), dst->encoding(), VEX_SIMD_F2, VEX_OPCODE_0F, &attributes);
@@ -4783,7 +4783,7 @@ void Assembler::vdivsd(XMMRegister dst, XMMRegister nds, Address src) {
 
 void Assembler::vdivsd(XMMRegister dst, XMMRegister nds, XMMRegister src) {
   assert(VM_Version::supports_avx(), "");
-  InstructionAttr attributes(AVX_128bit, /* vex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ false);
+  InstructionAttr attributes(AVX_128bit, /* vex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false);
   attributes.set_rex_vex_w_reverted();
   int encode = vex_prefix_and_encode(dst->encoding(), nds->encoding(), src->encoding(), VEX_SIMD_F2, VEX_OPCODE_0F, &attributes);
   emit_int8(0x5E);
@@ -4793,7 +4793,7 @@ void Assembler::vdivsd(XMMRegister dst, XMMRegister nds, XMMRegister src) {
 void Assembler::vdivss(XMMRegister dst, XMMRegister nds, Address src) {
   assert(VM_Version::supports_avx(), "");
   InstructionMark im(this);
-  InstructionAttr attributes(AVX_128bit, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ false);
+  InstructionAttr attributes(AVX_128bit, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false);
   attributes.set_address_attributes(/* tuple_type */ EVEX_T1S, /* input_size_in_bits */ EVEX_32bit);
   vex_prefix(src, nds->encoding(), dst->encoding(), VEX_SIMD_F3, VEX_OPCODE_0F, &attributes);
   emit_int8(0x5E);
@@ -4802,7 +4802,7 @@ void Assembler::vdivss(XMMRegister dst, XMMRegister nds, Address src) {
 
 void Assembler::vdivss(XMMRegister dst, XMMRegister nds, XMMRegister src) {
   assert(VM_Version::supports_avx(), "");
-  InstructionAttr attributes(AVX_128bit, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ false);
+  InstructionAttr attributes(AVX_128bit, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false);
   int encode = vex_prefix_and_encode(dst->encoding(), nds->encoding(), src->encoding(), VEX_SIMD_F3, VEX_OPCODE_0F, &attributes);
   emit_int8(0x5E);
   emit_int8((unsigned char)(0xC0 | encode));
@@ -4810,7 +4810,7 @@ void Assembler::vdivss(XMMRegister dst, XMMRegister nds, XMMRegister src) {
 
 void Assembler::vfmadd231sd(XMMRegister dst, XMMRegister src1, XMMRegister src2) {
   assert(VM_Version::supports_fma(), "");
-  InstructionAttr attributes(AVX_128bit, /* vex_w */ true, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ false);
+  InstructionAttr attributes(AVX_128bit, /* vex_w */ true, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false);
   int encode = vex_prefix_and_encode(dst->encoding(), src1->encoding(), src2->encoding(), VEX_SIMD_66, VEX_OPCODE_0F_38, &attributes);
   emit_int8((unsigned char)0xB9);
   emit_int8((unsigned char)(0xC0 | encode));
@@ -4818,7 +4818,7 @@ void Assembler::vfmadd231sd(XMMRegister dst, XMMRegister src1, XMMRegister src2)
 
 void Assembler::vfmadd231ss(XMMRegister dst, XMMRegister src1, XMMRegister src2) {
   assert(VM_Version::supports_fma(), "");
-  InstructionAttr attributes(AVX_128bit, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ false);
+  InstructionAttr attributes(AVX_128bit, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false);
   int encode = vex_prefix_and_encode(dst->encoding(), src1->encoding(), src2->encoding(), VEX_SIMD_66, VEX_OPCODE_0F_38, &attributes);
   emit_int8((unsigned char)0xB9);
   emit_int8((unsigned char)(0xC0 | encode));
@@ -4827,7 +4827,7 @@ void Assembler::vfmadd231ss(XMMRegister dst, XMMRegister src1, XMMRegister src2)
 void Assembler::vmulsd(XMMRegister dst, XMMRegister nds, Address src) {
   assert(VM_Version::supports_avx(), "");
   InstructionMark im(this);
-  InstructionAttr attributes(AVX_128bit, /* vex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ false);
+  InstructionAttr attributes(AVX_128bit, /* vex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false);
   attributes.set_address_attributes(/* tuple_type */ EVEX_T1S, /* input_size_in_bits */ EVEX_64bit);
   attributes.set_rex_vex_w_reverted();
   vex_prefix(src, nds->encoding(), dst->encoding(), VEX_SIMD_F2, VEX_OPCODE_0F, &attributes);
@@ -4837,7 +4837,7 @@ void Assembler::vmulsd(XMMRegister dst, XMMRegister nds, Address src) {
 
 void Assembler::vmulsd(XMMRegister dst, XMMRegister nds, XMMRegister src) {
   assert(VM_Version::supports_avx(), "");
-  InstructionAttr attributes(AVX_128bit, /* vex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ false);
+  InstructionAttr attributes(AVX_128bit, /* vex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false);
   attributes.set_rex_vex_w_reverted();
   int encode = vex_prefix_and_encode(dst->encoding(), nds->encoding(), src->encoding(), VEX_SIMD_F2, VEX_OPCODE_0F, &attributes);
   emit_int8(0x59);
@@ -4847,7 +4847,7 @@ void Assembler::vmulsd(XMMRegister dst, XMMRegister nds, XMMRegister src) {
 void Assembler::vmulss(XMMRegister dst, XMMRegister nds, Address src) {
   assert(VM_Version::supports_avx(), "");
   InstructionMark im(this);
-  InstructionAttr attributes(AVX_128bit, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ false);
+  InstructionAttr attributes(AVX_128bit, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false);
   attributes.set_address_attributes(/* tuple_type */ EVEX_T1S, /* input_size_in_bits */ EVEX_32bit);
   vex_prefix(src, nds->encoding(), dst->encoding(), VEX_SIMD_F3, VEX_OPCODE_0F, &attributes);
   emit_int8(0x59);
@@ -4856,7 +4856,7 @@ void Assembler::vmulss(XMMRegister dst, XMMRegister nds, Address src) {
 
 void Assembler::vmulss(XMMRegister dst, XMMRegister nds, XMMRegister src) {
   assert(VM_Version::supports_avx(), "");
-  InstructionAttr attributes(AVX_128bit, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ false);
+  InstructionAttr attributes(AVX_128bit, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false);
   int encode = vex_prefix_and_encode(dst->encoding(), nds->encoding(), src->encoding(), VEX_SIMD_F3, VEX_OPCODE_0F, &attributes);
   emit_int8(0x59);
   emit_int8((unsigned char)(0xC0 | encode));
@@ -4865,7 +4865,7 @@ void Assembler::vmulss(XMMRegister dst, XMMRegister nds, XMMRegister src) {
 void Assembler::vsubsd(XMMRegister dst, XMMRegister nds, Address src) {
   assert(VM_Version::supports_avx(), "");
   InstructionMark im(this);
-  InstructionAttr attributes(AVX_128bit, /* vex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ false);
+  InstructionAttr attributes(AVX_128bit, /* vex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false);
   attributes.set_address_attributes(/* tuple_type */ EVEX_T1S, /* input_size_in_bits */ EVEX_64bit);
   attributes.set_rex_vex_w_reverted();
   vex_prefix(src, nds->encoding(), dst->encoding(), VEX_SIMD_F2, VEX_OPCODE_0F, &attributes);
@@ -4875,7 +4875,7 @@ void Assembler::vsubsd(XMMRegister dst, XMMRegister nds, Address src) {
 
 void Assembler::vsubsd(XMMRegister dst, XMMRegister nds, XMMRegister src) {
   assert(VM_Version::supports_avx(), "");
-  InstructionAttr attributes(AVX_128bit, /* vex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ false);
+  InstructionAttr attributes(AVX_128bit, /* vex_w */ VM_Version::supports_evex(), /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false);
   attributes.set_rex_vex_w_reverted();
   int encode = vex_prefix_and_encode(dst->encoding(), nds->encoding(), src->encoding(), VEX_SIMD_F2, VEX_OPCODE_0F, &attributes);
   emit_int8(0x5C);
@@ -4885,7 +4885,7 @@ void Assembler::vsubsd(XMMRegister dst, XMMRegister nds, XMMRegister src) {
 void Assembler::vsubss(XMMRegister dst, XMMRegister nds, Address src) {
   assert(VM_Version::supports_avx(), "");
   InstructionMark im(this);
-  InstructionAttr attributes(AVX_128bit, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ false);
+  InstructionAttr attributes(AVX_128bit, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false);
   attributes.set_address_attributes(/* tuple_type */ EVEX_T1S, /* input_size_in_bits */ EVEX_32bit);
   vex_prefix(src, nds->encoding(), dst->encoding(), VEX_SIMD_F3, VEX_OPCODE_0F, &attributes);
   emit_int8(0x5C);
@@ -4894,7 +4894,7 @@ void Assembler::vsubss(XMMRegister dst, XMMRegister nds, Address src) {
 
 void Assembler::vsubss(XMMRegister dst, XMMRegister nds, XMMRegister src) {
   assert(VM_Version::supports_avx(), "");
-  InstructionAttr attributes(AVX_128bit, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ false);
+  InstructionAttr attributes(AVX_128bit, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ true, /* uses_vl */ false);
   int encode = vex_prefix_and_encode(dst->encoding(), nds->encoding(), src->encoding(), VEX_SIMD_F3, VEX_OPCODE_0F, &attributes);
   emit_int8(0x5C);
   emit_int8((unsigned char)(0xC0 | encode));
@@ -5203,6 +5203,24 @@ void Assembler::vsqrtpd(XMMRegister dst, Address src, int vector_len) {
   emit_operand(dst, src);
 }
 
+void Assembler::vsqrtps(XMMRegister dst, XMMRegister src, int vector_len) {
+  assert(VM_Version::supports_avx(), "");
+  InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ true);
+  int encode = vex_prefix_and_encode(dst->encoding(), 0, src->encoding(), VEX_SIMD_NONE, VEX_OPCODE_0F, &attributes);
+  emit_int8(0x51);
+  emit_int8((unsigned char)(0xC0 | encode));
+}
+
+void Assembler::vsqrtps(XMMRegister dst, Address src, int vector_len) {
+  assert(VM_Version::supports_avx(), "");
+  InstructionMark im(this);
+  InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ false, /* no_mask_reg */ false, /* uses_vl */ true);
+  attributes.set_address_attributes(/* tuple_type */ EVEX_FV, /* input_size_in_bits */ EVEX_64bit);
+  vex_prefix(src, 0, dst->encoding(), VEX_SIMD_NONE, VEX_OPCODE_0F, &attributes);
+  emit_int8(0x51);
+  emit_operand(dst, src);
+}
+
 void Assembler::andpd(XMMRegister dst, XMMRegister src) {
   NOT_LP64(assert(VM_Version::supports_sse2(), ""));
   InstructionAttr attributes(AVX_128bit, /* rex_w */ !_legacy_mode_dq, /* legacy_mode */ _legacy_mode_dq, /* no_mask_reg */ false, /* uses_vl */ true);
@@ -5377,7 +5395,7 @@ void Assembler::vxorps(XMMRegister dst, XMMRegister nds, Address src, int vector
 void Assembler::vphaddw(XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len) {
   assert(VM_Version::supports_avx() && (vector_len == 0) ||
          VM_Version::supports_avx2(), "256 bit integer vectors requires AVX2");
-  InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ true, /* no_mask_reg */ false, /* uses_vl */ false);
+  InstructionAttr attributes(vector_len, /* vex_w */ false, /* legacy_mode */ true, /* no_mask_reg */ true, /* uses_vl */ false);
   int encode = vex_prefix_and_encode(dst->encoding(), nds->encoding(), src->encoding(), VEX_SIMD_66, VEX_OPCODE_0F_38, &attributes);
   emit_int8(0x01);
   emit_int8((unsigned char)(0xC0 | encode));
@@ -5436,7 +5454,7 @@ void Assembler::paddq(XMMRegister dst, XMMRegister src) {
 
 void Assembler::phaddw(XMMRegister dst, XMMRegister src) {
   assert(VM_Version::supports_sse3(), "");
-  InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ true, /* no_mask_reg */ false, /* uses_vl */ false);
+  InstructionAttr attributes(AVX_128bit, /* rex_w */ false, /* legacy_mode */ true, /* no_mask_reg */ true, /* uses_vl */ false);
   int encode = simd_prefix_and_encode(dst, dst, src, VEX_SIMD_66, VEX_OPCODE_0F_38, &attributes);
   emit_int8(0x01);
   emit_int8((unsigned char)(0xC0 | encode));
@@ -6679,7 +6697,7 @@ void Assembler::vpclmulqdq(XMMRegister dst, XMMRegister nds, XMMRegister src, in
 
 void Assembler::vzeroupper() {
   if (VM_Version::supports_vzeroupper()) {
-    InstructionAttr attributes(AVX_128bit, /* vex_w */ false, /* legacy_mode */ true, /* no_mask_reg */ false, /* uses_vl */ false);
+    InstructionAttr attributes(AVX_128bit, /* vex_w */ false, /* legacy_mode */ true, /* no_mask_reg */ true, /* uses_vl */ false);
     (void)vex_prefix_and_encode(0, 0, 0, VEX_SIMD_NONE, VEX_OPCODE_0F, &attributes);
     emit_int8(0x77);
   }
@@ -7442,7 +7460,7 @@ void Assembler::vpblendd(XMMRegister dst, XMMRegister nds, XMMRegister src, int
 
 void Assembler::shlxl(Register dst, Register src1, Register src2) {
   assert(VM_Version::supports_bmi2(), "");
-  InstructionAttr attributes(AVX_128bit, /* vex_w */ false, /* legacy_mode */ true, /* no_mask_reg */ false, /* uses_vl */ false);
+  InstructionAttr attributes(AVX_128bit, /* vex_w */ false, /* legacy_mode */ true, /* no_mask_reg */ true, /* uses_vl */ false);
   int encode = vex_prefix_and_encode(dst->encoding(), src2->encoding(), src1->encoding(), VEX_SIMD_66, VEX_OPCODE_0F_38, &attributes);
   emit_int8((unsigned char)0xF7);
   emit_int8((unsigned char)(0xC0 | encode));
@@ -7450,7 +7468,7 @@ void Assembler::shlxl(Register dst, Register src1, Register src2) {
 
 void Assembler::shlxq(Register dst, Register src1, Register src2) {
   assert(VM_Version::supports_bmi2(), "");
-  InstructionAttr attributes(AVX_128bit, /* vex_w */ true, /* legacy_mode */ true, /* no_mask_reg */ false, /* uses_vl */ false);
+  InstructionAttr attributes(AVX_128bit, /* vex_w */ true, /* legacy_mode */ true, /* no_mask_reg */ true, /* uses_vl */ false);
   int encode = vex_prefix_and_encode(dst->encoding(), src2->encoding(), src1->encoding(), VEX_SIMD_66, VEX_OPCODE_0F_38, &attributes);
   emit_int8((unsigned char)0xF7);
   emit_int8((unsigned char)(0xC0 | encode));
@@ -7985,7 +8003,7 @@ void Assembler::andq(Register dst, Register src) {
 
 void Assembler::andnq(Register dst, Register src1, Register src2) {
   assert(VM_Version::supports_bmi1(), "bit manipulation instructions not supported");
-  InstructionAttr attributes(AVX_128bit, /* vex_w */ true, /* legacy_mode */ true, /* no_mask_reg */ false, /* uses_vl */ false);
+  InstructionAttr attributes(AVX_128bit, /* vex_w */ true, /* legacy_mode */ true, /* no_mask_reg */ true, /* uses_vl */ false);
   int encode = vex_prefix_and_encode(dst->encoding(), src1->encoding(), src2->encoding(), VEX_SIMD_NONE, VEX_OPCODE_0F_38, &attributes);
   emit_int8((unsigned char)0xF2);
   emit_int8((unsigned char)(0xC0 | encode));
@@ -7994,7 +8012,7 @@ void Assembler::andnq(Register dst, Register src1, Register src2) {
 void Assembler::andnq(Register dst, Register src1, Address src2) {
   assert(VM_Version::supports_bmi1(), "bit manipulation instructions not supported");
   InstructionMark im(this);
-  InstructionAttr attributes(AVX_128bit, /* vex_w */ true, /* legacy_mode */ true, /* no_mask_reg */ false, /* uses_vl */ false);
+  InstructionAttr attributes(AVX_128bit, /* vex_w */ true, /* legacy_mode */ true, /* no_mask_reg */ true, /* uses_vl */ false);
   vex_prefix(src2, src1->encoding(), dst->encoding(), VEX_SIMD_NONE, VEX_OPCODE_0F_38, &attributes);
   emit_int8((unsigned char)0xF2);
   emit_operand(dst, src2);
@@ -8022,7 +8040,7 @@ void Assembler::bswapq(Register reg) {
 
 void Assembler::blsiq(Register dst, Register src) {
   assert(VM_Version::supports_bmi1(), "bit manipulation instructions not supported");
-  InstructionAttr attributes(AVX_128bit, /* vex_w */ true, /* legacy_mode */ true, /* no_mask_reg */ false, /* uses_vl */ false);
+  InstructionAttr attributes(AVX_128bit, /* vex_w */ true, /* legacy_mode */ true, /* no_mask_reg */ true, /* uses_vl */ false);
   int encode = vex_prefix_and_encode(rbx->encoding(), dst->encoding(), src->encoding(), VEX_SIMD_NONE, VEX_OPCODE_0F_38, &attributes);
   emit_int8((unsigned char)0xF3);
   emit_int8((unsigned char)(0xC0 | encode));
@@ -8031,7 +8049,7 @@ void Assembler::blsiq(Register dst, Register src) {
 void Assembler::blsiq(Register dst, Address src) {
   assert(VM_Version::supports_bmi1(), "bit manipulation instructions not supported");
   InstructionMark im(this);
-  InstructionAttr attributes(AVX_128bit, /* vex_w */ true, /* legacy_mode */ true, /* no_mask_reg */ false, /* uses_vl */ false);
+  InstructionAttr attributes(AVX_128bit, /* vex_w */ true, /* legacy_mode */ true, /* no_mask_reg */ true, /* uses_vl */ false);
   vex_prefix(src, dst->encoding(), rbx->encoding(), VEX_SIMD_NONE, VEX_OPCODE_0F_38, &attributes);
   emit_int8((unsigned char)0xF3);
   emit_operand(rbx, src);
@@ -8039,7 +8057,7 @@ void Assembler::blsiq(Register dst, Address src) {
 
 void Assembler::blsmskq(Register dst, Register src) {
   assert(VM_Version::supports_bmi1(), "bit manipulation instructions not supported");
-  InstructionAttr attributes(AVX_128bit, /* vex_w */ true, /* legacy_mode */ true, /* no_mask_reg */ false, /* uses_vl */ false);
+  InstructionAttr attributes(AVX_128bit, /* vex_w */ true, /* legacy_mode */ true, /* no_mask_reg */ true, /* uses_vl */ false);
   int encode = vex_prefix_and_encode(rdx->encoding(), dst->encoding(), src->encoding(), VEX_SIMD_NONE, VEX_OPCODE_0F_38, &attributes);
   emit_int8((unsigned char)0xF3);
   emit_int8((unsigned char)(0xC0 | encode));
@@ -8048,7 +8066,7 @@ void Assembler::blsmskq(Register dst, Register src) {
 void Assembler::blsmskq(Register dst, Address src) {
   assert(VM_Version::supports_bmi1(), "bit manipulation instructions not supported");
   InstructionMark im(this);
-  InstructionAttr attributes(AVX_128bit, /* vex_w */ true, /* legacy_mode */ true, /* no_mask_reg */ false, /* uses_vl */ false);
+  InstructionAttr attributes(AVX_128bit, /* vex_w */ true, /* legacy_mode */ true, /* no_mask_reg */ true, /* uses_vl */ false);
   vex_prefix(src, dst->encoding(), rdx->encoding(), VEX_SIMD_NONE, VEX_OPCODE_0F_38, &attributes);
   emit_int8((unsigned char)0xF3);
   emit_operand(rdx, src);
@@ -8056,7 +8074,7 @@ void Assembler::blsmskq(Register dst, Address src) {
 
 void Assembler::blsrq(Register dst, Register src) {
   assert(VM_Version::supports_bmi1(), "bit manipulation instructions not supported");
-  InstructionAttr attributes(AVX_128bit, /* vex_w */ true, /* legacy_mode */ true, /* no_mask_reg */ false, /* uses_vl */ false);
+  InstructionAttr attributes(AVX_128bit, /* vex_w */ true, /* legacy_mode */ true, /* no_mask_reg */ true, /* uses_vl */ false);
   int encode = vex_prefix_and_encode(rcx->encoding(), dst->encoding(), src->encoding(), VEX_SIMD_NONE, VEX_OPCODE_0F_38, &attributes);
   emit_int8((unsigned char)0xF3);
   emit_int8((unsigned char)(0xC0 | encode));
@@ -8065,7 +8083,7 @@ void Assembler::blsrq(Register dst, Register src) {
 void Assembler::blsrq(Register dst, Address src) {
   assert(VM_Version::supports_bmi1(), "bit manipulation instructions not supported");
   InstructionMark im(this);
-  InstructionAttr attributes(AVX_128bit, /* vex_w */ true, /* legacy_mode */ true, /* no_mask_reg */ false, /* uses_vl */ false);
+  InstructionAttr attributes(AVX_128bit, /* vex_w */ true, /* legacy_mode */ true, /* no_mask_reg */ true, /* uses_vl */ false);
   vex_prefix(src, dst->encoding(), rcx->encoding(), VEX_SIMD_NONE, VEX_OPCODE_0F_38, &attributes);
   emit_int8((unsigned char)0xF3);
   emit_operand(rcx, src);
@@ -8504,7 +8522,7 @@ void Assembler::mulq(Register src) {
 
 void Assembler::mulxq(Register dst1, Register dst2, Register src) {
   assert(VM_Version::supports_bmi2(), "bit manipulation instructions not supported");
-  InstructionAttr attributes(AVX_128bit, /* vex_w */ true, /* legacy_mode */ true, /* no_mask_reg */ false, /* uses_vl */ false);
+  InstructionAttr attributes(AVX_128bit, /* vex_w */ true, /* legacy_mode */ true, /* no_mask_reg */ true, /* uses_vl */ false);
   int encode = vex_prefix_and_encode(dst1->encoding(), dst2->encoding(), src->encoding(), VEX_SIMD_F2, VEX_OPCODE_0F_38, &attributes);
   emit_int8((unsigned char)0xF6);
   emit_int8((unsigned char)(0xC0 | encode));
@@ -8667,7 +8685,7 @@ void Assembler::rorq(Register dst, int imm8) {
 
 void Assembler::rorxq(Register dst, Register src, int imm8) {
   assert(VM_Version::supports_bmi2(), "bit manipulation instructions not supported");
-  InstructionAttr attributes(AVX_128bit, /* vex_w */ true, /* legacy_mode */ true, /* no_mask_reg */ false, /* uses_vl */ false);
+  InstructionAttr attributes(AVX_128bit, /* vex_w */ true, /* legacy_mode */ true, /* no_mask_reg */ true, /* uses_vl */ false);
   int encode = vex_prefix_and_encode(dst->encoding(), 0, src->encoding(), VEX_SIMD_F2, VEX_OPCODE_0F_3A, &attributes);
   emit_int8((unsigned char)0xF0);
   emit_int8((unsigned char)(0xC0 | encode));
@@ -8676,7 +8694,7 @@ void Assembler::rorxq(Register dst, Register src, int imm8) {
 
 void Assembler::rorxd(Register dst, Register src, int imm8) {
   assert(VM_Version::supports_bmi2(), "bit manipulation instructions not supported");
-  InstructionAttr attributes(AVX_128bit, /* vex_w */ false, /* legacy_mode */ true, /* no_mask_reg */ false, /* uses_vl */ false);
+  InstructionAttr attributes(AVX_128bit, /* vex_w */ false, /* legacy_mode */ true, /* no_mask_reg */ true, /* uses_vl */ false);
   int encode = vex_prefix_and_encode(dst->encoding(), 0, src->encoding(), VEX_SIMD_F2, VEX_OPCODE_0F_3A, &attributes);
   emit_int8((unsigned char)0xF0);
   emit_int8((unsigned char)(0xC0 | encode));
diff --git a/src/hotspot/cpu/x86/assembler_x86.hpp b/src/hotspot/cpu/x86/assembler_x86.hpp
index c4e6645e319..2739cf3b5eb 100644
--- a/src/hotspot/cpu/x86/assembler_x86.hpp
+++ b/src/hotspot/cpu/x86/assembler_x86.hpp
@@ -1919,9 +1919,11 @@ private:
   void vdivpd(XMMRegister dst, XMMRegister nds, Address src, int vector_len);
   void vdivps(XMMRegister dst, XMMRegister nds, Address src, int vector_len);
 
-  // Sqrt Packed Floating-Point Values - Double precision only
+  // Sqrt Packed Floating-Point Values
   void vsqrtpd(XMMRegister dst, XMMRegister src, int vector_len);
   void vsqrtpd(XMMRegister dst, Address src, int vector_len);
+  void vsqrtps(XMMRegister dst, XMMRegister src, int vector_len);
+  void vsqrtps(XMMRegister dst, Address src, int vector_len);
 
   // Bitwise Logical AND of Packed Floating-Point Values
   void andpd(XMMRegister dst, XMMRegister src);
diff --git a/src/hotspot/cpu/x86/macroAssembler_x86.cpp b/src/hotspot/cpu/x86/macroAssembler_x86.cpp
index 3ac35d752e4..112321cce78 100644
--- a/src/hotspot/cpu/x86/macroAssembler_x86.cpp
+++ b/src/hotspot/cpu/x86/macroAssembler_x86.cpp
@@ -6630,6 +6630,13 @@ void MacroAssembler::restore_cpu_control_state_after_jni() {
   }
   // Clear upper bits of YMM registers to avoid SSE <-> AVX transition penalty.
   vzeroupper();
+  // Reset k1 to 0xffff.
+  if (VM_Version::supports_evex()) {
+    push(rcx);
+    movl(rcx, 0xffff);
+    kmovwl(k1, rcx);
+    pop(rcx);
+  }
 
 #ifndef _LP64
   // Either restore the x87 floating pointer control word after returning
diff --git a/src/hotspot/cpu/x86/nativeInst_x86.hpp b/src/hotspot/cpu/x86/nativeInst_x86.hpp
index 436a48caf34..de4b448396b 100644
--- a/src/hotspot/cpu/x86/nativeInst_x86.hpp
+++ b/src/hotspot/cpu/x86/nativeInst_x86.hpp
@@ -706,14 +706,11 @@ inline bool NativeInstruction::is_cond_jump()    { return (int_at(0) & 0xF0FF) =
 inline bool NativeInstruction::is_safepoint_poll() {
 #ifdef AMD64
   if (SafepointMechanism::uses_thread_local_poll()) {
-    // We know that the poll must have a REX_B prefix since we enforce its source to be
-    // a rex-register and the destination to be rax.
     const bool has_rex_prefix = ubyte_at(0) == NativeTstRegMem::instruction_rex_b_prefix;
-    const bool is_test_opcode = ubyte_at(1) == NativeTstRegMem::instruction_code_memXregl;
-    const bool is_rax_target = (ubyte_at(2) & NativeTstRegMem::modrm_mask) == NativeTstRegMem::modrm_reg;
-    if (has_rex_prefix && is_test_opcode && is_rax_target) {
-      return true;
-    }
+    const int test_offset = has_rex_prefix ? 1 : 0;
+    const bool is_test_opcode = ubyte_at(test_offset) == NativeTstRegMem::instruction_code_memXregl;
+    const bool is_rax_target = (ubyte_at(test_offset + 1) & NativeTstRegMem::modrm_mask) == NativeTstRegMem::modrm_reg;
+    return is_test_opcode && is_rax_target;
   }
   // Try decoding a near safepoint first:
   if (ubyte_at(0) == NativeTstRegMem::instruction_code_memXregl &&
diff --git a/src/hotspot/cpu/x86/sharedRuntime_x86_64.cpp b/src/hotspot/cpu/x86/sharedRuntime_x86_64.cpp
index cf85d5807a2..b02015a08c2 100644
--- a/src/hotspot/cpu/x86/sharedRuntime_x86_64.cpp
+++ b/src/hotspot/cpu/x86/sharedRuntime_x86_64.cpp
@@ -3388,26 +3388,63 @@ SafepointBlob* SharedRuntime::generate_handler_blob(address call_ptr, int poll_t
   // No exception case
   __ bind(noException);
 
-  Label no_adjust, bail;
+  Label no_adjust, bail, no_prefix, not_special;
   if (SafepointMechanism::uses_thread_local_poll() && !cause_return) {
     // If our stashed return pc was modified by the runtime we avoid touching it
     __ cmpptr(rbx, Address(rbp, wordSize));
     __ jccb(Assembler::notEqual, no_adjust);
 
+    // Skip over the poll instruction.
+    // See NativeInstruction::is_safepoint_poll()
+    // Possible encodings:
+    //      85 00       test   %eax,(%rax)
+    //      85 01       test   %eax,(%rcx)
+    //      85 02       test   %eax,(%rdx)
+    //      85 03       test   %eax,(%rbx)
+    //      85 06       test   %eax,(%rsi)
+    //      85 07       test   %eax,(%rdi)
+    //
+    //   41 85 00       test   %eax,(%r8)
+    //   41 85 01       test   %eax,(%r9)
+    //   41 85 02       test   %eax,(%r10)
+    //   41 85 03       test   %eax,(%r11)
+    //   41 85 06       test   %eax,(%r14)
+    //   41 85 07       test   %eax,(%r15)
+    //
+    //      85 04 24    test   %eax,(%rsp)
+    //   41 85 04 24    test   %eax,(%r12)
+    //      85 45 00    test   %eax,0x0(%rbp)
+    //   41 85 45 00    test   %eax,0x0(%r13)
+
+    __ cmpb(Address(rbx, 0), NativeTstRegMem::instruction_rex_b_prefix);
+    __ jcc(Assembler::notEqual, no_prefix);
+    __ addptr(rbx, 1);
+    __ bind(no_prefix);
+#ifdef ASSERT
+    __ movptr(rax, rbx); // remember where 0x85 should be, for verification below
+#endif
+    // r12/r13/rsp/rbp base encoding takes 3 bytes with the following register values:
+    // r12/rsp 0x04
+    // r13/rbp 0x05
+    __ movzbq(rcx, Address(rbx, 1));
+    __ andptr(rcx, 0x07); // looking for 0x04 .. 0x05
+    __ subptr(rcx, 4);    // looking for 0x00 .. 0x01
+    __ cmpptr(rcx, 1);
+    __ jcc(Assembler::above, not_special);
+    __ addptr(rbx, 1);
+    __ bind(not_special);
 #ifdef ASSERT
     // Verify the correct encoding of the poll we're about to skip.
-    // See NativeInstruction::is_safepoint_poll()
-    __ cmpb(Address(rbx, 0), NativeTstRegMem::instruction_rex_b_prefix);
-    __ jcc(Assembler::notEqual, bail);
-    __ cmpb(Address(rbx, 1), NativeTstRegMem::instruction_code_memXregl);
+    __ cmpb(Address(rax, 0), NativeTstRegMem::instruction_code_memXregl);
     __ jcc(Assembler::notEqual, bail);
     // Mask out the modrm bits
-    __ testb(Address(rbx, 2), NativeTstRegMem::modrm_mask);
+    __ testb(Address(rax, 1), NativeTstRegMem::modrm_mask);
     // rax encodes to 0, so if the bits are nonzero it's incorrect
     __ jcc(Assembler::notZero, bail);
 #endif
     // Adjust return pc forward to step over the safepoint poll instruction
-    __ addptr(Address(rbp, wordSize), 3);
+    __ addptr(rbx, 2);
+    __ movptr(Address(rbp, wordSize), rbx);
   }
 
   __ bind(no_adjust);
diff --git a/src/hotspot/cpu/x86/stubGenerator_x86_64.cpp b/src/hotspot/cpu/x86/stubGenerator_x86_64.cpp
index 7db8ba32981..5b28a4e28d5 100644
--- a/src/hotspot/cpu/x86/stubGenerator_x86_64.cpp
+++ b/src/hotspot/cpu/x86/stubGenerator_x86_64.cpp
@@ -1264,9 +1264,12 @@ class StubGenerator: public StubCodeGenerator {
           CardTableModRefBS* ct = barrier_set_cast<CardTableModRefBS>(bs);
           assert(sizeof(*ct->byte_map_base) == sizeof(jbyte), "adjust this code");
 
-          Label L_loop;
+          Label L_loop, L_done;
           const Register end = count;
 
+          __ testl(count, count);
+          __ jcc(Assembler::zero, L_done); // zero count - nothing to do
+
           __ leaq(end, Address(start, count, TIMES_OOP, 0));  // end == start+count*oop_size
           __ subptr(end, BytesPerHeapOop); // end - 1 to make inclusive
           __ shrptr(start, CardTableModRefBS::card_shift);
@@ -1280,6 +1283,7 @@ class StubGenerator: public StubCodeGenerator {
           __ movb(Address(start, count, Address::times_1), 0);
           __ decrement(count);
           __ jcc(Assembler::greaterEqual, L_loop);
+        __ BIND(L_done);
         }
         break;
       default:
diff --git a/src/hotspot/cpu/x86/vm_version_x86.cpp b/src/hotspot/cpu/x86/vm_version_x86.cpp
index f322d60a6ed..563ff1f111b 100644
--- a/src/hotspot/cpu/x86/vm_version_x86.cpp
+++ b/src/hotspot/cpu/x86/vm_version_x86.cpp
@@ -629,18 +629,26 @@ void VM_Version::get_processor_features() {
     _features &= ~CPU_SSE;
 
   // first try initial setting and detect what we can support
+  int use_avx_limit = 0;
   if (UseAVX > 0) {
     if (UseAVX > 2 && supports_evex()) {
-      UseAVX = 3;
+      use_avx_limit = 3;
     } else if (UseAVX > 1 && supports_avx2()) {
-      UseAVX = 2;
+      use_avx_limit = 2;
     } else if (UseAVX > 0 && supports_avx()) {
-      UseAVX = 1;
+      use_avx_limit = 1;
     } else {
-      UseAVX = 0;
+      use_avx_limit = 0;
     }
+  }
+  if (FLAG_IS_DEFAULT(UseAVX)) {
+    FLAG_SET_DEFAULT(UseAVX, use_avx_limit);
+  } else if (UseAVX > use_avx_limit) {
+    warning("UseAVX=%d is not supported on this CPU, setting it to UseAVX=%d", (int) UseAVX, use_avx_limit);
+    FLAG_SET_DEFAULT(UseAVX, use_avx_limit);
   } else if (UseAVX < 0) {
-    UseAVX = 0;
+    warning("UseAVX=%d is not valid, setting it to UseAVX=0", (int) UseAVX);
+    FLAG_SET_DEFAULT(UseAVX, 0);
   }
 
   if (UseAVX < 3) {
@@ -710,16 +718,29 @@ void VM_Version::get_processor_features() {
   // UseSSE is set to the smaller of what hardware supports and what
   // the command line requires.  I.e., you cannot set UseSSE to 2 on
   // older Pentiums which do not support it.
-  if (UseSSE > 4) UseSSE=4;
-  if (UseSSE < 0) UseSSE=0;
-  if (!supports_sse4_1()) // Drop to 3 if no SSE4 support
-    UseSSE = MIN2((intx)3,UseSSE);
-  if (!supports_sse3()) // Drop to 2 if no SSE3 support
-    UseSSE = MIN2((intx)2,UseSSE);
-  if (!supports_sse2()) // Drop to 1 if no SSE2 support
-    UseSSE = MIN2((intx)1,UseSSE);
-  if (!supports_sse ()) // Drop to 0 if no SSE  support
-    UseSSE = 0;
+  int use_sse_limit = 0;
+  if (UseSSE > 0) {
+    if (UseSSE > 3 && supports_sse4_1()) {
+      use_sse_limit = 4;
+    } else if (UseSSE > 2 && supports_sse3()) {
+      use_sse_limit = 3;
+    } else if (UseSSE > 1 && supports_sse2()) {
+      use_sse_limit = 2;
+    } else if (UseSSE > 0 && supports_sse()) {
+      use_sse_limit = 1;
+    } else {
+      use_sse_limit = 0;
+    }
+  }
+  if (FLAG_IS_DEFAULT(UseSSE)) {
+    FLAG_SET_DEFAULT(UseSSE, use_sse_limit);
+  } else if (UseSSE > use_sse_limit) {
+    warning("UseSSE=%d is not supported on this CPU, setting it to UseSSE=%d", (int) UseSSE, use_sse_limit);
+    FLAG_SET_DEFAULT(UseSSE, use_sse_limit);
+  } else if (UseSSE < 0) {
+    warning("UseSSE=%d is not valid, setting it to UseSSE=0", (int) UseSSE);
+    FLAG_SET_DEFAULT(UseSSE, 0);
+  }
 
   // Use AES instructions if available.
   if (supports_aes()) {
diff --git a/src/hotspot/cpu/x86/x86.ad b/src/hotspot/cpu/x86/x86.ad
index afaa2da23c7..124e95a4dcd 100644
--- a/src/hotspot/cpu/x86/x86.ad
+++ b/src/hotspot/cpu/x86/x86.ad
@@ -1252,6 +1252,7 @@ const bool Matcher::match_rule_supported(int opcode) {
         ret_value = false;
       break;
     case Op_SqrtVD:
+    case Op_SqrtVF:
       if (UseAVX < 1) // enabled for AVX only
         ret_value = false;
       break;
@@ -2580,7 +2581,7 @@ instruct negD_reg_reg(regD dst, regD src) %{
 
 instruct sqrtF_reg(regF dst, regF src) %{
   predicate(UseSSE>=1);
-  match(Set dst (ConvD2F (SqrtD (ConvF2D src))));
+  match(Set dst (SqrtF src));
 
   format %{ "sqrtss  $dst, $src" %}
   ins_cost(150);
@@ -2592,7 +2593,7 @@ instruct sqrtF_reg(regF dst, regF src) %{
 
 instruct sqrtF_mem(regF dst, memory src) %{
   predicate(UseSSE>=1);
-  match(Set dst (ConvD2F (SqrtD (ConvF2D (LoadF src)))));
+  match(Set dst (SqrtF (LoadF src)));
 
   format %{ "sqrtss  $dst, $src" %}
   ins_cost(150);
@@ -2604,7 +2605,8 @@ instruct sqrtF_mem(regF dst, memory src) %{
 
 instruct sqrtF_imm(regF dst, immF con) %{
   predicate(UseSSE>=1);
-  match(Set dst (ConvD2F (SqrtD (ConvF2D con))));
+  match(Set dst (SqrtF con));
+
   format %{ "sqrtss  $dst, [$constantaddress]\t# load from constant table: float=$con" %}
   ins_cost(150);
   ins_encode %{
@@ -8388,7 +8390,7 @@ instruct vshiftcnt(vecS dst, rRegI cnt) %{
 
 // --------------------------------- Sqrt --------------------------------------
 
-// Floating point vector sqrt - double precision only
+// Floating point vector sqrt
 instruct vsqrt2D_reg(vecX dst, vecX src) %{
   predicate(UseAVX > 0 && n->as_Vector()->length() == 2);
   match(Set dst (SqrtVD src));
@@ -8455,6 +8457,94 @@ instruct vsqrt8D_mem(vecZ dst, memory mem) %{
   ins_pipe( pipe_slow );
 %}
 
+instruct vsqrt2F_reg(vecD dst, vecD src) %{
+  predicate(UseAVX > 0 && n->as_Vector()->length() == 2);
+  match(Set dst (SqrtVF src));
+  format %{ "vsqrtps  $dst,$src\t! sqrt packed2F" %}
+  ins_encode %{
+    int vector_len = 0;
+    __ vsqrtps($dst$$XMMRegister, $src$$XMMRegister, vector_len);
+  %}
+  ins_pipe( pipe_slow );
+%}
+
+instruct vsqrt2F_mem(vecD dst, memory mem) %{
+  predicate(UseAVX > 0 && n->as_Vector()->length() == 2);
+  match(Set dst (SqrtVF (LoadVector mem)));
+  format %{ "vsqrtps  $dst,$mem\t! sqrt packed2F" %}
+  ins_encode %{
+    int vector_len = 0;
+    __ vsqrtps($dst$$XMMRegister, $mem$$Address, vector_len);
+  %}
+  ins_pipe( pipe_slow );
+%}
+
+instruct vsqrt4F_reg(vecX dst, vecX src) %{
+  predicate(UseAVX > 0 && n->as_Vector()->length() == 4);
+  match(Set dst (SqrtVF src));
+  format %{ "vsqrtps  $dst,$src\t! sqrt packed4F" %}
+  ins_encode %{
+    int vector_len = 0;
+    __ vsqrtps($dst$$XMMRegister, $src$$XMMRegister, vector_len);
+  %}
+  ins_pipe( pipe_slow );
+%}
+
+instruct vsqrt4F_mem(vecX dst, memory mem) %{
+  predicate(UseAVX > 0 && n->as_Vector()->length() == 4);
+  match(Set dst (SqrtVF (LoadVector mem)));
+  format %{ "vsqrtps  $dst,$mem\t! sqrt packed4F" %}
+  ins_encode %{
+    int vector_len = 0;
+    __ vsqrtps($dst$$XMMRegister, $mem$$Address, vector_len);
+  %}
+  ins_pipe( pipe_slow );
+%}
+
+instruct vsqrt8F_reg(vecY dst, vecY src) %{
+  predicate(UseAVX > 0 && n->as_Vector()->length() == 8);
+  match(Set dst (SqrtVF src));
+  format %{ "vsqrtps  $dst,$src\t! sqrt packed8F" %}
+  ins_encode %{
+    int vector_len = 1;
+    __ vsqrtps($dst$$XMMRegister, $src$$XMMRegister, vector_len);
+  %}
+  ins_pipe( pipe_slow );
+%}
+
+instruct vsqrt8F_mem(vecY dst, memory mem) %{
+  predicate(UseAVX > 0 && n->as_Vector()->length() == 8);
+  match(Set dst (SqrtVF (LoadVector mem)));
+  format %{ "vsqrtps  $dst,$mem\t! sqrt packed8F" %}
+  ins_encode %{
+    int vector_len = 1;
+    __ vsqrtps($dst$$XMMRegister, $mem$$Address, vector_len);
+  %}
+  ins_pipe( pipe_slow );
+%}
+
+instruct vsqrt16F_reg(vecZ dst, vecZ src) %{
+  predicate(UseAVX > 2 && n->as_Vector()->length() == 16);
+  match(Set dst (SqrtVF src));
+  format %{ "vsqrtps  $dst,$src\t! sqrt packed16F" %}
+  ins_encode %{
+    int vector_len = 2;
+    __ vsqrtps($dst$$XMMRegister, $src$$XMMRegister, vector_len);
+  %}
+  ins_pipe( pipe_slow );
+%}
+
+instruct vsqrt16F_mem(vecZ dst, memory mem) %{
+  predicate(UseAVX > 2 && n->as_Vector()->length() == 16);
+  match(Set dst (SqrtVF (LoadVector mem)));
+  format %{ "vsqrtps  $dst,$mem\t! sqrt packed16F" %}
+  ins_encode %{
+    int vector_len = 2;
+    __ vsqrtps($dst$$XMMRegister, $mem$$Address, vector_len);
+  %}
+  ins_pipe( pipe_slow );
+%}
+
 // ------------------------------ LeftShift -----------------------------------
 
 // Shorts/Chars vector left shift
diff --git a/src/hotspot/os/aix/osThread_aix.cpp b/src/hotspot/os/aix/osThread_aix.cpp
index 0b13454ffef..6303dc27eb8 100644
--- a/src/hotspot/os/aix/osThread_aix.cpp
+++ b/src/hotspot/os/aix/osThread_aix.cpp
@@ -25,6 +25,7 @@
 
 // no precompiled headers
 
+#include "memory/allocation.inline.hpp"
 #include "runtime/handles.inline.hpp"
 #include "runtime/mutexLocker.hpp"
 #include "runtime/os.hpp"
diff --git a/src/hotspot/os/aix/os_aix.cpp b/src/hotspot/os/aix/os_aix.cpp
index df5959b26d0..0325824e021 100644
--- a/src/hotspot/os/aix/os_aix.cpp
+++ b/src/hotspot/os/aix/os_aix.cpp
@@ -2490,6 +2490,22 @@ bool os::can_execute_large_page_memory() {
   return false;
 }
 
+char* os::pd_attempt_reserve_memory_at(size_t bytes, char* requested_addr, int file_desc) {
+  assert(file_desc >= 0, "file_desc is not valid");
+  char* result = NULL;
+
+  // Always round to os::vm_page_size(), which may be larger than 4K.
+  bytes = align_up(bytes, os::vm_page_size());
+  result = reserve_mmaped_memory(bytes, requested_addr, 0);
+
+  if (result != NULL) {
+    if (replace_existing_mapping_with_file_mapping(result, bytes, file_desc) == NULL) {
+      vm_exit_during_initialization(err_msg("Error in mapping Java heap at the given filesystem directory"));
+    }
+  }
+  return result;
+}
+
 // Reserve memory at an arbitrary address, only if that area is
 // available (and not reserved for something else).
 char* os::pd_attempt_reserve_memory_at(size_t bytes, char* requested_addr) {
diff --git a/src/hotspot/os/bsd/osThread_bsd.cpp b/src/hotspot/os/bsd/osThread_bsd.cpp
index de1383be848..c614f3825e3 100644
--- a/src/hotspot/os/bsd/osThread_bsd.cpp
+++ b/src/hotspot/os/bsd/osThread_bsd.cpp
@@ -23,6 +23,7 @@
  */
 
 // no precompiled headers
+#include "memory/allocation.inline.hpp"
 #include "runtime/mutexLocker.hpp"
 #include "runtime/osThread.hpp"
 
diff --git a/src/hotspot/os/bsd/os_bsd.cpp b/src/hotspot/os/bsd/os_bsd.cpp
index 6ebcb627fdc..64afc14318c 100644
--- a/src/hotspot/os/bsd/os_bsd.cpp
+++ b/src/hotspot/os/bsd/os_bsd.cpp
@@ -2350,6 +2350,17 @@ bool os::can_execute_large_page_memory() {
   return UseHugeTLBFS;
 }
 
+char* os::pd_attempt_reserve_memory_at(size_t bytes, char* requested_addr, int file_desc) {
+  assert(file_desc >= 0, "file_desc is not valid");
+  char* result = pd_attempt_reserve_memory_at(bytes, requested_addr);
+  if (result != NULL) {
+    if (replace_existing_mapping_with_file_mapping(result, bytes, file_desc) == NULL) {
+      vm_exit_during_initialization(err_msg("Error in mapping Java heap at the given filesystem directory"));
+    }
+  }
+  return result;
+}
+
 // Reserve memory at an arbitrary address, only if that area is
 // available (and not reserved for something else).
 
diff --git a/src/hotspot/os/linux/osContainer_linux.cpp b/src/hotspot/os/linux/osContainer_linux.cpp
index 03fd695cbc4..dacd53b27dc 100644
--- a/src/hotspot/os/linux/osContainer_linux.cpp
+++ b/src/hotspot/os/linux/osContainer_linux.cpp
@@ -323,7 +323,12 @@ void OSContainer::init() {
     }
   }
 
-  if (mntinfo != NULL) fclose(mntinfo);
+  fclose(mntinfo);
+
+  if (memory == NULL || cpuset == NULL || cpu == NULL || cpuacct == NULL) {
+    log_debug(os, container)("Required cgroup subsystems not found");
+    return;
+  }
 
   /*
    * Read /proc/self/cgroup and map host mount point to
@@ -383,12 +388,7 @@ void OSContainer::init() {
     }
   }
 
-  if (cgroup != NULL) fclose(cgroup);
-
-  if (memory == NULL || cpuset == NULL || cpu == NULL) {
-    log_debug(os, container)("Required cgroup subsystems not found");
-    return;
-  }
+  fclose(cgroup);
 
   // We need to update the amount of physical memory now that
   // command line arguments have been processed.
diff --git a/src/hotspot/os/linux/osThread_linux.cpp b/src/hotspot/os/linux/osThread_linux.cpp
index 381e7b0e7ba..6f7e074a522 100644
--- a/src/hotspot/os/linux/osThread_linux.cpp
+++ b/src/hotspot/os/linux/osThread_linux.cpp
@@ -23,6 +23,7 @@
  */
 
 // no precompiled headers
+#include "memory/allocation.inline.hpp"
 #include "runtime/mutex.hpp"
 #include "runtime/osThread.hpp"
 
diff --git a/src/hotspot/os/linux/os_linux.cpp b/src/hotspot/os/linux/os_linux.cpp
index 548e7823f7f..a91fac1b9fe 100644
--- a/src/hotspot/os/linux/os_linux.cpp
+++ b/src/hotspot/os/linux/os_linux.cpp
@@ -59,6 +59,7 @@
 #include "runtime/stubRoutines.hpp"
 #include "runtime/thread.inline.hpp"
 #include "runtime/threadCritical.hpp"
+#include "runtime/threadSMR.hpp"
 #include "runtime/timer.hpp"
 #include "semaphore_posix.hpp"
 #include "services/attachListener.hpp"
@@ -129,6 +130,7 @@
 #define ALL_64_BITS CONST64(0xFFFFFFFFFFFFFFFF)
 
 #define LARGEPAGES_BIT (1 << 6)
+#define DAX_SHARED_BIT (1 << 8)
 ////////////////////////////////////////////////////////////////////////////////
 // global variables
 julong os::Linux::_physical_memory = 0;
@@ -1646,7 +1648,10 @@ void * os::dll_load(const char *filename, char *ebuf, int ebuflen) {
         //
         // Dynamic loader will make all stacks executable after
         // this function returns, and will not do that again.
-        assert(Threads::first() == NULL, "no Java threads should exist yet.");
+#ifdef ASSERT
+        ThreadsListHandle tlh;
+        assert(tlh.length() == 0, "no Java threads should exist yet.");
+#endif
       } else {
         warning("You have loaded library %s which might have disabled stack guard. "
                 "The VM will try to fix the stack guard now.\n"
@@ -1874,16 +1879,13 @@ void * os::Linux::dll_load_in_vmthread(const char *filename, char *ebuf,
   // may have been queued at the same time.
 
   if (!_stack_is_executable) {
-    JavaThread *jt = Threads::first();
-
-    while (jt) {
+    for (JavaThreadIteratorWithHandle jtiwh; JavaThread *jt = jtiwh.next(); ) {
       if (!jt->stack_guard_zone_unused() &&     // Stack not yet fully initialized
           jt->stack_guards_enabled()) {         // No pending stack overflow exceptions
         if (!os::guard_memory((char *)jt->stack_end(), jt->stack_guard_zone_size())) {
           warning("Attempt to reguard stack yellow zone failed.");
         }
       }
-      jt = jt->next();
     }
   }
 
@@ -3369,10 +3371,13 @@ bool os::Linux::hugetlbfs_sanity_check(bool warn, size_t page_size) {
 //           effective only if the bit 2 is cleared)
 // - (bit 5) hugetlb private memory
 // - (bit 6) hugetlb shared memory
+// - (bit 7) dax private memory
+// - (bit 8) dax shared memory
 //
-static void set_coredump_filter(void) {
+static void set_coredump_filter(bool largepages, bool dax_shared) {
   FILE *f;
   long cdm;
+  bool filter_changed = false;
 
   if ((f = fopen("/proc/self/coredump_filter", "r+")) == NULL) {
     return;
@@ -3385,8 +3390,15 @@ static void set_coredump_filter(void) {
 
   rewind(f);
 
-  if ((cdm & LARGEPAGES_BIT) == 0) {
+  if (largepages && (cdm & LARGEPAGES_BIT) == 0) {
     cdm |= LARGEPAGES_BIT;
+    filter_changed = true;
+  }
+  if (dax_shared && (cdm & DAX_SHARED_BIT) == 0) {
+    cdm |= DAX_SHARED_BIT;
+    filter_changed = true;
+  }
+  if (filter_changed) {
     fprintf(f, "%#lx", cdm);
   }
 
@@ -3525,7 +3537,7 @@ void os::large_page_init() {
   size_t large_page_size = Linux::setup_large_page_size();
   UseLargePages          = Linux::setup_large_page_type(large_page_size);
 
-  set_coredump_filter();
+  set_coredump_filter(true /*largepages*/, false /*dax_shared*/);
 }
 
 #ifndef SHM_HUGETLB
@@ -3896,6 +3908,17 @@ bool os::can_execute_large_page_memory() {
   return UseTransparentHugePages || UseHugeTLBFS;
 }
 
+char* os::pd_attempt_reserve_memory_at(size_t bytes, char* requested_addr, int file_desc) {
+  assert(file_desc >= 0, "file_desc is not valid");
+  char* result = pd_attempt_reserve_memory_at(bytes, requested_addr);
+  if (result != NULL) {
+    if (replace_existing_mapping_with_file_mapping(result, bytes, file_desc) == NULL) {
+      vm_exit_during_initialization(err_msg("Error in mapping Java heap at the given filesystem directory"));
+    }
+  }
+  return result;
+}
+
 // Reserve memory at an arbitrary address, only if that area is
 // available (and not reserved for something else).
 
@@ -4947,25 +4970,20 @@ jint os::init_2(void) {
         UseNUMA = false;
       }
     }
-    // With SHM and HugeTLBFS large pages we cannot uncommit a page, so there's no way
-    // we can make the adaptive lgrp chunk resizing work. If the user specified
-    // both UseNUMA and UseLargePages (or UseSHM/UseHugeTLBFS) on the command line - warn and
-    // disable adaptive resizing.
-    if (UseNUMA && UseLargePages && !can_commit_large_page_memory()) {
-      if (FLAG_IS_DEFAULT(UseNUMA)) {
-        UseNUMA = false;
-      } else {
-        if (FLAG_IS_DEFAULT(UseLargePages) &&
-            FLAG_IS_DEFAULT(UseSHM) &&
-            FLAG_IS_DEFAULT(UseHugeTLBFS)) {
-          UseLargePages = false;
-        } else if (UseAdaptiveSizePolicy || UseAdaptiveNUMAChunkSizing) {
-          warning("UseNUMA is not fully compatible with SHM/HugeTLBFS large pages, disabling adaptive resizing (-XX:-UseAdaptiveSizePolicy -XX:-UseAdaptiveNUMAChunkSizing)");
-          UseAdaptiveSizePolicy = false;
-          UseAdaptiveNUMAChunkSizing = false;
-        }
+
+    if (UseParallelGC && UseNUMA && UseLargePages && !can_commit_large_page_memory()) {
+      // With SHM and HugeTLBFS large pages we cannot uncommit a page, so there's no way
+      // we can make the adaptive lgrp chunk resizing work. If the user specified both
+      // UseNUMA and UseLargePages (or UseSHM/UseHugeTLBFS) on the command line - warn
+      // and disable adaptive resizing.
+      if (UseAdaptiveSizePolicy || UseAdaptiveNUMAChunkSizing) {
+        warning("UseNUMA is not fully compatible with SHM/HugeTLBFS large pages, "
+                "disabling adaptive resizing (-XX:-UseAdaptiveSizePolicy -XX:-UseAdaptiveNUMAChunkSizing)");
+        UseAdaptiveSizePolicy = false;
+        UseAdaptiveNUMAChunkSizing = false;
       }
     }
+
     if (!UseNUMA && ForceNUMA) {
       UseNUMA = true;
     }
@@ -5012,6 +5030,9 @@ jint os::init_2(void) {
   // initialize thread priority policy
   prio_init();
 
+  if (!FLAG_IS_DEFAULT(AllocateHeapAt)) {
+    set_coredump_filter(false /*largepages*/, true /*dax_shared*/);
+  }
   return JNI_OK;
 }
 
diff --git a/src/hotspot/os/posix/os_posix.cpp b/src/hotspot/os/posix/os_posix.cpp
index fed874d30eb..8c7cf351ef0 100644
--- a/src/hotspot/os/posix/os_posix.cpp
+++ b/src/hotspot/os/posix/os_posix.cpp
@@ -40,6 +40,7 @@
 #include <pthread.h>
 #include <semaphore.h>
 #include <signal.h>
+#include <sys/mman.h>
 #include <sys/resource.h>
 #include <sys/utsname.h>
 #include <time.h>
@@ -52,6 +53,20 @@
 #endif
 #define IS_VALID_PID(p) (p > 0 && p < MAX_PID)
 
+#ifndef MAP_ANONYMOUS
+  #define MAP_ANONYMOUS MAP_ANON
+#endif
+
+#define check_with_errno(check_type, cond, msg)                             \
+  do {                                                                      \
+    int err = errno;                                                        \
+    check_type(cond, "%s; error='%s' (errno=%s)", msg, os::strerror(err),   \
+               os::errno_name(err));                                        \
+} while (false)
+
+#define assert_with_errno(cond, msg)    check_with_errno(assert, cond, msg)
+#define guarantee_with_errno(cond, msg) check_with_errno(guarantee, cond, msg)
+
 // Check core dump limit and report possible place where core can be found
 void os::check_dump_limit(char* buffer, size_t bufferSize) {
   if (!FLAG_IS_DEFAULT(CreateCoredumpOnCrash) && !CreateCoredumpOnCrash) {
@@ -145,10 +160,124 @@ void os::wait_for_keypress_at_exit(void) {
   return;
 }
 
+int os::create_file_for_heap(const char* dir) {
+
+  const char name_template[] = "/jvmheap.XXXXXX";
+
+  char *fullname = (char*)os::malloc((strlen(dir) + strlen(name_template) + 1), mtInternal);
+  if (fullname == NULL) {
+    vm_exit_during_initialization(err_msg("Malloc failed during creation of backing file for heap (%s)", os::strerror(errno)));
+    return -1;
+  }
+  (void)strncpy(fullname, dir, strlen(dir)+1);
+  (void)strncat(fullname, name_template, strlen(name_template));
+
+  os::native_path(fullname);
+
+  sigset_t set, oldset;
+  int ret = sigfillset(&set);
+  assert_with_errno(ret == 0, "sigfillset returned error");
+
+  // set the file creation mask.
+  mode_t file_mode = S_IRUSR | S_IWUSR;
+
+  // create a new file.
+  int fd = mkstemp(fullname);
+
+  if (fd < 0) {
+    warning("Could not create file for heap with template %s", fullname);
+    os::free(fullname);
+    return -1;
+  }
+
+  // delete the name from the filesystem. When 'fd' is closed, the file (and space) will be deleted.
+  ret = unlink(fullname);
+  assert_with_errno(ret == 0, "unlink returned error");
+
+  os::free(fullname);
+  return fd;
+}
+
+static char* reserve_mmapped_memory(size_t bytes, char* requested_addr) {
+  char * addr;
+  int flags = MAP_PRIVATE NOT_AIX( | MAP_NORESERVE ) | MAP_ANONYMOUS;
+  if (requested_addr != NULL) {
+    assert((uintptr_t)requested_addr % os::vm_page_size() == 0, "Requested address should be aligned to OS page size");
+    flags |= MAP_FIXED;
+  }
+
+  // Map reserved/uncommitted pages PROT_NONE so we fail early if we
+  // touch an uncommitted page. Otherwise, the read/write might
+  // succeed if we have enough swap space to back the physical page.
+  addr = (char*)::mmap(requested_addr, bytes, PROT_NONE,
+                       flags, -1, 0);
+
+  if (addr != MAP_FAILED) {
+    MemTracker::record_virtual_memory_reserve((address)addr, bytes, CALLER_PC);
+    return addr;
+  }
+  return NULL;
+}
+
+static int util_posix_fallocate(int fd, off_t offset, off_t len) {
+#ifdef __APPLE__
+  fstore_t store = { F_ALLOCATECONTIG, F_PEOFPOSMODE, 0, len };
+  // First we try to get a continuous chunk of disk space
+  int ret = fcntl(fd, F_PREALLOCATE, &store);
+  if (ret == -1) {
+    // Maybe we are too fragmented, try to allocate non-continuous range
+    store.fst_flags = F_ALLOCATEALL;
+    ret = fcntl(fd, F_PREALLOCATE, &store);
+  }
+  if(ret != -1) {
+    return ftruncate(fd, len);
+  }
+  return -1;
+#else
+  return posix_fallocate(fd, offset, len);
+#endif
+}
+
+// Map the given address range to the provided file descriptor.
+char* os::map_memory_to_file(char* base, size_t size, int fd) {
+  assert(fd != -1, "File descriptor is not valid");
+
+  // allocate space for the file
+  if (util_posix_fallocate(fd, 0, (off_t)size) != 0) {
+    vm_exit_during_initialization(err_msg("Error in mapping Java heap at the given filesystem directory."));
+    return NULL;
+  }
+
+  int prot = PROT_READ | PROT_WRITE;
+  int flags = MAP_SHARED;
+  if (base != NULL) {
+    flags |= MAP_FIXED;
+  }
+  char* addr = (char*)mmap(base, size, prot, flags, fd, 0);
+
+  if (addr == MAP_FAILED) {
+    return NULL;
+  }
+  if (base != NULL && addr != base) {
+    if (!os::release_memory(addr, size)) {
+      warning("Could not release memory on unsuccessful file mapping");
+     }
+    return NULL;
+  }
+  return addr;
+}
+
+char* os::replace_existing_mapping_with_file_mapping(char* base, size_t size, int fd) {
+  assert(fd != -1, "File descriptor is not valid");
+  assert(base != NULL, "Base cannot be NULL");
+
+  return map_memory_to_file(base, size, fd);
+}
+
 // Multiple threads can race in this code, and can remap over each other with MAP_FIXED,
 // so on posix, unmap the section at the start and at the end of the chunk that we mapped
 // rather than unmapping and remapping the whole chunk to get requested alignment.
-char* os::reserve_memory_aligned(size_t size, size_t alignment) {
+char* os::reserve_memory_aligned(size_t size, size_t alignment, int file_desc) {
   assert((alignment & (os::vm_allocation_granularity() - 1)) == 0,
       "Alignment must be a multiple of allocation granularity (page size)");
   assert((size & (alignment -1)) == 0, "size must be 'alignment' aligned");
@@ -156,7 +285,20 @@ char* os::reserve_memory_aligned(size_t size, size_t alignment) {
   size_t extra_size = size + alignment;
   assert(extra_size >= size, "overflow, size is too large to allow alignment");
 
-  char* extra_base = os::reserve_memory(extra_size, NULL, alignment);
+  char* extra_base;
+  if (file_desc != -1) {
+    // For file mapping, we do not call os:reserve_memory(extra_size, NULL, alignment, file_desc) because
+    // we need to deal with shrinking of the file space later when we release extra memory after alignment.
+    // We also cannot called os:reserve_memory() with file_desc set to -1 because on aix we might get SHM memory.
+    // So here to call a helper function while reserve memory for us. After we have a aligned base,
+    // we will replace anonymous mapping with file mapping.
+    extra_base = reserve_mmapped_memory(extra_size, NULL);
+    if (extra_base != NULL) {
+      MemTracker::record_virtual_memory_reserve((address)extra_base, extra_size, CALLER_PC);
+    }
+  } else {
+    extra_base = os::reserve_memory(extra_size, NULL, alignment);
+  }
 
   if (extra_base == NULL) {
     return NULL;
@@ -183,6 +325,13 @@ char* os::reserve_memory_aligned(size_t size, size_t alignment) {
       os::release_memory(extra_base + begin_offset + size, end_offset);
   }
 
+  if (file_desc != -1) {
+    // After we have an aligned address, we can replace anonymous mapping with file mapping
+    if (replace_existing_mapping_with_file_mapping(aligned_base, size, file_desc) == NULL) {
+      vm_exit_during_initialization(err_msg("Error in mapping Java heap at the given filesystem directory"));
+    }
+    MemTracker::record_virtual_memory_commit((address)aligned_base, size, CALLER_PC);
+  }
   return aligned_base;
 }
 
@@ -478,8 +627,7 @@ int os::sleep(Thread* thread, jlong millis, bool interruptible) {
 // interrupt support
 
 void os::interrupt(Thread* thread) {
-  assert(Thread::current() == thread || Threads_lock->owned_by_self(),
-    "possibility of dangling Thread pointer");
+  debug_only(Thread::check_for_dangling_thread_pointer(thread);)
 
   OSThread* osthread = thread->osthread();
 
@@ -499,12 +647,10 @@ void os::interrupt(Thread* thread) {
 
   ParkEvent * ev = thread->_ParkEvent ;
   if (ev != NULL) ev->unpark() ;
-
 }
 
 bool os::is_interrupted(Thread* thread, bool clear_interrupted) {
-  assert(Thread::current() == thread || Threads_lock->owned_by_self(),
-    "possibility of dangling Thread pointer");
+  debug_only(Thread::check_for_dangling_thread_pointer(thread);)
 
   OSThread* osthread = thread->osthread();
 
@@ -1351,16 +1497,6 @@ void os::ThreadCrashProtection::check_crash_protection(int sig,
   }
 }
 
-#define check_with_errno(check_type, cond, msg)                             \
-  do {                                                                      \
-    int err = errno;                                                        \
-    check_type(cond, "%s; error='%s' (errno=%s)", msg, os::strerror(err),   \
-               os::errno_name(err));                                        \
-} while (false)
-
-#define assert_with_errno(cond, msg)    check_with_errno(assert, cond, msg)
-#define guarantee_with_errno(cond, msg) check_with_errno(guarantee, cond, msg)
-
 // POSIX unamed semaphores are not supported on OS X.
 #ifndef __APPLE__
 
diff --git a/src/hotspot/os/solaris/os_solaris.cpp b/src/hotspot/os/solaris/os_solaris.cpp
index f136cec8275..ef7c1deaea4 100644
--- a/src/hotspot/os/solaris/os_solaris.cpp
+++ b/src/hotspot/os/solaris/os_solaris.cpp
@@ -2585,6 +2585,17 @@ char* os::pd_reserve_memory(size_t bytes, char* requested_addr,
   return addr;
 }
 
+char* os::pd_attempt_reserve_memory_at(size_t bytes, char* requested_addr, int file_desc) {
+  assert(file_desc >= 0, "file_desc is not valid");
+  char* result = pd_attempt_reserve_memory_at(bytes, requested_addr);
+  if (result != NULL) {
+    if (replace_existing_mapping_with_file_mapping(result, bytes, file_desc) == NULL) {
+      vm_exit_during_initialization(err_msg("Error in mapping Java heap at the given filesystem directory"));
+    }
+  }
+  return result;
+}
+
 // Reserve memory at an arbitrary address, only if that area is
 // available (and not reserved for something else).
 
diff --git a/src/hotspot/os/windows/os_windows.cpp b/src/hotspot/os/windows/os_windows.cpp
index c52cddd391c..656a119d4d4 100644
--- a/src/hotspot/os/windows/os_windows.cpp
+++ b/src/hotspot/os/windows/os_windows.cpp
@@ -2904,6 +2904,75 @@ void os::large_page_init() {
   UseLargePages = success;
 }
 
+int os::create_file_for_heap(const char* dir) {
+
+  const char name_template[] = "/jvmheap.XXXXXX";
+  char *fullname = (char*)os::malloc((strlen(dir) + strlen(name_template) + 1), mtInternal);
+  if (fullname == NULL) {
+    vm_exit_during_initialization(err_msg("Malloc failed during creation of backing file for heap (%s)", os::strerror(errno)));
+    return -1;
+  }
+
+  (void)strncpy(fullname, dir, strlen(dir)+1);
+  (void)strncat(fullname, name_template, strlen(name_template));
+
+  os::native_path(fullname);
+
+  char *path = _mktemp(fullname);
+  if (path == NULL) {
+    warning("_mktemp could not create file name from template %s (%s)", fullname, os::strerror(errno));
+    os::free(fullname);
+    return -1;
+  }
+
+  int fd = _open(path, O_RDWR | O_CREAT | O_TEMPORARY | O_EXCL, S_IWRITE | S_IREAD);
+
+  os::free(fullname);
+  if (fd < 0) {
+    warning("Problem opening file for heap (%s)", os::strerror(errno));
+    return -1;
+  }
+  return fd;
+}
+
+// If 'base' is not NULL, function will return NULL if it cannot get 'base'
+char* os::map_memory_to_file(char* base, size_t size, int fd) {
+  assert(fd != -1, "File descriptor is not valid");
+
+  HANDLE fh = (HANDLE)_get_osfhandle(fd);
+#ifdef _LP64
+  HANDLE fileMapping = CreateFileMapping(fh, NULL, PAGE_READWRITE,
+    (DWORD)(size >> 32), (DWORD)(size & 0xFFFFFFFF), NULL);
+#else
+  HANDLE fileMapping = CreateFileMapping(fh, NULL, PAGE_READWRITE,
+    0, (DWORD)size, NULL);
+#endif
+  if (fileMapping == NULL) {
+    if (GetLastError() == ERROR_DISK_FULL) {
+      vm_exit_during_initialization(err_msg("Could not allocate sufficient disk space for Java heap"));
+    }
+    else {
+      vm_exit_during_initialization(err_msg("Error in mapping Java heap at the given filesystem directory"));
+    }
+
+    return NULL;
+  }
+
+  LPVOID addr = MapViewOfFileEx(fileMapping, FILE_MAP_WRITE, 0, 0, size, base);
+
+  CloseHandle(fileMapping);
+
+  return (char*)addr;
+}
+
+char* os::replace_existing_mapping_with_file_mapping(char* base, size_t size, int fd) {
+  assert(fd != -1, "File descriptor is not valid");
+  assert(base != NULL, "Base address cannot be NULL");
+
+  release_memory(base, size);
+  return map_memory_to_file(base, size, fd);
+}
+
 // On win32, one cannot release just a part of reserved memory, it's an
 // all or nothing deal.  When we split a reservation, we must break the
 // reservation into two reservations.
@@ -2923,7 +2992,7 @@ void os::pd_split_reserved_memory(char *base, size_t size, size_t split,
 // Multiple threads can race in this code but it's not possible to unmap small sections of
 // virtual space to get requested alignment, like posix-like os's.
 // Windows prevents multiple thread from remapping over each other so this loop is thread-safe.
-char* os::reserve_memory_aligned(size_t size, size_t alignment) {
+char* os::reserve_memory_aligned(size_t size, size_t alignment, int file_desc) {
   assert((alignment & (os::vm_allocation_granularity() - 1)) == 0,
          "Alignment must be a multiple of allocation granularity (page size)");
   assert((size & (alignment -1)) == 0, "size must be 'alignment' aligned");
@@ -2934,16 +3003,20 @@ char* os::reserve_memory_aligned(size_t size, size_t alignment) {
   char* aligned_base = NULL;
 
   do {
-    char* extra_base = os::reserve_memory(extra_size, NULL, alignment);
+    char* extra_base = os::reserve_memory(extra_size, NULL, alignment, file_desc);
     if (extra_base == NULL) {
       return NULL;
     }
     // Do manual alignment
     aligned_base = align_up(extra_base, alignment);
 
-    os::release_memory(extra_base, extra_size);
+    if (file_desc != -1) {
+      os::unmap_memory(extra_base, extra_size);
+    } else {
+      os::release_memory(extra_base, extra_size);
+    }
 
-    aligned_base = os::reserve_memory(size, aligned_base);
+    aligned_base = os::reserve_memory(size, aligned_base, 0, file_desc);
 
   } while (aligned_base == NULL);
 
@@ -2989,6 +3062,11 @@ char* os::pd_attempt_reserve_memory_at(size_t bytes, char* requested_addr) {
   return reserve_memory(bytes, requested_addr);
 }
 
+char* os::pd_attempt_reserve_memory_at(size_t bytes, char* requested_addr, int file_desc) {
+  assert(file_desc >= 0, "file_desc is not valid");
+  return map_memory_to_file(requested_addr, bytes, file_desc);
+}
+
 size_t os::large_page_size() {
   return _large_page_size;
 }
@@ -3490,9 +3568,7 @@ OSReturn os::get_native_priority(const Thread* const thread,
 void os::hint_no_preempt() {}
 
 void os::interrupt(Thread* thread) {
-  assert(!thread->is_Java_thread() || Thread::current() == thread ||
-         Threads_lock->owned_by_self(),
-         "possibility of dangling Thread pointer");
+  debug_only(Thread::check_for_dangling_thread_pointer(thread);)
 
   OSThread* osthread = thread->osthread();
   osthread->set_interrupted(true);
@@ -3513,8 +3589,7 @@ void os::interrupt(Thread* thread) {
 
 
 bool os::is_interrupted(Thread* thread, bool clear_interrupted) {
-  assert(!thread->is_Java_thread() || Thread::current() == thread || Threads_lock->owned_by_self(),
-         "possibility of dangling Thread pointer");
+  debug_only(Thread::check_for_dangling_thread_pointer(thread);)
 
   OSThread* osthread = thread->osthread();
   // There is no synchronization between the setting of the interrupt
diff --git a/src/hotspot/os_cpu/linux_zero/atomic_linux_zero.hpp b/src/hotspot/os_cpu/linux_zero/atomic_linux_zero.hpp
index 0713b6de460..af9eb3fb85c 100644
--- a/src/hotspot/os_cpu/linux_zero/atomic_linux_zero.hpp
+++ b/src/hotspot/os_cpu/linux_zero/atomic_linux_zero.hpp
@@ -30,74 +30,6 @@
 
 // Implementation of class atomic
 
-#ifdef M68K
-
-/*
- * __m68k_cmpxchg
- *
- * Atomically store newval in *ptr if *ptr is equal to oldval for user space.
- * Returns newval on success and oldval if no exchange happened.
- * This implementation is processor specific and works on
- * 68020 68030 68040 and 68060.
- *
- * It will not work on ColdFire, 68000 and 68010 since they lack the CAS
- * instruction.
- * Using a kernelhelper would be better for arch complete implementation.
- *
- */
-
-static inline int __m68k_cmpxchg(int oldval, int newval, volatile int *ptr) {
-  int ret;
-  __asm __volatile ("cas%.l %0,%2,%1"
-                   : "=d" (ret), "+m" (*(ptr))
-                   : "d" (newval), "0" (oldval));
-  return ret;
-}
-
-/* Perform an atomic compare and swap: if the current value of `*PTR'
-   is OLDVAL, then write NEWVAL into `*PTR'.  Return the contents of
-   `*PTR' before the operation.*/
-static inline int m68k_compare_and_swap(int newval,
-                                        volatile int *ptr,
-                                        int oldval) {
-  for (;;) {
-      int prev = *ptr;
-      if (prev != oldval)
-        return prev;
-
-      if (__m68k_cmpxchg (prev, newval, ptr) == newval)
-        // Success.
-        return prev;
-
-      // We failed even though prev == oldval.  Try again.
-    }
-}
-
-/* Atomically add an int to memory.  */
-static inline int m68k_add_and_fetch(int add_value, volatile int *ptr) {
-  for (;;) {
-      // Loop until success.
-
-      int prev = *ptr;
-
-      if (__m68k_cmpxchg (prev, prev + add_value, ptr) == prev + add_value)
-        return prev + add_value;
-    }
-}
-
-/* Atomically write VALUE into `*PTR' and returns the previous
-   contents of `*PTR'.  */
-static inline int m68k_lock_test_and_set(int newval, volatile int *ptr) {
-  for (;;) {
-      // Loop until success.
-      int prev = *ptr;
-
-      if (__m68k_cmpxchg (prev, newval, ptr) == prev)
-        return prev;
-    }
-}
-#endif // M68K
-
 #ifdef ARM
 
 /*
@@ -175,12 +107,8 @@ inline D Atomic::PlatformAdd<4>::add_and_fetch(I add_value, D volatile* dest) co
 
 #ifdef ARM
   return add_using_helper<int>(arm_add_and_fetch, add_value, dest);
-#else
-#ifdef M68K
-  return add_using_helper<int>(m68k_add_and_fetch, add_value, dest);
 #else
   return __sync_add_and_fetch(dest, add_value);
-#endif // M68K
 #endif // ARM
 }
 
@@ -200,9 +128,6 @@ inline T Atomic::PlatformXchg<4>::operator()(T exchange_value,
   STATIC_ASSERT(4 == sizeof(T));
 #ifdef ARM
   return xchg_using_helper<int>(arm_lock_test_and_set, exchange_value, dest);
-#else
-#ifdef M68K
-  return xchg_using_helper<int>(m68k_lock_test_and_set, exchange_value, dest);
 #else
   // __sync_lock_test_and_set is a bizarrely named atomic exchange
   // operation.  Note that some platforms only support this with the
@@ -215,7 +140,6 @@ inline T Atomic::PlatformXchg<4>::operator()(T exchange_value,
   // barrier.
   __sync_synchronize();
   return result;
-#endif // M68K
 #endif // ARM
 }
 
@@ -242,12 +166,8 @@ inline T Atomic::PlatformCmpxchg<4>::operator()(T exchange_value,
   STATIC_ASSERT(4 == sizeof(T));
 #ifdef ARM
   return cmpxchg_using_helper<int>(arm_compare_and_swap, exchange_value, dest, compare_value);
-#else
-#ifdef M68K
-  return cmpxchg_using_helper<int>(m68k_compare_and_swap, exchange_value, dest, compare_value);
 #else
   return __sync_val_compare_and_swap(dest, compare_value, exchange_value);
-#endif // M68K
 #endif // ARM
 }
 
diff --git a/src/hotspot/os_cpu/linux_zero/os_linux_zero.hpp b/src/hotspot/os_cpu/linux_zero/os_linux_zero.hpp
index 8e6dc325c4c..a36e4792efd 100644
--- a/src/hotspot/os_cpu/linux_zero/os_linux_zero.hpp
+++ b/src/hotspot/os_cpu/linux_zero/os_linux_zero.hpp
@@ -36,12 +36,18 @@
 
   // Atomically copy 64 bits of data
   static void atomic_copy64(const volatile void *src, volatile void *dst) {
-#if defined(PPC32)
+#if defined(PPC32) && !defined(__SPE__)
     double tmp;
     asm volatile ("lfd  %0, %2\n"
                   "stfd %0, %1\n"
                   : "=&f"(tmp), "=Q"(*(volatile double*)dst)
                   : "Q"(*(volatile double*)src));
+#elif defined(PPC32) && defined(__SPE__)
+    long tmp;
+    asm volatile ("evldd  %0, %2\n"
+                  "evstdd %0, %1\n"
+                  : "=&r"(tmp), "=Q"(*(volatile long*)dst)
+                  : "Q"(*(volatile long*)src));
 #elif defined(S390) && !defined(_LP64)
     double tmp;
     asm volatile ("ld  %0, 0(%1)\n"
diff --git a/src/hotspot/share/adlc/formssel.cpp b/src/hotspot/share/adlc/formssel.cpp
index 73229920040..fafd0ce7b27 100644
--- a/src/hotspot/share/adlc/formssel.cpp
+++ b/src/hotspot/share/adlc/formssel.cpp
@@ -4034,6 +4034,7 @@ int MatchRule::is_expensive() const {
         strcmp(opType,"ModF")==0 ||
         strcmp(opType,"ModI")==0 ||
         strcmp(opType,"SqrtD")==0 ||
+        strcmp(opType,"SqrtF")==0 ||
         strcmp(opType,"TanD")==0 ||
         strcmp(opType,"ConvD2F")==0 ||
         strcmp(opType,"ConvD2I")==0 ||
@@ -4167,7 +4168,7 @@ bool MatchRule::is_vector() const {
     "DivVF","DivVD",
     "AbsVF","AbsVD",
     "NegVF","NegVD",
-    "SqrtVD",
+    "SqrtVD","SqrtVF",
     "AndV" ,"XorV" ,"OrV",
     "AddReductionVI", "AddReductionVL",
     "AddReductionVF", "AddReductionVD",
diff --git a/src/hotspot/share/aot/aotCodeHeap.cpp b/src/hotspot/share/aot/aotCodeHeap.cpp
index 2a9b3982cd2..e22e228acf1 100644
--- a/src/hotspot/share/aot/aotCodeHeap.cpp
+++ b/src/hotspot/share/aot/aotCodeHeap.cpp
@@ -167,6 +167,7 @@ void AOTLib::verify_config() {
   verify_flag(_config->_compactFields, CompactFields, "CompactFields");
   verify_flag(_config->_enableContended, EnableContended, "EnableContended");
   verify_flag(_config->_restrictContended, RestrictContended, "RestrictContended");
+  verify_flag(_config->_threadLocalHandshakes, ThreadLocalHandshakes, "ThreadLocalHandshakes");
 
   if (!TieredCompilation && _config->_tieredAOT) {
     handle_config_error("Shared file %s error: Expected to run with tiered compilation on", _name);
diff --git a/src/hotspot/share/aot/aotCodeHeap.hpp b/src/hotspot/share/aot/aotCodeHeap.hpp
index 7bfd5c67531..ae6e9ff2556 100644
--- a/src/hotspot/share/aot/aotCodeHeap.hpp
+++ b/src/hotspot/share/aot/aotCodeHeap.hpp
@@ -92,7 +92,7 @@ typedef struct {
 } AOTHeader;
 
 typedef struct {
-  enum { CONFIG_SIZE = 7 * jintSize + 11 };
+  enum { CONFIG_SIZE = 7 * jintSize + 12 };
   // 7 int values
   int _config_size;
   int _narrowOopShift;
@@ -101,7 +101,7 @@ typedef struct {
   int _fieldsAllocationStyle;
   int _objectAlignment;
   int _codeSegmentSize;
-  // byte[11] array map to boolean values here
+  // byte[12] array map to boolean values here
   bool _debug_VM;
   bool _useCompressedOops;
   bool _useCompressedClassPointers;
@@ -113,6 +113,7 @@ typedef struct {
   bool _enableContended;
   bool _restrictContended;
   bool _omitAssertions;
+  bool _threadLocalHandshakes;
 } AOTConfiguration;
 
 class AOTLib : public CHeapObj<mtCode> {
diff --git a/src/hotspot/share/aot/aotLoader.cpp b/src/hotspot/share/aot/aotLoader.cpp
index c91c04b9b1b..9b77338e43d 100644
--- a/src/hotspot/share/aot/aotLoader.cpp
+++ b/src/hotspot/share/aot/aotLoader.cpp
@@ -146,15 +146,6 @@ void AOTLoader::initialize() {
       return;
     }
 
-    const char* home = Arguments::get_java_home();
-    const char* file_separator = os::file_separator();
-
-    for (int i = 0; i < (int) (sizeof(modules) / sizeof(const char*)); i++) {
-      char library[JVM_MAXPATHLEN];
-      jio_snprintf(library, sizeof(library), "%s%slib%slib%s%s%s%s", home, file_separator, file_separator, modules[i], UseCompressedOops ? "-coop" : "", UseG1GC ? "" : "-nong1", os::dll_file_extension());
-      load_library(library, false);
-    }
-
     // Scan the AOTLibrary option.
     if (AOTLibrary != NULL) {
       const int len = (int)strlen(AOTLibrary);
@@ -172,6 +163,16 @@ void AOTLoader::initialize() {
         }
       }
     }
+
+    // Load well-know AOT libraries from Java installation directory.
+    const char* home = Arguments::get_java_home();
+    const char* file_separator = os::file_separator();
+
+    for (int i = 0; i < (int) (sizeof(modules) / sizeof(const char*)); i++) {
+      char library[JVM_MAXPATHLEN];
+      jio_snprintf(library, sizeof(library), "%s%slib%slib%s%s%s%s", home, file_separator, file_separator, modules[i], UseCompressedOops ? "-coop" : "", UseG1GC ? "" : "-nong1", os::dll_file_extension());
+      load_library(library, false);
+    }
   }
 }
 
@@ -239,6 +240,21 @@ void AOTLoader::set_narrow_klass_shift() {
 }
 
 void AOTLoader::load_library(const char* name, bool exit_on_error) {
+  // Skip library if a library with the same name is already loaded.
+  const int file_separator = *os::file_separator();
+  const char* start = strrchr(name, file_separator);
+  const char* new_name = (start == NULL) ? name : (start + 1);
+  FOR_ALL_AOT_LIBRARIES(lib) {
+    const char* lib_name = (*lib)->name();
+    start = strrchr(lib_name, file_separator);
+    const char* old_name = (start == NULL) ? lib_name : (start + 1);
+    if (strcmp(old_name, new_name) == 0) {
+      if (PrintAOT) {
+        warning("AOT library %s is already loaded as %s.", name, lib_name);
+      }
+      return;
+    }
+  }
   char ebuf[1024];
   void* handle = os::dll_load(name, ebuf, sizeof ebuf);
   if (handle == NULL) {
diff --git a/src/hotspot/share/c1/c1_LIR.hpp b/src/hotspot/share/c1/c1_LIR.hpp
index 757c5e79274..980cd4d07dc 100644
--- a/src/hotspot/share/c1/c1_LIR.hpp
+++ b/src/hotspot/share/c1/c1_LIR.hpp
@@ -196,8 +196,8 @@ class LIR_OprDesc: public CompilationResourceObj {
   //     data       opr-type opr-kind
   // +--------------+-------+-------+
   // [max...........|7 6 5 4|3 2 1 0]
-  //                             ^
-  //                    is_pointer bit
+  //                               ^
+  //                         is_pointer bit
   //
   // lowest bit cleared, means it is a structure pointer
   // we need  4 bits to represent types
diff --git a/src/hotspot/share/classfile/classFileParser.cpp b/src/hotspot/share/classfile/classFileParser.cpp
index 5074ba4ca83..6326ca1e76a 100644
--- a/src/hotspot/share/classfile/classFileParser.cpp
+++ b/src/hotspot/share/classfile/classFileParser.cpp
@@ -86,7 +86,7 @@
 
 #define JAVA_CLASSFILE_MAGIC              0xCAFEBABE
 #define JAVA_MIN_SUPPORTED_VERSION        45
-#define JAVA_MAX_SUPPORTED_VERSION        53
+#define JAVA_MAX_SUPPORTED_VERSION        54
 #define JAVA_MAX_SUPPORTED_MINOR_VERSION  0
 
 // Used for two backward compatibility reasons:
@@ -108,6 +108,8 @@
 
 #define JAVA_9_VERSION                    53
 
+#define JAVA_10_VERSION                   54
+
 void ClassFileParser::set_class_bad_constant_seen(short bad_constant) {
   assert((bad_constant == 19 || bad_constant == 20) && _major_version >= JAVA_9_VERSION,
          "Unexpected bad constant pool entry");
diff --git a/src/hotspot/share/classfile/classListParser.cpp b/src/hotspot/share/classfile/classListParser.cpp
index 8f7be316b50..b1fa759ae10 100644
--- a/src/hotspot/share/classfile/classListParser.cpp
+++ b/src/hotspot/share/classfile/classListParser.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2017, 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
@@ -23,13 +23,32 @@
  */
 
 #include "precompiled.hpp"
+#include "jvm.h"
+#include "jimage.hpp"
 #include "classfile/classListParser.hpp"
-#include "runtime/os.hpp"
-#include "runtime/java.hpp"
+#include "classfile/classLoaderExt.hpp"
+#include "classfile/sharedClassUtil.hpp"
+#include "classfile/symbolTable.hpp"
+#include "classfile/systemDictionary.hpp"
+#include "classfile/systemDictionaryShared.hpp"
+#include "memory/metaspaceShared.hpp"
+#include "memory/resourceArea.hpp"
+#include "runtime/fieldType.hpp"
+#include "runtime/javaCalls.hpp"
+#include "utilities/defaultStream.hpp"
+#include "utilities/hashtable.inline.hpp"
+#include "utilities/macros.hpp"
+
+ClassListParser* ClassListParser::_instance = NULL;
 
 ClassListParser::ClassListParser(const char* file) {
+  assert(_instance == NULL, "must be singleton");
+  _instance = this;
   _classlist_file = file;
   _file = fopen(file, "r");
+  _line_no = 0;
+  _interfaces = new (ResourceObj::C_HEAP, mtClass) GrowableArray<int>(10, true);
+
   if (_file == NULL) {
     char errmsg[JVM_MAXPATHLEN];
     os::lasterror(errmsg, JVM_MAXPATHLEN);
@@ -41,6 +60,7 @@ ClassListParser::~ClassListParser() {
   if (_file) {
     fclose(_file);
   }
+  _instance = NULL;
 }
 
 bool ClassListParser::parse_one_line() {
@@ -48,10 +68,10 @@ bool ClassListParser::parse_one_line() {
     if (fgets(_line, sizeof(_line), _file) == NULL) {
       return false;
     }
-    int line_len = (int)strlen(_line);
-    if (line_len > _max_allowed_line_len) {
-      tty->print_cr("input line too long (must be no longer than %d chars)", _max_allowed_line_len);
-      vm_exit_during_initialization("Loading classlist failed");
+    ++ _line_no;
+    _line_len = (int)strlen(_line);
+    if (_line_len > _max_allowed_line_len) {
+      error("input line too long (must be no longer than %d chars)", _max_allowed_line_len);
     }
     if (*_line == '#') { // comment
       continue;
@@ -59,8 +79,380 @@ bool ClassListParser::parse_one_line() {
     break;
   }
 
-  // Remove trailing \r\n
-  _line[strcspn(_line, "\r\n")] = 0;
+  _id = _unspecified;
+  _super = _unspecified;
+  _interfaces->clear();
+  _source = NULL;
+  _interfaces_specified = false;
+
+  {
+    int len = (int)strlen(_line);
+    int i;
+    // Replace \t\r\n with ' '
+    for (i=0; i<len; i++) {
+      if (_line[i] == '\t' || _line[i] == '\r' || _line[i] == '\n') {
+        _line[i] = ' ';
+      }
+    }
+
+    // Remove trailing newline/space
+    while (len > 0) {
+      if (_line[len-1] == ' ') {
+        _line[len-1] = '\0';
+        len --;
+      } else {
+        break;
+      }
+    }
+    _line_len = len;
+    _class_name = _line;
+  }
+
+  if ((_token = strchr(_line, ' ')) == NULL) {
+    // No optional arguments are specified.
+    return true;
+  }
+
+  // Mark the end of the name, and go to the next input char
+  *_token++ = '\0';
+
+  while (*_token) {
+    skip_whitespaces();
+
+    if (parse_int_option("id:", &_id)) {
+      continue;
+    } else if (parse_int_option("super:", &_super)) {
+      check_already_loaded("Super class", _super);
+      continue;
+    } else if (skip_token("interfaces:")) {
+      int i;
+      while (try_parse_int(&i)) {
+        check_already_loaded("Interface", i);
+        _interfaces->append(i);
+      }
+    } else if (skip_token("source:")) {
+      skip_whitespaces();
+      _source = _token;
+      char* s = strchr(_token, ' ');
+      if (s == NULL) {
+        break; // end of input line
+      } else {
+        *s = '\0'; // mark the end of _source
+        _token = s+1;
+      }
+    } else {
+      error("Unknown input");
+    }
+  }
+
+  // if src is specified
+  //     id super interfaces must all be specified
+  //     loader may be specified
+  // else
+  //     # the class is loaded from classpath
+  //     id may be specified
+  //     super, interfaces, loader must not be specified
   return true;
 }
 
+void ClassListParser::skip_whitespaces() {
+  while (*_token == ' ' || *_token == '\t') {
+    _token ++;
+  }
+}
+
+void ClassListParser::skip_non_whitespaces() {
+  while (*_token && *_token != ' ' && *_token != '\t') {
+    _token ++;
+  }
+}
+
+void ClassListParser::parse_int(int* value) {
+  skip_whitespaces();
+  if (sscanf(_token, "%i", value) == 1) {
+    skip_non_whitespaces();
+    if (*value < 0) {
+      error("Error: negative integers not allowed (%d)", *value);
+    }
+  } else {
+    error("Error: expected integer");
+  }
+}
+
+bool ClassListParser::try_parse_int(int* value) {
+  skip_whitespaces();
+  if (sscanf(_token, "%i", value) == 1) {
+    skip_non_whitespaces();
+    return true;
+  }
+  return false;
+}
+
+bool ClassListParser::skip_token(const char* option_name) {
+  size_t len = strlen(option_name);
+  if (strncmp(_token, option_name, len) == 0) {
+    _token += len;
+    return true;
+  } else {
+    return false;
+  }
+}
+
+bool ClassListParser::parse_int_option(const char* option_name, int* value) {
+  if (skip_token(option_name)) {
+    if (*value != _unspecified) {
+      error("%s specified twice", option_name);
+    } else {
+      parse_int(value);
+      return true;
+    }
+  }
+  return false;
+}
+
+void ClassListParser::print_specified_interfaces() {
+  const int n = _interfaces->length();
+  jio_fprintf(defaultStream::error_stream(), "Currently specified interfaces[%d] = {\n", n);
+  for (int i=0; i<n; i++) {
+    InstanceKlass* k = lookup_class_by_id(_interfaces->at(i));
+    jio_fprintf(defaultStream::error_stream(), "  %4d = %s\n", _interfaces->at(i), k->name()->as_klass_external_name());
+  }
+  jio_fprintf(defaultStream::error_stream(), "}\n");
+}
+
+void ClassListParser::print_actual_interfaces(InstanceKlass *ik) {
+  int n = ik->local_interfaces()->length();
+  jio_fprintf(defaultStream::error_stream(), "Actual interfaces[%d] = {\n", n);
+  for (int i = 0; i < n; i++) {
+    InstanceKlass* e = InstanceKlass::cast(ik->local_interfaces()->at(i));
+    jio_fprintf(defaultStream::error_stream(), "  %s\n", e->name()->as_klass_external_name());
+  }
+  jio_fprintf(defaultStream::error_stream(), "}\n");
+}
+
+void ClassListParser::error(const char *msg, ...) {
+  va_list ap;
+  va_start(ap, msg);
+  int error_index = _token - _line;
+  if (error_index >= _line_len) {
+    error_index = _line_len - 1;
+  }
+  if (error_index < 0) {
+    error_index = 0;
+  }
+
+  jio_fprintf(defaultStream::error_stream(),
+              "An error has occurred while processing class list file %s %d:%d.\n",
+              _classlist_file, _line_no, (error_index + 1));
+  jio_vfprintf(defaultStream::error_stream(), msg, ap);
+
+  if (_line_len <= 0) {
+    jio_fprintf(defaultStream::error_stream(), "\n");
+  } else {
+    jio_fprintf(defaultStream::error_stream(), ":\n");
+    for (int i=0; i<_line_len; i++) {
+      char c = _line[i];
+      if (c == '\0') {
+        jio_fprintf(defaultStream::error_stream(), "%s", " ");
+      } else {
+        jio_fprintf(defaultStream::error_stream(), "%c", c);
+      }
+    }
+    jio_fprintf(defaultStream::error_stream(), "\n");
+    for (int i=0; i<error_index; i++) {
+      jio_fprintf(defaultStream::error_stream(), "%s", " ");
+    }
+    jio_fprintf(defaultStream::error_stream(), "^\n");
+  }
+
+  vm_exit_during_initialization("class list format error.", NULL);
+  va_end(ap);
+}
+
+// This function is used for loading classes for customized class loaders
+// during archive dumping.
+InstanceKlass* ClassListParser::load_class_from_source(Symbol* class_name, TRAPS) {
+#if !(defined(_LP64) && (defined(LINUX)|| defined(SOLARIS) || defined(AIX)))
+  // The only supported platforms are: (1) Linux/64-bit; (2) Solaris/64-bit; (3) AIX/64-bit
+  //
+  // This #if condition should be in sync with the areCustomLoadersSupportedForCDS
+  // method in test/lib/jdk/test/lib/Platform.java.
+  error("AppCDS custom class loaders not supported on this platform");
+#endif
+
+  assert(UseAppCDS, "must be");
+  if (!is_super_specified()) {
+    error("If source location is specified, super class must be also specified");
+  }
+  if (!is_id_specified()) {
+    error("If source location is specified, id must be also specified");
+  }
+  InstanceKlass* k = ClassLoaderExt::load_class(class_name, _source, THREAD);
+
+  if (strncmp(_class_name, "java/", 5) == 0) {
+    log_info(cds)("Prohibited package for non-bootstrap classes: %s.class from %s",
+          _class_name, _source);
+    return NULL;
+  }
+
+  if (k != NULL) {
+    if (k->local_interfaces()->length() != _interfaces->length()) {
+      print_specified_interfaces();
+      print_actual_interfaces(k);
+      error("The number of interfaces (%d) specified in class list does not match the class file (%d)",
+            _interfaces->length(), k->local_interfaces()->length());
+    }
+
+    if (!SystemDictionaryShared::add_non_builtin_klass(class_name, ClassLoaderData::the_null_class_loader_data(),
+                                                       k, THREAD)) {
+      error("Duplicated class %s", _class_name);
+    }
+
+    // This tells JVM_FindLoadedClass to not find this class.
+    k->set_shared_classpath_index(UNREGISTERED_INDEX);
+  }
+
+  return k;
+}
+
+InstanceKlass* ClassListParser::load_current_class(TRAPS) {
+  TempNewSymbol class_name_symbol = SymbolTable::new_symbol(_class_name, THREAD);
+  guarantee(!HAS_PENDING_EXCEPTION, "Exception creating a symbol.");
+
+  InstanceKlass *klass = NULL;
+  if (!is_loading_from_source()) {
+    if (is_super_specified()) {
+      error("If source location is not specified, super class must not be specified");
+    }
+    if (are_interfaces_specified()) {
+      error("If source location is not specified, interface(s) must not be specified");
+    }
+
+    bool non_array = !FieldType::is_array(class_name_symbol);
+
+    Handle s = java_lang_String::create_from_symbol(class_name_symbol, CHECK_0);
+    // Translate to external class name format, i.e., convert '/' chars to '.'
+    Handle string = java_lang_String::externalize_classname(s, CHECK_0);
+    JavaValue result(T_OBJECT);
+    InstanceKlass* spec_klass =  non_array ?
+      SystemDictionary::ClassLoader_klass() : SystemDictionary::Class_klass();
+    Symbol* method_name = non_array ?
+      vmSymbols::loadClass_name() : vmSymbols::forName_name();
+    Handle loader = Handle(THREAD, SystemDictionary::java_system_loader());
+
+    if (non_array) {
+      JavaCalls::call_virtual(&result,
+                              loader, //SystemDictionary::java_system_loader(),
+                              spec_klass,
+                              method_name, //vmSymbols::loadClass_name(),
+                              vmSymbols::string_class_signature(),
+                              string,
+                              THREAD);
+    } else {
+      JavaCalls::call_static(&result,
+                             spec_klass,
+                             method_name,
+                             vmSymbols::string_class_signature(),
+                             string,
+                             CHECK_NULL);
+    }
+    assert(result.get_type() == T_OBJECT, "just checking");
+    oop obj = (oop) result.get_jobject();
+    if (!HAS_PENDING_EXCEPTION && (obj != NULL)) {
+      if (non_array) {
+        klass = InstanceKlass::cast(java_lang_Class::as_Klass(obj));
+      } else {
+        klass = static_cast<InstanceKlass*>(java_lang_Class::array_klass_acquire(obj));
+      }
+    } else { // load classes in bootclasspath/a
+      if (HAS_PENDING_EXCEPTION) {
+        CLEAR_PENDING_EXCEPTION;
+      }
+
+      if (non_array) {
+        Klass* k = SystemDictionary::resolve_or_null(class_name_symbol, CHECK_NULL);
+        if (k != NULL) {
+          klass = InstanceKlass::cast(k);
+        } else {
+          if (!HAS_PENDING_EXCEPTION) {
+            THROW_NULL(vmSymbols::java_lang_ClassNotFoundException());
+          }
+        }
+      }
+    }
+  } else {
+    // If "source:" tag is specified, all super class and super interfaces must be specified in the
+    // class list file.
+    if (UseAppCDS) {
+      klass = load_class_from_source(class_name_symbol, CHECK_NULL);
+    }
+  }
+
+  if (klass != NULL && is_id_specified()) {
+    int id = this->id();
+    SystemDictionaryShared::update_shared_entry(klass, id);
+    InstanceKlass* old = table()->lookup(id);
+    if (old != NULL && old != klass) {
+      error("Duplicated ID %d for class %s", id, _class_name);
+    }
+    table()->add(id, klass);
+  }
+
+  return klass;
+}
+
+bool ClassListParser::is_loading_from_source() {
+  return (_source != NULL);
+}
+
+InstanceKlass* ClassListParser::lookup_class_by_id(int id) {
+  InstanceKlass* klass = table()->lookup(id);
+  if (klass == NULL) {
+    error("Class ID %d has not been defined", id);
+  }
+  return klass;
+}
+
+
+InstanceKlass* ClassListParser::lookup_super_for_current_class(Symbol* super_name) {
+  if (!is_loading_from_source()) {
+    return NULL;
+  }
+
+  InstanceKlass* k = lookup_class_by_id(super());
+  if (super_name != k->name()) {
+    error("The specified super class %s (id %d) does not match actual super class %s",
+          k->name()->as_klass_external_name(), super(),
+          super_name->as_klass_external_name());
+  }
+  return k;
+}
+
+InstanceKlass* ClassListParser::lookup_interface_for_current_class(Symbol* interface_name) {
+  if (!is_loading_from_source()) {
+    return NULL;
+  }
+
+  const int n = _interfaces->length();
+  if (n == 0) {
+    error("Class %s implements the interface %s, but no interface has been specified in the input line",
+          _class_name, interface_name->as_klass_external_name());
+    ShouldNotReachHere();
+  }
+
+  int i;
+  for (i=0; i<n; i++) {
+    InstanceKlass* k = lookup_class_by_id(_interfaces->at(i));
+    if (interface_name == k->name()) {
+      return k;
+    }
+  }
+
+  // interface_name is not specified by the "interfaces:" keyword.
+  print_specified_interfaces();
+  error("The interface %s implemented by class %s does not match any of the specified interface IDs",
+        interface_name->as_klass_external_name(), _class_name);
+  ShouldNotReachHere();
+  return NULL;
+}
+
diff --git a/src/hotspot/share/classfile/classListParser.hpp b/src/hotspot/share/classfile/classListParser.hpp
index 912ae3175a3..e6d48f41c8d 100644
--- a/src/hotspot/share/classfile/classListParser.hpp
+++ b/src/hotspot/share/classfile/classListParser.hpp
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2017, 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
@@ -27,30 +27,122 @@
 
 #include "utilities/exceptions.hpp"
 #include "utilities/globalDefinitions.hpp"
+#include "utilities/hashtable.hpp"
+
+class CDSClassInfo;
+
+// Look up from ID -> InstanceKlass*
+class ID2KlassTable : public Hashtable<InstanceKlass*, mtClass> {
+public:
+  ID2KlassTable() : Hashtable<InstanceKlass*, mtClass>(1987, sizeof(HashtableEntry<InstanceKlass*, mtClass>)) { }
+  void add(int id, InstanceKlass* klass) {
+    unsigned int hash = (unsigned int)id;
+    HashtableEntry<InstanceKlass*, mtClass>* entry = new_entry(hash, klass);
+    add_entry(hash_to_index(hash), entry);
+  }
+
+  InstanceKlass* lookup(int id) {
+    unsigned int hash = (unsigned int)id;
+    int index = hash_to_index(id);
+    for (HashtableEntry<InstanceKlass*, mtClass>* e = bucket(index); e != NULL; e = e->next()) {
+      if (e->hash() == hash) {
+        return e->literal();
+      }
+    }
+    return NULL;
+  }
+};
 
 class ClassListParser : public StackObj {
   enum {
+    _unspecified      = -999,
+
     // Max number of bytes allowed per line in the classlist.
-    // Theoretically Java class names could be 65535 bytes in length. In reality,
+    // Theoretically Java class names could be 65535 bytes in length. Also, an input line
+    // could have a very long path name up to JVM_MAXPATHLEN bytes in length. In reality,
     // 4K bytes is more than enough.
     _max_allowed_line_len = 4096,
     _line_buf_extra       = 10, // for detecting input too long
     _line_buf_size        = _max_allowed_line_len + _line_buf_extra
   };
 
+  static ClassListParser* _instance; // the singleton.
   const char* _classlist_file;
   FILE* _file;
-  char  _line[_line_buf_size];  // The buffer that holds the current line.
 
+  ID2KlassTable _id2klass_table;
+
+  // The following field contains information from the *current* line being
+  // parsed.
+  char                _line[_line_buf_size];  // The buffer that holds the current line. Some characters in
+                                              // the buffer may be overwritten by '\0' during parsing.
+  int                 _line_len;              // Original length of the input line.
+  int                 _line_no;               // Line number for current line being parsed
+  const char*         _class_name;
+  int                 _id;
+  int                 _super;
+  GrowableArray<int>* _interfaces;
+  bool                _interfaces_specified;
+  const char*         _source;
+
+  bool parse_int_option(const char* option_name, int* value);
+  InstanceKlass* load_class_from_source(Symbol* class_name, TRAPS);
+  ID2KlassTable *table() {
+    return &_id2klass_table;
+  }
+  InstanceKlass* lookup_class_by_id(int id);
+  void print_specified_interfaces();
+  void print_actual_interfaces(InstanceKlass *ik);
 public:
   ClassListParser(const char* file);
   ~ClassListParser();
+
+  static ClassListParser* instance() {
+    return _instance;
+  }
   bool parse_one_line();
+  char* _token;
+  void error(const char* msg, ...);
+  void parse_int(int* value);
+  bool try_parse_int(int* value);
+  bool skip_token(const char* option_name);
+  void skip_whitespaces();
+  void skip_non_whitespaces();
+
+  bool is_id_specified() {
+    return _id != _unspecified;
+  }
+  bool is_super_specified() {
+    return _super != _unspecified;
+  }
+  bool are_interfaces_specified() {
+    return _interfaces->length() > 0;
+  }
+  int id() {
+    assert(is_id_specified(), "do not query unspecified id");
+    return _id;
+  }
+  int super() {
+    assert(is_super_specified(), "do not query unspecified super");
+    return _super;
+  }
+  void check_already_loaded(const char* which, int id) {
+    if (_id2klass_table.lookup(id) == NULL) {
+      error("%s id %d is not yet loaded", which, id);
+    }
+  }
 
   const char* current_class_name() {
-    return _line;
+    return _class_name;
   }
+
+  InstanceKlass* load_current_class(TRAPS);
+
+  bool is_loading_from_source();
+
+  // Look up the super or interface of the current class being loaded
+  // (in this->load_current_class()).
+  InstanceKlass* lookup_super_for_current_class(Symbol* super_name);
+  InstanceKlass* lookup_interface_for_current_class(Symbol* interface_name);
 };
-
-
-#endif // SHARE_VM_MEMORY_CLASSLISTPARSER_HPP
+#endif
diff --git a/src/hotspot/share/classfile/classLoader.hpp b/src/hotspot/share/classfile/classLoader.hpp
index 3ba612ec747..202a170979f 100644
--- a/src/hotspot/share/classfile/classLoader.hpp
+++ b/src/hotspot/share/classfile/classLoader.hpp
@@ -26,6 +26,7 @@
 #define SHARE_VM_CLASSFILE_CLASSLOADER_HPP
 
 #include "jimage.hpp"
+#include "runtime/handles.hpp"
 #include "runtime/orderAccess.hpp"
 #include "runtime/perfData.hpp"
 #include "utilities/exceptions.hpp"
@@ -42,6 +43,7 @@
 class JImageFile;
 class ClassFileStream;
 class PackageEntry;
+template <typename T> class GrowableArray;
 
 class ClassPathEntry : public CHeapObj<mtClass> {
 private:
diff --git a/src/hotspot/share/classfile/classLoaderExt.cpp b/src/hotspot/share/classfile/classLoaderExt.cpp
index 44efabec083..a7256bb8e72 100644
--- a/src/hotspot/share/classfile/classLoaderExt.cpp
+++ b/src/hotspot/share/classfile/classLoaderExt.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2017, 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
@@ -23,14 +23,329 @@
  */
 
 #include "precompiled.hpp"
+#include "classfile/classFileParser.hpp"
+#include "classfile/classFileStream.hpp"
 #include "classfile/classListParser.hpp"
+#include "classfile/classLoader.hpp"
 #include "classfile/classLoaderExt.hpp"
-#include "classfile/symbolTable.hpp"
-#include "classfile/systemDictionary.hpp"
+#include "classfile/classLoaderData.inline.hpp"
+#include "classfile/klassFactory.hpp"
+#include "classfile/sharedClassUtil.hpp"
+#include "classfile/sharedPathsMiscInfo.hpp"
+#include "classfile/systemDictionaryShared.hpp"
+#include "classfile/vmSymbols.hpp"
+#include "memory/allocation.inline.hpp"
+#include "memory/filemap.hpp"
+#include "memory/resourceArea.hpp"
+#include "oops/instanceKlass.hpp"
+#include "oops/oop.inline.hpp"
+#include "oops/symbol.hpp"
+#include "runtime/arguments.hpp"
+#include "runtime/java.hpp"
+#include "runtime/javaCalls.hpp"
+#include "runtime/os.hpp"
+#include "services/threadService.hpp"
+#include "utilities/stringUtils.hpp"
 
+jshort ClassLoaderExt::_app_paths_start_index = ClassLoaderExt::max_classpath_index;
+bool ClassLoaderExt::_has_app_classes = false;
+bool ClassLoaderExt::_has_platform_classes = false;
+
+void ClassLoaderExt::setup_app_search_path() {
+  assert(DumpSharedSpaces, "this function is only used with -Xshare:dump and -XX:+UseAppCDS");
+  _app_paths_start_index = ClassLoader::num_boot_classpath_entries();
+  char* app_class_path = os::strdup(Arguments::get_appclasspath());
+
+  if (strcmp(app_class_path, ".") == 0) {
+    // This doesn't make any sense, even for AppCDS, so let's skip it. We
+    // don't want to throw an error here because -cp "." is usually assigned
+    // by the launcher when classpath is not specified.
+    trace_class_path("app loader class path (skipped)=", app_class_path);
+  } else {
+    trace_class_path("app loader class path=", app_class_path);
+    shared_paths_misc_info()->add_app_classpath(app_class_path);
+    ClassLoader::setup_app_search_path(app_class_path);
+  }
+}
+
+char* ClassLoaderExt::read_manifest(ClassPathEntry* entry, jint *manifest_size, bool clean_text, TRAPS) {
+  const char* name = "META-INF/MANIFEST.MF";
+  char* manifest;
+  jint size;
+
+  assert(entry->is_jar_file(), "must be");
+  manifest = (char*) ((ClassPathZipEntry*)entry )->open_entry(name, &size, true, CHECK_NULL);
+
+  if (manifest == NULL) { // No Manifest
+    *manifest_size = 0;
+    return NULL;
+  }
+
+
+  if (clean_text) {
+    // See http://docs.oracle.com/javase/6/docs/technotes/guides/jar/jar.html#JAR%20Manifest
+    // (1): replace all CR/LF and CR with LF
+    StringUtils::replace_no_expand(manifest, "\r\n", "\n");
+
+    // (2) remove all new-line continuation (remove all "\n " substrings)
+    StringUtils::replace_no_expand(manifest, "\n ", "");
+  }
+
+  *manifest_size = (jint)strlen(manifest);
+  return manifest;
+}
+
+char* ClassLoaderExt::get_class_path_attr(const char* jar_path, char* manifest, jint manifest_size) {
+  const char* tag = "Class-Path: ";
+  const int tag_len = (int)strlen(tag);
+  char* found = NULL;
+  char* line_start = manifest;
+  char* end = manifest + manifest_size;
+
+  assert(*end == 0, "must be nul-terminated");
+
+  while (line_start < end) {
+    char* line_end = strchr(line_start, '\n');
+    if (line_end == NULL) {
+      // JAR spec require the manifest file to be terminated by a new line.
+      break;
+    }
+    if (strncmp(tag, line_start, tag_len) == 0) {
+      if (found != NULL) {
+        // Same behavior as jdk/src/share/classes/java/util/jar/Attributes.java
+        // If duplicated entries are found, the last one is used.
+        tty->print_cr("Warning: Duplicate name in Manifest: %s.\n"
+                      "Ensure that the manifest does not have duplicate entries, and\n"
+                      "that blank lines separate individual sections in both your\n"
+                      "manifest and in the META-INF/MANIFEST.MF entry in the jar file:\n%s\n", tag, jar_path);
+      }
+      found = line_start + tag_len;
+      assert(found <= line_end, "sanity");
+      *line_end = '\0';
+    }
+    line_start = line_end + 1;
+  }
+  return found;
+}
+
+void ClassLoaderExt::process_jar_manifest(ClassPathEntry* entry,
+                                          bool check_for_duplicates) {
+  Thread* THREAD = Thread::current();
+  ResourceMark rm(THREAD);
+  jint manifest_size;
+  char* manifest = read_manifest(entry, &manifest_size, CHECK);
+
+  if (manifest == NULL) {
+    return;
+  }
+
+  if (strstr(manifest, "Extension-List:") != NULL) {
+    tty->print_cr("-Xshare:dump does not support Extension-List in JAR manifest: %s", entry->name());
+    vm_exit(1);
+  }
+
+  char* cp_attr = get_class_path_attr(entry->name(), manifest, manifest_size);
+
+  if (cp_attr != NULL && strlen(cp_attr) > 0) {
+    trace_class_path("found Class-Path: ", cp_attr);
+
+    char sep = os::file_separator()[0];
+    const char* dir_name = entry->name();
+    const char* dir_tail = strrchr(dir_name, sep);
+    int dir_len;
+    if (dir_tail == NULL) {
+      dir_len = 0;
+    } else {
+      dir_len = dir_tail - dir_name + 1;
+    }
+
+    // Split the cp_attr by spaces, and add each file
+    char* file_start = cp_attr;
+    char* end = file_start + strlen(file_start);
+
+    while (file_start < end) {
+      char* file_end = strchr(file_start, ' ');
+      if (file_end != NULL) {
+        *file_end = 0;
+        file_end += 1;
+      } else {
+        file_end = end;
+      }
+
+      int name_len = (int)strlen(file_start);
+      if (name_len > 0) {
+        ResourceMark rm(THREAD);
+        char* libname = NEW_RESOURCE_ARRAY(char, dir_len + name_len + 1);
+        *libname = 0;
+        strncat(libname, dir_name, dir_len);
+        strncat(libname, file_start, name_len);
+        trace_class_path("library = ", libname);
+        ClassLoader::update_class_path_entry_list(libname, true, false);
+      }
+
+      file_start = file_end;
+    }
+  }
+}
+
+void ClassLoaderExt::setup_search_paths() {
+  if (UseAppCDS) {
+    shared_paths_misc_info()->record_app_offset();
+    ClassLoaderExt::setup_app_search_path();
+  }
+}
+
+Thread* ClassLoaderExt::Context::_dump_thread = NULL;
+
+bool ClassLoaderExt::check(ClassLoaderExt::Context *context,
+                           const ClassFileStream* stream,
+                           const int classpath_index) {
+  if (stream != NULL) {
+    // Ignore any App classes from signed JAR file during CDS archiving
+    // dumping
+    if (DumpSharedSpaces &&
+        SharedClassUtil::is_classpath_entry_signed(classpath_index) &&
+        classpath_index >= _app_paths_start_index) {
+      tty->print_cr("Preload Warning: Skipping %s from signed JAR",
+                    context->class_name());
+      return false;
+    }
+    if (classpath_index >= _app_paths_start_index) {
+      _has_app_classes = true;
+      _has_platform_classes = true;
+    }
+  }
+
+  return true;
+}
+
+void ClassLoaderExt::record_result(ClassLoaderExt::Context *context,
+                                   Symbol* class_name,
+                                   const s2 classpath_index,
+                                   InstanceKlass* result,
+                                   TRAPS) {
+  assert(DumpSharedSpaces, "Sanity");
+
+  // We need to remember where the class comes from during dumping.
+  oop loader = result->class_loader();
+  s2 classloader_type = ClassLoader::BOOT_LOADER;
+  if (SystemDictionary::is_system_class_loader(loader)) {
+    classloader_type = ClassLoader::APP_LOADER;
+    ClassLoaderExt::set_has_app_classes();
+  } else if (SystemDictionary::is_platform_class_loader(loader)) {
+    classloader_type = ClassLoader::PLATFORM_LOADER;
+    ClassLoaderExt::set_has_platform_classes();
+  }
+  result->set_shared_classpath_index(classpath_index);
+  result->set_class_loader_type(classloader_type);
+}
+
+void ClassLoaderExt::finalize_shared_paths_misc_info() {
+  if (UseAppCDS) {
+    if (!_has_app_classes) {
+      shared_paths_misc_info()->pop_app();
+    }
+  }
+}
+
+// Load the class of the given name from the location given by path. The path is specified by
+// the "source:" in the class list file (see classListParser.cpp), and can be a directory or
+// a JAR file.
+InstanceKlass* ClassLoaderExt::load_class(Symbol* name, const char* path, TRAPS) {
+
+  assert(name != NULL, "invariant");
+  assert(DumpSharedSpaces && UseAppCDS, "this function is only used with -Xshare:dump and -XX:+UseAppCDS");
+  ResourceMark rm(THREAD);
+  const char* class_name = name->as_C_string();
+
+  const char* file_name = file_name_for_class_name(class_name,
+                                                   name->utf8_length());
+  assert(file_name != NULL, "invariant");
+
+  // Lookup stream for parsing .class file
+  ClassFileStream* stream = NULL;
+  ClassPathEntry* e = find_classpath_entry_from_cache(path, CHECK_NULL);
+  if (e == NULL) {
+    return NULL;
+  }
+  {
+    PerfClassTraceTime vmtimer(perf_sys_class_lookup_time(),
+                               ((JavaThread*) THREAD)->get_thread_stat()->perf_timers_addr(),
+                               PerfClassTraceTime::CLASS_LOAD);
+    stream = e->open_stream(file_name, CHECK_NULL);
+  }
+
+  if (NULL == stream) {
+    tty->print_cr("Preload Warning: Cannot find %s", class_name);
+    return NULL;
+  }
+
+  assert(stream != NULL, "invariant");
+  stream->set_verify(true);
+
+  ClassLoaderData* loader_data = ClassLoaderData::the_null_class_loader_data();
+  Handle protection_domain;
+
+  InstanceKlass* result = KlassFactory::create_from_stream(stream,
+                                                           name,
+                                                           loader_data,
+                                                           protection_domain,
+                                                           NULL, // host_klass
+                                                           NULL, // cp_patches
+                                                           THREAD);
+
+  if (HAS_PENDING_EXCEPTION) {
+    tty->print_cr("Preload Error: Failed to load %s", class_name);
+    return NULL;
+  }
+  result->set_shared_classpath_index(UNREGISTERED_INDEX);
+  SystemDictionaryShared::set_shared_class_misc_info(result, stream);
+  return result;
+}
+
+struct CachedClassPathEntry {
+  const char* _path;
+  ClassPathEntry* _entry;
+};
+
+static GrowableArray<CachedClassPathEntry>* cached_path_entries = NULL;
+
+ClassPathEntry* ClassLoaderExt::find_classpath_entry_from_cache(const char* path, TRAPS) {
+  // This is called from dump time so it's single threaded and there's no need for a lock.
+  assert(DumpSharedSpaces && UseAppCDS, "this function is only used with -Xshare:dump and -XX:+UseAppCDS");
+  if (cached_path_entries == NULL) {
+    cached_path_entries = new (ResourceObj::C_HEAP, mtClass) GrowableArray<CachedClassPathEntry>(20, /*c heap*/ true);
+  }
+  CachedClassPathEntry ccpe;
+  for (int i=0; i<cached_path_entries->length(); i++) {
+    ccpe = cached_path_entries->at(i);
+    if (strcmp(ccpe._path, path) == 0) {
+      if (i != 0) {
+        // Put recent entries at the beginning to speed up searches.
+        cached_path_entries->remove_at(i);
+        cached_path_entries->insert_before(0, ccpe);
+      }
+      return ccpe._entry;
+    }
+  }
+
+  struct stat st;
+  if (os::stat(path, &st) != 0) {
+    // File or directory not found
+    return NULL;
+  }
+  ClassPathEntry* new_entry = NULL;
+
+  new_entry = create_class_path_entry(path, &st, false, false, CHECK_NULL);
+  if (new_entry == NULL) {
+    return NULL;
+  }
+  ccpe._path = strdup(path);
+  ccpe._entry = new_entry;
+  cached_path_entries->insert_before(0, ccpe);
+  return new_entry;
+}
 
 Klass* ClassLoaderExt::load_one_class(ClassListParser* parser, TRAPS) {
-  TempNewSymbol class_name_symbol = SymbolTable::new_symbol(parser->current_class_name(), THREAD);
-  guarantee(!HAS_PENDING_EXCEPTION, "Exception creating a symbol.");
-  return SystemDictionary::resolve_or_null(class_name_symbol, THREAD);
+  return parser->load_current_class(THREAD);
 }
diff --git a/src/hotspot/share/classfile/classLoaderExt.hpp b/src/hotspot/share/classfile/classLoaderExt.hpp
index 09cb592b0d6..27e9ce25ef5 100644
--- a/src/hotspot/share/classfile/classLoaderExt.hpp
+++ b/src/hotspot/share/classfile/classLoaderExt.hpp
@@ -26,65 +26,152 @@
 #define SHARE_VM_CLASSFILE_CLASSLOADEREXT_HPP
 
 #include "classfile/classLoader.hpp"
-#include "classfile/systemDictionary.hpp"
-#include "oops/instanceKlass.hpp"
-#include "runtime/handles.hpp"
+#include "utilities/macros.hpp"
 
-class ClassListParser;
+CDS_ONLY(class SharedPathsMiscInfoExt;)
+CDS_ONLY(class ClassListParser;)
 
 class ClassLoaderExt: public ClassLoader { // AllStatic
 public:
-
+  enum SomeConstants {
+    max_classpath_index = 0x7fff
+  };
+  // ClassLoaderExt::Context --
+  //
+  // This is used by DumpSharedSpaces only - it enforces the same classloader
+  // delegation model as would be in run-time. I.e.,
+  // + classes defined by the NULL class loader cannot load classes in the PLATFORM or APP paths.
+  // + classes defined by the PLATFORM class loader cannot load classes in the APP paths.
   class Context {
+    static Thread* _dump_thread;
+    const char* _class_name;
     const char* _file_name;
   public:
+    const char* class_name() {
+      return _class_name;
+    }
+    const char* file_name() {
+      return _file_name;
+    }
+
     Context(const char* class_name, const char* file_name, TRAPS) {
+      _class_name = class_name;
       _file_name = file_name;
+#if INCLUDE_CDS
+      if (!DumpSharedSpaces && !UseSharedSpaces) {
+        // Must not modify _app_paths_start_index if we're not using CDS.
+        assert(_app_paths_start_index == ClassLoaderExt::max_classpath_index, "must be");
+      }
+#endif
     }
 
     bool check(const ClassFileStream* stream, const int classpath_index) {
-      return true;
+      CDS_ONLY(return ClassLoaderExt::check(this, stream, classpath_index);)
+      NOT_CDS(return true;)
     }
 
     bool should_verify(int classpath_index) {
-      return false;
+      CDS_ONLY(return (classpath_index >= _app_paths_start_index);)
+      NOT_CDS(return false;)
     }
 
     void record_result(Symbol* class_name,
                        const s2 classpath_index,
-                       InstanceKlass* result, TRAPS) {
+                       InstanceKlass* result,
+                       TRAPS) {
 #if INCLUDE_CDS
-      assert(DumpSharedSpaces, "Sanity");
-      oop loader = result->class_loader();
-      s2 classloader_type = ClassLoader::BOOT_LOADER;
-      if (SystemDictionary::is_system_class_loader(loader)) {
-        classloader_type = ClassLoader::APP_LOADER;
-        ClassLoaderExt::set_has_app_classes();
-      } else if (SystemDictionary::is_platform_class_loader(loader)) {
-        classloader_type = ClassLoader::PLATFORM_LOADER;
-        ClassLoaderExt::set_has_platform_classes();
-      }
-      result->set_shared_classpath_index(classpath_index);
-      result->set_class_loader_type(classloader_type);
+      ClassLoaderExt::record_result(this, class_name, classpath_index, result, THREAD);
 #endif
     }
-  };
 
+    ~Context() {
+#if INCLUDE_CDS
+      if (!DumpSharedSpaces && !UseSharedSpaces) {
+        // Must not modify app_paths_start_index if we're not using CDS.
+        assert(_app_paths_start_index == ClassLoaderExt::max_classpath_index, "must be");
+      }
+#endif
+    }
+  }; // end ClassLoaderExt::Context
+
+private:
+#if INCLUDE_CDS
+  static char* get_class_path_attr(const char* jar_path, char* manifest, jint manifest_size);
+  static void setup_app_search_path(); // Only when -Xshare:dump
+  static SharedPathsMiscInfoExt* shared_paths_misc_info() {
+    return (SharedPathsMiscInfoExt*)_shared_paths_misc_info;
+  }
+  static jshort _app_paths_start_index; // index of first app JAR in shared classpath entry table
+  static bool _has_app_classes;
+  static bool _has_platform_classes;
+#endif
+
+public:
+  CDS_ONLY(static void process_jar_manifest(ClassPathEntry* entry, bool check_for_duplicates);)
+
+  // Called by JVMTI code to add boot classpath
   static void append_boot_classpath(ClassPathEntry* new_entry) {
+#if INCLUDE_CDS
+    if (UseAppCDS) {
+      warning("UseAppCDS is disabled because bootstrap classpath has been appended");
+      UseAppCDS = false;
+    }
+#endif
     ClassLoader::add_to_boot_append_entries(new_entry);
   }
-  static void setup_search_paths() {}
-  static bool is_boot_classpath(int classpath_index) {
-   return true;
- }
-  static Klass* load_one_class(ClassListParser* parser, TRAPS);
+
+  static void setup_search_paths() NOT_CDS_RETURN;
+
 #if INCLUDE_CDS
-  static void set_has_app_classes() {}
-  static void set_has_platform_classes() {}
+private:
+  static char* read_manifest(ClassPathEntry* entry, jint *manifest_size, bool clean_text, TRAPS);
+  static ClassPathEntry* find_classpath_entry_from_cache(const char* path, TRAPS);
+
+public:
   static char* read_manifest(ClassPathEntry* entry, jint *manifest_size, TRAPS) {
-    return NULL;
+    // Remove all the new-line continuations (which wrap long lines at 72 characters, see
+    // http://docs.oracle.com/javase/6/docs/technotes/guides/jar/jar.html#JAR%20Manifest), so
+    // that the manifest is easier to parse.
+    return read_manifest(entry, manifest_size, true, THREAD);
+  }
+  static char* read_raw_manifest(ClassPathEntry* entry, jint *manifest_size, TRAPS) {
+    // Do not remove new-line continuations, so we can easily pass it as an argument to
+    // java.util.jar.Manifest.getManifest() at run-time.
+    return read_manifest(entry, manifest_size, false, THREAD);
+  }
+
+  static void finalize_shared_paths_misc_info();
+
+  static jshort app_paths_start_index() { return _app_paths_start_index; }
+
+  static void init_paths_start_index(jshort app_start) {
+    _app_paths_start_index = app_start;
+  }
+
+  static bool is_boot_classpath(int classpath_index) {
+    return classpath_index < _app_paths_start_index;
+  }
+
+  static bool has_platform_or_app_classes() {
+    return _has_app_classes || _has_platform_classes;
+  }
+
+  static bool check(class ClassLoaderExt::Context *context,
+                    const ClassFileStream* stream,
+                    const int classpath_index);
+
+  static void record_result(class ClassLoaderExt::Context *context,
+                            Symbol* class_name,
+                            const s2 classpath_index,
+                            InstanceKlass* result, TRAPS);
+  static InstanceKlass* load_class(Symbol* h_name, const char* path, TRAPS);
+  static Klass* load_one_class(ClassListParser* parser, TRAPS);
+  static void set_has_app_classes() {
+    _has_app_classes = true;
+  }
+  static void set_has_platform_classes() {
+    _has_platform_classes = true;
   }
-  static void process_jar_manifest(ClassPathEntry* entry, bool check_for_duplicates) {}
 #endif
 };
 
diff --git a/src/hotspot/share/classfile/klassFactory.hpp b/src/hotspot/share/classfile/klassFactory.hpp
index cb3ed851dd9..c08f8b9a119 100644
--- a/src/hotspot/share/classfile/klassFactory.hpp
+++ b/src/hotspot/share/classfile/klassFactory.hpp
@@ -25,7 +25,7 @@
 #ifndef SHARE_VM_CLASSFILE_KLASSFACTORY_HPP
 #define SHARE_VM_CLASSFILE_KLASSFACTORY_HPP
 
-#include "memory/allocation.inline.hpp"
+#include "memory/allocation.hpp"
 #include "runtime/handles.hpp"
 
 class ClassFileStream;
diff --git a/src/hotspot/share/classfile/sharedClassUtil.cpp b/src/hotspot/share/classfile/sharedClassUtil.cpp
new file mode 100644
index 00000000000..2080472e2f1
--- /dev/null
+++ b/src/hotspot/share/classfile/sharedClassUtil.cpp
@@ -0,0 +1,251 @@
+/*
+ * Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+#include "precompiled.hpp"
+#include "classfile/classLoader.hpp"
+#include "classfile/classLoaderExt.hpp"
+#include "classfile/dictionary.hpp"
+#include "classfile/javaClasses.hpp"
+#include "classfile/sharedClassUtil.hpp"
+#include "classfile/stringTable.hpp"
+#include "classfile/symbolTable.hpp"
+#include "classfile/systemDictionary.hpp"
+#include "classfile/systemDictionaryShared.hpp"
+#include "memory/filemap.hpp"
+#include "memory/metadataFactory.hpp"
+#include "memory/resourceArea.hpp"
+#include "oops/instanceKlass.hpp"
+#include "runtime/arguments.hpp"
+#include "runtime/java.hpp"
+#include "runtime/os.hpp"
+
+class ManifestStream: public ResourceObj {
+  private:
+  u1*   _buffer_start; // Buffer bottom
+  u1*   _buffer_end;   // Buffer top (one past last element)
+  u1*   _current;      // Current buffer position
+
+ public:
+  // Constructor
+  ManifestStream(u1* buffer, int length) : _buffer_start(buffer),
+                                           _current(buffer) {
+    _buffer_end = buffer + length;
+  }
+
+  static bool is_attr(u1* attr, const char* name) {
+    return strncmp((const char*)attr, name, strlen(name)) == 0;
+  }
+
+  static char* copy_attr(u1* value, size_t len) {
+    char* buf = NEW_RESOURCE_ARRAY(char, len + 1);
+    strncpy(buf, (char*)value, len);
+    buf[len] = 0;
+    return buf;
+  }
+
+  // The return value indicates if the JAR is signed or not
+  bool check_is_signed() {
+    u1* attr = _current;
+    bool isSigned = false;
+    while (_current < _buffer_end) {
+      if (*_current == '\n') {
+        *_current = '\0';
+        u1* value = (u1*)strchr((char*)attr, ':');
+        if (value != NULL) {
+          assert(*(value+1) == ' ', "Unrecognized format" );
+          if (strstr((char*)attr, "-Digest") != NULL) {
+            isSigned = true;
+            break;
+          }
+        }
+        *_current = '\n'; // restore
+        attr = _current + 1;
+      }
+      _current ++;
+    }
+    return isSigned;
+  }
+};
+
+void SharedPathsMiscInfoExt::print_path(outputStream* out, int type, const char* path) {
+  switch(type) {
+  case APP:
+    ClassLoader::trace_class_path("Expecting -Djava.class.path=", path);
+    break;
+  default:
+    SharedPathsMiscInfo::print_path(out, type, path);
+  }
+}
+
+bool SharedPathsMiscInfoExt::check(jint type, const char* path) {
+
+  switch (type) {
+  case APP:
+    {
+      // Prefix is OK: E.g., dump with -cp foo.jar, but run with -cp foo.jar:bar.jar
+      size_t len = strlen(path);
+      const char *appcp = Arguments::get_appclasspath();
+      assert(appcp != NULL, "NULL app classpath");
+      size_t appcp_len = strlen(appcp);
+      if (appcp_len < len) {
+        return fail("Run time APP classpath is shorter than the one at dump time: ", appcp);
+      }
+      ResourceMark rm;
+      char* tmp_path;
+      if (len == appcp_len) {
+        tmp_path = (char*)appcp;
+      } else {
+        tmp_path = NEW_RESOURCE_ARRAY(char, len + 1);
+        strncpy(tmp_path, appcp, len);
+        tmp_path[len] = 0;
+      }
+      if (os::file_name_strcmp(path, tmp_path) != 0) {
+        return fail("[APP classpath mismatch, actual: -Djava.class.path=", appcp);
+      }
+      if (appcp[len] != '\0' && appcp[len] != os::path_separator()[0]) {
+        return fail("Dump time APP classpath is not a proper prefix of run time APP classpath: ", appcp);
+      }
+    }
+    break;
+  default:
+    return SharedPathsMiscInfo::check(type, path);
+  }
+
+  return true;
+}
+
+void SharedClassUtil::update_shared_classpath(ClassPathEntry *cpe, SharedClassPathEntry* e, TRAPS) {
+  ClassLoaderData* loader_data = ClassLoaderData::the_null_class_loader_data();
+  SharedClassPathEntryExt* ent = (SharedClassPathEntryExt*)e;
+  ResourceMark rm(THREAD);
+  jint manifest_size;
+  bool isSigned;
+
+  if (cpe->is_jar_file()) {
+    char* manifest = ClassLoaderExt::read_manifest(cpe, &manifest_size, CHECK);
+    if (manifest != NULL) {
+      ManifestStream* stream = new ManifestStream((u1*)manifest,
+                                                  manifest_size);
+      isSigned = stream->check_is_signed();
+      if (isSigned) {
+        ent->_is_signed = true;
+      } else {
+        // Copy the manifest into the shared archive
+        manifest = ClassLoaderExt::read_raw_manifest(cpe, &manifest_size, CHECK);
+        Array<u1>* buf = MetadataFactory::new_array<u1>(loader_data,
+                                                        manifest_size,
+                                                        THREAD);
+        char* p = (char*)(buf->data());
+        memcpy(p, manifest, manifest_size);
+        ent->set_manifest(buf);
+        ent->_is_signed = false;
+      }
+    }
+  }
+}
+
+void SharedClassUtil::initialize(TRAPS) {
+  if (UseSharedSpaces) {
+    int size = FileMapInfo::get_number_of_share_classpaths();
+    if (size > 0) {
+      SystemDictionaryShared::allocate_shared_data_arrays(size, THREAD);
+      if (!DumpSharedSpaces) {
+        FileMapHeaderExt* header = (FileMapHeaderExt*)FileMapInfo::current_info()->header();
+        ClassLoaderExt::init_paths_start_index(header->_app_paths_start_index);
+      }
+    }
+  }
+
+  if (DumpSharedSpaces) {
+    if (SharedArchiveConfigFile) {
+      read_extra_data(SharedArchiveConfigFile, THREAD);
+    }
+  }
+}
+
+void SharedClassUtil::read_extra_data(const char* filename, TRAPS) {
+  HashtableTextDump reader(filename);
+  reader.check_version("VERSION: 1.0");
+
+  while (reader.remain() > 0) {
+    int utf8_length;
+    int prefix_type = reader.scan_prefix(&utf8_length);
+    ResourceMark rm(THREAD);
+    char* utf8_buffer = NEW_RESOURCE_ARRAY(char, utf8_length);
+    reader.get_utf8(utf8_buffer, utf8_length);
+
+    if (prefix_type == HashtableTextDump::SymbolPrefix) {
+      SymbolTable::new_symbol(utf8_buffer, utf8_length, THREAD);
+    } else{
+      assert(prefix_type == HashtableTextDump::StringPrefix, "Sanity");
+      utf8_buffer[utf8_length] = '\0';
+      oop s = StringTable::intern(utf8_buffer, THREAD);
+    }
+  }
+}
+
+bool SharedClassUtil::is_classpath_entry_signed(int classpath_index) {
+  assert(classpath_index >= 0, "Sanity");
+  SharedClassPathEntryExt* ent = (SharedClassPathEntryExt*)
+    FileMapInfo::shared_classpath(classpath_index);
+  return ent->_is_signed;
+}
+
+void FileMapHeaderExt::populate(FileMapInfo* mapinfo, size_t alignment) {
+  FileMapInfo::FileMapHeader::populate(mapinfo, alignment);
+
+  ClassLoaderExt::finalize_shared_paths_misc_info();
+  _app_paths_start_index = ClassLoaderExt::app_paths_start_index();
+
+  _verify_local = BytecodeVerificationLocal;
+  _verify_remote = BytecodeVerificationRemote;
+  _has_platform_or_app_classes = ClassLoaderExt::has_platform_or_app_classes();
+}
+
+bool FileMapHeaderExt::validate() {
+  if (UseAppCDS) {
+    const char* prop = Arguments::get_property("java.system.class.loader");
+    if (prop != NULL) {
+      warning("UseAppCDS is disabled because the java.system.class.loader property is specified (value = \"%s\"). "
+              "To enable UseAppCDS, this property must be not be set", prop);
+      UseAppCDS = false;
+    }
+  }
+
+  if (!FileMapInfo::FileMapHeader::validate()) {
+    return false;
+  }
+
+  // For backwards compatibility, we don't check the verification setting
+  // if the archive only contains system classes.
+  if (_has_platform_or_app_classes &&
+      ((!_verify_local && BytecodeVerificationLocal) ||
+       (!_verify_remote && BytecodeVerificationRemote))) {
+    FileMapInfo::fail_continue("The shared archive file was created with less restrictive "
+                  "verification setting than the current setting.");
+    return false;
+  }
+
+  return true;
+}
diff --git a/src/hotspot/share/classfile/sharedClassUtil.hpp b/src/hotspot/share/classfile/sharedClassUtil.hpp
index 236087f1871..c3b7f603466 100644
--- a/src/hotspot/share/classfile/sharedClassUtil.hpp
+++ b/src/hotspot/share/classfile/sharedClassUtil.hpp
@@ -27,37 +27,108 @@
 
 #include "classfile/sharedPathsMiscInfo.hpp"
 #include "memory/filemap.hpp"
+#include "classfile/classLoaderExt.hpp"
+#include "classfile/dictionary.hpp"
+#include "classfile/systemDictionaryShared.hpp"
+#include "oops/klass.hpp"
 
-class SharedClassUtil : AllStatic {
+class FileMapHeaderExt: public FileMapInfo::FileMapHeader {
 public:
+  jshort _app_paths_start_index;    // Index of first app classpath entry
+  bool   _verify_local;             // BytecodeVerificationLocal setting
+  bool   _verify_remote;            // BytecodeVerificationRemote setting
+  bool   _has_platform_or_app_classes;          // Archive contains app classes
 
-  static SharedPathsMiscInfo* allocate_shared_paths_misc_info() {
-    return new SharedPathsMiscInfo();
+  FileMapHeaderExt() {
+    _has_platform_or_app_classes = true;
+  }
+  virtual void populate(FileMapInfo* mapinfo, size_t alignment);
+  virtual bool validate();
+};
+
+// In addition to SharedPathsMiscInfo, the following information is also stored
+//
+//
+// + The value of Arguments::get_appclasspath() used during dumping.
+//
+class SharedPathsMiscInfoExt : public SharedPathsMiscInfo {
+private:
+  int   _app_offset;
+public:
+  enum {
+    APP       = 5
+  };
+
+  virtual const char* type_name(int type) {
+    switch (type) {
+    case APP:     return "APP";
+    default:      return SharedPathsMiscInfo::type_name(type);
+    }
   }
 
-  static SharedPathsMiscInfo* allocate_shared_paths_misc_info(char* buf, int size) {
-    return new SharedPathsMiscInfo(buf, size);
+  virtual void print_path(outputStream* out, int type, const char* path);
+
+  SharedPathsMiscInfoExt() : SharedPathsMiscInfo() {
+    _app_offset = 0;
+  }
+  SharedPathsMiscInfoExt(char* buf, int size) : SharedPathsMiscInfo(buf, size) {
+    _app_offset = 0;
   }
 
-  static FileMapInfo::FileMapHeader* allocate_file_map_header() {
-    return new FileMapInfo::FileMapHeader();
+  virtual bool check(jint type, const char* path);
+
+  void add_app_classpath(const char* path) {
+    add_path(path, APP);
   }
 
-  static size_t file_map_header_size() {
-    return sizeof(FileMapInfo::FileMapHeader);
+  void record_app_offset() {
+    _app_offset = get_used_bytes();
   }
-
-  static size_t shared_class_path_entry_size() {
-    return sizeof(SharedClassPathEntry);
-  }
-
-  static void update_shared_classpath(ClassPathEntry *cpe,
-                                      SharedClassPathEntry* ent, TRAPS) {}
-  static void initialize(TRAPS) {}
-
-  inline static bool is_shared_boot_class(Klass* klass) {
-    return (klass->_shared_class_path_index >= 0);
+  void pop_app() {
+    _cur_ptr = _buf_start + _app_offset;
+    write_jint(0);
   }
 };
 
+class SharedClassPathEntryExt: public SharedClassPathEntry {
+public:
+  //Maniest attributes
+  bool _is_signed;
+  void set_manifest(Array<u1>* manifest) {
+    _manifest = manifest;
+  }
+};
+
+class SharedClassUtil : AllStatic {
+public:
+  static SharedPathsMiscInfo* allocate_shared_paths_misc_info() {
+    return new SharedPathsMiscInfoExt();
+  }
+
+  static SharedPathsMiscInfo* allocate_shared_paths_misc_info(char* buf, int size) {
+    return new SharedPathsMiscInfoExt(buf, size);
+  }
+
+  static FileMapInfo::FileMapHeader* allocate_file_map_header() {
+    return new FileMapHeaderExt();
+  }
+
+  static size_t file_map_header_size() {
+    return sizeof(FileMapHeaderExt);
+  }
+
+  static size_t shared_class_path_entry_size() {
+    return sizeof(SharedClassPathEntryExt);
+  }
+
+  static void update_shared_classpath(ClassPathEntry *cpe, SharedClassPathEntry* ent, TRAPS);
+  static void initialize(TRAPS);
+
+private:
+  static void read_extra_data(const char* filename, TRAPS);
+
+public:
+  static bool is_classpath_entry_signed(int classpath_index);
+};
+
 #endif // SHARE_VM_CLASSFILE_SHAREDCLASSUTIL_HPP
diff --git a/src/hotspot/share/classfile/sharedPathsMiscInfo.cpp b/src/hotspot/share/classfile/sharedPathsMiscInfo.cpp
index 7f9314ea63c..98a76d1455d 100644
--- a/src/hotspot/share/classfile/sharedPathsMiscInfo.cpp
+++ b/src/hotspot/share/classfile/sharedPathsMiscInfo.cpp
@@ -34,6 +34,18 @@
 #include "runtime/arguments.hpp"
 #include "utilities/ostream.hpp"
 
+SharedPathsMiscInfo::SharedPathsMiscInfo() {
+  _buf_size = INITIAL_BUF_SIZE;
+  _cur_ptr = _buf_start = NEW_C_HEAP_ARRAY(char, _buf_size, mtClass);
+  _allocated = true;
+}
+
+SharedPathsMiscInfo::~SharedPathsMiscInfo() {
+  if (_allocated) {
+    FREE_C_HEAP_ARRAY(char, _buf_start);
+  }
+}
+
 void SharedPathsMiscInfo::add_path(const char* path, int type) {
   log_info(class, path)("type=%s ", type_name(type));
   ClassLoader::trace_class_path("add misc shared path ", path);
@@ -127,7 +139,8 @@ bool SharedPathsMiscInfo::check() {
 bool SharedPathsMiscInfo::check(jint type, const char* path) {
   switch (type) {
   case BOOT:
-    if (os::file_name_strcmp(path, Arguments::get_sysclasspath()) != 0) {
+    // In the future we should perform the check based on the content of the mapped archive.
+    if (UseAppCDS && os::file_name_strcmp(path, Arguments::get_sysclasspath()) != 0) {
       return fail("[BOOT classpath mismatch, actual =", Arguments::get_sysclasspath());
     }
     break;
diff --git a/src/hotspot/share/classfile/sharedPathsMiscInfo.hpp b/src/hotspot/share/classfile/sharedPathsMiscInfo.hpp
index 2099dc24881..e5576156e67 100644
--- a/src/hotspot/share/classfile/sharedPathsMiscInfo.hpp
+++ b/src/hotspot/share/classfile/sharedPathsMiscInfo.hpp
@@ -74,11 +74,7 @@ public:
     INITIAL_BUF_SIZE = 128
   };
   // This constructor is used when creating the misc information (during dump)
-  SharedPathsMiscInfo() {
-    _buf_size = INITIAL_BUF_SIZE;
-    _cur_ptr = _buf_start = NEW_C_HEAP_ARRAY(char, _buf_size, mtClass);
-    _allocated = true;
-  }
+  SharedPathsMiscInfo();
   // This constructor is used when validating the misc info (during run time)
   SharedPathsMiscInfo(char *buff, int size) {
     _cur_ptr = _buf_start = buff;
@@ -86,11 +82,8 @@ public:
     _buf_size = size;
     _allocated = false;
   }
-  ~SharedPathsMiscInfo() {
-    if (_allocated) {
-      FREE_C_HEAP_ARRAY(char, _buf_start);
-    }
-  }
+  ~SharedPathsMiscInfo();
+
   int get_used_bytes() {
     return _cur_ptr - _buf_start;
   }
diff --git a/src/hotspot/share/classfile/stringTable.hpp b/src/hotspot/share/classfile/stringTable.hpp
index a08bd225277..77538a53e5c 100644
--- a/src/hotspot/share/classfile/stringTable.hpp
+++ b/src/hotspot/share/classfile/stringTable.hpp
@@ -25,7 +25,7 @@
 #ifndef SHARE_VM_CLASSFILE_STRINGTABLE_HPP
 #define SHARE_VM_CLASSFILE_STRINGTABLE_HPP
 
-#include "memory/allocation.inline.hpp"
+#include "memory/allocation.hpp"
 #include "utilities/hashtable.hpp"
 
 template <class T, class N> class CompactHashtable;
diff --git a/src/hotspot/share/classfile/symbolTable.hpp b/src/hotspot/share/classfile/symbolTable.hpp
index 931669650b5..bf3c36257da 100644
--- a/src/hotspot/share/classfile/symbolTable.hpp
+++ b/src/hotspot/share/classfile/symbolTable.hpp
@@ -25,7 +25,7 @@
 #ifndef SHARE_VM_CLASSFILE_SYMBOLTABLE_HPP
 #define SHARE_VM_CLASSFILE_SYMBOLTABLE_HPP
 
-#include "memory/allocation.inline.hpp"
+#include "memory/allocation.hpp"
 #include "oops/symbol.hpp"
 #include "utilities/hashtable.hpp"
 
diff --git a/src/hotspot/share/classfile/systemDictionary.cpp b/src/hotspot/share/classfile/systemDictionary.cpp
index df4a38f0306..f760c758273 100644
--- a/src/hotspot/share/classfile/systemDictionary.cpp
+++ b/src/hotspot/share/classfile/systemDictionary.cpp
@@ -1087,7 +1087,7 @@ InstanceKlass* SystemDictionary::resolve_from_stream(Symbol* class_name,
 #if INCLUDE_CDS
   ResourceMark rm(THREAD);
   if (DumpSharedSpaces && !class_loader.is_null() &&
-      !ArgumentsExt::using_AppCDS() && strcmp(class_name->as_C_string(), "Unnamed") != 0) {
+      !UseAppCDS && strcmp(class_name->as_C_string(), "Unnamed") != 0) {
     // If AppCDS is not enabled, don't define the class at dump time (except for the "Unnamed"
     // class, which is used by MethodHandles).
     THROW_MSG_NULL(vmSymbols::java_lang_ClassNotFoundException(), class_name->as_C_string());
diff --git a/src/hotspot/share/classfile/systemDictionaryShared.cpp b/src/hotspot/share/classfile/systemDictionaryShared.cpp
new file mode 100644
index 00000000000..692ba891823
--- /dev/null
+++ b/src/hotspot/share/classfile/systemDictionaryShared.cpp
@@ -0,0 +1,1086 @@
+/*
+ * Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+#include "precompiled.hpp"
+#include "classfile/classFileStream.hpp"
+#include "classfile/classListParser.hpp"
+#include "classfile/classLoader.hpp"
+#include "classfile/classLoaderData.inline.hpp"
+#include "classfile/classLoaderExt.hpp"
+#include "classfile/compactHashtable.inline.hpp"
+#include "classfile/dictionary.hpp"
+#include "classfile/javaClasses.hpp"
+#include "classfile/sharedClassUtil.hpp"
+#include "classfile/symbolTable.hpp"
+#include "classfile/systemDictionary.hpp"
+#include "classfile/systemDictionaryShared.hpp"
+#include "classfile/verificationType.hpp"
+#include "classfile/vmSymbols.hpp"
+#include "logging/log.hpp"
+#include "memory/allocation.hpp"
+#include "memory/filemap.hpp"
+#include "memory/metadataFactory.hpp"
+#include "memory/metaspaceClosure.hpp"
+#include "memory/oopFactory.hpp"
+#include "memory/resourceArea.hpp"
+#include "oops/instanceKlass.hpp"
+#include "oops/klass.inline.hpp"
+#include "oops/objArrayOop.inline.hpp"
+#include "oops/oop.inline.hpp"
+#include "runtime/java.hpp"
+#include "runtime/javaCalls.hpp"
+#include "runtime/mutexLocker.hpp"
+#include "utilities/hashtable.inline.hpp"
+#include "utilities/stringUtils.hpp"
+
+
+objArrayOop SystemDictionaryShared::_shared_protection_domains  =  NULL;
+objArrayOop SystemDictionaryShared::_shared_jar_urls            =  NULL;
+objArrayOop SystemDictionaryShared::_shared_jar_manifests       =  NULL;
+
+static Mutex* SharedDictionary_lock = NULL;
+
+void SystemDictionaryShared::initialize(TRAPS) {
+  if (_java_system_loader != NULL) {
+    SharedDictionary_lock = new Mutex(Mutex::leaf, "SharedDictionary_lock", true);
+
+    // These classes need to be initialized before calling get_shared_jar_manifest(), etc.
+    SystemDictionary::ByteArrayInputStream_klass()->initialize(CHECK);
+    SystemDictionary::File_klass()->initialize(CHECK);
+    SystemDictionary::Jar_Manifest_klass()->initialize(CHECK);
+    SystemDictionary::CodeSource_klass()->initialize(CHECK);
+  }
+}
+
+oop SystemDictionaryShared::shared_protection_domain(int index) {
+  return _shared_protection_domains->obj_at(index);
+}
+
+oop SystemDictionaryShared::shared_jar_url(int index) {
+  return _shared_jar_urls->obj_at(index);
+}
+
+oop SystemDictionaryShared::shared_jar_manifest(int index) {
+  return _shared_jar_manifests->obj_at(index);
+}
+
+
+Handle SystemDictionaryShared::get_shared_jar_manifest(int shared_path_index, TRAPS) {
+  Handle empty;
+  Handle manifest ;
+  if (shared_jar_manifest(shared_path_index) == NULL) {
+    SharedClassPathEntryExt* ent = (SharedClassPathEntryExt*)FileMapInfo::shared_classpath(shared_path_index);
+    long size = ent->manifest_size();
+    if (size <= 0) {
+      return empty; // No manifest - return NULL handle
+    }
+
+    // ByteArrayInputStream bais = new ByteArrayInputStream(buf);
+    InstanceKlass* bais_klass = SystemDictionary::ByteArrayInputStream_klass();
+    Handle bais = bais_klass->allocate_instance_handle(CHECK_(empty));
+    {
+      const char* src = ent->manifest();
+      assert(src != NULL, "No Manifest data");
+      typeArrayOop buf = oopFactory::new_byteArray(size, CHECK_(empty));
+      typeArrayHandle bufhandle(THREAD, buf);
+      char* dst = (char*)(buf->byte_at_addr(0));
+      memcpy(dst, src, (size_t)size);
+
+      JavaValue result(T_VOID);
+      JavaCalls::call_special(&result, bais, bais_klass,
+                              vmSymbols::object_initializer_name(),
+                              vmSymbols::byte_array_void_signature(),
+                              bufhandle, CHECK_(empty));
+    }
+
+    // manifest = new Manifest(bais)
+    InstanceKlass* manifest_klass = SystemDictionary::Jar_Manifest_klass();
+    manifest = manifest_klass->allocate_instance_handle(CHECK_(empty));
+    {
+      JavaValue result(T_VOID);
+      JavaCalls::call_special(&result, manifest, manifest_klass,
+                              vmSymbols::object_initializer_name(),
+                              vmSymbols::input_stream_void_signature(),
+                              bais, CHECK_(empty));
+    }
+    atomic_set_shared_jar_manifest(shared_path_index, manifest());
+  }
+
+  manifest = Handle(THREAD, shared_jar_manifest(shared_path_index));
+  assert(manifest.not_null(), "sanity");
+  return manifest;
+}
+
+Handle SystemDictionaryShared::get_shared_jar_url(int shared_path_index, TRAPS) {
+  Handle url_h;
+  if (shared_jar_url(shared_path_index) == NULL) {
+    JavaValue result(T_OBJECT);
+    const char* path = FileMapInfo::shared_classpath_name(shared_path_index);
+    Handle path_string = java_lang_String::create_from_str(path, CHECK_(url_h));
+    Klass* classLoaders_klass =
+        SystemDictionary::jdk_internal_loader_ClassLoaders_klass();
+        JavaCalls::call_static(&result, classLoaders_klass,
+                               vmSymbols::toFileURL_name(),
+                               vmSymbols::toFileURL_signature(),
+                               path_string, CHECK_(url_h));
+
+    atomic_set_shared_jar_url(shared_path_index, (oop)result.get_jobject());
+  }
+
+  url_h = Handle(THREAD, shared_jar_url(shared_path_index));
+  assert(url_h.not_null(), "sanity");
+  return url_h;
+}
+
+Handle SystemDictionaryShared::get_package_name(Symbol* class_name, TRAPS) {
+  ResourceMark rm(THREAD);
+  Handle pkgname_string;
+  char* pkgname = (char*) ClassLoader::package_from_name((const char*) class_name->as_C_string());
+  if (pkgname != NULL) { // Package prefix found
+    StringUtils::replace_no_expand(pkgname, "/", ".");
+    pkgname_string = java_lang_String::create_from_str(pkgname,
+                                                       CHECK_(pkgname_string));
+  }
+  return pkgname_string;
+}
+
+// Define Package for shared app classes from JAR file and also checks for
+// package sealing (all done in Java code)
+// See http://docs.oracle.com/javase/tutorial/deployment/jar/sealman.html
+void SystemDictionaryShared::define_shared_package(Symbol*  class_name,
+                                                   Handle class_loader,
+                                                   Handle manifest,
+                                                   Handle url,
+                                                   TRAPS) {
+  assert(class_loader == _java_system_loader, "unexpected class loader");
+  // get_package_name() returns a NULL handle if the class is in unnamed package
+  Handle pkgname_string = get_package_name(class_name, CHECK);
+  if (pkgname_string.not_null()) {
+    Klass* app_classLoader_klass = SystemDictionary::jdk_internal_loader_ClassLoaders_AppClassLoader_klass();
+    JavaValue result(T_OBJECT);
+    JavaCallArguments args(3);
+    args.set_receiver(class_loader);
+    args.push_oop(pkgname_string);
+    args.push_oop(manifest);
+    args.push_oop(url);
+    JavaCalls::call_virtual(&result, app_classLoader_klass,
+                            vmSymbols::defineOrCheckPackage_name(),
+                            vmSymbols::defineOrCheckPackage_signature(),
+                            &args,
+                            CHECK);
+  }
+}
+
+// Define Package for shared app/platform classes from named module
+void SystemDictionaryShared::define_shared_package(Symbol* class_name,
+                                                   Handle class_loader,
+                                                   ModuleEntry* mod_entry,
+                                                   TRAPS) {
+  assert(mod_entry != NULL, "module_entry should not be NULL");
+  Handle module_handle(THREAD, mod_entry->module());
+
+  Handle pkg_name = get_package_name(class_name, CHECK);
+  assert(pkg_name.not_null(), "Package should not be null for class in named module");
+
+  Klass* classLoader_klass;
+  if (SystemDictionary::is_system_class_loader(class_loader())) {
+    classLoader_klass = SystemDictionary::jdk_internal_loader_ClassLoaders_AppClassLoader_klass();
+  } else {
+    assert(SystemDictionary::is_platform_class_loader(class_loader()), "unexpected classloader");
+    classLoader_klass = SystemDictionary::jdk_internal_loader_ClassLoaders_PlatformClassLoader_klass();
+  }
+
+  JavaValue result(T_OBJECT);
+  JavaCallArguments args(2);
+  args.set_receiver(class_loader);
+  args.push_oop(pkg_name);
+  args.push_oop(module_handle);
+  JavaCalls::call_virtual(&result, classLoader_klass,
+                          vmSymbols::definePackage_name(),
+                          vmSymbols::definePackage_signature(),
+                          &args,
+                          CHECK);
+}
+
+// Get the ProtectionDomain associated with the CodeSource from the classloader.
+Handle SystemDictionaryShared::get_protection_domain_from_classloader(Handle class_loader,
+                                                                      Handle url, TRAPS) {
+  // CodeSource cs = new CodeSource(url, null);
+  InstanceKlass* cs_klass = SystemDictionary::CodeSource_klass();
+  Handle cs = cs_klass->allocate_instance_handle(CHECK_NH);
+  JavaValue void_result(T_VOID);
+  JavaCalls::call_special(&void_result, cs, cs_klass,
+                          vmSymbols::object_initializer_name(),
+                          vmSymbols::url_code_signer_array_void_signature(),
+                          url, Handle(), CHECK_NH);
+
+  // protection_domain = SecureClassLoader.getProtectionDomain(cs);
+  Klass* secureClassLoader_klass = SystemDictionary::SecureClassLoader_klass();
+  JavaValue obj_result(T_OBJECT);
+  JavaCalls::call_virtual(&obj_result, class_loader, secureClassLoader_klass,
+                          vmSymbols::getProtectionDomain_name(),
+                          vmSymbols::getProtectionDomain_signature(),
+                          cs, CHECK_NH);
+  return Handle(THREAD, (oop)obj_result.get_jobject());
+}
+
+// Returns the ProtectionDomain associated with the JAR file identified by the url.
+Handle SystemDictionaryShared::get_shared_protection_domain(Handle class_loader,
+                                                            int shared_path_index,
+                                                            Handle url,
+                                                            TRAPS) {
+  Handle protection_domain;
+  if (shared_protection_domain(shared_path_index) == NULL) {
+    Handle pd = get_protection_domain_from_classloader(class_loader, url, THREAD);
+    atomic_set_shared_protection_domain(shared_path_index, pd());
+  }
+
+  // Acquire from the cache because if another thread beats the current one to
+  // set the shared protection_domain and the atomic_set fails, the current thread
+  // needs to get the updated protection_domain from the cache.
+  protection_domain = Handle(THREAD, shared_protection_domain(shared_path_index));
+  assert(protection_domain.not_null(), "sanity");
+  return protection_domain;
+}
+
+// Returns the ProtectionDomain associated with the moduleEntry.
+Handle SystemDictionaryShared::get_shared_protection_domain(Handle class_loader,
+                                                            ModuleEntry* mod, TRAPS) {
+  ClassLoaderData *loader_data = mod->loader_data();
+  Handle protection_domain;
+  if (mod->shared_protection_domain() == NULL) {
+    Symbol* location = mod->location();
+    if (location != NULL) {
+      Handle url_string = java_lang_String::create_from_symbol(
+                                 location, CHECK_(protection_domain));
+      JavaValue result(T_OBJECT);
+      Klass* classLoaders_klass =
+        SystemDictionary::jdk_internal_loader_ClassLoaders_klass();
+        JavaCalls::call_static(&result, classLoaders_klass, vmSymbols::toFileURL_name(),
+                               vmSymbols::toFileURL_signature(),
+                               url_string, CHECK_(protection_domain));
+      Handle url = Handle(THREAD, (oop)result.get_jobject());
+
+      Handle pd = get_protection_domain_from_classloader(class_loader, url, THREAD);
+      mod->set_shared_protection_domain(loader_data, pd);
+    }
+  }
+
+  protection_domain = Handle(THREAD, mod->shared_protection_domain());
+  assert(protection_domain.not_null(), "sanity");
+  return protection_domain;
+}
+
+// Initializes the java.lang.Package and java.security.ProtectionDomain objects associated with
+// the given InstanceKlass.
+// Returns the ProtectionDomain for the InstanceKlass.
+Handle SystemDictionaryShared::init_security_info(Handle class_loader, InstanceKlass* ik, TRAPS) {
+  Handle pd;
+
+  if (ik != NULL) {
+    int index = ik->shared_classpath_index();
+    assert(index >= 0, "Sanity");
+    SharedClassPathEntryExt* ent =
+            (SharedClassPathEntryExt*)FileMapInfo::shared_classpath(index);
+    Symbol* class_name = ik->name();
+
+    if (ent->is_modules_image()) {
+      // For shared app/platform classes originated from the run-time image:
+      //   The ProtectionDomains are cached in the corresponding ModuleEntries
+      //   for fast access by the VM.
+      ResourceMark rm;
+      ClassLoaderData *loader_data =
+                ClassLoaderData::class_loader_data(class_loader());
+      PackageEntryTable* pkgEntryTable = loader_data->packages();
+      TempNewSymbol pkg_name = InstanceKlass::package_from_name(class_name, CHECK_(pd));
+      if (pkg_name != NULL) {
+        PackageEntry* pkg_entry = pkgEntryTable->lookup_only(pkg_name);
+        if (pkg_entry != NULL) {
+          ModuleEntry* mod_entry = pkg_entry->module();
+          pd = get_shared_protection_domain(class_loader, mod_entry, THREAD);
+          define_shared_package(class_name, class_loader, mod_entry, CHECK_(pd));
+        }
+      }
+    } else {
+      // For shared app/platform classes originated from JAR files on the class path:
+      //   Each of the 3 SystemDictionaryShared::_shared_xxx arrays has the same length
+      //   as the shared classpath table in the shared archive (see
+      //   FileMap::_classpath_entry_table in filemap.hpp for details).
+      //
+      //   If a shared InstanceKlass k is loaded from the class path, let
+      //
+      //     index = k->shared_classpath_index():
+      //
+      //   FileMap::_classpath_entry_table[index] identifies the JAR file that contains k.
+      //
+      //   k's protection domain is:
+      //
+      //     ProtectionDomain pd = _shared_protection_domains[index];
+      //
+      //   and k's Package is initialized using
+      //
+      //     manifest = _shared_jar_manifests[index];
+      //     url = _shared_jar_urls[index];
+      //     define_shared_package(class_name, class_loader, manifest, url, CHECK_(pd));
+      //
+      //   Note that if an element of these 3 _shared_xxx arrays is NULL, it will be initialized by
+      //   the corresponding SystemDictionaryShared::get_shared_xxx() function.
+      Handle manifest = get_shared_jar_manifest(index, CHECK_(pd));
+      Handle url = get_shared_jar_url(index, CHECK_(pd));
+      define_shared_package(class_name, class_loader, manifest, url, CHECK_(pd));
+      pd = get_shared_protection_domain(class_loader, index, url, CHECK_(pd));
+    }
+  }
+  return pd;
+}
+
+// Currently AppCDS only archives classes from the run-time image, the
+// -Xbootclasspath/a path, and the class path. The following rules need to be
+// revised when AppCDS is changed to archive classes from other code sources
+// in the future, for example the module path (specified by -p).
+//
+// Check if a shared class can be loaded by the specific classloader. Following
+// are the "visible" archived classes for different classloaders.
+//
+// NULL classloader:
+//   - see SystemDictionary::is_shared_class_visible()
+// Platform classloader:
+//   - Module class from "modules" jimage. ModuleEntry must be defined in the
+//     classloader.
+// App Classloader:
+//   - Module class from "modules" jimage. ModuleEntry must be defined in the
+//     classloader.
+//   - Class from -cp. The class must have no PackageEntry defined in any of the
+//     boot/platform/app classloader, or must be in the unnamed module defined in the
+//     AppClassLoader.
+bool SystemDictionaryShared::is_shared_class_visible_for_classloader(
+                                                     InstanceKlass* ik,
+                                                     Handle class_loader,
+                                                     const char* pkg_string,
+                                                     Symbol* pkg_name,
+                                                     PackageEntry* pkg_entry,
+                                                     ModuleEntry* mod_entry,
+                                                     TRAPS) {
+  assert(class_loader.not_null(), "Class loader should not be NULL");
+  assert(Universe::is_module_initialized(), "Module system is not initialized");
+
+  int path_index = ik->shared_classpath_index();
+  SharedClassPathEntry* ent =
+            (SharedClassPathEntry*)FileMapInfo::shared_classpath(path_index);
+
+  if (SystemDictionary::is_platform_class_loader(class_loader())) {
+    assert(ent != NULL, "shared class for PlatformClassLoader should have valid SharedClassPathEntry");
+    // The PlatformClassLoader can only load archived class originated from the
+    // run-time image. The class' PackageEntry/ModuleEntry must be
+    // defined by the PlatformClassLoader.
+    if (mod_entry != NULL) {
+      // PackageEntry/ModuleEntry is found in the classloader. Check if the
+      // ModuleEntry's location agrees with the archived class' origination.
+      if (ent->is_modules_image() && mod_entry->location()->starts_with("jrt:")) {
+        return true; // Module class from the "modules" jimage
+      }
+    }
+  } else if (SystemDictionary::is_system_class_loader(class_loader())) {
+    assert(ent != NULL, "shared class for system loader should have valid SharedClassPathEntry");
+    if (pkg_string == NULL) {
+      // The archived class is in the unnamed package. Currently, the boot image
+      // does not contain any class in the unnamed package.
+      assert(!ent->is_modules_image(), "Class in the unnamed package must be from the classpath");
+      if (path_index >= ClassLoaderExt::app_paths_start_index()) {
+        return true;
+      }
+    } else {
+      // Check if this is from a PackageEntry/ModuleEntry defined in the AppClassloader.
+      if (pkg_entry == NULL) {
+        // It's not guaranteed that the class is from the classpath if the
+        // PackageEntry cannot be found from the AppClassloader. Need to check
+        // the boot and platform classloader as well.
+        if (get_package_entry(pkg_name, ClassLoaderData::class_loader_data_or_null(SystemDictionary::java_platform_loader())) == NULL &&
+            get_package_entry(pkg_name, ClassLoaderData::the_null_class_loader_data()) == NULL) {
+          // The PackageEntry is not defined in any of the boot/platform/app classloaders.
+          // The archived class must from -cp path and not from the run-time image.
+          if (!ent->is_modules_image() && path_index >= ClassLoaderExt::app_paths_start_index()) {
+            return true;
+          }
+        }
+      } else if (mod_entry != NULL) {
+        // The package/module is defined in the AppClassLoader. Currently we only
+        // support archiving application module class from the run-time image.
+        // Packages from the -cp path are in the unnamed_module.
+        if ((ent->is_modules_image() && mod_entry->location()->starts_with("jrt:")) ||
+            (pkg_entry->in_unnamed_module() && path_index >= ClassLoaderExt::app_paths_start_index())) {
+          DEBUG_ONLY( \
+            ClassLoaderData* loader_data = class_loader_data(class_loader); \
+            if (pkg_entry->in_unnamed_module()) \
+              assert(mod_entry == loader_data->unnamed_module(), "the unnamed module is not defined in the classloader");)
+
+          return true;
+        }
+      }
+    }
+  } else {
+    // TEMP: if a shared class can be found by a custom loader, consider it visible now.
+    // FIXME: is this actually correct?
+    return true;
+  }
+  return false;
+}
+
+// The following stack shows how this code is reached:
+//
+//   [0] SystemDictionaryShared::find_or_load_shared_class()
+//   [1] JVM_FindLoadedClass
+//   [2] java.lang.ClassLoader.findLoadedClass0()
+//   [3] java.lang.ClassLoader.findLoadedClass()
+//   [4] java.lang.ClassLoader.loadClass()
+//   [5] jdk.internal.loader.ClassLoaders$AppClassLoader_klass.loadClass()
+//
+// Because AppCDS supports only the PlatformClassLoader and AppClassLoader, we make the following
+// assumptions (based on the JDK 8.0 source code):
+//
+// [a] these two loaders use the default implementation of
+//     ClassLoader.loadClass(String name, boolean resolve), which
+// [b] calls findLoadedClass(name), immediately followed by parent.loadClass(),
+//     immediately followed by findClass(name).
+// [c] If the requested class is a shared class of the current class loader, parent.loadClass()
+//     always returns null, and
+// [d] if AppCDS is not enabled, the class would be loaded by findClass() by decoding it from a
+//     JAR file and then parsed.
+//
+// Given these assumptions, we intercept the findLoadedClass() call to invoke
+// SystemDictionaryShared::find_or_load_shared_class() to load the shared class from
+// the archive. The reasons are:
+//
+// + Because AppCDS is a commercial feature, we want to hide the implementation. There
+//   is currently no easy way to hide Java code, so we did it with native code.
+// + Start-up is improved because we avoid decoding the JAR file, and avoid delegating
+//   to the parent (since we know the parent will not find this class).
+//
+// NOTE: there's a lot of assumption about the Java code. If any of that change, this
+// needs to be redesigned.
+//
+// An alternative is to modify the Java code of AppClassLoader.loadClass().
+//
+InstanceKlass* SystemDictionaryShared::find_or_load_shared_class(
+                 Symbol* name, Handle class_loader, TRAPS) {
+  if (DumpSharedSpaces) {
+    return NULL;
+  }
+
+  InstanceKlass* k = NULL;
+  if (shared_dictionary() != NULL &&
+      UseAppCDS && (SystemDictionary::is_system_class_loader(class_loader()) ||
+                    SystemDictionary::is_platform_class_loader(class_loader()))) {
+
+    // Fix for 4474172; see evaluation for more details
+    class_loader = Handle(
+      THREAD, java_lang_ClassLoader::non_reflection_class_loader(class_loader()));
+    ClassLoaderData *loader_data = register_loader(class_loader, CHECK_NULL);
+    Dictionary* dictionary = loader_data->dictionary();
+
+    unsigned int d_hash = dictionary->compute_hash(name);
+
+    bool DoObjectLock = true;
+    if (is_parallelCapable(class_loader)) {
+      DoObjectLock = false;
+    }
+
+    // Make sure we are synchronized on the class loader before we proceed
+    //
+    // Note: currently, find_or_load_shared_class is called only from
+    // JVM_FindLoadedClass and used for PlatformClassLoader and AppClassLoader,
+    // which are parallel-capable loaders, so this lock is NOT taken.
+    Handle lockObject = compute_loader_lock_object(class_loader, THREAD);
+    check_loader_lock_contention(lockObject, THREAD);
+    ObjectLocker ol(lockObject, THREAD, DoObjectLock);
+
+    {
+      MutexLocker mu(SystemDictionary_lock, THREAD);
+      Klass* check = find_class(d_hash, name, dictionary);
+      if (check != NULL) {
+        return InstanceKlass::cast(check);
+      }
+    }
+
+    k = load_shared_class_for_builtin_loader(name, class_loader, THREAD);
+    if (k != NULL) {
+      define_instance_class(k, CHECK_NULL);
+    }
+  }
+
+  return k;
+}
+
+InstanceKlass* SystemDictionaryShared::load_shared_class_for_builtin_loader(
+                 Symbol* class_name, Handle class_loader, TRAPS) {
+  assert(UseAppCDS && shared_dictionary() != NULL, "already checked");
+  Klass* k = shared_dictionary()->find_class_for_builtin_loader(class_name);
+
+  if (k != NULL) {
+    InstanceKlass* ik = InstanceKlass::cast(k);
+    if ((ik->is_shared_app_class() &&
+         SystemDictionary::is_system_class_loader(class_loader()))  ||
+        (ik->is_shared_platform_class() &&
+         SystemDictionary::is_platform_class_loader(class_loader()))) {
+      Handle protection_domain =
+        SystemDictionaryShared::init_security_info(class_loader, ik, CHECK_NULL);
+      return load_shared_class(ik, class_loader, protection_domain, THREAD);
+    }
+  }
+
+  return NULL;
+}
+
+void SystemDictionaryShared::oops_do(OopClosure* f) {
+  f->do_oop((oop*)&_shared_protection_domains);
+  f->do_oop((oop*)&_shared_jar_urls);
+  f->do_oop((oop*)&_shared_jar_manifests);
+}
+
+void SystemDictionaryShared::allocate_shared_protection_domain_array(int size, TRAPS) {
+  if (_shared_protection_domains == NULL) {
+    _shared_protection_domains = oopFactory::new_objArray(
+        SystemDictionary::ProtectionDomain_klass(), size, CHECK);
+  }
+}
+
+void SystemDictionaryShared::allocate_shared_jar_url_array(int size, TRAPS) {
+  if (_shared_jar_urls == NULL) {
+    _shared_jar_urls = oopFactory::new_objArray(
+        SystemDictionary::URL_klass(), size, CHECK);
+  }
+}
+
+void SystemDictionaryShared::allocate_shared_jar_manifest_array(int size, TRAPS) {
+  if (_shared_jar_manifests == NULL) {
+    _shared_jar_manifests = oopFactory::new_objArray(
+        SystemDictionary::Jar_Manifest_klass(), size, CHECK);
+  }
+}
+
+void SystemDictionaryShared::allocate_shared_data_arrays(int size, TRAPS) {
+  allocate_shared_protection_domain_array(size, CHECK);
+  allocate_shared_jar_url_array(size, CHECK);
+  allocate_shared_jar_manifest_array(size, CHECK);
+}
+
+
+InstanceKlass* SystemDictionaryShared::lookup_from_stream(const Symbol* class_name,
+                                                          Handle class_loader,
+                                                          Handle protection_domain,
+                                                          const ClassFileStream* cfs,
+                                                          TRAPS) {
+  if (!UseAppCDS || shared_dictionary() == NULL) {
+    return NULL;
+  }
+  if (class_name == NULL) {  // don't do this for anonymous classes
+    return NULL;
+  }
+  if (class_loader.is_null() ||
+      SystemDictionary::is_system_class_loader(class_loader()) ||
+      SystemDictionary::is_platform_class_loader(class_loader())) {
+    // This function is called for loading only UNREGISTERED classes.
+    // Do nothing for the BUILTIN loaders.
+    return NULL;
+  }
+
+  ClassLoaderData* loader_data = ClassLoaderData::class_loader_data(class_loader());
+  Klass* k;
+
+  { // UNREGISTERED loader
+    if (!shared_dictionary()->class_exists_for_unregistered_loader(class_name)) {
+      // No classes of this name for unregistered loaders.
+      return NULL;
+    }
+
+    int clsfile_size  = cfs->length();
+    int clsfile_crc32 = ClassLoader::crc32(0, (const char*)cfs->buffer(), cfs->length());
+
+    k = shared_dictionary()->find_class_for_unregistered_loader(class_name,
+                                                                clsfile_size, clsfile_crc32);
+  }
+
+  if (k == NULL) { // not archived
+    return NULL;
+  }
+
+  return acquire_class_for_current_thread(InstanceKlass::cast(k), class_loader,
+                                          protection_domain, THREAD);
+}
+
+InstanceKlass* SystemDictionaryShared::acquire_class_for_current_thread(
+                   InstanceKlass *ik,
+                   Handle class_loader,
+                   Handle protection_domain,
+                   TRAPS) {
+  ClassLoaderData* loader_data = ClassLoaderData::class_loader_data(class_loader());
+
+  {
+    MutexLocker mu(SharedDictionary_lock, THREAD);
+    if (ik->class_loader_data() != NULL) {
+      //    ik is already loaded (by this loader or by a different loader)
+      // or ik is being loaded by a different thread (by this loader or by a different loader)
+      return NULL;
+    }
+
+    // No other thread has acquired this yet, so give it to *this thread*
+    ik->set_class_loader_data(loader_data);
+  }
+
+  // No longer holding SharedDictionary_lock
+  // No need to lock, as <ik> can be held only by a single thread.
+  loader_data->add_class(ik);
+
+  // Load and check super/interfaces, restore unsharable info
+  InstanceKlass* shared_klass = load_shared_class(ik, class_loader, protection_domain, THREAD);
+  if (shared_klass == NULL || HAS_PENDING_EXCEPTION) {
+    // TODO: clean up <ik> so it can be used again
+    return NULL;
+  }
+
+  return shared_klass;
+}
+
+bool SystemDictionaryShared::add_non_builtin_klass(Symbol* name, ClassLoaderData* loader_data,
+                                                   InstanceKlass* k,
+                                                   TRAPS) {
+  assert(DumpSharedSpaces, "only when dumping");
+  assert(UseAppCDS && boot_loader_dictionary() != NULL, "must be");
+
+  if (boot_loader_dictionary()->add_non_builtin_klass(name, loader_data, k)) {
+    MutexLocker mu_r(Compile_lock, THREAD); // not really necessary, but add_to_hierarchy asserts this.
+    add_to_hierarchy(k, CHECK_0);
+    return true;
+  }
+  return false;
+}
+
+// This function is called to resolve the super/interfaces of shared classes for
+// non-built-in loaders. E.g., ChildClass in the below example
+// where "super:" (and optionally "interface:") have been specified.
+//
+// java/lang/Object id: 0
+// Interface   id: 2 super: 0 source: cust.jar
+// ChildClass  id: 4 super: 0 interfaces: 2 source: cust.jar
+Klass* SystemDictionaryShared::dump_time_resolve_super_or_fail(
+    Symbol* child_name, Symbol* class_name, Handle class_loader,
+    Handle protection_domain, bool is_superclass, TRAPS) {
+
+  assert(DumpSharedSpaces, "only when dumping");
+
+  ClassListParser* parser = ClassListParser::instance();
+  if (parser == NULL) {
+    // We're still loading the well-known classes, before the ClassListParser is created.
+    return NULL;
+  }
+  if (child_name->equals(parser->current_class_name())) {
+    // When this function is called, all the numbered super and interface types
+    // must have already been loaded. Hence this function is never recursively called.
+    if (is_superclass) {
+      return parser->lookup_super_for_current_class(class_name);
+    } else {
+      return parser->lookup_interface_for_current_class(class_name);
+    }
+  } else {
+    // The VM is not trying to resolve a super type of parser->current_class_name().
+    // Instead, it's resolving an error class (because parser->current_class_name() has
+    // failed parsing or verification). Don't do anything here.
+    return NULL;
+  }
+}
+
+struct SharedMiscInfo {
+  Klass* _klass;
+  int _clsfile_size;
+  int _clsfile_crc32;
+};
+
+static GrowableArray<SharedMiscInfo>* misc_info_array = NULL;
+
+void SystemDictionaryShared::set_shared_class_misc_info(Klass* k, ClassFileStream* cfs) {
+  assert(DumpSharedSpaces, "only when dumping");
+  int clsfile_size  = cfs->length();
+  int clsfile_crc32 = ClassLoader::crc32(0, (const char*)cfs->buffer(), cfs->length());
+
+  if (misc_info_array == NULL) {
+    misc_info_array = new (ResourceObj::C_HEAP, mtClass) GrowableArray<SharedMiscInfo>(20, /*c heap*/ true);
+  }
+
+  SharedMiscInfo misc_info;
+  DEBUG_ONLY({
+      for (int i=0; i<misc_info_array->length(); i++) {
+        misc_info = misc_info_array->at(i);
+        assert(misc_info._klass != k, "cannot call set_shared_class_misc_info twice for the same class");
+      }
+    });
+
+  misc_info._klass = k;
+  misc_info._clsfile_size = clsfile_size;
+  misc_info._clsfile_crc32 = clsfile_crc32;
+
+  misc_info_array->append(misc_info);
+}
+
+void SystemDictionaryShared::init_shared_dictionary_entry(Klass* k, DictionaryEntry* ent) {
+  SharedDictionaryEntry* entry = (SharedDictionaryEntry*)ent;
+  entry->_id = -1;
+  entry->_clsfile_size = -1;
+  entry->_clsfile_crc32 = -1;
+  entry->_verifier_constraints = NULL;
+  entry->_verifier_constraint_flags = NULL;
+
+  if (misc_info_array != NULL) {
+    for (int i=0; i<misc_info_array->length(); i++) {
+      SharedMiscInfo misc_info = misc_info_array->at(i);
+      if (misc_info._klass == k) {
+        entry->_clsfile_size = misc_info._clsfile_size;
+        entry->_clsfile_crc32 = misc_info._clsfile_crc32;
+        misc_info_array->remove_at(i);
+        return;
+      }
+    }
+  }
+}
+
+bool SystemDictionaryShared::add_verification_constraint(Klass* k, Symbol* name,
+         Symbol* from_name, bool from_field_is_protected, bool from_is_array, bool from_is_object) {
+  assert(DumpSharedSpaces, "called at dump time only");
+
+  // Skip anonymous classes, which are not archived as they are not in
+  // dictionary (see assert_no_anonymoys_classes_in_dictionaries() in
+  // VM_PopulateDumpSharedSpace::doit()).
+  if (k->class_loader_data()->is_anonymous()) {
+    return true; // anonymous classes are not archived, skip
+  }
+
+  SharedDictionaryEntry* entry = ((SharedDictionary*)(k->class_loader_data()->dictionary()))->find_entry_for(k);
+  ResourceMark rm;
+  // Lambda classes are not archived and will be regenerated at runtime.
+  if (entry == NULL && strstr(k->name()->as_C_string(), "Lambda$") != NULL) {
+    return true;
+  }
+  assert(entry != NULL, "class should be in dictionary before being verified");
+  entry->add_verification_constraint(name, from_name, from_field_is_protected,
+                                     from_is_array, from_is_object);
+  if (entry->is_builtin()) {
+    // For builtin class loaders, we can try to complete the verification check at dump time,
+    // because we can resolve all the constraint classes.
+    return false;
+  } else {
+    // For non-builtin class loaders, we cannot complete the verification check at dump time,
+    // because at dump time we don't know how to resolve classes for such loaders.
+    return true;
+  }
+}
+
+void SystemDictionaryShared::finalize_verification_constraints() {
+  boot_loader_dictionary()->finalize_verification_constraints();
+}
+
+void SystemDictionaryShared::check_verification_constraints(InstanceKlass* klass,
+                                                             TRAPS) {
+  assert(!DumpSharedSpaces && UseSharedSpaces, "called at run time with CDS enabled only");
+  SharedDictionaryEntry* entry = shared_dictionary()->find_entry_for(klass);
+  assert(entry != NULL, "call this only for shared classes");
+  entry->check_verification_constraints(klass, THREAD);
+}
+
+SharedDictionaryEntry* SharedDictionary::find_entry_for(Klass* klass) {
+  Symbol* class_name = klass->name();
+  unsigned int hash = compute_hash(class_name);
+  int index = hash_to_index(hash);
+
+  for (SharedDictionaryEntry* entry = bucket(index);
+                              entry != NULL;
+                              entry = entry->next()) {
+    if (entry->hash() == hash && entry->literal() == klass) {
+      return entry;
+    }
+  }
+
+  return NULL;
+}
+
+void SharedDictionary::finalize_verification_constraints() {
+  int bytes = 0, count = 0;
+  for (int index = 0; index < table_size(); index++) {
+    for (SharedDictionaryEntry *probe = bucket(index);
+                                probe != NULL;
+                               probe = probe->next()) {
+      int n = probe->finalize_verification_constraints();
+      if (n > 0) {
+        bytes += n;
+        count ++;
+      }
+    }
+  }
+  if (log_is_enabled(Info, cds, verification)) {
+    double avg = 0;
+    if (count > 0) {
+      avg = double(bytes) / double(count);
+    }
+    log_info(cds, verification)("Recorded verification constraints for %d classes = %d bytes (avg = %.2f bytes) ", count, bytes, avg);
+  }
+}
+
+void SharedDictionaryEntry::add_verification_constraint(Symbol* name,
+         Symbol* from_name, bool from_field_is_protected, bool from_is_array, bool from_is_object) {
+  if (_verifier_constraints == NULL) {
+    _verifier_constraints = new(ResourceObj::C_HEAP, mtClass) GrowableArray<Symbol*>(8, true, mtClass);
+  }
+  if (_verifier_constraint_flags == NULL) {
+    _verifier_constraint_flags = new(ResourceObj::C_HEAP, mtClass) GrowableArray<char>(4, true, mtClass);
+  }
+  GrowableArray<Symbol*>* vc_array = (GrowableArray<Symbol*>*)_verifier_constraints;
+  for (int i=0; i<vc_array->length(); i+= 2) {
+    if (name      == vc_array->at(i) &&
+        from_name == vc_array->at(i+1)) {
+      return;
+    }
+  }
+  vc_array->append(name);
+  vc_array->append(from_name);
+
+  GrowableArray<char>* vcflags_array = (GrowableArray<char>*)_verifier_constraint_flags;
+  char c = 0;
+  c |= from_field_is_protected ? FROM_FIELD_IS_PROTECTED : 0;
+  c |= from_is_array           ? FROM_IS_ARRAY           : 0;
+  c |= from_is_object          ? FROM_IS_OBJECT          : 0;
+  vcflags_array->append(c);
+
+  if (log_is_enabled(Trace, cds, verification)) {
+    ResourceMark rm;
+    log_trace(cds, verification)("add_verification_constraint: %s: %s must be subclass of %s",
+                                 instance_klass()->external_name(), from_name->as_klass_external_name(),
+                                 name->as_klass_external_name());
+  }
+}
+
+int SharedDictionaryEntry::finalize_verification_constraints() {
+  assert(DumpSharedSpaces, "called at dump time only");
+  Thread* THREAD = Thread::current();
+  ClassLoaderData* loader_data = ClassLoaderData::the_null_class_loader_data();
+  GrowableArray<Symbol*>* vc_array = (GrowableArray<Symbol*>*)_verifier_constraints;
+  GrowableArray<char>* vcflags_array = (GrowableArray<char>*)_verifier_constraint_flags;
+
+  if (vc_array != NULL) {
+    if (log_is_enabled(Trace, cds, verification)) {
+      ResourceMark rm;
+      log_trace(cds, verification)("finalize_verification_constraint: %s",
+                                   literal()->external_name());
+    }
+
+    // Copy the constraints from C_HEAP-alloced GrowableArrays to Metaspace-alloced
+    // Arrays
+    int size = 0;
+    {
+      // FIXME: change this to be done after relocation, so we can use symbol offset??
+      int length = vc_array->length();
+      Array<Symbol*>* out = MetadataFactory::new_array<Symbol*>(loader_data, length, 0, THREAD);
+      assert(out != NULL, "Dump time allocation failure would have aborted VM");
+      for (int i=0; i<length; i++) {
+        out->at_put(i, vc_array->at(i));
+      }
+      _verifier_constraints = out;
+      size += out->size() * BytesPerWord;
+      delete vc_array;
+    }
+    {
+      int length = vcflags_array->length();
+      Array<char>* out = MetadataFactory::new_array<char>(loader_data, length, 0, THREAD);
+      assert(out != NULL, "Dump time allocation failure would have aborted VM");
+      for (int i=0; i<length; i++) {
+        out->at_put(i, vcflags_array->at(i));
+      }
+      _verifier_constraint_flags = out;
+      size += out->size() * BytesPerWord;
+      delete vcflags_array;
+    }
+
+    return size;
+  }
+  return 0;
+}
+
+void SharedDictionaryEntry::check_verification_constraints(InstanceKlass* klass, TRAPS) {
+  Array<Symbol*>* vc_array = (Array<Symbol*>*)_verifier_constraints;
+  Array<char>* vcflags_array = (Array<char>*)_verifier_constraint_flags;
+
+  if (vc_array != NULL) {
+    int length = vc_array->length();
+    for (int i=0; i<length; i+=2) {
+      Symbol* name      = vc_array->at(i);
+      Symbol* from_name = vc_array->at(i+1);
+      char c = vcflags_array->at(i/2);
+
+      bool from_field_is_protected = (c & FROM_FIELD_IS_PROTECTED) ? true : false;
+      bool from_is_array           = (c & FROM_IS_ARRAY)           ? true : false;
+      bool from_is_object          = (c & FROM_IS_OBJECT)          ? true : false;
+
+      bool ok = VerificationType::resolve_and_check_assignability(klass, name,
+         from_name, from_field_is_protected, from_is_array, from_is_object, CHECK);
+      if (!ok) {
+        ResourceMark rm(THREAD);
+        stringStream ss;
+
+        ss.print_cr("Bad type on operand stack");
+        ss.print_cr("Exception Details:");
+        ss.print_cr("  Location:\n    %s", klass->name()->as_C_string());
+        ss.print_cr("  Reason:\n    Type '%s' is not assignable to '%s'",
+                    from_name->as_quoted_ascii(), name->as_quoted_ascii());
+        THROW_MSG(vmSymbols::java_lang_VerifyError(), ss.as_string());
+      }
+    }
+  }
+}
+
+void SharedDictionaryEntry::metaspace_pointers_do(MetaspaceClosure* it) {
+  it->push((Array<Symbol*>**)&_verifier_constraints);
+  it->push((Array<char>**)&_verifier_constraint_flags);
+}
+
+bool SharedDictionary::add_non_builtin_klass(const Symbol* class_name,
+                                             ClassLoaderData* loader_data,
+                                             InstanceKlass* klass) {
+
+  assert(DumpSharedSpaces, "supported only when dumping");
+  assert(klass != NULL, "adding NULL klass");
+  assert(klass->name() == class_name, "sanity check on name");
+  assert(klass->shared_classpath_index() < 0,
+         "the shared classpath index should not be set for shared class loaded by the custom loaders");
+
+  // Add an entry for a non-builtin class.
+  // For a shared class for custom class loaders, SystemDictionary::resolve_or_null will
+  // not find this class, because is_builtin() is false.
+  unsigned int hash = compute_hash(class_name);
+  int index = hash_to_index(hash);
+
+  for (SharedDictionaryEntry* entry = bucket(index);
+                              entry != NULL;
+                              entry = entry->next()) {
+    if (entry->hash() == hash) {
+      Klass* klass = (Klass*)entry->literal();
+      if (klass->name() == class_name && klass->class_loader_data() == loader_data) {
+        // There is already a class defined with the same name
+        return false;
+      }
+    }
+  }
+
+  assert(Dictionary::entry_size() >= sizeof(SharedDictionaryEntry), "must be big enough");
+  SharedDictionaryEntry* entry = (SharedDictionaryEntry*)new_entry(hash, klass);
+  add_entry(index, entry);
+
+  assert(entry->is_unregistered(), "sanity");
+  assert(!entry->is_builtin(), "sanity");
+  return true;
+}
+
+
+//-----------------
+// SharedDictionary
+//-----------------
+
+
+Klass* SharedDictionary::find_class_for_builtin_loader(const Symbol* name) const {
+  SharedDictionaryEntry* entry = get_entry_for_builtin_loader(name);
+  return entry != NULL ? entry->instance_klass() : (Klass*)NULL;
+}
+
+Klass* SharedDictionary::find_class_for_unregistered_loader(const Symbol* name,
+                                                            int clsfile_size,
+                                                            int clsfile_crc32) const {
+
+  const SharedDictionaryEntry* entry = get_entry_for_unregistered_loader(name,
+                                                                         clsfile_size,
+                                                                         clsfile_crc32);
+  return entry != NULL ? entry->instance_klass() : (Klass*)NULL;
+}
+
+void SharedDictionary::update_entry(Klass* klass, int id) {
+  assert(DumpSharedSpaces, "supported only when dumping");
+  Symbol* class_name = klass->name();
+  unsigned int hash = compute_hash(class_name);
+  int index = hash_to_index(hash);
+
+  for (SharedDictionaryEntry* entry = bucket(index);
+                              entry != NULL;
+                              entry = entry->next()) {
+    if (entry->hash() == hash && entry->literal() == klass) {
+      entry->_id = id;
+      return;
+    }
+  }
+
+  ShouldNotReachHere();
+}
+
+SharedDictionaryEntry* SharedDictionary::get_entry_for_builtin_loader(const Symbol* class_name) const {
+  assert(!DumpSharedSpaces, "supported only when at runtime");
+  unsigned int hash = compute_hash(class_name);
+  const int index = hash_to_index(hash);
+
+  for (SharedDictionaryEntry* entry = bucket(index);
+                              entry != NULL;
+                              entry = entry->next()) {
+    if (entry->hash() == hash && entry->equals(class_name)) {
+      if (entry->is_builtin()) {
+        return entry;
+      }
+    }
+  }
+  return NULL;
+}
+
+SharedDictionaryEntry* SharedDictionary::get_entry_for_unregistered_loader(const Symbol* class_name,
+                                                                           int clsfile_size,
+                                                                           int clsfile_crc32) const {
+  assert(!DumpSharedSpaces, "supported only when at runtime");
+  unsigned int hash = compute_hash(class_name);
+  int index = hash_to_index(hash);
+
+  for (SharedDictionaryEntry* entry = bucket(index);
+                              entry != NULL;
+                              entry = entry->next()) {
+    if (entry->hash() == hash && entry->equals(class_name)) {
+      if (entry->is_unregistered()) {
+        if (clsfile_size == -1) {
+          // We're called from class_exists_for_unregistered_loader. At run time, we want to
+          // compute the CRC of a ClassFileStream only if there is an UNREGISTERED class
+          // with the matching name.
+          return entry;
+        } else {
+          // We're called from find_class_for_unregistered_loader
+          if (entry->_clsfile_size && clsfile_crc32 == entry->_clsfile_crc32) {
+            return entry;
+          }
+        }
+
+        // There can be only 1 class with this name for unregistered loaders.
+        return NULL;
+      }
+    }
+  }
+  return NULL;
+}
diff --git a/src/hotspot/share/classfile/systemDictionaryShared.hpp b/src/hotspot/share/classfile/systemDictionaryShared.hpp
index 244e98e5d74..c1b87348a5a 100644
--- a/src/hotspot/share/classfile/systemDictionaryShared.hpp
+++ b/src/hotspot/share/classfile/systemDictionaryShared.hpp
@@ -25,75 +25,362 @@
 #ifndef SHARE_VM_CLASSFILE_SYSTEMDICTIONARYSHARED_HPP
 #define SHARE_VM_CLASSFILE_SYSTEMDICTIONARYSHARED_HPP
 
-#include "classfile/systemDictionary.hpp"
+#include "oops/klass.hpp"
 #include "classfile/dictionary.hpp"
+#include "classfile/systemDictionary.hpp"
+#include "memory/filemap.hpp"
+
+
+/*===============================================================================
+
+    Handling of the classes in the AppCDS archive
+
+    To ensure safety and to simplify the implementation, archived classes are
+    "segregated" into several types. The following rules describe how they
+    are stored and looked up.
+
+[1] Category of archived classes
+
+    There are 3 disjoint groups of classes stored in the AppCDS archive. They are
+    categorized as by their SharedDictionaryEntry::loader_type()
+
+    BUILTIN:              These classes may be defined ONLY by the BOOT/PLATFORM/APP
+                          loaders.
+
+    UNREGISTERED:         These classes may be defined ONLY by a ClassLoader
+                          instance that's not listed above (using fingerprint matching)
+
+[2] How classes from different categories are specified in the classlist:
+
+    Starting from JDK9, each class in the classlist may be specified with
+    these keywords: "id", "super", "interfaces", "loader" and "source".
+
+
+    BUILTIN               Only the "id" keyword may be (optionally) specified. All other
+                          keywords are forbidden.
+
+                          The named class is looked up from the jimage and from
+                          Xbootclasspath/a and CLASSPATH.
+
+    UNREGISTERED:         The "id", "super", and "source" keywords must all be
+                          specified.
+
+                          The "interfaces" keyword must be specified if the class implements
+                          one or more local interfaces. The "interfaces" keyword must not be
+                          specified if the class does not implement local interfaces.
+
+                          The named class is looked up from the location specified in the
+                          "source" keyword.
+
+    Example classlist:
+
+    # BUILTIN
+    java/lang/Object id: 0
+    java/lang/Cloneable id: 1
+    java/lang/String
+
+    # UNREGISTERED
+    Bar id: 3 super: 0 interfaces: 1 source: /foo.jar
+
+
+[3] Identifying the loader_type of archived classes in the shared dictionary
+
+    Each archived Klass* C is associated with a SharedDictionaryEntry* E
+
+    BUILTIN:              (C->shared_classpath_index() >= 0)
+    UNREGISTERED:         (C->shared_classpath_index() <  0)
+
+[4] Lookup of archived classes at run time:
+
+    (a) BUILTIN loaders:
+
+        Search the shared directory for a BUILTIN class with a matching name.
+
+    (b) UNREGISTERED loaders:
+
+        The search originates with SystemDictionaryShared::lookup_from_stream().
+
+        Search the shared directory for a UNREGISTERED class with a matching
+        (name, clsfile_len, clsfile_crc32) tuple.
+
+===============================================================================*/
+#define UNREGISTERED_INDEX -9999
 
 class ClassFileStream;
 
-class SystemDictionaryShared: public SystemDictionary {
+// Archived classes need extra information not needed by traditionally loaded classes.
+// To keep footprint small, we add these in the dictionary entry instead of the InstanceKlass.
+class SharedDictionaryEntry : public DictionaryEntry {
+
 public:
-  static void initialize(TRAPS) {}
-  static InstanceKlass* find_or_load_shared_class(Symbol* class_name,
-                                                  Handle class_loader,
-                                                  TRAPS) {
-    return NULL;
-  }
-  static void roots_oops_do(OopClosure* blk) {}
-  static void oops_do(OopClosure* f) {}
-  static bool is_sharing_possible(ClassLoaderData* loader_data) {
-    oop class_loader = loader_data->class_loader();
-    return (class_loader == NULL);
-  }
-  static bool is_shared_class_visible_for_classloader(
-                                      InstanceKlass* ik,
-                                      Handle class_loader,
-                                      const char* pkg_string,
-                                      Symbol* pkg_name,
-                                      PackageEntry* pkg_entry,
-                                      ModuleEntry* mod_entry,
-                                      TRAPS) {
-    return false;
+  enum LoaderType {
+    LT_BUILTIN,
+    LT_UNREGISTERED
+  };
+
+  enum {
+    FROM_FIELD_IS_PROTECTED = 1 << 0,
+    FROM_IS_ARRAY           = 1 << 1,
+    FROM_IS_OBJECT          = 1 << 2
+  };
+
+  int             _id;
+  int             _clsfile_size;
+  int             _clsfile_crc32;
+  void*           _verifier_constraints; // FIXME - use a union here to avoid type casting??
+  void*           _verifier_constraint_flags;
+
+  // See "Identifying the loader_type of archived classes" comments above.
+  LoaderType loader_type() const {
+    Klass* k = (Klass*)literal();
+
+    if ((k->shared_classpath_index() != UNREGISTERED_INDEX)) {
+      return LT_BUILTIN;
+    } else {
+      return LT_UNREGISTERED;
+    }
   }
 
+  SharedDictionaryEntry* next() {
+    return (SharedDictionaryEntry*)(DictionaryEntry::next());
+  }
+
+  bool is_builtin() const {
+    return loader_type() == LT_BUILTIN;
+  }
+  bool is_unregistered() const {
+    return loader_type() == LT_UNREGISTERED;
+  }
+
+  void add_verification_constraint(Symbol* name,
+         Symbol* from_name, bool from_field_is_protected, bool from_is_array, bool from_is_object);
+  int finalize_verification_constraints();
+  void check_verification_constraints(InstanceKlass* klass, TRAPS);
+  void metaspace_pointers_do(MetaspaceClosure* it) NOT_CDS_RETURN;
+};
+
+class SharedDictionary : public Dictionary {
+  SharedDictionaryEntry* get_entry_for_builtin_loader(const Symbol* name) const;
+  SharedDictionaryEntry* get_entry_for_unregistered_loader(const Symbol* name,
+                                                           int clsfile_size,
+                                                           int clsfile_crc32) const;
+
+  // Convenience functions
+  SharedDictionaryEntry* bucket(int index) const {
+    return (SharedDictionaryEntry*)(Dictionary::bucket(index));
+  }
+
+public:
+  SharedDictionaryEntry* find_entry_for(Klass* klass);
+  void finalize_verification_constraints();
+
+  bool add_non_builtin_klass(const Symbol* class_name,
+                             ClassLoaderData* loader_data,
+                             InstanceKlass* obj);
+
+  void update_entry(Klass* klass, int id);
+
+  Klass* find_class_for_builtin_loader(const Symbol* name) const;
+  Klass* find_class_for_unregistered_loader(const Symbol* name,
+                                            int clsfile_size,
+                                            int clsfile_crc32) const;
+  bool class_exists_for_unregistered_loader(const Symbol* name) {
+    return (get_entry_for_unregistered_loader(name, -1, -1) != NULL);
+  }
+};
+
+class SystemDictionaryShared: public SystemDictionary {
+private:
+  // These _shared_xxxs arrays are used to initialize the java.lang.Package and
+  // java.security.ProtectionDomain objects associated with each shared class.
+  //
+  // See SystemDictionaryShared::init_security_info for more info.
+  static objArrayOop _shared_protection_domains;
+  static objArrayOop _shared_jar_urls;
+  static objArrayOop _shared_jar_manifests;
+
+  static InstanceKlass* load_shared_class_for_builtin_loader(
+                                               Symbol* class_name,
+                                               Handle class_loader,
+                                               TRAPS);
+  static Handle get_package_name(Symbol*  class_name, TRAPS);
+
+
+  // Package handling:
+  //
+  // 1. For named modules in the runtime image
+  //    BOOT classes: Reuses the existing JVM_GetSystemPackage(s) interfaces
+  //                  to get packages in named modules for shared classes.
+  //                  Package for non-shared classes in named module is also
+  //                  handled using JVM_GetSystemPackage(s).
+  //
+  //    APP  classes: VM calls ClassLoaders.AppClassLoader::definePackage(String, Module)
+  //                  to define package for shared app classes from named
+  //                  modules.
+  //
+  //    PLATFORM  classes: VM calls ClassLoaders.PlatformClassLoader::definePackage(String, Module)
+  //                  to define package for shared platform classes from named
+  //                  modules.
+  //
+  // 2. For unnamed modules
+  //    BOOT classes: Reuses the existing JVM_GetSystemPackage(s) interfaces to
+  //                  get packages for shared boot classes in unnamed modules.
+  //
+  //    APP  classes: VM calls ClassLoaders.AppClassLoader::defineOrCheckPackage()
+  //                  with with the manifest and url from archived data.
+  //
+  //    PLATFORM  classes: No package is defined.
+  //
+  // The following two define_shared_package() functions are used to define
+  // package for shared APP and PLATFORM classes.
+  static void define_shared_package(Symbol*  class_name,
+                                    Handle class_loader,
+                                    Handle manifest,
+                                    Handle url,
+                                    TRAPS);
+  static void define_shared_package(Symbol* class_name,
+                                    Handle class_loader,
+                                    ModuleEntry* mod_entry,
+                                    TRAPS);
+
+  static Handle get_shared_jar_manifest(int shared_path_index, TRAPS);
+  static Handle get_shared_jar_url(int shared_path_index, TRAPS);
+  static Handle get_protection_domain_from_classloader(Handle class_loader,
+                                                       Handle url, TRAPS);
+  static Handle get_shared_protection_domain(Handle class_loader,
+                                             int shared_path_index,
+                                             Handle url,
+                                             TRAPS);
+  static Handle get_shared_protection_domain(Handle class_loader,
+                                             ModuleEntry* mod, TRAPS);
+  static Handle init_security_info(Handle class_loader, InstanceKlass* ik, TRAPS);
+
+  static void atomic_set_array_index(objArrayOop array, int index, oop o) {
+    // Benign race condition:  array.obj_at(index) may already be filled in.
+    // The important thing here is that all threads pick up the same result.
+    // It doesn't matter which racing thread wins, as long as only one
+    // result is used by all threads, and all future queries.
+    array->atomic_compare_exchange_oop(index, o, NULL);
+  }
+
+  static oop shared_protection_domain(int index);
+  static void atomic_set_shared_protection_domain(int index, oop pd) {
+    atomic_set_array_index(_shared_protection_domains, index, pd);
+  }
+  static void allocate_shared_protection_domain_array(int size, TRAPS);
+  static oop shared_jar_url(int index);
+  static void atomic_set_shared_jar_url(int index, oop url) {
+    atomic_set_array_index(_shared_jar_urls, index, url);
+  }
+  static void allocate_shared_jar_url_array(int size, TRAPS);
+  static oop shared_jar_manifest(int index);
+  static void atomic_set_shared_jar_manifest(int index, oop man) {
+    atomic_set_array_index(_shared_jar_manifests, index, man);
+  }
+  static void allocate_shared_jar_manifest_array(int size, TRAPS);
+  static InstanceKlass* acquire_class_for_current_thread(
+                                 InstanceKlass *ik,
+                                 Handle class_loader,
+                                 Handle protection_domain,
+                                 TRAPS);
+
+public:
+  static void initialize(TRAPS);
+
+  // Called by PLATFORM/APP loader only
+  static InstanceKlass* find_or_load_shared_class(Symbol* class_name,
+                                               Handle class_loader,
+                                               TRAPS);
+
+
+  static void allocate_shared_data_arrays(int size, TRAPS);
+  static void oops_do(OopClosure* f);
+  static void roots_oops_do(OopClosure* f) {
+    oops_do(f);
+  }
+
+  // Check if sharing is supported for the class loader.
+  static bool is_sharing_possible(ClassLoaderData* loader_data) {
+    oop class_loader = loader_data->class_loader();
+    return (class_loader == NULL ||
+            (UseAppCDS && (SystemDictionary::is_system_class_loader(class_loader) ||
+                           SystemDictionary::is_platform_class_loader(class_loader)))
+            );
+  }
+  static bool is_shared_class_visible_for_classloader(InstanceKlass* ik,
+                                                      Handle class_loader,
+                                                      const char* pkg_string,
+                                                      Symbol* pkg_name,
+                                                      PackageEntry* pkg_entry,
+                                                      ModuleEntry* mod_entry,
+                                                      TRAPS);
+  static PackageEntry* get_package_entry(Symbol* pkg,
+                                         ClassLoaderData *loader_data) {
+    if (loader_data != NULL) {
+      PackageEntryTable* pkgEntryTable = loader_data->packages();
+      return pkgEntryTable->lookup_only(pkg);
+    }
+    return NULL;
+  }
+
+  static bool add_non_builtin_klass(Symbol* class_name, ClassLoaderData* loader_data,
+                                    InstanceKlass* k, TRAPS);
   static Klass* dump_time_resolve_super_or_fail(Symbol* child_name,
                                                 Symbol* class_name,
                                                 Handle class_loader,
                                                 Handle protection_domain,
                                                 bool is_superclass,
-                                                TRAPS) {
-    return NULL;
-  }
+                                                TRAPS);
 
   static size_t dictionary_entry_size() {
-    return sizeof(DictionaryEntry);
+    return (DumpSharedSpaces) ? sizeof(SharedDictionaryEntry) : sizeof(DictionaryEntry);
+  }
+  static void init_shared_dictionary_entry(Klass* k, DictionaryEntry* entry) NOT_CDS_RETURN;
+  static bool is_builtin(DictionaryEntry* ent) {
+    // Can't use virtual function is_builtin because DictionaryEntry doesn't initialize
+    // vtable because it's not constructed properly.
+    SharedDictionaryEntry* entry = (SharedDictionaryEntry*)ent;
+    return entry->is_builtin();
   }
 
-  static void init_shared_dictionary_entry(Klass* k, DictionaryEntry* entry) {}
-  static bool is_builtin(DictionaryEntry* entry) { return true; }
+  // For convenient access to the SharedDictionaryEntry's of the archived classes.
+  static SharedDictionary* shared_dictionary() {
+    assert(!DumpSharedSpaces, "not for dumping");
+    return (SharedDictionary*)SystemDictionary::shared_dictionary();
+  }
 
-  static InstanceKlass* lookup_from_stream(Symbol* class_name,
+  static SharedDictionary* boot_loader_dictionary() {
+    return (SharedDictionary*)ClassLoaderData::the_null_class_loader_data()->dictionary();
+  }
+
+  static void update_shared_entry(Klass* klass, int id) {
+    assert(DumpSharedSpaces, "sanity");
+    assert((SharedDictionary*)(klass->class_loader_data()->dictionary()) != NULL, "sanity");
+    ((SharedDictionary*)(klass->class_loader_data()->dictionary()))->update_entry(klass, id);
+  }
+
+  static void set_shared_class_misc_info(Klass* k, ClassFileStream* cfs);
+
+  static InstanceKlass* lookup_from_stream(const Symbol* class_name,
                                            Handle class_loader,
                                            Handle protection_domain,
                                            const ClassFileStream* st,
-                                           TRAPS) {
-    return NULL;
-  }
-
-  // The (non-application) CDS implementation supports only classes in the boot
-  // class loader, which ensures that the verification constraints are the same
-  // during archive creation time and runtime. Thus we can do the constraint checks
-  // entirely during archive creation time.
+                                           TRAPS);
+  // "verification_constraints" are a set of checks performed by
+  // VerificationType::is_reference_assignable_from when verifying a shared class during
+  // dump time.
+  //
+  // With AppCDS, it is possible to override archived classes by calling
+  // ClassLoader.defineClass() directly. SystemDictionary::load_shared_class() already
+  // ensures that you cannot load a shared class if its super type(s) are changed. However,
+  // we need an additional check to ensure that the verification_constraints did not change
+  // between dump time and runtime.
   static bool add_verification_constraint(Klass* k, Symbol* name,
                   Symbol* from_name, bool from_field_is_protected,
-                  bool from_is_array, bool from_is_object) {return false;}
-  static void finalize_verification_constraints() {}
+                  bool from_is_array, bool from_is_object) NOT_CDS_RETURN_(false);
+  static void finalize_verification_constraints() NOT_CDS_RETURN;
   static void check_verification_constraints(InstanceKlass* klass,
-                                              TRAPS) {}
-};
-
-class SharedDictionaryEntry : public DictionaryEntry {
-public:
-  void metaspace_pointers_do(MetaspaceClosure* it) {}
+                                              TRAPS) NOT_CDS_RETURN;
 };
 
 #endif // SHARE_VM_CLASSFILE_SYSTEMDICTIONARYSHARED_HPP
diff --git a/src/hotspot/share/classfile/systemDictionary_ext.hpp b/src/hotspot/share/classfile/systemDictionary_ext.hpp
index 698805b657d..6d257cd09e9 100644
--- a/src/hotspot/share/classfile/systemDictionary_ext.hpp
+++ b/src/hotspot/share/classfile/systemDictionary_ext.hpp
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2017 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
@@ -25,6 +25,17 @@
 #ifndef SHARE_VM_CLASSFILE_SYSTEMDICTIONARY_EXT_HPP
 #define SHARE_VM_CLASSFILE_SYSTEMDICTIONARY_EXT_HPP
 
+#if INCLUDE_CDS
+
+#define WK_KLASSES_DO_EXT(do_klass) \
+  /* well-known classes */                                                                                            \
+  do_klass(jdk_internal_loader_ClassLoaders_klass,         jdk_internal_loader_ClassLoaders,            Pre )         \
+  /*end*/
+
+#else
+
 #define WK_KLASSES_DO_EXT(do_klass)
 
+#endif // INCLUDE_CDS
+
 #endif // SHARE_VM_CLASSFILE_SYSTEMDICTIONARY_EXT_HPP
diff --git a/src/hotspot/share/classfile/vmSymbols.hpp b/src/hotspot/share/classfile/vmSymbols.hpp
index 73fb9296772..65246e04bae 100644
--- a/src/hotspot/share/classfile/vmSymbols.hpp
+++ b/src/hotspot/share/classfile/vmSymbols.hpp
@@ -26,7 +26,6 @@
 #define SHARE_VM_CLASSFILE_VMSYMBOLS_HPP
 
 #include "classfile/moduleEntry.hpp"
-#include "classfile/vmSymbols_ext.hpp"
 #include "oops/symbol.hpp"
 #include "memory/iterator.hpp"
 #include "trace/traceMacros.hpp"
@@ -673,8 +672,12 @@
   /* trace signatures */                                                                                          \
   TRACE_TEMPLATES(template)                                                                                       \
                                                                                                                   \
-  /* extensions */                                                                                                \
-  VM_SYMBOLS_DO_EXT(template, do_alias)                                                                           \
+  /* cds */                                                                                                       \
+  template(jdk_internal_loader_ClassLoaders,       "jdk/internal/loader/ClassLoaders")                            \
+  template(jdk_vm_cds_SharedClassInfo,             "jdk/vm/cds/SharedClassInfo")                                  \
+  template(url_void_signature,                     "(Ljava/net/URL;)V")                                           \
+  template(toFileURL_name,                         "toFileURL")                                                   \
+  template(toFileURL_signature,                    "(Ljava/lang/String;)Ljava/net/URL;")                          \
                                                                                                                   \
   /*end*/
 
diff --git a/src/hotspot/share/code/codeCache.cpp b/src/hotspot/share/code/codeCache.cpp
index b49892b1bf0..cf37f8db29c 100644
--- a/src/hotspot/share/code/codeCache.cpp
+++ b/src/hotspot/share/code/codeCache.cpp
@@ -259,12 +259,12 @@ void CodeCache::initialize_heaps() {
   }
 
   // We do not need the profiled CodeHeap, use all space for the non-profiled CodeHeap
-  if(!heap_available(CodeBlobType::MethodProfiled)) {
+  if (!heap_available(CodeBlobType::MethodProfiled)) {
     non_profiled_size += profiled_size;
     profiled_size = 0;
   }
   // We do not need the non-profiled CodeHeap, use all space for the non-nmethod CodeHeap
-  if(!heap_available(CodeBlobType::MethodNonProfiled)) {
+  if (!heap_available(CodeBlobType::MethodNonProfiled)) {
     non_nmethod_size += non_profiled_size;
     non_profiled_size = 0;
   }
@@ -282,10 +282,11 @@ void CodeCache::initialize_heaps() {
   FLAG_SET_ERGO(uintx, ProfiledCodeHeapSize, profiled_size);
   FLAG_SET_ERGO(uintx, NonProfiledCodeHeapSize, non_profiled_size);
 
-  // Align CodeHeaps
-  size_t alignment = heap_alignment();
+  // If large page support is enabled, align code heaps according to large
+  // page size to make sure that code cache is covered by large pages.
+  const size_t alignment = MAX2(page_size(false), (size_t) os::vm_allocation_granularity());
   non_nmethod_size = align_up(non_nmethod_size, alignment);
-  profiled_size   = align_down(profiled_size, alignment);
+  profiled_size    = align_down(profiled_size, alignment);
 
   // Reserve one continuous chunk of memory for CodeHeaps and split it into
   // parts for the individual heaps. The memory layout looks like this:
@@ -308,37 +309,29 @@ void CodeCache::initialize_heaps() {
   add_heap(non_profiled_space, "CodeHeap 'non-profiled nmethods'", CodeBlobType::MethodNonProfiled);
 }
 
-size_t CodeCache::heap_alignment() {
-  // If large page support is enabled, align code heaps according to large
-  // page size to make sure that code cache is covered by large pages.
-  const size_t page_size = os::can_execute_large_page_memory() ?
-             os::page_size_for_region_unaligned(ReservedCodeCacheSize, 8) :
-             os::vm_page_size();
-  return MAX2(page_size, (size_t) os::vm_allocation_granularity());
+size_t CodeCache::page_size(bool aligned) {
+  if (os::can_execute_large_page_memory()) {
+    return aligned ? os::page_size_for_region_aligned(ReservedCodeCacheSize, 8) :
+                     os::page_size_for_region_unaligned(ReservedCodeCacheSize, 8);
+  } else {
+    return os::vm_page_size();
+  }
 }
 
 ReservedCodeSpace CodeCache::reserve_heap_memory(size_t size) {
-  // Determine alignment
-  const size_t page_size = os::can_execute_large_page_memory() ?
-          MIN2(os::page_size_for_region_aligned(InitialCodeCacheSize, 8),
-               os::page_size_for_region_aligned(size, 8)) :
-          os::vm_page_size();
-  const size_t granularity = os::vm_allocation_granularity();
-  const size_t r_align = MAX2(page_size, granularity);
-  const size_t r_size = align_up(size, r_align);
-  const size_t rs_align = page_size == (size_t) os::vm_page_size() ? 0 :
-    MAX2(page_size, granularity);
-
-  ReservedCodeSpace rs(r_size, rs_align, rs_align > 0);
-
+  // Align and reserve space for code cache
+  const size_t rs_ps = page_size();
+  const size_t rs_align = MAX2(rs_ps, (size_t) os::vm_allocation_granularity());
+  const size_t rs_size = align_up(size, rs_align);
+  ReservedCodeSpace rs(rs_size, rs_align, rs_ps > (size_t) os::vm_page_size());
   if (!rs.is_reserved()) {
-    vm_exit_during_initialization("Could not reserve enough space for code cache");
+    vm_exit_during_initialization(err_msg("Could not reserve enough space for code cache (" SIZE_FORMAT "K)",
+                                          rs_size/K));
   }
 
   // Initialize bounds
   _low_bound = (address)rs.base();
   _high_bound = _low_bound + rs.size();
-
   return rs;
 }
 
@@ -415,7 +408,8 @@ void CodeCache::add_heap(ReservedSpace rs, const char* name, int code_blob_type)
   size_t size_initial = MIN2(InitialCodeCacheSize, rs.size());
   size_initial = align_up(size_initial, os::vm_page_size());
   if (!heap->reserve(rs, size_initial, CodeCacheSegmentSize)) {
-    vm_exit_during_initialization("Could not reserve enough space for code cache");
+    vm_exit_during_initialization(err_msg("Could not reserve enough space in %s (" SIZE_FORMAT "K)",
+                                          heap->name(), size_initial/K));
   }
 
   // Register the CodeHeap
diff --git a/src/hotspot/share/code/codeCache.hpp b/src/hotspot/share/code/codeCache.hpp
index 2749acd05b7..e8a098d0279 100644
--- a/src/hotspot/share/code/codeCache.hpp
+++ b/src/hotspot/share/code/codeCache.hpp
@@ -107,7 +107,7 @@ class CodeCache : AllStatic {
   static CodeHeap* get_code_heap(int code_blob_type);         // Returns the CodeHeap for the given CodeBlobType
   // Returns the name of the VM option to set the size of the corresponding CodeHeap
   static const char* get_code_heap_flag_name(int code_blob_type);
-  static size_t heap_alignment();                             // Returns the alignment of the CodeHeaps in bytes
+  static size_t page_size(bool aligned = true);               // Returns the page size used by the CodeCache
   static ReservedCodeSpace reserve_heap_memory(size_t size);  // Reserves one continuous chunk of memory for the CodeHeaps
 
   // Iteration
diff --git a/src/hotspot/share/code/debugInfo.cpp b/src/hotspot/share/code/debugInfo.cpp
index 76a92387528..c81ef90e9d0 100644
--- a/src/hotspot/share/code/debugInfo.cpp
+++ b/src/hotspot/share/code/debugInfo.cpp
@@ -28,6 +28,8 @@
 #include "code/nmethod.hpp"
 #include "oops/oop.inline.hpp"
 #include "runtime/handles.inline.hpp"
+#include "runtime/interfaceSupport.hpp"
+#include "runtime/thread.hpp"
 
 // Constructors
 
@@ -209,14 +211,24 @@ void ConstantDoubleValue::print_on(outputStream* st) const {
 // ConstantOopWriteValue
 
 void ConstantOopWriteValue::write_on(DebugInfoWriteStream* stream) {
-  assert(JNIHandles::resolve(value()) == NULL ||
-         Universe::heap()->is_in_reserved(JNIHandles::resolve(value())),
-         "Should be in heap");
+#ifdef ASSERT
+  {
+    // cannot use ThreadInVMfromNative here since in case of JVMCI compiler,
+    // thread is already in VM state.
+    ThreadInVMfromUnknown tiv;
+    assert(JNIHandles::resolve(value()) == NULL ||
+           Universe::heap()->is_in_reserved(JNIHandles::resolve(value())),
+           "Should be in heap");
+ }
+#endif
   stream->write_int(CONSTANT_OOP_CODE);
   stream->write_handle(value());
 }
 
 void ConstantOopWriteValue::print_on(outputStream* st) const {
+  // using ThreadInVMfromUnknown here since in case of JVMCI compiler,
+  // thread is already in VM state.
+  ThreadInVMfromUnknown tiv;
   JNIHandles::resolve(value())->print_value_on(st);
 }
 
diff --git a/src/hotspot/share/code/nmethod.cpp b/src/hotspot/share/code/nmethod.cpp
index 82757c0e2ff..83cc30d79b3 100644
--- a/src/hotspot/share/code/nmethod.cpp
+++ b/src/hotspot/share/code/nmethod.cpp
@@ -438,14 +438,14 @@ nmethod* nmethod::new_native_nmethod(const methodHandle& method,
                                             basic_lock_sp_offset, oop_maps);
     NOT_PRODUCT(if (nm != NULL)  native_nmethod_stats.note_native_nmethod(nm));
   }
-  // verify nmethod
-  debug_only(if (nm) nm->verify();) // might block
 
   if (nm != NULL) {
-    nm->log_new_nmethod();
-  }
+    // verify nmethod
+    debug_only(nm->verify();) // might block
 
-  nm->make_in_use();
+    nm->log_new_nmethod();
+    nm->make_in_use();
+  }
   return nm;
 }
 
diff --git a/src/hotspot/share/code/nmethod.hpp b/src/hotspot/share/code/nmethod.hpp
index 4ac6ab9226d..c2862c84a0b 100644
--- a/src/hotspot/share/code/nmethod.hpp
+++ b/src/hotspot/share/code/nmethod.hpp
@@ -124,7 +124,7 @@ class nmethod : public CompiledMethod {
   bool _unload_reported;
 
   // Protected by Patching_lock
-  volatile char _state;             // {not_installed, in_use, not_entrant, zombie, unloaded}
+  volatile signed char _state;               // {not_installed, in_use, not_entrant, zombie, unloaded}
 
 #ifdef ASSERT
   bool _oops_are_stale;  // indicates that it's no longer safe to access oops section
diff --git a/src/hotspot/share/code/stubs.cpp b/src/hotspot/share/code/stubs.cpp
index 56883bc623d..81717b919ba 100644
--- a/src/hotspot/share/code/stubs.cpp
+++ b/src/hotspot/share/code/stubs.cpp
@@ -78,7 +78,6 @@ StubQueue::StubQueue(StubInterface* stub_interface, int buffer_size,
   _queue_begin     = 0;
   _queue_end       = 0;
   _number_of_stubs = 0;
-  register_queue(this);
 }
 
 
@@ -205,36 +204,6 @@ void StubQueue::remove_all(){
 }
 
 
-enum { StubQueueLimit = 10 };  // there are only a few in the world
-static StubQueue* registered_stub_queues[StubQueueLimit];
-
-void StubQueue::register_queue(StubQueue* sq) {
-  for (int i = 0; i < StubQueueLimit; i++) {
-    if (registered_stub_queues[i] == NULL) {
-      registered_stub_queues[i] = sq;
-      return;
-    }
-  }
-  ShouldNotReachHere();
-}
-
-
-void StubQueue::queues_do(void f(StubQueue* sq)) {
-  for (int i = 0; i < StubQueueLimit; i++) {
-    if (registered_stub_queues[i] != NULL) {
-      f(registered_stub_queues[i]);
-    }
-  }
-}
-
-
-void StubQueue::stubs_do(void f(Stub* s)) {
-  debug_only(verify();)
-  MutexLockerEx lock(_mutex);
-  for (Stub* s = first(); s != NULL; s = next(s)) f(s);
-}
-
-
 void StubQueue::verify() {
   // verify only if initialized
   if (_stub_buffer == NULL) return;
diff --git a/src/hotspot/share/code/stubs.hpp b/src/hotspot/share/code/stubs.hpp
index ab84ffaada9..d50e30d1bd3 100644
--- a/src/hotspot/share/code/stubs.hpp
+++ b/src/hotspot/share/code/stubs.hpp
@@ -172,8 +172,6 @@ class StubQueue: public CHeapObj<mtCode> {
   void  stub_verify(Stub* s)                     { _stub_interface->verify(s); }
   void  stub_print(Stub* s)                      { _stub_interface->print(s); }
 
-  static void register_queue(StubQueue*);
-
  public:
   StubQueue(StubInterface* stub_interface, int buffer_size, Mutex* lock,
             const char* name);
@@ -204,8 +202,6 @@ class StubQueue: public CHeapObj<mtCode> {
   void deallocate_unused_tail();                 // deallocate the unused tail of the underlying CodeBlob
                                                  // only used from TemplateInterpreter::initialize()
   // Iteration
-  static void queues_do(void f(StubQueue* s));   // call f with each StubQueue
-  void  stubs_do(void f(Stub* s));               // call f with all stubs
   Stub* first() const                            { return number_of_stubs() > 0 ? stub_at(_queue_begin) : NULL; }
   Stub* next(Stub* s) const                      { int i = index_of(s) + stub_size(s);
                                                    // Only wrap around in the non-contiguous case (see stubss.cpp)
@@ -213,9 +209,6 @@ class StubQueue: public CHeapObj<mtCode> {
                                                    return (i == _queue_end) ? NULL : stub_at(i);
                                                  }
 
-  address stub_code_begin(Stub* s) const         { return _stub_interface->code_begin(s); }
-  address stub_code_end(Stub* s) const           { return _stub_interface->code_end(s);   }
-
   // Debugging/printing
   void  verify();                                // verifies the stub queue
   void  print();                                 // prints information about the stub queue
diff --git a/src/hotspot/share/compiler/compileTask.hpp b/src/hotspot/share/compiler/compileTask.hpp
index c7329c9ba45..3522ee4f10e 100644
--- a/src/hotspot/share/compiler/compileTask.hpp
+++ b/src/hotspot/share/compiler/compileTask.hpp
@@ -25,10 +25,10 @@
 #ifndef SHARE_VM_COMPILER_COMPILETASK_HPP
 #define SHARE_VM_COMPILER_COMPILETASK_HPP
 
-#include "code/nmethod.hpp"
 #include "ci/ciMethod.hpp"
+#include "code/nmethod.hpp"
 #include "compiler/compileLog.hpp"
-#include "memory/allocation.inline.hpp"
+#include "memory/allocation.hpp"
 #include "utilities/xmlstream.hpp"
 
 // CompileTask
diff --git a/src/hotspot/share/compiler/methodMatcher.hpp b/src/hotspot/share/compiler/methodMatcher.hpp
index 546af4d5ed7..4adf6587417 100644
--- a/src/hotspot/share/compiler/methodMatcher.hpp
+++ b/src/hotspot/share/compiler/methodMatcher.hpp
@@ -25,7 +25,7 @@
 #ifndef SHARE_VM_COMPILER_METHODMATCHER_HPP
 #define SHARE_VM_COMPILER_METHODMATCHER_HPP
 
-#include "memory/allocation.inline.hpp"
+#include "memory/allocation.hpp"
 #include "runtime/handles.inline.hpp"
 #include "memory/resourceArea.hpp"
 
diff --git a/src/hotspot/share/compiler/oopMap.hpp b/src/hotspot/share/compiler/oopMap.hpp
index 6c9fe4ee299..853f42a6f0d 100644
--- a/src/hotspot/share/compiler/oopMap.hpp
+++ b/src/hotspot/share/compiler/oopMap.hpp
@@ -28,6 +28,7 @@
 #include "code/compressedStream.hpp"
 #include "code/vmreg.hpp"
 #include "memory/allocation.hpp"
+#include "oops/oopsHierarchy.hpp"
 #include "utilities/growableArray.hpp"
 
 // Interface for generating the frame map for compiled code.  A frame map
@@ -42,6 +43,7 @@
 class frame;
 class RegisterMap;
 class DerivedPointerEntry;
+class OopClosure;
 
 class OopMapValue: public StackObj {
   friend class VMStructs;
diff --git a/src/hotspot/share/gc/cms/allocationStats.cpp b/src/hotspot/share/gc/cms/allocationStats.cpp
index f23fa37c313..3bc7074a46e 100644
--- a/src/hotspot/share/gc/cms/allocationStats.cpp
+++ b/src/hotspot/share/gc/cms/allocationStats.cpp
@@ -30,3 +30,20 @@
 // Technically this should be derived from machine speed, and
 // ideally it would be dynamically adjusted.
 float AllocationStats::_threshold = ((float)CMS_SweepTimerThresholdMillis)/1000;
+
+void AllocationStats::initialize(bool split_birth)   {
+  AdaptivePaddedAverage* dummy =
+    new (&_demand_rate_estimate) AdaptivePaddedAverage(CMS_FLSWeight,
+                                                       CMS_FLSPadding);
+  _desired = 0;
+  _coal_desired = 0;
+  _surplus = 0;
+  _bfr_surp = 0;
+  _prev_sweep = 0;
+  _before_sweep = 0;
+  _coal_births = 0;
+  _coal_deaths = 0;
+  _split_births = (split_birth ? 1 : 0);
+  _split_deaths = 0;
+  _returned_bytes = 0;
+}
diff --git a/src/hotspot/share/gc/cms/allocationStats.hpp b/src/hotspot/share/gc/cms/allocationStats.hpp
index 747b2904bed..c98b23f6918 100644
--- a/src/hotspot/share/gc/cms/allocationStats.hpp
+++ b/src/hotspot/share/gc/cms/allocationStats.hpp
@@ -64,22 +64,7 @@ class AllocationStats VALUE_OBJ_CLASS_SPEC {
   ssize_t     _split_deaths;     // loss from splitting
   size_t      _returned_bytes;   // number of bytes returned to list.
  public:
-  void initialize(bool split_birth = false) {
-    AdaptivePaddedAverage* dummy =
-      new (&_demand_rate_estimate) AdaptivePaddedAverage(CMS_FLSWeight,
-                                                         CMS_FLSPadding);
-    _desired = 0;
-    _coal_desired = 0;
-    _surplus = 0;
-    _bfr_surp = 0;
-    _prev_sweep = 0;
-    _before_sweep = 0;
-    _coal_births = 0;
-    _coal_deaths = 0;
-    _split_births = (split_birth ? 1 : 0);
-    _split_deaths = 0;
-    _returned_bytes = 0;
-  }
+  void initialize(bool split_birth = false);
 
   AllocationStats() {
     initialize();
diff --git a/src/hotspot/share/gc/cms/cmsCollectorPolicy.cpp b/src/hotspot/share/gc/cms/cmsCollectorPolicy.cpp
index 0333b4f433e..e7160fb622a 100644
--- a/src/hotspot/share/gc/cms/cmsCollectorPolicy.cpp
+++ b/src/hotspot/share/gc/cms/cmsCollectorPolicy.cpp
@@ -71,6 +71,6 @@ void ConcurrentMarkSweepPolicy::initialize_size_policy(size_t init_eden_size,
 }
 
 void ConcurrentMarkSweepPolicy::initialize_gc_policy_counters() {
-  // initialize the policy counters - 2 collectors, 3 generations
-  _gc_policy_counters = new GCPolicyCounters("ParNew:CMS", 2, 3);
+  // initialize the policy counters - 2 collectors, 2 generations
+  _gc_policy_counters = new GCPolicyCounters("ParNew:CMS", 2, 2);
 }
diff --git a/src/hotspot/share/gc/cms/cmsHeap.cpp b/src/hotspot/share/gc/cms/cmsHeap.cpp
index da344cce24d..8b44d900312 100644
--- a/src/hotspot/share/gc/cms/cmsHeap.cpp
+++ b/src/hotspot/share/gc/cms/cmsHeap.cpp
@@ -23,17 +23,48 @@
  */
 
 #include "precompiled.hpp"
+#include "gc/cms/compactibleFreeListSpace.hpp"
+#include "gc/cms/concurrentMarkSweepGeneration.hpp"
 #include "gc/cms/concurrentMarkSweepThread.hpp"
 #include "gc/cms/cmsHeap.hpp"
+#include "gc/cms/parNewGeneration.hpp"
 #include "gc/cms/vmCMSOperations.hpp"
+#include "gc/shared/genMemoryPools.hpp"
 #include "gc/shared/genOopClosures.inline.hpp"
 #include "gc/shared/strongRootsScope.hpp"
 #include "gc/shared/workgroup.hpp"
 #include "oops/oop.inline.hpp"
 #include "runtime/vmThread.hpp"
+#include "services/memoryManager.hpp"
 #include "utilities/stack.inline.hpp"
 
-CMSHeap::CMSHeap(GenCollectorPolicy *policy) : GenCollectedHeap(policy) {
+class CompactibleFreeListSpacePool : public CollectedMemoryPool {
+private:
+  CompactibleFreeListSpace* _space;
+public:
+  CompactibleFreeListSpacePool(CompactibleFreeListSpace* space,
+                               const char* name,
+                               size_t max_size,
+                               bool support_usage_threshold) :
+    CollectedMemoryPool(name, space->capacity(), max_size, support_usage_threshold),
+    _space(space) {
+  }
+
+  MemoryUsage get_memory_usage() {
+    size_t max_heap_size   = (available_for_allocation() ? max_size() : 0);
+    size_t used      = used_in_bytes();
+    size_t committed = _space->capacity();
+
+    return MemoryUsage(initial_size(), used, committed, max_heap_size);
+  }
+
+  size_t used_in_bytes() {
+    return _space->used();
+  }
+};
+
+CMSHeap::CMSHeap(GenCollectorPolicy *policy) :
+  GenCollectedHeap(policy), _eden_pool(NULL), _survivor_pool(NULL), _old_pool(NULL) {
   _workers = new WorkGang("GC Thread", ParallelGCThreads,
                           /* are_GC_task_threads */true,
                           /* are_ConcurrentGC_threads */false);
@@ -54,6 +85,38 @@ jint CMSHeap::initialize() {
   return JNI_OK;
 }
 
+void CMSHeap::initialize_serviceability() {
+  _young_manager = new GCMemoryManager("ParNew", "end of minor GC");
+  _old_manager = new GCMemoryManager("ConcurrentMarkSweep", "end of major GC");
+
+  ParNewGeneration* young = (ParNewGeneration*) young_gen();
+  _eden_pool = new ContiguousSpacePool(young->eden(),
+                                       "Par Eden Space",
+                                       young->max_eden_size(),
+                                       false);
+
+  _survivor_pool = new SurvivorContiguousSpacePool(young,
+                                                   "Par Survivor Space",
+                                                   young->max_survivor_size(),
+                                                   false);
+
+  ConcurrentMarkSweepGeneration* old = (ConcurrentMarkSweepGeneration*) old_gen();
+  _old_pool = new CompactibleFreeListSpacePool(old->cmsSpace(),
+                                               "CMS Old Gen",
+                                               old->reserved().byte_size(),
+                                               true);
+
+  _young_manager->add_pool(_eden_pool);
+  _young_manager->add_pool(_survivor_pool);
+  young->set_gc_manager(_young_manager);
+
+  _old_manager->add_pool(_eden_pool);
+  _old_manager->add_pool(_survivor_pool);
+  _old_manager->add_pool(_old_pool);
+  old ->set_gc_manager(_old_manager);
+
+}
+
 void CMSHeap::check_gen_kinds() {
   assert(young_gen()->kind() == Generation::ParNew,
          "Wrong youngest generation type");
@@ -183,3 +246,18 @@ void CMSHeap::gc_epilogue(bool full) {
   GenCollectedHeap::gc_epilogue(full);
   always_do_update_barrier = true;
 };
+
+GrowableArray<GCMemoryManager*> CMSHeap::memory_managers() {
+  GrowableArray<GCMemoryManager*> memory_managers(2);
+  memory_managers.append(_young_manager);
+  memory_managers.append(_old_manager);
+  return memory_managers;
+}
+
+GrowableArray<MemoryPool*> CMSHeap::memory_pools() {
+  GrowableArray<MemoryPool*> memory_pools(3);
+  memory_pools.append(_eden_pool);
+  memory_pools.append(_survivor_pool);
+  memory_pools.append(_old_pool);
+  return memory_pools;
+}
diff --git a/src/hotspot/share/gc/cms/cmsHeap.hpp b/src/hotspot/share/gc/cms/cmsHeap.hpp
index bcd30cba859..93079d7cf2b 100644
--- a/src/hotspot/share/gc/cms/cmsHeap.hpp
+++ b/src/hotspot/share/gc/cms/cmsHeap.hpp
@@ -29,9 +29,12 @@
 #include "gc/shared/collectedHeap.hpp"
 #include "gc/shared/gcCause.hpp"
 #include "gc/shared/genCollectedHeap.hpp"
+#include "utilities/growableArray.hpp"
 
 class CLDClosure;
 class GenCollectorPolicy;
+class GCMemoryManager;
+class MemoryPool;
 class OopsInGenClosure;
 class outputStream;
 class StrongRootsScope;
@@ -80,6 +83,9 @@ public:
   void safepoint_synchronize_begin();
   void safepoint_synchronize_end();
 
+  virtual GrowableArray<GCMemoryManager*> memory_managers();
+  virtual GrowableArray<MemoryPool*> memory_pools();
+
   // If "young_gen_as_roots" is false, younger generations are
   // not scanned as roots; in this case, the caller must be arranging to
   // scan the younger generations itself.  (For example, a generation might
@@ -92,12 +98,19 @@ public:
                          OopsInGenClosure* root_closure,
                          CLDClosure* cld_closure);
 
+  GCMemoryManager* old_manager() const { return _old_manager; }
+
 private:
   WorkGang* _workers;
+  MemoryPool* _eden_pool;
+  MemoryPool* _survivor_pool;
+  MemoryPool* _old_pool;
 
   virtual void gc_prologue(bool full);
   virtual void gc_epilogue(bool full);
 
+  virtual void initialize_serviceability();
+
   // Accessor for memory state verification support
   NOT_PRODUCT(
     virtual size_t skip_header_HeapWords() { return CMSCollector::skip_header_HeapWords(); }
diff --git a/src/hotspot/share/gc/cms/concurrentMarkSweepGeneration.cpp b/src/hotspot/share/gc/cms/concurrentMarkSweepGeneration.cpp
index c49112957b0..4f8f7836550 100644
--- a/src/hotspot/share/gc/cms/concurrentMarkSweepGeneration.cpp
+++ b/src/hotspot/share/gc/cms/concurrentMarkSweepGeneration.cpp
@@ -8116,42 +8116,42 @@ size_t MarkDeadObjectsClosure::do_blk(HeapWord* addr) {
 }
 
 TraceCMSMemoryManagerStats::TraceCMSMemoryManagerStats(CMSCollector::CollectorState phase, GCCause::Cause cause): TraceMemoryManagerStats() {
-
+  GCMemoryManager* manager = CMSHeap::heap()->old_manager();
   switch (phase) {
     case CMSCollector::InitialMarking:
-      initialize(true  /* fullGC */ ,
-                 cause /* cause of the GC */,
-                 true  /* recordGCBeginTime */,
-                 true  /* recordPreGCUsage */,
-                 false /* recordPeakUsage */,
-                 false /* recordPostGCusage */,
-                 true  /* recordAccumulatedGCTime */,
-                 false /* recordGCEndTime */,
-                 false /* countCollection */  );
+      initialize(manager /* GC manager */ ,
+                 cause   /* cause of the GC */,
+                 true    /* recordGCBeginTime */,
+                 true    /* recordPreGCUsage */,
+                 false   /* recordPeakUsage */,
+                 false   /* recordPostGCusage */,
+                 true    /* recordAccumulatedGCTime */,
+                 false   /* recordGCEndTime */,
+                 false   /* countCollection */  );
       break;
 
     case CMSCollector::FinalMarking:
-      initialize(true  /* fullGC */ ,
-                 cause /* cause of the GC */,
-                 false /* recordGCBeginTime */,
-                 false /* recordPreGCUsage */,
-                 false /* recordPeakUsage */,
-                 false /* recordPostGCusage */,
-                 true  /* recordAccumulatedGCTime */,
-                 false /* recordGCEndTime */,
-                 false /* countCollection */  );
+      initialize(manager /* GC manager */ ,
+                 cause   /* cause of the GC */,
+                 false   /* recordGCBeginTime */,
+                 false   /* recordPreGCUsage */,
+                 false   /* recordPeakUsage */,
+                 false   /* recordPostGCusage */,
+                 true    /* recordAccumulatedGCTime */,
+                 false   /* recordGCEndTime */,
+                 false   /* countCollection */  );
       break;
 
     case CMSCollector::Sweeping:
-      initialize(true  /* fullGC */ ,
-                 cause /* cause of the GC */,
-                 false /* recordGCBeginTime */,
-                 false /* recordPreGCUsage */,
-                 true  /* recordPeakUsage */,
-                 true  /* recordPostGCusage */,
-                 false /* recordAccumulatedGCTime */,
-                 true  /* recordGCEndTime */,
-                 true  /* countCollection */  );
+      initialize(manager /* GC manager */ ,
+                 cause   /* cause of the GC */,
+                 false   /* recordGCBeginTime */,
+                 false   /* recordPreGCUsage */,
+                 true    /* recordPeakUsage */,
+                 true    /* recordPostGCusage */,
+                 false   /* recordAccumulatedGCTime */,
+                 true    /* recordGCEndTime */,
+                 true    /* countCollection */  );
       break;
 
     default:
diff --git a/src/hotspot/share/gc/cms/gSpaceCounters.cpp b/src/hotspot/share/gc/cms/gSpaceCounters.cpp
index dce1f39f6d5..36776c52945 100644
--- a/src/hotspot/share/gc/cms/gSpaceCounters.cpp
+++ b/src/hotspot/share/gc/cms/gSpaceCounters.cpp
@@ -25,6 +25,7 @@
 #include "precompiled.hpp"
 #include "gc/cms/gSpaceCounters.hpp"
 #include "gc/shared/generation.hpp"
+#include "memory/allocation.inline.hpp"
 #include "memory/resourceArea.hpp"
 #include "utilities/macros.hpp"
 
@@ -71,3 +72,7 @@ GSpaceCounters::GSpaceCounters(const char* name, int ordinal, size_t max_size,
                                      _gen->capacity(), CHECK);
   }
 }
+
+GSpaceCounters::~GSpaceCounters() {
+  if (_name_space != NULL) FREE_C_HEAP_ARRAY(char, _name_space);
+}
diff --git a/src/hotspot/share/gc/cms/gSpaceCounters.hpp b/src/hotspot/share/gc/cms/gSpaceCounters.hpp
index 64ae6c60b26..40f181f4c4c 100644
--- a/src/hotspot/share/gc/cms/gSpaceCounters.hpp
+++ b/src/hotspot/share/gc/cms/gSpaceCounters.hpp
@@ -52,9 +52,7 @@ class GSpaceCounters: public CHeapObj<mtGC> {
   GSpaceCounters(const char* name, int ordinal, size_t max_size, Generation* g,
                  GenerationCounters* gc, bool sampled=true);
 
-  ~GSpaceCounters() {
-    if (_name_space != NULL) FREE_C_HEAP_ARRAY(char, _name_space);
-  }
+  ~GSpaceCounters();
 
   inline void update_capacity() {
     _capacity->set_value(_gen->capacity());
diff --git a/src/hotspot/share/gc/g1/dirtyCardQueue.cpp b/src/hotspot/share/gc/g1/dirtyCardQueue.cpp
index 11c2da764e4..ae6a476ff59 100644
--- a/src/hotspot/share/gc/g1/dirtyCardQueue.cpp
+++ b/src/hotspot/share/gc/g1/dirtyCardQueue.cpp
@@ -32,6 +32,7 @@
 #include "runtime/mutexLocker.hpp"
 #include "runtime/safepoint.hpp"
 #include "runtime/thread.inline.hpp"
+#include "runtime/threadSMR.hpp"
 
 // Closure used for updating remembered sets and recording references that
 // point into the collection set while the mutator is running.
@@ -319,7 +320,7 @@ void DirtyCardQueueSet::abandon_logs() {
   clear();
   // Since abandon is done only at safepoints, we can safely manipulate
   // these queues.
-  for (JavaThread* t = Threads::first(); t; t = t->next()) {
+  for (JavaThreadIteratorWithHandle jtiwh; JavaThread *t = jtiwh.next(); ) {
     t->dirty_card_queue().reset();
   }
   shared_dirty_card_queue()->reset();
@@ -338,7 +339,7 @@ void DirtyCardQueueSet::concatenate_logs() {
   int save_max_completed_queue = _max_completed_queue;
   _max_completed_queue = max_jint;
   assert(SafepointSynchronize::is_at_safepoint(), "Must be at safepoint.");
-  for (JavaThread* t = Threads::first(); t; t = t->next()) {
+  for (JavaThreadIteratorWithHandle jtiwh; JavaThread *t = jtiwh.next(); ) {
     concatenate_log(t->dirty_card_queue());
   }
   concatenate_log(_shared_dirty_card_queue);
diff --git a/src/hotspot/share/gc/g1/g1Arguments.cpp b/src/hotspot/share/gc/g1/g1Arguments.cpp
index bfe27a1cfae..b9ad5a02951 100644
--- a/src/hotspot/share/gc/g1/g1Arguments.cpp
+++ b/src/hotspot/share/gc/g1/g1Arguments.cpp
@@ -26,6 +26,7 @@
 #include "gc/g1/g1Arguments.hpp"
 #include "gc/g1/g1CollectedHeap.inline.hpp"
 #include "gc/g1/g1CollectorPolicy.hpp"
+#include "gc/g1/g1HeapVerifier.hpp"
 #include "gc/g1/heapRegion.hpp"
 #include "gc/shared/gcArguments.inline.hpp"
 #include "runtime/globals.hpp"
@@ -92,6 +93,22 @@ void G1Arguments::initialize_flags() {
   }
 
   log_trace(gc)("MarkStackSize: %uk  MarkStackSizeMax: %uk", (unsigned int) (MarkStackSize / K), (uint) (MarkStackSizeMax / K));
+
+#ifdef COMPILER2
+  // Enable loop strip mining to offer better pause time guarantees
+  if (FLAG_IS_DEFAULT(UseCountedLoopSafepoints)) {
+    FLAG_SET_DEFAULT(UseCountedLoopSafepoints, true);
+  }
+  if (UseCountedLoopSafepoints && FLAG_IS_DEFAULT(LoopStripMiningIter)) {
+    FLAG_SET_DEFAULT(LoopStripMiningIter, 1000);
+  }
+#endif
+}
+
+bool G1Arguments::parse_verification_type(const char* type) {
+  G1CollectedHeap::heap()->verifier()->parse_verification_type(type);
+  // Always return true because we want to parse all values.
+  return true;
 }
 
 CollectedHeap* G1Arguments::create_heap() {
diff --git a/src/hotspot/share/gc/g1/g1Arguments.hpp b/src/hotspot/share/gc/g1/g1Arguments.hpp
index 3f87c638c49..2dd753eb2a5 100644
--- a/src/hotspot/share/gc/g1/g1Arguments.hpp
+++ b/src/hotspot/share/gc/g1/g1Arguments.hpp
@@ -32,6 +32,7 @@ class CollectedHeap;
 class G1Arguments : public GCArguments {
 public:
   virtual void initialize_flags();
+  virtual bool parse_verification_type(const char* type);
   virtual size_t conservative_max_heap_alignment();
   virtual CollectedHeap* create_heap();
 };
diff --git a/src/hotspot/share/gc/g1/g1CollectedHeap.cpp b/src/hotspot/share/gc/g1/g1CollectedHeap.cpp
index a62a48eeac5..7245bf55388 100644
--- a/src/hotspot/share/gc/g1/g1CollectedHeap.cpp
+++ b/src/hotspot/share/gc/g1/g1CollectedHeap.cpp
@@ -39,12 +39,12 @@
 #include "gc/g1/g1ConcurrentRefineThread.hpp"
 #include "gc/g1/g1EvacStats.inline.hpp"
 #include "gc/g1/g1FullCollector.hpp"
-#include "gc/g1/g1FullGCScope.hpp"
 #include "gc/g1/g1GCPhaseTimes.hpp"
 #include "gc/g1/g1HeapSizingPolicy.hpp"
 #include "gc/g1/g1HeapTransition.hpp"
 #include "gc/g1/g1HeapVerifier.hpp"
 #include "gc/g1/g1HotCardCache.hpp"
+#include "gc/g1/g1MemoryPool.hpp"
 #include "gc/g1/g1OopClosures.inline.hpp"
 #include "gc/g1/g1ParScanThreadState.inline.hpp"
 #include "gc/g1/g1Policy.hpp"
@@ -81,6 +81,7 @@
 #include "runtime/atomic.hpp"
 #include "runtime/init.hpp"
 #include "runtime/orderAccess.inline.hpp"
+#include "runtime/threadSMR.hpp"
 #include "runtime/vmThread.hpp"
 #include "utilities/align.hpp"
 #include "utilities/globalDefinitions.hpp"
@@ -1083,7 +1084,6 @@ void G1CollectedHeap::print_hrm_post_compaction() {
     PostCompactionPrinterClosure cl(hr_printer());
     heap_region_iterate(&cl);
   }
-
 }
 
 void G1CollectedHeap::abort_concurrent_cycle() {
@@ -1132,7 +1132,7 @@ void G1CollectedHeap::verify_before_full_collection(bool explicit_gc) {
   assert(!GCCause::is_user_requested_gc(gc_cause()) || explicit_gc, "invariant");
   assert(used() == recalculate_used(), "Should be equal");
   _verifier->verify_region_sets_optional();
-  _verifier->verify_before_gc();
+  _verifier->verify_before_gc(G1HeapVerifier::G1VerifyFull);
   _verifier->check_bitmaps("Full GC Start");
 }
 
@@ -1173,7 +1173,7 @@ void G1CollectedHeap::verify_after_full_collection() {
   check_gc_time_stamps();
   _hrm.verify_optional();
   _verifier->verify_region_sets_optional();
-  _verifier->verify_after_gc();
+  _verifier->verify_after_gc(G1HeapVerifier::G1VerifyFull);
   // Clear the previous marking bitmap, if needed for bitmap verification.
   // Note we cannot do this when we clear the next marking bitmap in
   // G1ConcurrentMark::abort() above since VerifyDuringGC verifies the
@@ -1217,34 +1217,6 @@ void G1CollectedHeap::print_heap_after_full_collection(G1HeapTransition* heap_tr
 #endif
 }
 
-void G1CollectedHeap::do_full_collection_inner(G1FullGCScope* scope) {
-  GCTraceTime(Info, gc) tm("Pause Full", NULL, gc_cause(), true);
-  g1_policy()->record_full_collection_start();
-
-  print_heap_before_gc();
-  print_heap_regions();
-
-  abort_concurrent_cycle();
-  verify_before_full_collection(scope->is_explicit_gc());
-
-  gc_prologue(true);
-  prepare_heap_for_full_collection();
-
-  G1FullCollector collector(scope, ref_processor_stw(), concurrent_mark()->next_mark_bitmap(), workers()->active_workers());
-  collector.prepare_collection();
-  collector.collect();
-  collector.complete_collection();
-
-  prepare_heap_for_mutators();
-
-  g1_policy()->record_full_collection_end();
-  gc_epilogue(true);
-
-  verify_after_full_collection();
-
-  print_heap_after_full_collection(scope->heap_transition());
-}
-
 bool G1CollectedHeap::do_full_collection(bool explicit_gc,
                                          bool clear_all_soft_refs) {
   assert_at_safepoint(true /* should_be_vm_thread */);
@@ -1257,8 +1229,12 @@ bool G1CollectedHeap::do_full_collection(bool explicit_gc,
   const bool do_clear_all_soft_refs = clear_all_soft_refs ||
       collector_policy()->should_clear_all_soft_refs();
 
-  G1FullGCScope scope(explicit_gc, do_clear_all_soft_refs);
-  do_full_collection_inner(&scope);
+  G1FullCollector collector(this, &_full_gc_memory_manager, explicit_gc, do_clear_all_soft_refs);
+  GCTraceTime(Info, gc) tm("Pause Full", NULL, gc_cause(), true);
+
+  collector.prepare_collection();
+  collector.collect();
+  collector.complete_collection();
 
   // Full collection was successfully completed.
   return true;
@@ -1550,6 +1526,11 @@ G1CollectedHeap::G1CollectedHeap(G1CollectorPolicy* collector_policy) :
   CollectedHeap(),
   _young_gen_sampling_thread(NULL),
   _collector_policy(collector_policy),
+  _memory_manager("G1 Young Generation", "end of minor GC"),
+  _full_gc_memory_manager("G1 Old Generation", "end of major GC"),
+  _eden_pool(NULL),
+  _survivor_pool(NULL),
+  _old_pool(NULL),
   _gc_timer_stw(new (ResourceObj::C_HEAP, mtGC) STWGCTimer()),
   _gc_tracer_stw(new (ResourceObj::C_HEAP, mtGC) G1NewTracer()),
   _g1_policy(create_g1_policy(_gc_timer_stw)),
@@ -1854,6 +1835,20 @@ jint G1CollectedHeap::initialize() {
   return JNI_OK;
 }
 
+void G1CollectedHeap::initialize_serviceability() {
+  _eden_pool = new G1EdenPool(this);
+  _survivor_pool = new G1SurvivorPool(this);
+  _old_pool = new G1OldGenPool(this);
+
+  _full_gc_memory_manager.add_pool(_eden_pool);
+  _full_gc_memory_manager.add_pool(_survivor_pool);
+  _full_gc_memory_manager.add_pool(_old_pool);
+
+  _memory_manager.add_pool(_eden_pool);
+  _memory_manager.add_pool(_survivor_pool);
+
+}
+
 void G1CollectedHeap::stop() {
   // Stop all concurrent threads. We do this to make sure these threads
   // do not continue to execute and access resources (e.g. logging)
@@ -1879,6 +1874,7 @@ size_t G1CollectedHeap::conservative_max_heap_alignment() {
 }
 
 void G1CollectedHeap::post_initialize() {
+  CollectedHeap::post_initialize();
   ref_processing_init();
 }
 
@@ -2653,11 +2649,9 @@ G1CollectedHeap::doConcurrentMark() {
 
 size_t G1CollectedHeap::pending_card_num() {
   size_t extra_cards = 0;
-  JavaThread *curr = Threads::first();
-  while (curr != NULL) {
+  for (JavaThreadIteratorWithHandle jtiwh; JavaThread *curr = jtiwh.next(); ) {
     DirtyCardQueue& dcq = curr->dirty_card_queue();
     extra_cards += dcq.size();
-    curr = curr->next();
   }
   DirtyCardQueueSet& dcqs = JavaThread::dirty_card_queue_set();
   size_t buffer_size = dcqs.buffer_size();
@@ -2963,13 +2957,17 @@ G1CollectedHeap::do_collection_pause_at_safepoint(double target_pause_time_ms) {
 
     GCTraceCPUTime tcpu;
 
+    G1HeapVerifier::G1VerifyType verify_type;
     FormatBuffer<> gc_string("Pause ");
     if (collector_state()->during_initial_mark_pause()) {
       gc_string.append("Initial Mark");
+      verify_type = G1HeapVerifier::G1VerifyInitialMark;
     } else if (collector_state()->gcs_are_young()) {
       gc_string.append("Young");
+      verify_type = G1HeapVerifier::G1VerifyYoungOnly;
     } else {
       gc_string.append("Mixed");
+      verify_type = G1HeapVerifier::G1VerifyMixed;
     }
     GCTraceTime(Info, gc) tm(gc_string, NULL, gc_cause(), true);
 
@@ -2980,7 +2978,7 @@ G1CollectedHeap::do_collection_pause_at_safepoint(double target_pause_time_ms) {
     log_info(gc,task)("Using %u workers of %u for evacuation", active_workers, workers()->total_workers());
 
     TraceCollectorStats tcs(g1mm()->incremental_collection_counters());
-    TraceMemoryManagerStats tms(false /* fullGC */, gc_cause());
+    TraceMemoryManagerStats tms(&_memory_manager, gc_cause());
 
     // If the secondary_free_list is not empty, append it to the
     // free_list. No need to wait for the cleanup operation to finish;
@@ -3010,7 +3008,7 @@ G1CollectedHeap::do_collection_pause_at_safepoint(double target_pause_time_ms) {
         heap_region_iterate(&v_cl);
       }
 
-      _verifier->verify_before_gc();
+      _verifier->verify_before_gc(verify_type);
 
       _verifier->check_bitmaps("GC Start");
 
@@ -3170,7 +3168,7 @@ G1CollectedHeap::do_collection_pause_at_safepoint(double target_pause_time_ms) {
           heap_region_iterate(&v_cl);
         }
 
-        _verifier->verify_after_gc();
+        _verifier->verify_after_gc(verify_type);
         _verifier->check_bitmaps("GC End");
 
         assert(!ref_processor_stw()->discovery_enabled(), "Postcondition");
@@ -5394,3 +5392,18 @@ void G1CollectedHeap::rebuild_strong_code_roots() {
   RebuildStrongCodeRootClosure blob_cl(this);
   CodeCache::blobs_do(&blob_cl);
 }
+
+GrowableArray<GCMemoryManager*> G1CollectedHeap::memory_managers() {
+  GrowableArray<GCMemoryManager*> memory_managers(2);
+  memory_managers.append(&_memory_manager);
+  memory_managers.append(&_full_gc_memory_manager);
+  return memory_managers;
+}
+
+GrowableArray<MemoryPool*> G1CollectedHeap::memory_pools() {
+  GrowableArray<MemoryPool*> memory_pools(3);
+  memory_pools.append(_eden_pool);
+  memory_pools.append(_survivor_pool);
+  memory_pools.append(_old_pool);
+  return memory_pools;
+}
diff --git a/src/hotspot/share/gc/g1/g1CollectedHeap.hpp b/src/hotspot/share/gc/g1/g1CollectedHeap.hpp
index 7467a5493f9..d268e2ef7be 100644
--- a/src/hotspot/share/gc/g1/g1CollectedHeap.hpp
+++ b/src/hotspot/share/gc/g1/g1CollectedHeap.hpp
@@ -42,14 +42,15 @@
 #include "gc/g1/g1SATBCardTableModRefBS.hpp"
 #include "gc/g1/g1SurvivorRegions.hpp"
 #include "gc/g1/g1YCTypes.hpp"
-#include "gc/g1/hSpaceCounters.hpp"
 #include "gc/g1/heapRegionManager.hpp"
 #include "gc/g1/heapRegionSet.hpp"
 #include "gc/shared/barrierSet.hpp"
 #include "gc/shared/collectedHeap.hpp"
+#include "gc/shared/gcHeapSummary.hpp"
 #include "gc/shared/plab.hpp"
 #include "gc/shared/preservedMarks.hpp"
 #include "memory/memRegion.hpp"
+#include "services/memoryManager.hpp"
 #include "utilities/stack.hpp"
 
 // A "G1CollectedHeap" is an implementation of a java heap for HotSpot.
@@ -64,6 +65,7 @@ class GenerationSpec;
 class G1ParScanThreadState;
 class G1ParScanThreadStateSet;
 class G1ParScanThreadState;
+class MemoryPool;
 class ObjectClosure;
 class SpaceClosure;
 class CompactibleSpaceClosure;
@@ -126,6 +128,7 @@ class G1CollectedHeap : public CollectedHeap {
   friend class VM_G1IncCollectionPause;
   friend class VMStructs;
   friend class MutatorAllocRegion;
+  friend class G1FullCollector;
   friend class G1GCAllocRegion;
   friend class G1HeapVerifier;
 
@@ -148,6 +151,13 @@ private:
   WorkGang* _workers;
   G1CollectorPolicy* _collector_policy;
 
+  GCMemoryManager _memory_manager;
+  GCMemoryManager _full_gc_memory_manager;
+
+  MemoryPool* _eden_pool;
+  MemoryPool* _survivor_pool;
+  MemoryPool* _old_pool;
+
   static size_t _humongous_object_threshold_in_words;
 
   // The secondary free list which contains regions that have been
@@ -161,6 +171,8 @@ private:
   // It keeps track of the humongous regions.
   HeapRegionSet _humongous_set;
 
+  virtual void initialize_serviceability();
+
   void eagerly_reclaim_humongous_regions();
   // Start a new incremental collection set for the next pause.
   void start_new_collection_set();
@@ -517,7 +529,6 @@ protected:
 private:
   // Internal helpers used during full GC to split it up to
   // increase readability.
-  void do_full_collection_inner(G1FullGCScope* scope);
   void abort_concurrent_cycle();
   void verify_before_full_collection(bool explicit_gc);
   void prepare_heap_for_full_collection();
@@ -1006,6 +1017,9 @@ public:
   // Adaptive size policy.  No such thing for g1.
   virtual AdaptiveSizePolicy* size_policy() { return NULL; }
 
+  virtual GrowableArray<GCMemoryManager*> memory_managers();
+  virtual GrowableArray<MemoryPool*> memory_pools();
+
   // The rem set and barrier set.
   G1RemSet* g1_rem_set() const { return _g1_rem_set; }
 
diff --git a/src/hotspot/share/gc/g1/g1ConcurrentMark.cpp b/src/hotspot/share/gc/g1/g1ConcurrentMark.cpp
index 9073616c33f..a63013bdec4 100644
--- a/src/hotspot/share/gc/g1/g1ConcurrentMark.cpp
+++ b/src/hotspot/share/gc/g1/g1ConcurrentMark.cpp
@@ -1015,9 +1015,7 @@ void G1ConcurrentMark::checkpoint_roots_final(bool clear_all_soft_refs) {
   SvcGCMarker sgcm(SvcGCMarker::OTHER);
 
   if (VerifyDuringGC) {
-    HandleMark hm;  // handle scope
-    g1h->prepare_for_verify();
-    Universe::verify(VerifyOption_G1UsePrevMarking, "During GC (before)");
+    g1h->verifier()->verify(G1HeapVerifier::G1VerifyRemark, VerifyOption_G1UsePrevMarking, "During GC (before)");
   }
   g1h->verifier()->check_bitmaps("Remark Start");
 
@@ -1038,9 +1036,7 @@ void G1ConcurrentMark::checkpoint_roots_final(bool clear_all_soft_refs) {
 
     // Verify the heap w.r.t. the previous marking bitmap.
     if (VerifyDuringGC) {
-      HandleMark hm;  // handle scope
-      g1h->prepare_for_verify();
-      Universe::verify(VerifyOption_G1UsePrevMarking, "During GC (overflow)");
+      g1h->verifier()->verify(G1HeapVerifier::G1VerifyRemark, VerifyOption_G1UsePrevMarking, "During GC (overflow)");
     }
 
     // Clear the marking state because we will be restarting
@@ -1055,9 +1051,7 @@ void G1ConcurrentMark::checkpoint_roots_final(bool clear_all_soft_refs) {
                                        true /* expected_active */);
 
     if (VerifyDuringGC) {
-      HandleMark hm;  // handle scope
-      g1h->prepare_for_verify();
-      Universe::verify(VerifyOption_G1UseNextMarking, "During GC (after)");
+      g1h->verifier()->verify(G1HeapVerifier::G1VerifyRemark, VerifyOption_G1UseNextMarking, "During GC (after)");
     }
     g1h->verifier()->check_bitmaps("Remark End");
     assert(!restart_for_overflow(), "sanity");
@@ -1189,9 +1183,7 @@ void G1ConcurrentMark::cleanup() {
   g1h->verifier()->verify_region_sets_optional();
 
   if (VerifyDuringGC) {
-    HandleMark hm;  // handle scope
-    g1h->prepare_for_verify();
-    Universe::verify(VerifyOption_G1UsePrevMarking, "During GC (before)");
+    g1h->verifier()->verify(G1HeapVerifier::G1VerifyCleanup, VerifyOption_G1UsePrevMarking, "During GC (before)");
   }
   g1h->verifier()->check_bitmaps("Cleanup Start");
 
@@ -1263,9 +1255,7 @@ void G1ConcurrentMark::cleanup() {
   Universe::update_heap_info_at_gc();
 
   if (VerifyDuringGC) {
-    HandleMark hm;  // handle scope
-    g1h->prepare_for_verify();
-    Universe::verify(VerifyOption_G1UsePrevMarking, "During GC (after)");
+    g1h->verifier()->verify(G1HeapVerifier::G1VerifyCleanup, VerifyOption_G1UsePrevMarking, "During GC (after)");
   }
 
   g1h->verifier()->check_bitmaps("Cleanup End");
@@ -1756,28 +1746,24 @@ private:
   G1ConcurrentMark* _cm;
 public:
   void work(uint worker_id) {
-    // Since all available tasks are actually started, we should
-    // only proceed if we're supposed to be active.
-    if (worker_id < _cm->active_tasks()) {
-      G1CMTask* task = _cm->task(worker_id);
-      task->record_start_time();
-      {
-        ResourceMark rm;
-        HandleMark hm;
+    G1CMTask* task = _cm->task(worker_id);
+    task->record_start_time();
+    {
+      ResourceMark rm;
+      HandleMark hm;
 
-        G1RemarkThreadsClosure threads_f(G1CollectedHeap::heap(), task);
-        Threads::threads_do(&threads_f);
-      }
-
-      do {
-        task->do_marking_step(1000000000.0 /* something very large */,
-                              true         /* do_termination       */,
-                              false        /* is_serial            */);
-      } while (task->has_aborted() && !_cm->has_overflown());
-      // If we overflow, then we do not want to restart. We instead
-      // want to abort remark and do concurrent marking again.
-      task->record_end_time();
+      G1RemarkThreadsClosure threads_f(G1CollectedHeap::heap(), task);
+      Threads::threads_do(&threads_f);
     }
+
+    do {
+      task->do_marking_step(1000000000.0 /* something very large */,
+                            true         /* do_termination       */,
+                            false        /* is_serial            */);
+    } while (task->has_aborted() && !_cm->has_overflown());
+    // If we overflow, then we do not want to restart. We instead
+    // want to abort remark and do concurrent marking again.
+    task->record_end_time();
   }
 
   G1CMRemarkTask(G1ConcurrentMark* cm, uint active_workers) :
diff --git a/src/hotspot/share/gc/g1/g1ConcurrentRefine.cpp b/src/hotspot/share/gc/g1/g1ConcurrentRefine.cpp
index 8e39ff06698..31d09e9428b 100644
--- a/src/hotspot/share/gc/g1/g1ConcurrentRefine.cpp
+++ b/src/hotspot/share/gc/g1/g1ConcurrentRefine.cpp
@@ -26,6 +26,7 @@
 #include "gc/g1/g1ConcurrentRefine.hpp"
 #include "gc/g1/g1ConcurrentRefineThread.hpp"
 #include "logging/log.hpp"
+#include "memory/allocation.inline.hpp"
 #include "runtime/java.hpp"
 #include "runtime/thread.hpp"
 #include "utilities/debug.hpp"
@@ -33,6 +34,107 @@
 #include "utilities/pair.hpp"
 #include <math.h>
 
+G1ConcurrentRefineThread* G1ConcurrentRefineThreadControl::create_refinement_thread(uint worker_id, bool initializing) {
+  G1ConcurrentRefineThread* result = NULL;
+  if (initializing || !InjectGCWorkerCreationFailure) {
+    result = new G1ConcurrentRefineThread(_cr, worker_id);
+  }
+  if (result == NULL || result->osthread() == NULL) {
+    log_warning(gc)("Failed to create refinement thread %u, no more %s",
+                    worker_id,
+                    result == NULL ? "memory" : "OS threads");
+  }
+  return result;
+}
+
+G1ConcurrentRefineThreadControl::G1ConcurrentRefineThreadControl() :
+  _cr(NULL),
+  _threads(NULL),
+  _num_max_threads(0)
+{
+}
+
+G1ConcurrentRefineThreadControl::~G1ConcurrentRefineThreadControl() {
+  for (uint i = 0; i < _num_max_threads; i++) {
+    G1ConcurrentRefineThread* t = _threads[i];
+    if (t != NULL) {
+      delete t;
+    }
+  }
+  FREE_C_HEAP_ARRAY(G1ConcurrentRefineThread*, _threads);
+}
+
+jint G1ConcurrentRefineThreadControl::initialize(G1ConcurrentRefine* cr, uint num_max_threads) {
+  assert(cr != NULL, "G1ConcurrentRefine must not be NULL");
+  _cr = cr;
+  _num_max_threads = num_max_threads;
+
+  _threads = NEW_C_HEAP_ARRAY_RETURN_NULL(G1ConcurrentRefineThread*, num_max_threads, mtGC);
+  if (_threads == NULL) {
+    vm_shutdown_during_initialization("Could not allocate thread holder array.");
+    return JNI_ENOMEM;
+  }
+
+  for (uint i = 0; i < num_max_threads; i++) {
+    if (UseDynamicNumberOfGCThreads && i != 0 /* Always start first thread. */) {
+      _threads[i] = NULL;
+    } else {
+      _threads[i] = create_refinement_thread(i, true);
+      if (_threads[i] == NULL) {
+        vm_shutdown_during_initialization("Could not allocate refinement threads.");
+        return JNI_ENOMEM;
+      }
+    }
+  }
+  return JNI_OK;
+}
+
+void G1ConcurrentRefineThreadControl::maybe_activate_next(uint cur_worker_id) {
+  assert(cur_worker_id < _num_max_threads,
+         "Activating another thread from %u not allowed since there can be at most %u",
+         cur_worker_id, _num_max_threads);
+  if (cur_worker_id == (_num_max_threads - 1)) {
+    // Already the last thread, there is no more thread to activate.
+    return;
+  }
+
+  uint worker_id = cur_worker_id + 1;
+  G1ConcurrentRefineThread* thread_to_activate = _threads[worker_id];
+  if (thread_to_activate == NULL) {
+    // Still need to create the thread...
+    _threads[worker_id] = create_refinement_thread(worker_id, false);
+    thread_to_activate = _threads[worker_id];
+  }
+  if (thread_to_activate != NULL && !thread_to_activate->is_active()) {
+    thread_to_activate->activate();
+  }
+}
+
+void G1ConcurrentRefineThreadControl::print_on(outputStream* st) const {
+  for (uint i = 0; i < _num_max_threads; ++i) {
+    if (_threads[i] != NULL) {
+      _threads[i]->print_on(st);
+      st->cr();
+    }
+  }
+}
+
+void G1ConcurrentRefineThreadControl::worker_threads_do(ThreadClosure* tc) {
+  for (uint i = 0; i < _num_max_threads; i++) {
+    if (_threads[i] != NULL) {
+      tc->do_thread(_threads[i]);
+    }
+  }
+}
+
+void G1ConcurrentRefineThreadControl::stop() {
+  for (uint i = 0; i < _num_max_threads; i++) {
+    if (_threads[i] != NULL) {
+      _threads[i]->stop();
+    }
+  }
+}
+
 // Arbitrary but large limits, to simplify some of the zone calculations.
 // The general idea is to allow expressions like
 //   MIN2(x OP y, max_XXX_zone)
@@ -96,7 +198,7 @@ static Thresholds calc_thresholds(size_t green_zone,
                                   size_t yellow_zone,
                                   uint worker_i) {
   double yellow_size = yellow_zone - green_zone;
-  double step = yellow_size / G1ConcurrentRefine::thread_num();
+  double step = yellow_size / G1ConcurrentRefine::max_num_threads();
   if (worker_i == 0) {
     // Potentially activate worker 0 more aggressively, to keep
     // available buffers near green_zone value.  When yellow_size is
@@ -115,8 +217,7 @@ G1ConcurrentRefine::G1ConcurrentRefine(size_t green_zone,
                                        size_t yellow_zone,
                                        size_t red_zone,
                                        size_t min_yellow_zone_size) :
-  _threads(NULL),
-  _n_worker_threads(thread_num()),
+  _thread_control(),
   _green_zone(green_zone),
   _yellow_zone(yellow_zone),
   _red_zone(red_zone),
@@ -125,9 +226,13 @@ G1ConcurrentRefine::G1ConcurrentRefine(size_t green_zone,
   assert_zone_constraints_gyr(green_zone, yellow_zone, red_zone);
 }
 
+jint G1ConcurrentRefine::initialize() {
+  return _thread_control.initialize(this, max_num_threads());
+}
+
 static size_t calc_min_yellow_zone_size() {
   size_t step = G1ConcRefinementThresholdStep;
-  uint n_workers = G1ConcurrentRefine::thread_num();
+  uint n_workers = G1ConcurrentRefine::max_num_threads();
   if ((max_yellow_zone / step) < n_workers) {
     return max_yellow_zone;
   } else {
@@ -191,77 +296,27 @@ G1ConcurrentRefine* G1ConcurrentRefine::create(jint* ecode) {
     return NULL;
   }
 
-  cr->_threads = NEW_C_HEAP_ARRAY_RETURN_NULL(G1ConcurrentRefineThread*, cr->_n_worker_threads, mtGC);
-  if (cr->_threads == NULL) {
-    *ecode = JNI_ENOMEM;
-    vm_shutdown_during_initialization("Could not allocate an array for G1ConcurrentRefineThread");
-    return NULL;
-  }
-
-  uint worker_id_offset = DirtyCardQueueSet::num_par_ids();
-
-  G1ConcurrentRefineThread *next = NULL;
-  for (uint i = cr->_n_worker_threads - 1; i != UINT_MAX; i--) {
-    Thresholds thresholds = calc_thresholds(green_zone, yellow_zone, i);
-    G1ConcurrentRefineThread* t =
-      new G1ConcurrentRefineThread(cr,
-                                   next,
-                                   worker_id_offset,
-                                   i,
-                                   activation_level(thresholds),
-                                   deactivation_level(thresholds));
-    assert(t != NULL, "Conc refine should have been created");
-    if (t->osthread() == NULL) {
-      *ecode = JNI_ENOMEM;
-      vm_shutdown_during_initialization("Could not create G1ConcurrentRefineThread");
-      return NULL;
-    }
-
-    assert(t->cr() == cr, "Conc refine thread should refer to this");
-    cr->_threads[i] = t;
-    next = t;
-  }
-
-  *ecode = JNI_OK;
+  *ecode = cr->initialize();
   return cr;
 }
 
 void G1ConcurrentRefine::stop() {
-  for (uint i = 0; i < _n_worker_threads; i++) {
-    _threads[i]->stop();
-  }
-}
-
-void G1ConcurrentRefine::update_thread_thresholds() {
-  for (uint i = 0; i < _n_worker_threads; i++) {
-    Thresholds thresholds = calc_thresholds(_green_zone, _yellow_zone, i);
-    _threads[i]->update_thresholds(activation_level(thresholds),
-                                   deactivation_level(thresholds));
-  }
+  _thread_control.stop();
 }
 
 G1ConcurrentRefine::~G1ConcurrentRefine() {
-  for (uint i = 0; i < _n_worker_threads; i++) {
-    delete _threads[i];
-  }
-  FREE_C_HEAP_ARRAY(G1ConcurrentRefineThread*, _threads);
 }
 
 void G1ConcurrentRefine::threads_do(ThreadClosure *tc) {
-  for (uint i = 0; i < _n_worker_threads; i++) {
-    tc->do_thread(_threads[i]);
-  }
+  _thread_control.worker_threads_do(tc);
 }
 
-uint G1ConcurrentRefine::thread_num() {
+uint G1ConcurrentRefine::max_num_threads() {
   return G1ConcRefinementThreads;
 }
 
 void G1ConcurrentRefine::print_threads_on(outputStream* st) const {
-  for (uint i = 0; i < _n_worker_threads; ++i) {
-    _threads[i]->print_on(st);
-    st->cr();
-  }
+  _thread_control.print_on(st);
 }
 
 static size_t calc_new_green_zone(size_t green,
@@ -326,16 +381,15 @@ void G1ConcurrentRefine::adjust(double update_rs_time,
 
   if (G1UseAdaptiveConcRefinement) {
     update_zones(update_rs_time, update_rs_processed_buffers, goal_ms);
-    update_thread_thresholds();
 
     // Change the barrier params
-    if (_n_worker_threads == 0) {
+    if (max_num_threads() == 0) {
       // Disable dcqs notification when there are no threads to notify.
       dcqs.set_process_completed_threshold(INT_MAX);
     } else {
       // Worker 0 is the primary; wakeup is via dcqs notification.
       STATIC_ASSERT(max_yellow_zone <= INT_MAX);
-      size_t activate = _threads[0]->activation_threshold();
+      size_t activate = activation_threshold(0);
       dcqs.set_process_completed_threshold((int)activate);
     }
     dcqs.set_max_completed_queue((int)red_zone());
@@ -349,3 +403,42 @@ void G1ConcurrentRefine::adjust(double update_rs_time,
   }
   dcqs.notify_if_necessary();
 }
+
+size_t G1ConcurrentRefine::activation_threshold(uint worker_id) const {
+  Thresholds thresholds = calc_thresholds(_green_zone, _yellow_zone, worker_id);
+  return activation_level(thresholds);
+}
+
+size_t G1ConcurrentRefine::deactivation_threshold(uint worker_id) const {
+  Thresholds thresholds = calc_thresholds(_green_zone, _yellow_zone, worker_id);
+  return deactivation_level(thresholds);
+}
+
+uint G1ConcurrentRefine::worker_id_offset() {
+  return DirtyCardQueueSet::num_par_ids();
+}
+
+void G1ConcurrentRefine::maybe_activate_more_threads(uint worker_id, size_t num_cur_buffers) {
+  if (num_cur_buffers > activation_threshold(worker_id + 1)) {
+    _thread_control.maybe_activate_next(worker_id);
+  }
+}
+
+bool G1ConcurrentRefine::do_refinement_step(uint worker_id) {
+  DirtyCardQueueSet& dcqs = JavaThread::dirty_card_queue_set();
+
+  size_t curr_buffer_num = dcqs.completed_buffers_num();
+  // If the number of the buffers falls down into the yellow zone,
+  // that means that the transition period after the evacuation pause has ended.
+  // Since the value written to the DCQS is the same for all threads, there is no
+  // need to synchronize.
+  if (dcqs.completed_queue_padding() > 0 && curr_buffer_num <= yellow_zone()) {
+    dcqs.set_completed_queue_padding(0);
+  }
+
+  maybe_activate_more_threads(worker_id, curr_buffer_num);
+
+  // Process the next buffer, if there are enough left.
+  return dcqs.refine_completed_buffer_concurrently(worker_id + worker_id_offset(),
+                                                   deactivation_threshold(worker_id));
+}
diff --git a/src/hotspot/share/gc/g1/g1ConcurrentRefine.hpp b/src/hotspot/share/gc/g1/g1ConcurrentRefine.hpp
index b64d4e3ee80..52783881b9e 100644
--- a/src/hotspot/share/gc/g1/g1ConcurrentRefine.hpp
+++ b/src/hotspot/share/gc/g1/g1ConcurrentRefine.hpp
@@ -30,30 +30,63 @@
 
 // Forward decl
 class CardTableEntryClosure;
+class G1ConcurrentRefine;
 class G1ConcurrentRefineThread;
 class outputStream;
 class ThreadClosure;
 
-class G1ConcurrentRefine : public CHeapObj<mtGC> {
+// Helper class for refinement thread management. Used to start, stop and
+// iterate over them.
+class G1ConcurrentRefineThreadControl VALUE_OBJ_CLASS_SPEC {
+  G1ConcurrentRefine* _cr;
+
   G1ConcurrentRefineThread** _threads;
-  uint _n_worker_threads;
- /*
-  * The value of the update buffer queue length falls into one of 3 zones:
-  * green, yellow, red. If the value is in [0, green) nothing is
-  * done, the buffers are left unprocessed to enable the caching effect of the
-  * dirtied cards. In the yellow zone [green, yellow) the concurrent refinement
-  * threads are gradually activated. In [yellow, red) all threads are
-  * running. If the length becomes red (max queue length) the mutators start
-  * processing the buffers.
-  *
-  * There are some interesting cases (when G1UseAdaptiveConcRefinement
-  * is turned off):
-  * 1) green = yellow = red = 0. In this case the mutator will process all
-  *    buffers. Except for those that are created by the deferred updates
-  *    machinery during a collection.
-  * 2) green = 0. Means no caching. Can be a good way to minimize the
-  *    amount of time spent updating rsets during a collection.
-  */
+  uint _num_max_threads;
+
+  // Create the refinement thread for the given worker id.
+  // If initializing is true, ignore InjectGCWorkerCreationFailure.
+  G1ConcurrentRefineThread* create_refinement_thread(uint worker_id, bool initializing);
+public:
+  G1ConcurrentRefineThreadControl();
+  ~G1ConcurrentRefineThreadControl();
+
+  jint initialize(G1ConcurrentRefine* cr, uint num_max_threads);
+
+  // If there is a "successor" thread that can be activated given the current id,
+  // activate it.
+  void maybe_activate_next(uint cur_worker_id);
+
+  void print_on(outputStream* st) const;
+  void worker_threads_do(ThreadClosure* tc);
+  void stop();
+};
+
+// Controls refinement threads and their activation based on the number of completed
+// buffers currently available in the global dirty card queue.
+// Refinement threads pick work from the queue based on these thresholds. They are activated
+// gradually based on the amount of work to do.
+// Refinement thread n activates thread n+1 if the instance of this class determines there
+// is enough work available. Threads deactivate themselves if the current amount of
+// completed buffers falls below their individual threshold.
+class G1ConcurrentRefine : public CHeapObj<mtGC> {
+  G1ConcurrentRefineThreadControl _thread_control;
+  /*
+   * The value of the completed dirty card queue length falls into one of 3 zones:
+   * green, yellow, red. If the value is in [0, green) nothing is
+   * done, the buffers are left unprocessed to enable the caching effect of the
+   * dirtied cards. In the yellow zone [green, yellow) the concurrent refinement
+   * threads are gradually activated. In [yellow, red) all threads are
+   * running. If the length becomes red (max queue length) the mutators start
+   * processing the buffers.
+   *
+   * There are some interesting cases (when G1UseAdaptiveConcRefinement
+   * is turned off):
+   * 1) green = yellow = red = 0. In this case the mutator will process all
+   *    buffers. Except for those that are created by the deferred updates
+   *    machinery during a collection.
+   * 2) green = 0. Means no caching. Can be a good way to minimize the
+   *    amount of time spent updating remembered sets during a collection.
+   */
   size_t _green_zone;
   size_t _yellow_zone;
   size_t _red_zone;
@@ -69,24 +102,32 @@ class G1ConcurrentRefine : public CHeapObj<mtGC> {
                     size_t update_rs_processed_buffers,
                     double goal_ms);
 
-  // Update thread thresholds to account for updated zone values.
-  void update_thread_thresholds();
+  static uint worker_id_offset();
+  void maybe_activate_more_threads(uint worker_id, size_t num_cur_buffers);
 
- public:
+  jint initialize();
+public:
   ~G1ConcurrentRefine();
 
-  // Returns a G1ConcurrentRefine instance if succeeded to create/initialize G1ConcurrentRefine and G1ConcurrentRefineThreads.
-  // Otherwise, returns NULL with error code.
+  // Returns a G1ConcurrentRefine instance if succeeded to create/initialize the
+  // G1ConcurrentRefine instance. Otherwise, returns NULL with error code.
   static G1ConcurrentRefine* create(jint* ecode);
 
   void stop();
 
+  // Adjust refinement thresholds based on work done during the pause and the goal time.
   void adjust(double update_rs_time, size_t update_rs_processed_buffers, double goal_ms);
 
+  size_t activation_threshold(uint worker_id) const;
+  size_t deactivation_threshold(uint worker_id) const;
+  // Perform a single refinement step. Called by the refinement threads when woken up.
+  bool do_refinement_step(uint worker_id);
+
   // Iterate over all concurrent refinement threads applying the given closure.
   void threads_do(ThreadClosure *tc);
 
-  static uint thread_num();
+  // Maximum number of refinement threads.
+  static uint max_num_threads();
 
   void print_threads_on(outputStream* st) const;
 
diff --git a/src/hotspot/share/gc/g1/g1ConcurrentRefineThread.cpp b/src/hotspot/share/gc/g1/g1ConcurrentRefineThread.cpp
index f62e0c369e8..e1d1f8f4d01 100644
--- a/src/hotspot/share/gc/g1/g1ConcurrentRefineThread.cpp
+++ b/src/hotspot/share/gc/g1/g1ConcurrentRefineThread.cpp
@@ -25,32 +25,20 @@
 #include "precompiled.hpp"
 #include "gc/g1/g1ConcurrentRefine.hpp"
 #include "gc/g1/g1ConcurrentRefineThread.hpp"
-#include "gc/g1/g1CollectedHeap.inline.hpp"
-#include "gc/g1/g1RemSet.hpp"
 #include "gc/shared/suspendibleThreadSet.hpp"
 #include "logging/log.hpp"
 #include "memory/resourceArea.hpp"
 #include "runtime/handles.inline.hpp"
 #include "runtime/mutexLocker.hpp"
 
-G1ConcurrentRefineThread::G1ConcurrentRefineThread(G1ConcurrentRefine* cr,
-                                                   G1ConcurrentRefineThread *next,
-                                                   uint worker_id_offset,
-                                                   uint worker_id,
-                                                   size_t activate,
-                                                   size_t deactivate) :
+G1ConcurrentRefineThread::G1ConcurrentRefineThread(G1ConcurrentRefine* cr, uint worker_id) :
   ConcurrentGCThread(),
-  _worker_id_offset(worker_id_offset),
   _worker_id(worker_id),
   _active(false),
-  _next(next),
   _monitor(NULL),
   _cr(cr),
-  _vtime_accum(0.0),
-  _activation_threshold(activate),
-  _deactivation_threshold(deactivate)
+  _vtime_accum(0.0)
 {
-
   // Each thread has its own monitor. The i-th thread is responsible for signaling
   // to thread i+1 if the number of buffers in the queue exceeds a threshold for this
   // thread. Monitors are also used to wake up the threads during termination.
@@ -67,13 +55,6 @@ G1ConcurrentRefineThread::G1ConcurrentRefineThread(G1ConcurrentRefine* cr,
   create_and_start();
 }
 
-void G1ConcurrentRefineThread::update_thresholds(size_t activate,
-                                                 size_t deactivate) {
-  assert(deactivate < activate, "precondition");
-  _activation_threshold = activate;
-  _deactivation_threshold = deactivate;
-}
-
 void G1ConcurrentRefineThread::wait_for_completed_buffers() {
   MutexLockerEx x(_monitor, Mutex::_no_safepoint_check_flag);
   while (!should_terminate() && !is_active()) {
@@ -118,9 +99,9 @@ void G1ConcurrentRefineThread::run_service() {
     }
 
     size_t buffers_processed = 0;
-    DirtyCardQueueSet& dcqs = JavaThread::dirty_card_queue_set();
-    log_debug(gc, refine)("Activated %d, on threshold: " SIZE_FORMAT ", current: " SIZE_FORMAT,
-                          _worker_id, _activation_threshold, dcqs.completed_buffers_num());
+    log_debug(gc, refine)("Activated worker %d, on threshold: " SIZE_FORMAT ", current: " SIZE_FORMAT,
+                          _worker_id, _cr->activation_threshold(_worker_id),
+                           JavaThread::dirty_card_queue_set().completed_buffers_num());
 
     {
       SuspendibleThreadSetJoiner sts_join;
@@ -131,33 +112,18 @@ void G1ConcurrentRefineThread::run_service() {
           continue;             // Re-check for termination after yield delay.
         }
 
-        size_t curr_buffer_num = dcqs.completed_buffers_num();
-        // If the number of the buffers falls down into the yellow zone,
-        // that means that the transition period after the evacuation pause has ended.
-        if (dcqs.completed_queue_padding() > 0 && curr_buffer_num <= cr()->yellow_zone()) {
-          dcqs.set_completed_queue_padding(0);
-        }
-
-        // Check if we need to activate the next thread.
-        if ((_next != NULL) &&
-            !_next->is_active() &&
-            (curr_buffer_num > _next->_activation_threshold)) {
-          _next->activate();
-        }
-
-        // Process the next buffer, if there are enough left.
-        if (!dcqs.refine_completed_buffer_concurrently(_worker_id + _worker_id_offset, _deactivation_threshold)) {
-          break; // Deactivate, number of buffers fell below threshold.
+        if (!_cr->do_refinement_step(_worker_id)) {
+          break;
         }
         ++buffers_processed;
       }
     }
 
     deactivate();
-    log_debug(gc, refine)("Deactivated %d, off threshold: " SIZE_FORMAT
+    log_debug(gc, refine)("Deactivated worker %d, off threshold: " SIZE_FORMAT
                           ", current: " SIZE_FORMAT ", processed: " SIZE_FORMAT,
-                          _worker_id, _deactivation_threshold,
-                          dcqs.completed_buffers_num(),
+                          _worker_id, _cr->deactivation_threshold(_worker_id),
+                          JavaThread::dirty_card_queue_set().completed_buffers_num(),
                           buffers_processed);
 
     if (os::supports_vtime()) {
diff --git a/src/hotspot/share/gc/g1/g1ConcurrentRefineThread.hpp b/src/hotspot/share/gc/g1/g1ConcurrentRefineThread.hpp
index fbc10fcfb4c..8b3694411fa 100644
--- a/src/hotspot/share/gc/g1/g1ConcurrentRefineThread.hpp
+++ b/src/hotspot/share/gc/g1/g1ConcurrentRefineThread.hpp
@@ -43,43 +43,29 @@ class G1ConcurrentRefineThread: public ConcurrentGCThread {
   uint _worker_id;
   uint _worker_id_offset;
 
-  // The refinement threads collection is linked list. A predecessor can activate a successor
-  // when the number of the rset update buffer crosses a certain threshold. A successor
-  // would self-deactivate when the number of the buffers falls below the threshold.
   bool _active;
-  G1ConcurrentRefineThread* _next;
   Monitor* _monitor;
   G1ConcurrentRefine* _cr;
 
-  // This thread's activation/deactivation thresholds
-  size_t _activation_threshold;
-  size_t _deactivation_threshold;
-
   void wait_for_completed_buffers();
 
   void set_active(bool x) { _active = x; }
-  bool is_active();
-  void activate();
+  // Deactivate this thread.
   void deactivate();
 
   bool is_primary() { return (_worker_id == 0); }
 
   void run_service();
   void stop_service();
-
 public:
-  // Constructor
-  G1ConcurrentRefineThread(G1ConcurrentRefine* cr, G1ConcurrentRefineThread* next,
-                           uint worker_id_offset, uint worker_id,
-                           size_t activate, size_t deactivate);
+  G1ConcurrentRefineThread(G1ConcurrentRefine* cg1r, uint worker_id);
 
-  void update_thresholds(size_t activate, size_t deactivate);
-  size_t activation_threshold() const { return _activation_threshold; }
+  bool is_active();
+  // Activate this thread.
+  void activate();
 
   // Total virtual time so far.
   double vtime_accum() { return _vtime_accum; }
-
-  G1ConcurrentRefine* cr() { return _cr;     }
 };
 
 #endif // SHARE_VM_GC_G1_G1CONCURRENTREFINETHREAD_HPP
diff --git a/src/hotspot/share/gc/g1/g1DefaultPolicy.cpp b/src/hotspot/share/gc/g1/g1DefaultPolicy.cpp
index 409c8ba99aa..4c480ca5827 100644
--- a/src/hotspot/share/gc/g1/g1DefaultPolicy.cpp
+++ b/src/hotspot/share/gc/g1/g1DefaultPolicy.cpp
@@ -52,7 +52,7 @@ G1DefaultPolicy::G1DefaultPolicy(STWGCTimer* gc_timer) :
   _analytics(new G1Analytics(&_predictor)),
   _mmu_tracker(new G1MMUTrackerQueue(GCPauseIntervalMillis / 1000.0, MaxGCPauseMillis / 1000.0)),
   _ihop_control(create_ihop_control(&_predictor)),
-  _policy_counters(new GCPolicyCounters("GarbageFirst", 1, 3)),
+  _policy_counters(new GCPolicyCounters("GarbageFirst", 1, 2)),
   _young_list_fixed_length(0),
   _short_lived_surv_rate_group(new SurvRateGroup()),
   _survivor_surv_rate_group(new SurvRateGroup()),
diff --git a/src/hotspot/share/gc/g1/g1FullCollector.cpp b/src/hotspot/share/gc/g1/g1FullCollector.cpp
index 81ec9a05e25..ffe54d28332 100644
--- a/src/hotspot/share/gc/g1/g1FullCollector.cpp
+++ b/src/hotspot/share/gc/g1/g1FullCollector.cpp
@@ -35,6 +35,7 @@
 #include "gc/g1/g1FullGCReferenceProcessorExecutor.hpp"
 #include "gc/g1/g1FullGCScope.hpp"
 #include "gc/g1/g1OopClosures.hpp"
+#include "gc/g1/g1Policy.hpp"
 #include "gc/g1/g1StringDedup.hpp"
 #include "gc/shared/gcTraceTime.inline.hpp"
 #include "gc/shared/preservedMarks.hpp"
@@ -62,20 +63,24 @@ static void update_derived_pointers() {
 #endif
 }
 
-G1FullCollector::G1FullCollector(G1FullGCScope* scope,
-                                 ReferenceProcessor* reference_processor,
-                                 G1CMBitMap* bitmap,
-                                 uint workers) :
-    _scope(scope),
-    _num_workers(workers),
-    _mark_bitmap(bitmap),
+G1CMBitMap* G1FullCollector::mark_bitmap() {
+  return _heap->concurrent_mark()->next_mark_bitmap();
+}
+
+ReferenceProcessor* G1FullCollector::reference_processor() {
+  return _heap->ref_processor_stw();
+}
+
+G1FullCollector::G1FullCollector(G1CollectedHeap* heap, GCMemoryManager* memory_manager, bool explicit_gc, bool clear_soft_refs) :
+    _heap(heap),
+    _scope(memory_manager, explicit_gc, clear_soft_refs),
+    _num_workers(heap->workers()->active_workers()),
     _oop_queue_set(_num_workers),
     _array_queue_set(_num_workers),
     _preserved_marks_set(true),
-    _reference_processor(reference_processor),
     _serial_compaction_point(),
-    _is_alive(_mark_bitmap),
-    _is_alive_mutator(_reference_processor, &_is_alive) {
+    _is_alive(heap->concurrent_mark()->next_mark_bitmap()),
+    _is_alive_mutator(heap->ref_processor_stw(), &_is_alive) {
   assert(SafepointSynchronize::is_at_safepoint(), "must be at a safepoint");
 
   _preserved_marks_set.init(_num_workers);
@@ -99,8 +104,19 @@ G1FullCollector::~G1FullCollector() {
 }
 
 void G1FullCollector::prepare_collection() {
-  _reference_processor->enable_discovery();
-  _reference_processor->setup_policy(scope()->should_clear_soft_refs());
+  _heap->g1_policy()->record_full_collection_start();
+
+  _heap->print_heap_before_gc();
+  _heap->print_heap_regions();
+
+  _heap->abort_concurrent_cycle();
+  _heap->verify_before_full_collection(scope()->is_explicit_gc());
+
+  _heap->gc_prologue(true);
+  _heap->prepare_heap_for_full_collection();
+
+  reference_processor()->enable_discovery();
+  reference_processor()->setup_policy(scope()->should_clear_soft_refs());
 
   // When collecting the permanent generation Method*s may be moving,
   // so we either have to flush all bcp data or convert it into bci.
@@ -139,6 +155,15 @@ void G1FullCollector::complete_collection() {
   BiasedLocking::restore_marks();
   CodeCache::gc_epilogue();
   JvmtiExport::gc_epilogue();
+
+  _heap->prepare_heap_for_mutators();
+
+  _heap->g1_policy()->record_full_collection_end();
+  _heap->gc_epilogue(true);
+
+  _heap->verify_after_full_collection();
+
+  _heap->print_heap_after_full_collection(scope()->heap_transition());
 }
 
 void G1FullCollector::phase1_mark_live_objects() {
@@ -164,11 +189,11 @@ void G1FullCollector::phase1_mark_live_objects() {
     GCTraceTime(Debug, gc, phases) debug("Phase 1: Class Unloading and Cleanup", scope()->timer());
     // Unload classes and purge the SystemDictionary.
     bool purged_class = SystemDictionary::do_unloading(&_is_alive, scope()->timer());
-    G1CollectedHeap::heap()->complete_cleaning(&_is_alive, purged_class);
+    _heap->complete_cleaning(&_is_alive, purged_class);
   } else {
     GCTraceTime(Debug, gc, phases) debug("Phase 1: String and Symbol Tables Cleanup", scope()->timer());
     // If no class unloading just clean out strings and symbols.
-    G1CollectedHeap::heap()->partial_cleaning(&_is_alive, true, true, G1StringDedup::is_enabled());
+    _heap->partial_cleaning(&_is_alive, true, true, G1StringDedup::is_enabled());
   }
 
   scope()->tracer()->report_object_count_after_gc(&_is_alive);
@@ -210,18 +235,18 @@ void G1FullCollector::phase4_do_compaction() {
 }
 
 void G1FullCollector::restore_marks() {
-  SharedRestorePreservedMarksTaskExecutor task_executor(G1CollectedHeap::heap()->workers());
+  SharedRestorePreservedMarksTaskExecutor task_executor(_heap->workers());
   _preserved_marks_set.restore(&task_executor);
   _preserved_marks_set.reclaim();
 }
 
 void G1FullCollector::run_task(AbstractGangTask* task) {
-  G1CollectedHeap::heap()->workers()->run_task(task, _num_workers);
+  _heap->workers()->run_task(task, _num_workers);
 }
 
 void G1FullCollector::verify_after_marking() {
-  if (!VerifyDuringGC) {
-    //Only do verification if VerifyDuringGC is set.
+  if (!VerifyDuringGC || !_heap->verifier()->should_verify(G1HeapVerifier::G1VerifyFull)) {
+    // Only do verification if VerifyDuringGC and G1VerifyFull is set.
     return;
   }
 
@@ -229,7 +254,7 @@ void G1FullCollector::verify_after_marking() {
 #if COMPILER2_OR_JVMCI
   DerivedPointerTableDeactivate dpt_deact;
 #endif
-  G1CollectedHeap::heap()->prepare_for_verify();
+  _heap->prepare_for_verify();
   // Note: we can verify only the heap here. When an object is
   // marked, the previous value of the mark word (including
   // identity hash values, ages, etc) is preserved, and the mark
@@ -240,6 +265,6 @@ void G1FullCollector::verify_after_marking() {
   // fail. At the end of the GC, the original mark word values
   // (including hash values) are restored to the appropriate
   // objects.
-  GCTraceTime(Info, gc, verify)("During GC (full)");
-  G1CollectedHeap::heap()->verify(VerifyOption_G1UseFullMarking);
+  GCTraceTime(Info, gc, verify)("Verifying During GC (full)");
+  _heap->verify(VerifyOption_G1UseFullMarking);
 }
diff --git a/src/hotspot/share/gc/g1/g1FullCollector.hpp b/src/hotspot/share/gc/g1/g1FullCollector.hpp
index 947304596ee..576aba5c8c6 100644
--- a/src/hotspot/share/gc/g1/g1FullCollector.hpp
+++ b/src/hotspot/share/gc/g1/g1FullCollector.hpp
@@ -28,6 +28,7 @@
 #include "gc/g1/g1FullGCCompactionPoint.hpp"
 #include "gc/g1/g1FullGCMarker.hpp"
 #include "gc/g1/g1FullGCOopClosures.hpp"
+#include "gc/g1/g1FullGCScope.hpp"
 #include "gc/shared/preservedMarks.hpp"
 #include "gc/shared/referenceProcessor.hpp"
 #include "gc/shared/taskqueue.hpp"
@@ -38,45 +39,41 @@ class G1CMBitMap;
 class G1FullGCMarker;
 class G1FullGCScope;
 class G1FullGCCompactionPoint;
+class GCMemoryManager;
 class ReferenceProcessor;
 
 // The G1FullCollector holds data associated with the current Full GC.
 class G1FullCollector : StackObj {
-  G1FullGCScope*            _scope;
+  G1CollectedHeap*          _heap;
+  G1FullGCScope             _scope;
   uint                      _num_workers;
   G1FullGCMarker**          _markers;
   G1FullGCCompactionPoint** _compaction_points;
-  G1CMBitMap*               _mark_bitmap;
   OopQueueSet               _oop_queue_set;
   ObjArrayTaskQueueSet      _array_queue_set;
   PreservedMarksSet         _preserved_marks_set;
-  ReferenceProcessor*       _reference_processor;
   G1FullGCCompactionPoint   _serial_compaction_point;
-
   G1IsAliveClosure          _is_alive;
   ReferenceProcessorIsAliveMutator _is_alive_mutator;
 
 public:
-  G1FullCollector(G1FullGCScope* scope,
-                  ReferenceProcessor* reference_processor,
-                  G1CMBitMap* mark_bitmap,
-                  uint workers);
+  G1FullCollector(G1CollectedHeap* heap, GCMemoryManager* memory_manager, bool explicit_gc, bool clear_soft_refs);
   ~G1FullCollector();
 
   void prepare_collection();
   void collect();
   void complete_collection();
 
-  G1FullGCScope*           scope() { return _scope; }
+  G1FullGCScope*           scope() { return &_scope; }
   uint                     workers() { return _num_workers; }
   G1FullGCMarker*          marker(uint id) { return _markers[id]; }
   G1FullGCCompactionPoint* compaction_point(uint id) { return _compaction_points[id]; }
-  G1CMBitMap*              mark_bitmap() { return _mark_bitmap; }
   OopQueueSet*             oop_queue_set() { return &_oop_queue_set; }
   ObjArrayTaskQueueSet*    array_queue_set() { return &_array_queue_set; }
   PreservedMarksSet*       preserved_mark_set() { return &_preserved_marks_set; }
-  ReferenceProcessor*      reference_processor() { return _reference_processor; }
   G1FullGCCompactionPoint* serial_compaction_point() { return &_serial_compaction_point; }
+  G1CMBitMap*              mark_bitmap();
+  ReferenceProcessor*      reference_processor();
 
 private:
   void phase1_mark_live_objects();
diff --git a/src/hotspot/share/gc/g1/g1FullGCCompactionPoint.hpp b/src/hotspot/share/gc/g1/g1FullGCCompactionPoint.hpp
index 95f01be62c6..0ec0b324aab 100644
--- a/src/hotspot/share/gc/g1/g1FullGCCompactionPoint.hpp
+++ b/src/hotspot/share/gc/g1/g1FullGCCompactionPoint.hpp
@@ -26,6 +26,7 @@
 #define SHARE_GC_G1_G1FULLGCCOMPACTIONPOINT_HPP
 
 #include "memory/allocation.hpp"
+#include "oops/oopsHierarchy.hpp"
 #include "utilities/growableArray.hpp"
 
 class HeapRegion;
diff --git a/src/hotspot/share/gc/g1/g1FullGCScope.cpp b/src/hotspot/share/gc/g1/g1FullGCScope.cpp
index 87dd42b93f3..2430451df1c 100644
--- a/src/hotspot/share/gc/g1/g1FullGCScope.cpp
+++ b/src/hotspot/share/gc/g1/g1FullGCScope.cpp
@@ -25,7 +25,7 @@
 #include "precompiled.hpp"
 #include "gc/g1/g1FullGCScope.hpp"
 
-G1FullGCScope::G1FullGCScope(bool explicit_gc, bool clear_soft) :
+G1FullGCScope::G1FullGCScope(GCMemoryManager* memory_manager, bool explicit_gc, bool clear_soft) :
     _rm(),
     _explicit_gc(explicit_gc),
     _g1h(G1CollectedHeap::heap()),
@@ -36,7 +36,7 @@ G1FullGCScope::G1FullGCScope(bool explicit_gc, bool clear_soft) :
     _active(),
     _cpu_time(),
     _soft_refs(clear_soft, _g1h->collector_policy()),
-    _memory_stats(true, _g1h->gc_cause()),
+    _memory_stats(memory_manager, _g1h->gc_cause()),
     _collector_stats(_g1h->g1mm()->full_collection_counters()),
     _heap_transition(_g1h) {
   _timer.register_gc_start();
diff --git a/src/hotspot/share/gc/g1/g1FullGCScope.hpp b/src/hotspot/share/gc/g1/g1FullGCScope.hpp
index 20dc8d21643..850ee0aea0f 100644
--- a/src/hotspot/share/gc/g1/g1FullGCScope.hpp
+++ b/src/hotspot/share/gc/g1/g1FullGCScope.hpp
@@ -37,6 +37,8 @@
 #include "memory/allocation.hpp"
 #include "services/memoryService.hpp"
 
+class GCMemoryManager;
+
 // Class used to group scoped objects used in the Full GC together.
 class G1FullGCScope : public StackObj {
   ResourceMark            _rm;
@@ -54,7 +56,7 @@ class G1FullGCScope : public StackObj {
   G1HeapTransition        _heap_transition;
 
 public:
-  G1FullGCScope(bool explicit_gc, bool clear_soft);
+  G1FullGCScope(GCMemoryManager* memory_manager, bool explicit_gc, bool clear_soft);
   ~G1FullGCScope();
 
   bool is_explicit_gc();
diff --git a/src/hotspot/share/gc/g1/g1HeapVerifier.cpp b/src/hotspot/share/gc/g1/g1HeapVerifier.cpp
index 70e5ed29c65..ca1a22c78e0 100644
--- a/src/hotspot/share/gc/g1/g1HeapVerifier.cpp
+++ b/src/hotspot/share/gc/g1/g1HeapVerifier.cpp
@@ -376,6 +376,37 @@ public:
   }
 };
 
+void G1HeapVerifier::parse_verification_type(const char* type) {
+  if (strcmp(type, "young-only") == 0) {
+    enable_verification_type(G1VerifyYoungOnly);
+  } else if (strcmp(type, "initial-mark") == 0) {
+    enable_verification_type(G1VerifyInitialMark);
+  } else if (strcmp(type, "mixed") == 0) {
+    enable_verification_type(G1VerifyMixed);
+  } else if (strcmp(type, "remark") == 0) {
+    enable_verification_type(G1VerifyRemark);
+  } else if (strcmp(type, "cleanup") == 0) {
+    enable_verification_type(G1VerifyCleanup);
+  } else if (strcmp(type, "full") == 0) {
+    enable_verification_type(G1VerifyFull);
+  } else {
+    log_warning(gc, verify)("VerifyGCType: '%s' is unknown. Available types are: "
+                            "young-only, initial-mark, mixed, remark, cleanup and full", type);
+  }
+}
+
+void G1HeapVerifier::enable_verification_type(G1VerifyType type) {
+  // First enable will clear _enabled_verification_types.
+  if (_enabled_verification_types == G1VerifyAll) {
+    _enabled_verification_types = type;
+  } else {
+    _enabled_verification_types |= type;
+  }
+}
+
+bool G1HeapVerifier::should_verify(G1VerifyType type) {
+  return (_enabled_verification_types & type) == type;
+}
 
 void G1HeapVerifier::verify(VerifyOption vo) {
   if (!SafepointSynchronize::is_at_safepoint()) {
@@ -541,28 +572,32 @@ void G1HeapVerifier::prepare_for_verify() {
   }
 }
 
-double G1HeapVerifier::verify(bool guard, const char* msg) {
+double G1HeapVerifier::verify(G1VerifyType type, VerifyOption vo, const char* msg) {
   double verify_time_ms = 0.0;
 
-  if (guard && _g1h->total_collections() >= VerifyGCStartAt) {
+  if (should_verify(type) && _g1h->total_collections() >= VerifyGCStartAt) {
     double verify_start = os::elapsedTime();
     HandleMark hm;  // Discard invalid handles created during verification
     prepare_for_verify();
-    Universe::verify(VerifyOption_G1UsePrevMarking, msg);
+    Universe::verify(vo, msg);
     verify_time_ms = (os::elapsedTime() - verify_start) * 1000;
   }
 
   return verify_time_ms;
 }
 
-void G1HeapVerifier::verify_before_gc() {
-  double verify_time_ms = verify(VerifyBeforeGC, "Before GC");
-  _g1h->g1_policy()->phase_times()->record_verify_before_time_ms(verify_time_ms);
+void G1HeapVerifier::verify_before_gc(G1VerifyType type) {
+  if (VerifyBeforeGC) {
+    double verify_time_ms = verify(type, VerifyOption_G1UsePrevMarking, "Before GC");
+    _g1h->g1_policy()->phase_times()->record_verify_before_time_ms(verify_time_ms);
+  }
 }
 
-void G1HeapVerifier::verify_after_gc() {
-  double verify_time_ms = verify(VerifyAfterGC, "After GC");
-  _g1h->g1_policy()->phase_times()->record_verify_after_time_ms(verify_time_ms);
+void G1HeapVerifier::verify_after_gc(G1VerifyType type) {
+  if (VerifyAfterGC) {
+    double verify_time_ms = verify(type, VerifyOption_G1UsePrevMarking, "After GC");
+    _g1h->g1_policy()->phase_times()->record_verify_after_time_ms(verify_time_ms);
+  }
 }
 
 
diff --git a/src/hotspot/share/gc/g1/g1HeapVerifier.hpp b/src/hotspot/share/gc/g1/g1HeapVerifier.hpp
index 6d3bb3899f8..ee7cb0a316b 100644
--- a/src/hotspot/share/gc/g1/g1HeapVerifier.hpp
+++ b/src/hotspot/share/gc/g1/g1HeapVerifier.hpp
@@ -34,6 +34,7 @@ class G1CollectedHeap;
 class G1HeapVerifier : public CHeapObj<mtGC> {
 private:
   G1CollectedHeap* _g1h;
+  int _enabled_verification_types;
 
   // verify_region_sets() performs verification over the region
   // lists. It will be compiled in the product code to be used when
@@ -41,8 +42,21 @@ private:
   void verify_region_sets();
 
 public:
+  enum G1VerifyType {
+    G1VerifyYoungOnly   =  1, // -XX:VerifyGCType=young-only
+    G1VerifyInitialMark =  2, // -XX:VerifyGCType=initial-mark
+    G1VerifyMixed       =  4, // -XX:VerifyGCType=mixed
+    G1VerifyRemark      =  8, // -XX:VerifyGCType=remark
+    G1VerifyCleanup     = 16, // -XX:VerifyGCType=cleanup
+    G1VerifyFull        = 32, // -XX:VerifyGCType=full
+    G1VerifyAll         = -1
+  };
 
-  G1HeapVerifier(G1CollectedHeap* heap) : _g1h(heap) { }
+  G1HeapVerifier(G1CollectedHeap* heap) : _g1h(heap), _enabled_verification_types(G1VerifyAll) { }
+
+  void parse_verification_type(const char* type);
+  void enable_verification_type(G1VerifyType type);
+  bool should_verify(G1VerifyType type);
 
   // Perform verification.
 
@@ -73,9 +87,9 @@ public:
 #endif // HEAP_REGION_SET_FORCE_VERIFY
 
   void prepare_for_verify();
-  double verify(bool guard, const char* msg);
-  void verify_before_gc();
-  void verify_after_gc();
+  double verify(G1VerifyType type, VerifyOption vo, const char* msg);
+  void verify_before_gc(G1VerifyType type);
+  void verify_after_gc(G1VerifyType type);
 
 #ifndef PRODUCT
   // Make sure that the given bitmap has no marked objects in the
diff --git a/src/hotspot/share/services/g1MemoryPool.cpp b/src/hotspot/share/gc/g1/g1MemoryPool.cpp
similarity index 95%
rename from src/hotspot/share/services/g1MemoryPool.cpp
rename to src/hotspot/share/gc/g1/g1MemoryPool.cpp
index 92daa7b726d..f658c0d2a27 100644
--- a/src/hotspot/share/services/g1MemoryPool.cpp
+++ b/src/hotspot/share/gc/g1/g1MemoryPool.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2007, 2017, 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
@@ -24,8 +24,8 @@
 
 #include "precompiled.hpp"
 #include "gc/g1/g1CollectedHeap.hpp"
+#include "gc/g1/g1MemoryPool.hpp"
 #include "gc/g1/heapRegion.hpp"
-#include "services/g1MemoryPool.hpp"
 
 G1MemoryPoolSuper::G1MemoryPoolSuper(G1CollectedHeap* g1h,
                                      const char* name,
@@ -33,7 +33,6 @@ G1MemoryPoolSuper::G1MemoryPoolSuper(G1CollectedHeap* g1h,
                                      size_t max_size,
                                      bool support_usage_threshold) :
   _g1mm(g1h->g1mm()), CollectedMemoryPool(name,
-                                          MemoryPool::Heap,
                                           init_size,
                                           max_size,
                                           support_usage_threshold) {
diff --git a/src/hotspot/share/services/g1MemoryPool.hpp b/src/hotspot/share/gc/g1/g1MemoryPool.hpp
similarity index 92%
rename from src/hotspot/share/services/g1MemoryPool.hpp
rename to src/hotspot/share/gc/g1/g1MemoryPool.hpp
index fddc439e899..a6771c3fd79 100644
--- a/src/hotspot/share/services/g1MemoryPool.hpp
+++ b/src/hotspot/share/gc/g1/g1MemoryPool.hpp
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2007, 2017, 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
@@ -22,15 +22,12 @@
  *
  */
 
-#ifndef SHARE_VM_SERVICES_G1MEMORYPOOL_HPP
-#define SHARE_VM_SERVICES_G1MEMORYPOOL_HPP
+#ifndef SHARE_VM_GC_G1_G1MEMORYPOOL_HPP
+#define SHARE_VM_GC_G1_G1MEMORYPOOL_HPP
 
-#include "utilities/macros.hpp"
-#if INCLUDE_ALL_GCS
 #include "gc/g1/g1MonitoringSupport.hpp"
 #include "services/memoryPool.hpp"
 #include "services/memoryUsage.hpp"
-#endif // INCLUDE_ALL_GCS
 
 // This file contains the three classes that represent the memory
 // pools of the G1 spaces: G1EdenPool, G1SurvivorPool, and
@@ -50,6 +47,8 @@
 // on this model.
 //
 
+class G1CollectedHeap;
+
 // This class is shared by the three G1 memory pool classes
 // (G1EdenPool, G1SurvivorPool, G1OldGenPool).
 class G1MemoryPoolSuper : public CollectedMemoryPool {
@@ -107,4 +106,4 @@ public:
   MemoryUsage get_memory_usage();
 };
 
-#endif // SHARE_VM_SERVICES_G1MEMORYPOOL_HPP
+#endif // SHARE_VM_GC_G1_G1MEMORYPOOL_HPP
diff --git a/src/hotspot/share/gc/g1/g1MonitoringSupport.cpp b/src/hotspot/share/gc/g1/g1MonitoringSupport.cpp
index a0cf7c2c3cf..0a566b3afc0 100644
--- a/src/hotspot/share/gc/g1/g1MonitoringSupport.cpp
+++ b/src/hotspot/share/gc/g1/g1MonitoringSupport.cpp
@@ -26,6 +26,7 @@
 #include "gc/g1/g1CollectedHeap.inline.hpp"
 #include "gc/g1/g1MonitoringSupport.hpp"
 #include "gc/g1/g1Policy.hpp"
+#include "gc/shared/hSpaceCounters.hpp"
 
 G1GenerationCounters::G1GenerationCounters(G1MonitoringSupport* g1mm,
                                            const char* name,
@@ -128,10 +129,10 @@ G1MonitoringSupport::G1MonitoringSupport(G1CollectedHeap* g1h) :
   //  name  "generation.1.space.0"
   // Counters are created from maxCapacity, capacity, initCapacity,
   // and used.
-  _old_space_counters = new HSpaceCounters("space", 0 /* ordinal */,
+  _old_space_counters = new HSpaceCounters(_old_collection_counters->name_space(),
+    "space", 0 /* ordinal */,
     pad_capacity(overall_reserved()) /* max_capacity */,
-    pad_capacity(old_space_committed()) /* init_capacity */,
-   _old_collection_counters);
+    pad_capacity(old_space_committed()) /* init_capacity */);
 
   //   Young collection set
   //  name "generation.0".  This is logically the young generation.
@@ -139,27 +140,29 @@ G1MonitoringSupport::G1MonitoringSupport(G1CollectedHeap* g1h) :
   // See  _old_collection_counters for additional counters
   _young_collection_counters = new G1YoungGenerationCounters(this, "young");
 
+  const char* young_collection_name_space = _young_collection_counters->name_space();
+
   //  name "generation.0.space.0"
   // See _old_space_counters for additional counters
-  _eden_counters = new HSpaceCounters("eden", 0 /* ordinal */,
+  _eden_counters = new HSpaceCounters(young_collection_name_space,
+    "eden", 0 /* ordinal */,
     pad_capacity(overall_reserved()) /* max_capacity */,
-    pad_capacity(eden_space_committed()) /* init_capacity */,
-    _young_collection_counters);
+    pad_capacity(eden_space_committed()) /* init_capacity */);
 
   //  name "generation.0.space.1"
   // See _old_space_counters for additional counters
   // Set the arguments to indicate that this survivor space is not used.
-  _from_counters = new HSpaceCounters("s0", 1 /* ordinal */,
+  _from_counters = new HSpaceCounters(young_collection_name_space,
+    "s0", 1 /* ordinal */,
     pad_capacity(0) /* max_capacity */,
-    pad_capacity(0) /* init_capacity */,
-    _young_collection_counters);
+    pad_capacity(0) /* init_capacity */);
 
   //  name "generation.0.space.2"
   // See _old_space_counters for additional counters
-  _to_counters = new HSpaceCounters("s1", 2 /* ordinal */,
+  _to_counters = new HSpaceCounters(young_collection_name_space,
+    "s1", 2 /* ordinal */,
     pad_capacity(overall_reserved()) /* max_capacity */,
-    pad_capacity(survivor_space_committed()) /* init_capacity */,
-    _young_collection_counters);
+    pad_capacity(survivor_space_committed()) /* init_capacity */);
 
   if (UsePerfData) {
     // Given that this survivor space is not used, we update it here
diff --git a/src/hotspot/share/gc/g1/g1MonitoringSupport.hpp b/src/hotspot/share/gc/g1/g1MonitoringSupport.hpp
index 3c1d7444108..ee103237009 100644
--- a/src/hotspot/share/gc/g1/g1MonitoringSupport.hpp
+++ b/src/hotspot/share/gc/g1/g1MonitoringSupport.hpp
@@ -25,9 +25,11 @@
 #ifndef SHARE_VM_GC_G1_G1MONITORINGSUPPORT_HPP
 #define SHARE_VM_GC_G1_G1MONITORINGSUPPORT_HPP
 
-#include "gc/g1/hSpaceCounters.hpp"
+#include "gc/shared/generationCounters.hpp"
 
+class CollectorCounters;
 class G1CollectedHeap;
+class HSpaceCounters;
 
 // Class for monitoring logical spaces in G1. It provides data for
 // both G1's jstat counters as well as G1's memory pools.
diff --git a/src/hotspot/share/gc/g1/g1ParScanThreadState.cpp b/src/hotspot/share/gc/g1/g1ParScanThreadState.cpp
index 32b405d4226..03e75f1989d 100644
--- a/src/hotspot/share/gc/g1/g1ParScanThreadState.cpp
+++ b/src/hotspot/share/gc/g1/g1ParScanThreadState.cpp
@@ -32,6 +32,7 @@
 #include "gc/g1/g1StringDedup.hpp"
 #include "gc/shared/gcTrace.hpp"
 #include "gc/shared/taskqueue.inline.hpp"
+#include "memory/allocation.inline.hpp"
 #include "oops/oop.inline.hpp"
 #include "runtime/prefetch.inline.hpp"
 
@@ -390,3 +391,21 @@ oop G1ParScanThreadState::handle_evacuation_failure_par(oop old, markOop m) {
   }
 }
 
+G1ParScanThreadStateSet::G1ParScanThreadStateSet(G1CollectedHeap* g1h, uint n_workers, size_t young_cset_length) :
+    _g1h(g1h),
+    _states(NEW_C_HEAP_ARRAY(G1ParScanThreadState*, n_workers, mtGC)),
+    _surviving_young_words_total(NEW_C_HEAP_ARRAY(size_t, young_cset_length, mtGC)),
+    _young_cset_length(young_cset_length),
+    _n_workers(n_workers),
+    _flushed(false) {
+  for (uint i = 0; i < n_workers; ++i) {
+    _states[i] = NULL;
+  }
+  memset(_surviving_young_words_total, 0, young_cset_length * sizeof(size_t));
+}
+
+G1ParScanThreadStateSet::~G1ParScanThreadStateSet() {
+  assert(_flushed, "thread local state from the per thread states should have been flushed");
+  FREE_C_HEAP_ARRAY(G1ParScanThreadState*, _states);
+  FREE_C_HEAP_ARRAY(size_t, _surviving_young_words_total);
+}
diff --git a/src/hotspot/share/gc/g1/g1ParScanThreadState.hpp b/src/hotspot/share/gc/g1/g1ParScanThreadState.hpp
index 9bbb9a90a62..310b4270b02 100644
--- a/src/hotspot/share/gc/g1/g1ParScanThreadState.hpp
+++ b/src/hotspot/share/gc/g1/g1ParScanThreadState.hpp
@@ -204,24 +204,8 @@ class G1ParScanThreadStateSet : public StackObj {
   bool _flushed;
 
  public:
-  G1ParScanThreadStateSet(G1CollectedHeap* g1h, uint n_workers, size_t young_cset_length) :
-      _g1h(g1h),
-      _states(NEW_C_HEAP_ARRAY(G1ParScanThreadState*, n_workers, mtGC)),
-      _surviving_young_words_total(NEW_C_HEAP_ARRAY(size_t, young_cset_length, mtGC)),
-      _young_cset_length(young_cset_length),
-      _n_workers(n_workers),
-      _flushed(false) {
-    for (uint i = 0; i < n_workers; ++i) {
-      _states[i] = NULL;
-    }
-    memset(_surviving_young_words_total, 0, young_cset_length * sizeof(size_t));
-  }
-
-  ~G1ParScanThreadStateSet() {
-    assert(_flushed, "thread local state from the per thread states should have been flushed");
-    FREE_C_HEAP_ARRAY(G1ParScanThreadState*, _states);
-    FREE_C_HEAP_ARRAY(size_t, _surviving_young_words_total);
-  }
+  G1ParScanThreadStateSet(G1CollectedHeap* g1h, uint n_workers, size_t young_cset_length);
+  ~G1ParScanThreadStateSet();
 
   void flush();
 
diff --git a/src/hotspot/share/gc/g1/g1RemSet.cpp b/src/hotspot/share/gc/g1/g1RemSet.cpp
index da0281c6130..c20a41e66f0 100644
--- a/src/hotspot/share/gc/g1/g1RemSet.cpp
+++ b/src/hotspot/share/gc/g1/g1RemSet.cpp
@@ -298,7 +298,7 @@ G1RemSet::~G1RemSet() {
 }
 
 uint G1RemSet::num_par_rem_sets() {
-  return MAX2(DirtyCardQueueSet::num_par_ids() + G1ConcurrentRefine::thread_num(), ParallelGCThreads);
+  return MAX2(DirtyCardQueueSet::num_par_ids() + G1ConcurrentRefine::max_num_threads(), ParallelGCThreads);
 }
 
 void G1RemSet::initialize(size_t capacity, uint max_regions) {
diff --git a/src/hotspot/share/gc/g1/g1RemSetSummary.cpp b/src/hotspot/share/gc/g1/g1RemSetSummary.cpp
index 2ddabe75d03..93a8f713836 100644
--- a/src/hotspot/share/gc/g1/g1RemSetSummary.cpp
+++ b/src/hotspot/share/gc/g1/g1RemSetSummary.cpp
@@ -86,7 +86,7 @@ G1RemSetSummary::G1RemSetSummary() :
   _num_processed_buf_mutator(0),
   _num_processed_buf_rs_threads(0),
   _num_coarsenings(0),
-  _num_vtimes(G1ConcurrentRefine::thread_num()),
+  _num_vtimes(G1ConcurrentRefine::max_num_threads()),
   _rs_threads_vtimes(NEW_C_HEAP_ARRAY(double, _num_vtimes, mtGC)),
   _sampling_thread_vtime(0.0f) {
 
@@ -99,7 +99,7 @@ G1RemSetSummary::G1RemSetSummary(G1RemSet* rem_set) :
   _num_processed_buf_mutator(0),
   _num_processed_buf_rs_threads(0),
   _num_coarsenings(0),
-  _num_vtimes(G1ConcurrentRefine::thread_num()),
+  _num_vtimes(G1ConcurrentRefine::max_num_threads()),
   _rs_threads_vtimes(NEW_C_HEAP_ARRAY(double, _num_vtimes, mtGC)),
   _sampling_thread_vtime(0.0f) {
   update();
diff --git a/src/hotspot/share/gc/g1/g1SATBCardTableModRefBS.cpp b/src/hotspot/share/gc/g1/g1SATBCardTableModRefBS.cpp
index 9db03fdfc5f..5c1bc491e5d 100644
--- a/src/hotspot/share/gc/g1/g1SATBCardTableModRefBS.cpp
+++ b/src/hotspot/share/gc/g1/g1SATBCardTableModRefBS.cpp
@@ -175,6 +175,9 @@ void G1SATBCardTableLoggingModRefBS::write_ref_field_post_slow(volatile jbyte* b
 
 void
 G1SATBCardTableLoggingModRefBS::invalidate(MemRegion mr) {
+  if (mr.is_empty()) {
+    return;
+  }
   volatile jbyte* byte = byte_for(mr.start());
   jbyte* last_byte = byte_for(mr.last());
   Thread* thr = Thread::current();
diff --git a/src/hotspot/share/gc/g1/satbMarkQueue.cpp b/src/hotspot/share/gc/g1/satbMarkQueue.cpp
index ddca44b038c..178cb450c9b 100644
--- a/src/hotspot/share/gc/g1/satbMarkQueue.cpp
+++ b/src/hotspot/share/gc/g1/satbMarkQueue.cpp
@@ -32,6 +32,7 @@
 #include "runtime/mutexLocker.hpp"
 #include "runtime/safepoint.hpp"
 #include "runtime/thread.hpp"
+#include "runtime/threadSMR.hpp"
 #include "runtime/vmThread.hpp"
 
 SATBMarkQueue::SATBMarkQueue(SATBMarkQueueSet* qset, bool permanent) :
@@ -214,7 +215,7 @@ void SATBMarkQueueSet::dump_active_states(bool expected_active) {
   log_error(gc, verify)("Expected SATB active state: %s", expected_active ? "ACTIVE" : "INACTIVE");
   log_error(gc, verify)("Actual SATB active states:");
   log_error(gc, verify)("  Queue set: %s", is_active() ? "ACTIVE" : "INACTIVE");
-  for (JavaThread* t = Threads::first(); t; t = t->next()) {
+  for (JavaThreadIteratorWithHandle jtiwh; JavaThread *t = jtiwh.next(); ) {
     log_error(gc, verify)("  Thread \"%s\" queue: %s", t->name(), t->satb_mark_queue().is_active() ? "ACTIVE" : "INACTIVE");
   }
   log_error(gc, verify)("  Shared queue: %s", shared_satb_queue()->is_active() ? "ACTIVE" : "INACTIVE");
@@ -228,7 +229,7 @@ void SATBMarkQueueSet::verify_active_states(bool expected_active) {
   }
 
   // Verify thread queue states
-  for (JavaThread* t = Threads::first(); t; t = t->next()) {
+  for (JavaThreadIteratorWithHandle jtiwh; JavaThread *t = jtiwh.next(); ) {
     if (t->satb_mark_queue().is_active() != expected_active) {
       dump_active_states(expected_active);
       guarantee(false, "Thread SATB queue has an unexpected active state");
@@ -249,14 +250,14 @@ void SATBMarkQueueSet::set_active_all_threads(bool active, bool expected_active)
   verify_active_states(expected_active);
 #endif // ASSERT
   _all_active = active;
-  for (JavaThread* t = Threads::first(); t; t = t->next()) {
+  for (JavaThreadIteratorWithHandle jtiwh; JavaThread *t = jtiwh.next(); ) {
     t->satb_mark_queue().set_active(active);
   }
   shared_satb_queue()->set_active(active);
 }
 
 void SATBMarkQueueSet::filter_thread_buffers() {
-  for(JavaThread* t = Threads::first(); t; t = t->next()) {
+  for (JavaThreadIteratorWithHandle jtiwh; JavaThread *t = jtiwh.next(); ) {
     t->satb_mark_queue().filter();
   }
   shared_satb_queue()->filter();
@@ -309,7 +310,7 @@ void SATBMarkQueueSet::print_all(const char* msg) {
     i += 1;
   }
 
-  for (JavaThread* t = Threads::first(); t; t = t->next()) {
+  for (JavaThreadIteratorWithHandle jtiwh; JavaThread *t = jtiwh.next(); ) {
     jio_snprintf(buffer, SATB_PRINTER_BUFFER_SIZE, "Thread: %s", t->name());
     t->satb_mark_queue().print(buffer);
   }
@@ -341,8 +342,8 @@ void SATBMarkQueueSet::abandon_partial_marking() {
   }
   assert(SafepointSynchronize::is_at_safepoint(), "Must be at safepoint.");
   // So we can safely manipulate these queues.
-  for (JavaThread* t = Threads::first(); t; t = t->next()) {
+  for (JavaThreadIteratorWithHandle jtiwh; JavaThread *t = jtiwh.next(); ) {
     t->satb_mark_queue().reset();
   }
- shared_satb_queue()->reset();
+  shared_satb_queue()->reset();
 }
diff --git a/src/hotspot/share/gc/parallel/mutableNUMASpace.cpp b/src/hotspot/share/gc/parallel/mutableNUMASpace.cpp
index 120ff95bae5..7d5fd87d5c1 100644
--- a/src/hotspot/share/gc/parallel/mutableNUMASpace.cpp
+++ b/src/hotspot/share/gc/parallel/mutableNUMASpace.cpp
@@ -29,6 +29,7 @@
 #include "oops/oop.inline.hpp"
 #include "runtime/atomic.hpp"
 #include "runtime/thread.inline.hpp"
+#include "runtime/threadSMR.hpp"
 #include "utilities/align.hpp"
 
 MutableNUMASpace::MutableNUMASpace(size_t alignment) : MutableSpace(alignment), _must_use_large_pages(false) {
@@ -90,14 +91,14 @@ void MutableNUMASpace::ensure_parsability() {
     MutableSpace *s = ls->space();
     if (s->top() < top()) { // For all spaces preceding the one containing top()
       if (s->free_in_words() > 0) {
-        intptr_t cur_top = (intptr_t)s->top();
+        HeapWord* cur_top = s->top();
         size_t words_left_to_fill = pointer_delta(s->end(), s->top());;
         while (words_left_to_fill > 0) {
           size_t words_to_fill = MIN2(words_left_to_fill, CollectedHeap::filler_array_max_size());
           assert(words_to_fill >= CollectedHeap::min_fill_size(),
                  "Remaining size (" SIZE_FORMAT ") is too small to fill (based on " SIZE_FORMAT " and " SIZE_FORMAT ")",
                  words_to_fill, words_left_to_fill, CollectedHeap::filler_array_max_size());
-          CollectedHeap::fill_with_object((HeapWord*)cur_top, words_to_fill);
+          CollectedHeap::fill_with_object(cur_top, words_to_fill);
           if (!os::numa_has_static_binding()) {
             size_t touched_words = words_to_fill;
 #ifndef ASSERT
@@ -107,19 +108,19 @@ void MutableNUMASpace::ensure_parsability() {
             }
 #endif
             MemRegion invalid;
-            HeapWord *crossing_start = align_up((HeapWord*)cur_top, os::vm_page_size());
-            HeapWord *crossing_end = align_down((HeapWord*)(cur_top + touched_words), os::vm_page_size());
+            HeapWord *crossing_start = align_up(cur_top, os::vm_page_size());
+            HeapWord *crossing_end = align_down(cur_top + touched_words, os::vm_page_size());
             if (crossing_start != crossing_end) {
               // If object header crossed a small page boundary we mark the area
               // as invalid rounding it to a page_size().
-              HeapWord *start = MAX2(align_down((HeapWord*)cur_top, page_size()), s->bottom());
-              HeapWord *end = MIN2(align_up((HeapWord*)(cur_top + touched_words), page_size()), s->end());
+              HeapWord *start = MAX2(align_down(cur_top, page_size()), s->bottom());
+              HeapWord *end = MIN2(align_up(cur_top + touched_words, page_size()), s->end());
               invalid = MemRegion(start, end);
             }
 
             ls->add_invalid_region(invalid);
           }
-          cur_top = cur_top + (words_to_fill * HeapWordSize);
+          cur_top += words_to_fill;
           words_left_to_fill -= words_to_fill;
         }
       }
@@ -287,7 +288,7 @@ bool MutableNUMASpace::update_layout(bool force) {
     FREE_C_HEAP_ARRAY(int, lgrp_ids);
 
     if (changed) {
-      for (JavaThread *thread = Threads::first(); thread; thread = thread->next()) {
+      for (JavaThreadIteratorWithHandle jtiwh; JavaThread *thread = jtiwh.next(); ) {
         thread->set_lgrp_id(-1);
       }
     }
diff --git a/src/hotspot/share/gc/parallel/parallelScavengeHeap.cpp b/src/hotspot/share/gc/parallel/parallelScavengeHeap.cpp
index 27fcf5c1adc..4fc4bc76e3b 100644
--- a/src/hotspot/share/gc/parallel/parallelScavengeHeap.cpp
+++ b/src/hotspot/share/gc/parallel/parallelScavengeHeap.cpp
@@ -33,6 +33,7 @@
 #include "gc/parallel/parallelScavengeHeap.inline.hpp"
 #include "gc/parallel/psAdaptiveSizePolicy.hpp"
 #include "gc/parallel/psMarkSweep.hpp"
+#include "gc/parallel/psMemoryPool.hpp"
 #include "gc/parallel/psParallelCompact.inline.hpp"
 #include "gc/parallel/psPromotionManager.hpp"
 #include "gc/parallel/psScavenge.hpp"
@@ -45,6 +46,7 @@
 #include "runtime/handles.inline.hpp"
 #include "runtime/java.hpp"
 #include "runtime/vmThread.hpp"
+#include "services/memoryManager.hpp"
 #include "services/memTracker.hpp"
 #include "utilities/vmError.hpp"
 
@@ -105,9 +107,9 @@ jint ParallelScavengeHeap::initialize() {
     (old_gen()->virtual_space()->high_boundary() ==
      young_gen()->virtual_space()->low_boundary()),
     "Boundaries must meet");
-  // initialize the policy counters - 2 collectors, 3 generations
+  // initialize the policy counters - 2 collectors, 2 generations
   _gc_policy_counters =
-    new PSGCAdaptivePolicyCounters("ParScav:MSC", 2, 3, _size_policy);
+    new PSGCAdaptivePolicyCounters("ParScav:MSC", 2, 2, _size_policy);
 
   // Set up the GCTaskManager
   _gc_task_manager = GCTaskManager::create(ParallelGCThreads);
@@ -119,7 +121,35 @@ jint ParallelScavengeHeap::initialize() {
   return JNI_OK;
 }
 
+void ParallelScavengeHeap::initialize_serviceability() {
+
+  _eden_pool = new EdenMutableSpacePool(_young_gen,
+                                        _young_gen->eden_space(),
+                                        "PS Eden Space",
+                                        false /* support_usage_threshold */);
+
+  _survivor_pool = new SurvivorMutableSpacePool(_young_gen,
+                                                "PS Survivor Space",
+                                                false /* support_usage_threshold */);
+
+  _old_pool = new PSGenerationPool(_old_gen,
+                                   "PS Old Gen",
+                                   true /* support_usage_threshold */);
+
+  _young_manager = new GCMemoryManager("PS Scavenge", "end of minor GC");
+  _old_manager = new GCMemoryManager("PS MarkSweep", "end of major GC");
+
+  _old_manager->add_pool(_eden_pool);
+  _old_manager->add_pool(_survivor_pool);
+  _old_manager->add_pool(_old_pool);
+
+  _young_manager->add_pool(_eden_pool);
+  _young_manager->add_pool(_survivor_pool);
+
+}
+
 void ParallelScavengeHeap::post_initialize() {
+  CollectedHeap::post_initialize();
   // Need to init the tenuring threshold
   PSScavenge::initialize();
   if (UseParallelOldGC) {
@@ -674,3 +704,19 @@ void ParallelScavengeHeap::register_nmethod(nmethod* nm) {
 void ParallelScavengeHeap::verify_nmethod(nmethod* nm) {
   CodeCache::verify_scavenge_root_nmethod(nm);
 }
+
+GrowableArray<GCMemoryManager*> ParallelScavengeHeap::memory_managers() {
+  GrowableArray<GCMemoryManager*> memory_managers(2);
+  memory_managers.append(_young_manager);
+  memory_managers.append(_old_manager);
+  return memory_managers;
+}
+
+GrowableArray<MemoryPool*> ParallelScavengeHeap::memory_pools() {
+  GrowableArray<MemoryPool*> memory_pools(3);
+  memory_pools.append(_eden_pool);
+  memory_pools.append(_survivor_pool);
+  memory_pools.append(_old_pool);
+  return memory_pools;
+}
+
diff --git a/src/hotspot/share/gc/parallel/parallelScavengeHeap.hpp b/src/hotspot/share/gc/parallel/parallelScavengeHeap.hpp
index bfdc55f07f7..2eca2b12dcd 100644
--- a/src/hotspot/share/gc/parallel/parallelScavengeHeap.hpp
+++ b/src/hotspot/share/gc/parallel/parallelScavengeHeap.hpp
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2001, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2001, 2017, 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
@@ -36,11 +36,14 @@
 #include "gc/shared/gcWhen.hpp"
 #include "gc/shared/strongRootsScope.hpp"
 #include "memory/metaspace.hpp"
+#include "utilities/growableArray.hpp"
 #include "utilities/ostream.hpp"
 
 class AdjoiningGenerations;
 class GCHeapSummary;
 class GCTaskManager;
+class MemoryManager;
+class MemoryPool;
 class PSAdaptiveSizePolicy;
 class PSHeapSummary;
 
@@ -64,6 +67,15 @@ class ParallelScavengeHeap : public CollectedHeap {
   // The task manager
   static GCTaskManager* _gc_task_manager;
 
+  GCMemoryManager* _young_manager;
+  GCMemoryManager* _old_manager;
+
+  MemoryPool* _eden_pool;
+  MemoryPool* _survivor_pool;
+  MemoryPool* _old_pool;
+
+  virtual void initialize_serviceability();
+
   void trace_heap(GCWhen::Type when, const GCTracer* tracer);
 
  protected:
@@ -94,6 +106,9 @@ class ParallelScavengeHeap : public CollectedHeap {
 
   virtual CollectorPolicy* collector_policy() const { return _collector_policy; }
 
+  virtual GrowableArray<GCMemoryManager*> memory_managers();
+  virtual GrowableArray<MemoryPool*> memory_pools();
+
   static PSYoungGen* young_gen() { return _young_gen; }
   static PSOldGen* old_gen()     { return _old_gen; }
 
@@ -244,6 +259,9 @@ class ParallelScavengeHeap : public CollectedHeap {
     ParStrongRootsScope();
     ~ParStrongRootsScope();
   };
+
+  GCMemoryManager* old_gc_manager() const { return _old_manager; }
+  GCMemoryManager* young_gc_manager() const { return _young_manager; }
 };
 
 // Simple class for storing info about the heap at the start of GC, to be used
diff --git a/src/hotspot/share/gc/parallel/psAdaptiveSizePolicy.cpp b/src/hotspot/share/gc/parallel/psAdaptiveSizePolicy.cpp
index d4350f7db6c..437356866df 100644
--- a/src/hotspot/share/gc/parallel/psAdaptiveSizePolicy.cpp
+++ b/src/hotspot/share/gc/parallel/psAdaptiveSizePolicy.cpp
@@ -29,6 +29,7 @@
 #include "gc/parallel/psScavenge.hpp"
 #include "gc/shared/collectorPolicy.hpp"
 #include "gc/shared/gcCause.hpp"
+#include "gc/shared/gcUtil.inline.hpp"
 #include "gc/shared/gcPolicyCounters.hpp"
 #include "logging/log.hpp"
 #include "runtime/timer.hpp"
diff --git a/src/hotspot/share/gc/parallel/psGenerationCounters.cpp b/src/hotspot/share/gc/parallel/psGenerationCounters.cpp
index d200258acdf..1b0e8d320a9 100644
--- a/src/hotspot/share/gc/parallel/psGenerationCounters.cpp
+++ b/src/hotspot/share/gc/parallel/psGenerationCounters.cpp
@@ -25,9 +25,9 @@
 
 #include "precompiled.hpp"
 #include "gc/parallel/psGenerationCounters.hpp"
+#include "memory/allocation.inline.hpp"
 #include "memory/resourceArea.hpp"
 
-
 PSGenerationCounters::PSGenerationCounters(const char* name,
                                        int ordinal, int spaces,
                                        size_t min_capacity,
diff --git a/src/hotspot/share/gc/parallel/psMarkSweep.cpp b/src/hotspot/share/gc/parallel/psMarkSweep.cpp
index 185cbb2f62d..2ac25afc016 100644
--- a/src/hotspot/share/gc/parallel/psMarkSweep.cpp
+++ b/src/hotspot/share/gc/parallel/psMarkSweep.cpp
@@ -172,7 +172,7 @@ bool PSMarkSweep::invoke_no_policy(bool clear_all_softrefs) {
     heap->pre_full_gc_dump(_gc_timer);
 
     TraceCollectorStats tcs(counters());
-    TraceMemoryManagerStats tms(true /* Full GC */,gc_cause);
+    TraceMemoryManagerStats tms(heap->old_gc_manager(),gc_cause);
 
     if (log_is_enabled(Debug, gc, heap, exit)) {
       accumulated_time()->start();
diff --git a/src/hotspot/share/services/psMemoryPool.cpp b/src/hotspot/share/gc/parallel/psMemoryPool.cpp
similarity index 81%
rename from src/hotspot/share/services/psMemoryPool.cpp
rename to src/hotspot/share/gc/parallel/psMemoryPool.cpp
index 8e356cea27b..92e94efdfa2 100644
--- a/src/hotspot/share/services/psMemoryPool.cpp
+++ b/src/hotspot/share/gc/parallel/psMemoryPool.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2007, 2017, 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
@@ -23,21 +23,12 @@
  */
 
 #include "precompiled.hpp"
-#include "classfile/systemDictionary.hpp"
-#include "classfile/vmSymbols.hpp"
-#include "oops/oop.inline.hpp"
-#include "runtime/handles.inline.hpp"
-#include "runtime/javaCalls.hpp"
-#include "services/lowMemoryDetector.hpp"
-#include "services/management.hpp"
-#include "services/memoryManager.hpp"
-#include "services/psMemoryPool.hpp"
+#include "gc/parallel/psMemoryPool.hpp"
 
 PSGenerationPool::PSGenerationPool(PSOldGen* old_gen,
                                    const char* name,
-                                   PoolType type,
                                    bool support_usage_threshold) :
-  CollectedMemoryPool(name, type, old_gen->capacity_in_bytes(),
+  CollectedMemoryPool(name, old_gen->capacity_in_bytes(),
                       old_gen->reserved().byte_size(), support_usage_threshold), _old_gen(old_gen) {
 }
 
@@ -58,9 +49,8 @@ MemoryUsage PSGenerationPool::get_memory_usage() {
 EdenMutableSpacePool::EdenMutableSpacePool(PSYoungGen* young_gen,
                                            MutableSpace* space,
                                            const char* name,
-                                           PoolType type,
                                            bool support_usage_threshold) :
-  CollectedMemoryPool(name, type, space->capacity_in_bytes(),
+  CollectedMemoryPool(name, space->capacity_in_bytes(),
                       (young_gen->max_size() - young_gen->from_space()->capacity_in_bytes() - young_gen->to_space()->capacity_in_bytes()),
                        support_usage_threshold),
   _young_gen(young_gen),
@@ -82,9 +72,8 @@ MemoryUsage EdenMutableSpacePool::get_memory_usage() {
 //
 SurvivorMutableSpacePool::SurvivorMutableSpacePool(PSYoungGen* young_gen,
                                                    const char* name,
-                                                   PoolType type,
                                                    bool support_usage_threshold) :
-  CollectedMemoryPool(name, type, young_gen->from_space()->capacity_in_bytes(),
+  CollectedMemoryPool(name, young_gen->from_space()->capacity_in_bytes(),
                       young_gen->from_space()->capacity_in_bytes(),
                       support_usage_threshold), _young_gen(young_gen) {
 }
diff --git a/src/hotspot/share/services/psMemoryPool.hpp b/src/hotspot/share/gc/parallel/psMemoryPool.hpp
similarity index 82%
rename from src/hotspot/share/services/psMemoryPool.hpp
rename to src/hotspot/share/gc/parallel/psMemoryPool.hpp
index f1a900caa0f..522799d8336 100644
--- a/src/hotspot/share/services/psMemoryPool.hpp
+++ b/src/hotspot/share/gc/parallel/psMemoryPool.hpp
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2007, 2017, 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
@@ -25,28 +25,22 @@
 #ifndef SHARE_VM_SERVICES_PSMEMORYPOOL_HPP
 #define SHARE_VM_SERVICES_PSMEMORYPOOL_HPP
 
-#include "utilities/macros.hpp"
-#if INCLUDE_ALL_GCS
 #include "gc/parallel/mutableSpace.hpp"
 #include "gc/parallel/psOldGen.hpp"
 #include "gc/parallel/psYoungGen.hpp"
-#include "gc/serial/defNewGeneration.hpp"
-#include "gc/shared/space.hpp"
-#include "memory/heap.hpp"
 #include "services/memoryPool.hpp"
 #include "services/memoryUsage.hpp"
-#endif // INCLUDE_ALL_GCS
 
 class PSGenerationPool : public CollectedMemoryPool {
 private:
   PSOldGen* _old_gen;
 
 public:
-  PSGenerationPool(PSOldGen* pool, const char* name, PoolType type, bool support_usage_threshold);
+  PSGenerationPool(PSOldGen* pool, const char* name, bool support_usage_threshold);
 
   MemoryUsage get_memory_usage();
-  size_t used_in_bytes()              { return _old_gen->used_in_bytes(); }
-  size_t max_size() const             { return _old_gen->reserved().byte_size(); }
+  size_t used_in_bytes() { return _old_gen->used_in_bytes(); }
+  size_t max_size() const { return _old_gen->reserved().byte_size(); }
 };
 
 class EdenMutableSpacePool : public CollectedMemoryPool {
@@ -58,7 +52,6 @@ public:
   EdenMutableSpacePool(PSYoungGen* young_gen,
                        MutableSpace* space,
                        const char* name,
-                       PoolType type,
                        bool support_usage_threshold);
 
   MutableSpace* space()                     { return _space; }
@@ -77,7 +70,6 @@ private:
 public:
   SurvivorMutableSpacePool(PSYoungGen* young_gen,
                            const char* name,
-                           PoolType type,
                            bool support_usage_threshold);
 
   MemoryUsage get_memory_usage();
diff --git a/src/hotspot/share/gc/parallel/psParallelCompact.cpp b/src/hotspot/share/gc/parallel/psParallelCompact.cpp
index c46f0d41aa0..be23ea77681 100644
--- a/src/hotspot/share/gc/parallel/psParallelCompact.cpp
+++ b/src/hotspot/share/gc/parallel/psParallelCompact.cpp
@@ -1772,7 +1772,7 @@ bool PSParallelCompact::invoke_no_policy(bool maximum_heap_compaction) {
     heap->pre_full_gc_dump(&_gc_timer);
 
     TraceCollectorStats tcs(counters());
-    TraceMemoryManagerStats tms(true /* Full GC */,gc_cause);
+    TraceMemoryManagerStats tms(heap->old_gc_manager(), gc_cause);
 
     if (log_is_enabled(Debug, gc, heap, exit)) {
       accumulated_time()->start();
diff --git a/src/hotspot/share/gc/parallel/psScavenge.cpp b/src/hotspot/share/gc/parallel/psScavenge.cpp
index 64c0b7486ab..5bcb21bf9ff 100644
--- a/src/hotspot/share/gc/parallel/psScavenge.cpp
+++ b/src/hotspot/share/gc/parallel/psScavenge.cpp
@@ -305,7 +305,7 @@ bool PSScavenge::invoke_no_policy() {
     GCTraceCPUTime tcpu;
     GCTraceTime(Info, gc) tm("Pause Young", NULL, gc_cause, true);
     TraceCollectorStats tcs(counters());
-    TraceMemoryManagerStats tms(false /* not full GC */,gc_cause);
+    TraceMemoryManagerStats tms(heap->young_gc_manager(), gc_cause);
 
     if (log_is_enabled(Debug, gc, heap, exit)) {
       accumulated_time()->start();
diff --git a/src/hotspot/share/gc/parallel/spaceCounters.cpp b/src/hotspot/share/gc/parallel/spaceCounters.cpp
index 36cb8099523..3a7231d8b25 100644
--- a/src/hotspot/share/gc/parallel/spaceCounters.cpp
+++ b/src/hotspot/share/gc/parallel/spaceCounters.cpp
@@ -24,6 +24,7 @@
 
 #include "precompiled.hpp"
 #include "gc/parallel/spaceCounters.hpp"
+#include "memory/allocation.inline.hpp"
 #include "memory/resourceArea.hpp"
 #include "utilities/macros.hpp"
 
@@ -63,3 +64,7 @@ SpaceCounters::SpaceCounters(const char* name, int ordinal, size_t max_size,
                                      _object_space->capacity_in_bytes(), CHECK);
   }
 }
+
+SpaceCounters::~SpaceCounters() {
+  if (_name_space != NULL) FREE_C_HEAP_ARRAY(char, _name_space);
+}
diff --git a/src/hotspot/share/gc/parallel/spaceCounters.hpp b/src/hotspot/share/gc/parallel/spaceCounters.hpp
index 38e6542085f..e2cf1621b3d 100644
--- a/src/hotspot/share/gc/parallel/spaceCounters.hpp
+++ b/src/hotspot/share/gc/parallel/spaceCounters.hpp
@@ -53,9 +53,7 @@ class SpaceCounters: public CHeapObj<mtGC> {
   SpaceCounters(const char* name, int ordinal, size_t max_size,
                 MutableSpace* m, GenerationCounters* gc);
 
-  ~SpaceCounters() {
-    if (_name_space != NULL) FREE_C_HEAP_ARRAY(char, _name_space);
-  }
+  ~SpaceCounters();
 
   inline void update_capacity() {
     _capacity->set_value(_object_space->capacity_in_bytes());
diff --git a/src/hotspot/share/gc/serial/cSpaceCounters.cpp b/src/hotspot/share/gc/serial/cSpaceCounters.cpp
index 84e9c96bd4e..076902d1dca 100644
--- a/src/hotspot/share/gc/serial/cSpaceCounters.cpp
+++ b/src/hotspot/share/gc/serial/cSpaceCounters.cpp
@@ -24,6 +24,7 @@
 
 #include "precompiled.hpp"
 #include "gc/serial/cSpaceCounters.hpp"
+#include "memory/allocation.inline.hpp"
 #include "memory/metaspace.hpp"
 #include "memory/resourceArea.hpp"
 
@@ -64,6 +65,10 @@ CSpaceCounters::CSpaceCounters(const char* name, int ordinal, size_t max_size,
   }
 }
 
+CSpaceCounters::~CSpaceCounters() {
+    if (_name_space != NULL) FREE_C_HEAP_ARRAY(char, _name_space);
+}
+
 void CSpaceCounters::update_capacity() {
   _capacity->set_value(_space->capacity());
 }
diff --git a/src/hotspot/share/gc/serial/cSpaceCounters.hpp b/src/hotspot/share/gc/serial/cSpaceCounters.hpp
index ffe43ab14a2..da0c4cfbf31 100644
--- a/src/hotspot/share/gc/serial/cSpaceCounters.hpp
+++ b/src/hotspot/share/gc/serial/cSpaceCounters.hpp
@@ -52,9 +52,7 @@ class CSpaceCounters: public CHeapObj<mtGC> {
   CSpaceCounters(const char* name, int ordinal, size_t max_size,
                  ContiguousSpace* s, GenerationCounters* gc);
 
-  ~CSpaceCounters() {
-      if (_name_space != NULL) FREE_C_HEAP_ARRAY(char, _name_space);
-  }
+  ~CSpaceCounters();
 
   virtual void update_capacity();
   virtual void update_used();
diff --git a/src/hotspot/share/gc/serial/serialHeap.cpp b/src/hotspot/share/gc/serial/serialHeap.cpp
index c4c17a8a386..c395e11fd27 100644
--- a/src/hotspot/share/gc/serial/serialHeap.cpp
+++ b/src/hotspot/share/gc/serial/serialHeap.cpp
@@ -23,9 +23,44 @@
  */
 
 #include "precompiled.hpp"
+#include "gc/serial/defNewGeneration.hpp"
 #include "gc/serial/serialHeap.hpp"
+#include "gc/shared/genMemoryPools.hpp"
+#include "services/memoryManager.hpp"
 
-SerialHeap::SerialHeap(GenCollectorPolicy* policy) : GenCollectedHeap(policy) {}
+SerialHeap::SerialHeap(GenCollectorPolicy* policy) :
+  GenCollectedHeap(policy), _eden_pool(NULL), _survivor_pool(NULL), _old_pool(NULL) {
+  _young_manager = new GCMemoryManager("Copy", "end of minor GC");
+  _old_manager = new GCMemoryManager("MarkSweepCompact", "end of major GC");
+}
+
+void SerialHeap::initialize_serviceability() {
+
+  DefNewGeneration* young = (DefNewGeneration*) young_gen();
+
+  // Add a memory pool for each space and young gen doesn't
+  // support low memory detection as it is expected to get filled up.
+  _eden_pool = new ContiguousSpacePool(young->eden(),
+                                       "Eden Space",
+                                       young->max_eden_size(),
+                                       false /* support_usage_threshold */);
+  _survivor_pool = new SurvivorContiguousSpacePool(young,
+                                                   "Survivor Space",
+                                                   young->max_survivor_size(),
+                                                   false /* support_usage_threshold */);
+  Generation* old = old_gen();
+  _old_pool = new GenerationPool(old, "Tenured Gen", true);
+
+  _young_manager->add_pool(_eden_pool);
+  _young_manager->add_pool(_survivor_pool);
+  young->set_gc_manager(_young_manager);
+
+  _old_manager->add_pool(_eden_pool);
+  _old_manager->add_pool(_survivor_pool);
+  _old_manager->add_pool(_old_pool);
+  old->set_gc_manager(_old_manager);
+
+}
 
 void SerialHeap::check_gen_kinds() {
   assert(young_gen()->kind() == Generation::DefNew,
@@ -33,3 +68,18 @@ void SerialHeap::check_gen_kinds() {
   assert(old_gen()->kind() == Generation::MarkSweepCompact,
          "Wrong generation kind");
 }
+
+GrowableArray<GCMemoryManager*> SerialHeap::memory_managers() {
+  GrowableArray<GCMemoryManager*> memory_managers(2);
+  memory_managers.append(_young_manager);
+  memory_managers.append(_old_manager);
+  return memory_managers;
+}
+
+GrowableArray<MemoryPool*> SerialHeap::memory_pools() {
+  GrowableArray<MemoryPool*> memory_pools(3);
+  memory_pools.append(_eden_pool);
+  memory_pools.append(_survivor_pool);
+  memory_pools.append(_old_pool);
+  return memory_pools;
+}
diff --git a/src/hotspot/share/gc/serial/serialHeap.hpp b/src/hotspot/share/gc/serial/serialHeap.hpp
index 8b96006638a..8df86f2e4bf 100644
--- a/src/hotspot/share/gc/serial/serialHeap.hpp
+++ b/src/hotspot/share/gc/serial/serialHeap.hpp
@@ -26,10 +26,20 @@
 #define SHARE_VM_GC_SERIAL_SERIALHEAP_HPP
 
 #include "gc/shared/genCollectedHeap.hpp"
+#include "utilities/growableArray.hpp"
 
 class GenCollectorPolicy;
+class GCMemoryManager;
+class MemoryPool;
 
 class SerialHeap : public GenCollectedHeap {
+private:
+  MemoryPool* _eden_pool;
+  MemoryPool* _survivor_pool;
+  MemoryPool* _old_pool;
+
+  virtual void initialize_serviceability();
+
 protected:
   virtual void check_gen_kinds();
 
@@ -44,6 +54,9 @@ public:
     return "Serial";
   }
 
+  virtual GrowableArray<GCMemoryManager*> memory_managers();
+  virtual GrowableArray<MemoryPool*> memory_pools();
+
   // override
   virtual bool is_in_closed_subset(const void* p) const {
     return is_in(p);
@@ -52,7 +65,6 @@ public:
   virtual bool card_mark_must_follow_store() const {
     return false;
   }
-
 };
 
 #endif // SHARE_VM_GC_CMS_CMSHEAP_HPP
diff --git a/src/hotspot/share/gc/shared/adaptiveSizePolicy.cpp b/src/hotspot/share/gc/shared/adaptiveSizePolicy.cpp
index 9aa3f79c08d..830835f19dc 100644
--- a/src/hotspot/share/gc/shared/adaptiveSizePolicy.cpp
+++ b/src/hotspot/share/gc/shared/adaptiveSizePolicy.cpp
@@ -26,6 +26,7 @@
 #include "gc/shared/adaptiveSizePolicy.hpp"
 #include "gc/shared/collectorPolicy.hpp"
 #include "gc/shared/gcCause.hpp"
+#include "gc/shared/gcUtil.inline.hpp"
 #include "gc/shared/workgroup.hpp"
 #include "logging/log.hpp"
 #include "runtime/timer.hpp"
diff --git a/src/hotspot/share/gc/shared/collectedHeap.cpp b/src/hotspot/share/gc/shared/collectedHeap.cpp
index b2a0dc53ceb..a32fd69a3fb 100644
--- a/src/hotspot/share/gc/shared/collectedHeap.cpp
+++ b/src/hotspot/share/gc/shared/collectedHeap.cpp
@@ -40,6 +40,7 @@
 #include "oops/oop.inline.hpp"
 #include "runtime/init.hpp"
 #include "runtime/thread.inline.hpp"
+#include "runtime/threadSMR.hpp"
 #include "services/heapDumper.hpp"
 #include "utilities/align.hpp"
 
@@ -540,10 +541,11 @@ void CollectedHeap::ensure_parsability(bool retire_tlabs) {
   const bool deferred = _defer_initial_card_mark;
   // The main thread starts allocating via a TLAB even before it
   // has added itself to the threads list at vm boot-up.
-  assert(!use_tlab || Threads::first() != NULL,
+  JavaThreadIteratorWithHandle jtiwh;
+  assert(!use_tlab || jtiwh.length() > 0,
          "Attempt to fill tlabs before main thread has been added"
          " to threads list is doomed to failure!");
-  for (JavaThread *thread = Threads::first(); thread; thread = thread->next()) {
+  for (; JavaThread *thread = jtiwh.next(); ) {
      if (use_tlab) thread->tlab().make_parsable(retire_tlabs);
 #if COMPILER2_OR_JVMCI
      // The deferred store barriers must all have been flushed to the
@@ -609,3 +611,7 @@ void CollectedHeap::initialize_reserved_region(HeapWord *start, HeapWord *end) {
   _reserved.set_start(start);
   _reserved.set_end(end);
 }
+
+void CollectedHeap::post_initialize() {
+  initialize_serviceability();
+}
diff --git a/src/hotspot/share/gc/shared/collectedHeap.hpp b/src/hotspot/share/gc/shared/collectedHeap.hpp
index 197eaf9b438..7be834d815c 100644
--- a/src/hotspot/share/gc/shared/collectedHeap.hpp
+++ b/src/hotspot/share/gc/shared/collectedHeap.hpp
@@ -34,6 +34,7 @@
 #include "utilities/debug.hpp"
 #include "utilities/events.hpp"
 #include "utilities/formatBuffer.hpp"
+#include "utilities/growableArray.hpp"
 
 // A "CollectedHeap" is an implementation of a java heap for HotSpot.  This
 // is an abstract class: there may be many different kinds of heaps.  This
@@ -46,6 +47,8 @@ class CollectorPolicy;
 class GCHeapSummary;
 class GCTimer;
 class GCTracer;
+class GCMemoryManager;
+class MemoryPool;
 class MetaspaceSummary;
 class Thread;
 class ThreadClosure;
@@ -217,7 +220,7 @@ class CollectedHeap : public CHeapObj<mtInternal> {
   // In many heaps, there will be a need to perform some initialization activities
   // after the Universe is fully formed, but before general heap allocation is allowed.
   // This is the correct place to place such initialization methods.
-  virtual void post_initialize() = 0;
+  virtual void post_initialize();
 
   // Stop any onging concurrent work and prepare for exit.
   virtual void stop() {}
@@ -485,6 +488,9 @@ class CollectedHeap : public CHeapObj<mtInternal> {
   // Return the CollectorPolicy for the heap
   virtual CollectorPolicy* collector_policy() const = 0;
 
+  virtual GrowableArray<GCMemoryManager*> memory_managers() = 0;
+  virtual GrowableArray<MemoryPool*> memory_pools() = 0;
+
   // Iterate over all objects, calling "cl.do_object" on each.
   virtual void object_iterate(ObjectClosure* cl) = 0;
 
@@ -529,6 +535,9 @@ class CollectedHeap : public CHeapObj<mtInternal> {
   // Generate any dumps preceding or following a full gc
  private:
   void full_gc_dump(GCTimer* timer, bool before);
+
+  virtual void initialize_serviceability() = 0;
+
  public:
   void pre_full_gc_dump(GCTimer* timer);
   void post_full_gc_dump(GCTimer* timer);
diff --git a/src/hotspot/share/gc/shared/collectorCounters.cpp b/src/hotspot/share/gc/shared/collectorCounters.cpp
index b204a02e272..2a2e60f99b3 100644
--- a/src/hotspot/share/gc/shared/collectorCounters.cpp
+++ b/src/hotspot/share/gc/shared/collectorCounters.cpp
@@ -24,7 +24,9 @@
 
 #include "precompiled.hpp"
 #include "gc/shared/collectorCounters.hpp"
+#include "memory/allocation.inline.hpp"
 #include "memory/resourceArea.hpp"
+#include "runtime/os.hpp"
 
 CollectorCounters::CollectorCounters(const char* name, int ordinal) {
 
@@ -59,3 +61,24 @@ CollectorCounters::CollectorCounters(const char* name, int ordinal) {
                                                        CHECK);
   }
 }
+
+CollectorCounters::~CollectorCounters() {
+  if (_name_space != NULL) {
+    FREE_C_HEAP_ARRAY(char, _name_space);
+  }
+}
+
+TraceCollectorStats::TraceCollectorStats(CollectorCounters* c) :
+    PerfTraceTimedEvent(c->time_counter(), c->invocation_counter()),
+    _c(c) {
+
+  if (UsePerfData) {
+     _c->last_entry_counter()->set_value(os::elapsed_counter());
+  }
+}
+
+TraceCollectorStats::~TraceCollectorStats() {
+  if (UsePerfData) {
+    _c->last_exit_counter()->set_value(os::elapsed_counter());
+  }
+}
diff --git a/src/hotspot/share/gc/shared/collectorCounters.hpp b/src/hotspot/share/gc/shared/collectorCounters.hpp
index 554d749e0cc..e9b272f03a9 100644
--- a/src/hotspot/share/gc/shared/collectorCounters.hpp
+++ b/src/hotspot/share/gc/shared/collectorCounters.hpp
@@ -49,9 +49,7 @@ class CollectorCounters: public CHeapObj<mtGC> {
 
     CollectorCounters(const char* name, int ordinal);
 
-    ~CollectorCounters() {
-      if (_name_space != NULL) FREE_C_HEAP_ARRAY(char, _name_space);
-    }
+    ~CollectorCounters();
 
     inline PerfCounter* invocation_counter() const  { return _invocations; }
 
@@ -70,18 +68,9 @@ class TraceCollectorStats: public PerfTraceTimedEvent {
     CollectorCounters* _c;
 
   public:
-    inline TraceCollectorStats(CollectorCounters* c) :
-           PerfTraceTimedEvent(c->time_counter(), c->invocation_counter()),
-           _c(c) {
+    TraceCollectorStats(CollectorCounters* c);
 
-      if (UsePerfData) {
-         _c->last_entry_counter()->set_value(os::elapsed_counter());
-      }
-    }
-
-    inline ~TraceCollectorStats() {
-      if (UsePerfData) _c->last_exit_counter()->set_value(os::elapsed_counter());
-    }
+    ~TraceCollectorStats();
 };
 
 #endif // SHARE_VM_GC_SHARED_COLLECTORCOUNTERS_HPP
diff --git a/src/hotspot/share/gc/shared/collectorPolicy.cpp b/src/hotspot/share/gc/shared/collectorPolicy.cpp
index 2d21024a3c7..7542662ee76 100644
--- a/src/hotspot/share/gc/shared/collectorPolicy.cpp
+++ b/src/hotspot/share/gc/shared/collectorPolicy.cpp
@@ -911,7 +911,7 @@ void MarkSweepPolicy::initialize_generations() {
 }
 
 void MarkSweepPolicy::initialize_gc_policy_counters() {
-  // Initialize the policy counters - 2 collectors, 3 generations.
-  _gc_policy_counters = new GCPolicyCounters("Copy:MSC", 2, 3);
+  // Initialize the policy counters - 2 collectors, 2 generations.
+  _gc_policy_counters = new GCPolicyCounters("Copy:MSC", 2, 2);
 }
 
diff --git a/src/hotspot/share/gc/shared/gcArguments.cpp b/src/hotspot/share/gc/shared/gcArguments.cpp
index 90432c5f48b..8162faf0c67 100644
--- a/src/hotspot/share/gc/shared/gcArguments.cpp
+++ b/src/hotspot/share/gc/shared/gcArguments.cpp
@@ -25,6 +25,7 @@
 #include "precompiled.hpp"
 #include "gc/shared/gcArguments.hpp"
 #include "gc/serial/serialArguments.hpp"
+#include "logging/log.hpp"
 #include "memory/allocation.inline.hpp"
 #include "runtime/arguments.hpp"
 #include "runtime/globals.hpp"
@@ -84,6 +85,12 @@ void GCArguments::select_gc_ergonomically() {
 #endif // INCLUDE_ALL_GCS
 }
 
+bool GCArguments::parse_verification_type(const char* type) {
+  log_warning(gc, verify)("VerifyGCType is not supported by this collector.");
+  // Return false to avoid multiple warnings.
+  return false;
+}
+
 void GCArguments::initialize_flags() {
 #if INCLUDE_ALL_GCS
   if (MinHeapFreeRatio == 100) {
@@ -99,6 +106,24 @@ void GCArguments::initialize_flags() {
 #endif // INCLUDE_ALL_GCS
 }
 
+void GCArguments::post_heap_initialize() {
+  if (strlen(VerifyGCType) > 0) {
+    const char delimiter[] = " ,\n";
+    size_t length = strlen(VerifyGCType);
+    char* type_list = NEW_C_HEAP_ARRAY(char, length + 1, mtInternal);
+    strncpy(type_list, VerifyGCType, length + 1);
+    char* token = strtok(type_list, delimiter);
+    while (token != NULL) {
+      bool success = parse_verification_type(token);
+      if (!success) {
+        break;
+      }
+      token = strtok(NULL, delimiter);
+    }
+    FREE_C_HEAP_ARRAY(char, type_list);
+  }
+}
+
 jint GCArguments::initialize() {
   assert(!is_initialized(), "GC arguments already initialized");
 
diff --git a/src/hotspot/share/gc/shared/gcArguments.hpp b/src/hotspot/share/gc/shared/gcArguments.hpp
index b04c12d4a85..3a3f89be136 100644
--- a/src/hotspot/share/gc/shared/gcArguments.hpp
+++ b/src/hotspot/share/gc/shared/gcArguments.hpp
@@ -46,8 +46,16 @@ public:
   static bool is_initialized();
   static GCArguments* arguments();
 
+  void post_heap_initialize();
+
   virtual void initialize_flags();
 
+  // Collector specific function to allow finer grained verification
+  // through VerifyGCType. If not overridden the default version will
+  // warn that the flag is not supported for the given collector.
+  // Returns true if parsing should continue, false otherwise.
+  virtual bool parse_verification_type(const char* type);
+
   virtual size_t conservative_max_heap_alignment() = 0;
 
   virtual CollectedHeap* create_heap() = 0;
diff --git a/src/hotspot/share/gc/shared/gcLocker.cpp b/src/hotspot/share/gc/shared/gcLocker.cpp
index 4b72cd80bfd..644d91116ba 100644
--- a/src/hotspot/share/gc/shared/gcLocker.cpp
+++ b/src/hotspot/share/gc/shared/gcLocker.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2017, 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
@@ -29,6 +29,7 @@
 #include "logging/log.hpp"
 #include "runtime/atomic.hpp"
 #include "runtime/thread.inline.hpp"
+#include "runtime/threadSMR.hpp"
 
 volatile jint GCLocker::_jni_lock_count = 0;
 volatile bool GCLocker::_needs_gc       = false;
@@ -45,14 +46,16 @@ void GCLocker::verify_critical_count() {
     assert(!needs_gc() || _debug_jni_lock_count == _jni_lock_count, "must agree");
     int count = 0;
     // Count the number of threads with critical operations in progress
-    for (JavaThread* thr = Threads::first(); thr; thr = thr->next()) {
+    JavaThreadIteratorWithHandle jtiwh;
+    for (; JavaThread *thr = jtiwh.next(); ) {
       if (thr->in_critical()) {
         count++;
       }
     }
     if (_jni_lock_count != count) {
       log_error(gc, verify)("critical counts don't match: %d != %d", _jni_lock_count, count);
-      for (JavaThread* thr = Threads::first(); thr; thr = thr->next()) {
+      jtiwh.rewind();
+      for (; JavaThread *thr = jtiwh.next(); ) {
         if (thr->in_critical()) {
           log_error(gc, verify)(INTPTR_FORMAT " in_critical %d", p2i(thr), thr->in_critical());
         }
diff --git a/src/hotspot/share/gc/shared/gcStats.cpp b/src/hotspot/share/gc/shared/gcStats.cpp
index 3a050f73066..14e63ea108f 100644
--- a/src/hotspot/share/gc/shared/gcStats.cpp
+++ b/src/hotspot/share/gc/shared/gcStats.cpp
@@ -24,8 +24,7 @@
 
 #include "precompiled.hpp"
 #include "gc/shared/gcStats.hpp"
-#include "gc/shared/gcUtil.hpp"
-#include "memory/allocation.inline.hpp"
+#include "gc/shared/gcUtil.inline.hpp"
 
 GCStats::GCStats() {
     _avg_promoted       = new AdaptivePaddedNoZeroDevAverage(
diff --git a/src/hotspot/share/gc/shared/gcUtil.hpp b/src/hotspot/share/gc/shared/gcUtil.hpp
index 6b2f929bb5d..1fafbb51a00 100644
--- a/src/hotspot/share/gc/shared/gcUtil.hpp
+++ b/src/hotspot/share/gc/shared/gcUtil.hpp
@@ -146,7 +146,7 @@ class AdaptivePaddedAverage : public AdaptiveWeightedAverage {
   // Placement support
   void* operator new(size_t ignored, void* p) throw() { return p; }
   // Allocator
-  void* operator new(size_t size) throw() { return CHeapObj<mtGC>::operator new(size); }
+  void* operator new(size_t size) throw();
 
   // Accessor
   float padded_average() const         { return _padded_avg; }
diff --git a/src/hotspot/share/gc/shared/gcUtil.inline.hpp b/src/hotspot/share/gc/shared/gcUtil.inline.hpp
new file mode 100644
index 00000000000..f03480becde
--- /dev/null
+++ b/src/hotspot/share/gc/shared/gcUtil.inline.hpp
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2002, 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+#ifndef SHARE_VM_GC_SHARED_GCUTIL_INLINE_HPP
+#define SHARE_VM_GC_SHARED_GCUTIL_INLINE_HPP
+
+#include "gc/shared/gcUtil.hpp"
+#include "memory/allocation.inline.hpp"
+
+inline void* AdaptivePaddedAverage::operator new(size_t size) throw() {
+  return CHeapObj<mtGC>::operator new(size);
+}
+
+#endif // SHARE_VM_GC_SHARED_GCUTIL_INLINE_HPP
diff --git a/src/hotspot/share/gc/shared/genCollectedHeap.cpp b/src/hotspot/share/gc/shared/genCollectedHeap.cpp
index 5b21eb6a294..ae67feca465 100644
--- a/src/hotspot/share/gc/shared/genCollectedHeap.cpp
+++ b/src/hotspot/share/gc/shared/genCollectedHeap.cpp
@@ -143,6 +143,7 @@ char* GenCollectedHeap::allocate(size_t alignment,
 }
 
 void GenCollectedHeap::post_initialize() {
+  CollectedHeap::post_initialize();
   ref_processing_init();
   check_gen_kinds();
   DefNewGeneration* def_new_gen = (DefNewGeneration*)_young_gen;
@@ -270,7 +271,7 @@ void GenCollectedHeap::collect_generation(Generation* gen, bool full, size_t siz
   FormatBuffer<> title("Collect gen: %s", gen->short_name());
   GCTraceTime(Trace, gc, phases) t1(title);
   TraceCollectorStats tcs(gen->counters());
-  TraceMemoryManagerStats tmms(gen->kind(),gc_cause());
+  TraceMemoryManagerStats tmms(gen->gc_manager(), gc_cause());
 
   gen->stat_record()->invocations++;
   gen->stat_record()->accumulated_time.start();
diff --git a/src/hotspot/share/gc/shared/genCollectedHeap.hpp b/src/hotspot/share/gc/shared/genCollectedHeap.hpp
index 5eb4f749d14..e0a91302b0e 100644
--- a/src/hotspot/share/gc/shared/genCollectedHeap.hpp
+++ b/src/hotspot/share/gc/shared/genCollectedHeap.hpp
@@ -112,6 +112,9 @@ protected:
   // (gen-specific) roots processing.
   SubTasksDone* _process_strong_tasks;
 
+  GCMemoryManager* _young_manager;
+  GCMemoryManager* _old_manager;
+
   // Helper functions for allocation
   HeapWord* attempt_allocation(size_t size,
                                bool   is_tlab,
diff --git a/src/hotspot/share/gc/shared/genMemoryPools.cpp b/src/hotspot/share/gc/shared/genMemoryPools.cpp
new file mode 100644
index 00000000000..292f2197c32
--- /dev/null
+++ b/src/hotspot/share/gc/shared/genMemoryPools.cpp
@@ -0,0 +1,92 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+#include "precompiled.hpp"
+#include "gc/serial/defNewGeneration.hpp"
+#include "gc/shared/generation.hpp"
+#include "gc/shared/genMemoryPools.hpp"
+#include "gc/shared/space.hpp"
+
+ContiguousSpacePool::ContiguousSpacePool(ContiguousSpace* space,
+                                         const char* name,
+                                         size_t max_size,
+                                         bool support_usage_threshold) :
+  CollectedMemoryPool(name, space->capacity(), max_size,
+                      support_usage_threshold), _space(space) {
+}
+
+size_t ContiguousSpacePool::used_in_bytes() {
+  return space()->used();
+}
+
+MemoryUsage ContiguousSpacePool::get_memory_usage() {
+  size_t maxSize   = (available_for_allocation() ? max_size() : 0);
+  size_t used      = used_in_bytes();
+  size_t committed = _space->capacity();
+
+  return MemoryUsage(initial_size(), used, committed, maxSize);
+}
+
+SurvivorContiguousSpacePool::SurvivorContiguousSpacePool(DefNewGeneration* young_gen,
+                                                         const char* name,
+                                                         size_t max_size,
+                                                         bool support_usage_threshold) :
+  CollectedMemoryPool(name, young_gen->from()->capacity(), max_size,
+                      support_usage_threshold), _young_gen(young_gen) {
+}
+
+size_t SurvivorContiguousSpacePool::used_in_bytes() {
+  return _young_gen->from()->used();
+}
+
+size_t SurvivorContiguousSpacePool::committed_in_bytes() {
+  return _young_gen->from()->capacity();
+}
+
+MemoryUsage SurvivorContiguousSpacePool::get_memory_usage() {
+  size_t maxSize = (available_for_allocation() ? max_size() : 0);
+  size_t used    = used_in_bytes();
+  size_t committed = committed_in_bytes();
+
+  return MemoryUsage(initial_size(), used, committed, maxSize);
+}
+
+GenerationPool::GenerationPool(Generation* gen,
+                               const char* name,
+                               bool support_usage_threshold) :
+  CollectedMemoryPool(name, gen->capacity(), gen->max_capacity(),
+                      support_usage_threshold), _gen(gen) {
+}
+
+size_t GenerationPool::used_in_bytes() {
+  return _gen->used();
+}
+
+MemoryUsage GenerationPool::get_memory_usage() {
+  size_t used      = used_in_bytes();
+  size_t committed = _gen->capacity();
+  size_t maxSize   = (available_for_allocation() ? max_size() : 0);
+
+  return MemoryUsage(initial_size(), used, committed, maxSize);
+}
diff --git a/src/hotspot/share/gc/shared/genMemoryPools.hpp b/src/hotspot/share/gc/shared/genMemoryPools.hpp
new file mode 100644
index 00000000000..93ee91bf959
--- /dev/null
+++ b/src/hotspot/share/gc/shared/genMemoryPools.hpp
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+#ifndef SHARE_VM_GC_SHARED_GENMEMORYPOOLS_HPP
+#define SHARE_VM_GC_SHARED_GENMEMORYPOOLS_HPP
+
+#include "services/memoryPool.hpp"
+
+class ContiguousSpace;
+class DefNewGeneration;
+class Generation;
+
+class ContiguousSpacePool : public CollectedMemoryPool {
+private:
+  ContiguousSpace* _space;
+
+public:
+  ContiguousSpacePool(ContiguousSpace* space,
+                      const char* name,
+                      size_t max_size,
+                      bool support_usage_threshold);
+
+  ContiguousSpace* space() { return _space; }
+  MemoryUsage get_memory_usage();
+  size_t used_in_bytes();
+};
+
+class SurvivorContiguousSpacePool : public CollectedMemoryPool {
+private:
+  DefNewGeneration* _young_gen;
+
+public:
+  SurvivorContiguousSpacePool(DefNewGeneration* young_gen,
+                              const char* name,
+                              size_t max_size,
+                              bool support_usage_threshold);
+
+  MemoryUsage get_memory_usage();
+
+  size_t used_in_bytes();
+  size_t committed_in_bytes();
+};
+
+class GenerationPool : public CollectedMemoryPool {
+private:
+  Generation* _gen;
+public:
+  GenerationPool(Generation* gen, const char* name, bool support_usage_threshold);
+
+  MemoryUsage get_memory_usage();
+  size_t used_in_bytes();
+};
+
+#endif // SHARE_VM_GC_SHARED_GENMEMORYPOOLS_HPP
diff --git a/src/hotspot/share/gc/shared/generation.cpp b/src/hotspot/share/gc/shared/generation.cpp
index 68c92230729..fbdb2f45b3f 100644
--- a/src/hotspot/share/gc/shared/generation.cpp
+++ b/src/hotspot/share/gc/shared/generation.cpp
@@ -44,7 +44,8 @@
 #include "utilities/events.hpp"
 
 Generation::Generation(ReservedSpace rs, size_t initial_size) :
-  _ref_processor(NULL) {
+  _ref_processor(NULL),
+  _gc_manager(NULL) {
   if (!_virtual_space.initialize(rs, initial_size)) {
     vm_exit_during_initialization("Could not reserve enough space for "
                     "object heap");
diff --git a/src/hotspot/share/gc/shared/generation.hpp b/src/hotspot/share/gc/shared/generation.hpp
index 00d17a22a33..ea8f8d8b8ee 100644
--- a/src/hotspot/share/gc/shared/generation.hpp
+++ b/src/hotspot/share/gc/shared/generation.hpp
@@ -58,6 +58,7 @@
 //
 
 class DefNewGeneration;
+class GCMemoryManager;
 class GenerationSpec;
 class CompactibleSpace;
 class ContiguousSpace;
@@ -86,6 +87,8 @@ class Generation: public CHeapObj<mtGC> {
   MemRegion _prev_used_region; // for collectors that want to "remember" a value for
                                // used region at some specific point during collection.
 
+  GCMemoryManager* _gc_manager;
+
  protected:
   // Minimum and maximum addresses for memory reserved (not necessarily
   // committed) for generation.
@@ -554,6 +557,16 @@ public:
   // Performance Counter support
   virtual void update_counters() = 0;
   virtual CollectorCounters* counters() { return _gc_counters; }
+
+  GCMemoryManager* gc_manager() const {
+    assert(_gc_manager != NULL, "not initialized yet");
+    return _gc_manager;
+  }
+
+  void set_gc_manager(GCMemoryManager* gc_manager) {
+    _gc_manager = gc_manager;
+  }
+
 };
 
 #endif // SHARE_VM_GC_SHARED_GENERATION_HPP
diff --git a/src/hotspot/share/gc/shared/generationCounters.cpp b/src/hotspot/share/gc/shared/generationCounters.cpp
index 134d28765e0..1efa58c4790 100644
--- a/src/hotspot/share/gc/shared/generationCounters.cpp
+++ b/src/hotspot/share/gc/shared/generationCounters.cpp
@@ -24,6 +24,7 @@
 
 #include "precompiled.hpp"
 #include "gc/shared/generationCounters.hpp"
+#include "memory/allocation.inline.hpp"
 #include "memory/resourceArea.hpp"
 
 void GenerationCounters::initialize(const char* name, int ordinal, int spaces,
@@ -78,6 +79,12 @@ GenerationCounters::GenerationCounters(const char* name,
   initialize(name, ordinal, spaces, min_capacity, max_capacity, curr_capacity);
 }
 
+GenerationCounters::~GenerationCounters() {
+  if (_name_space != NULL) {
+    FREE_C_HEAP_ARRAY(char, _name_space);
+  }
+}
+
 void GenerationCounters::update_all() {
   assert(_virtual_space != NULL, "otherwise, override this method");
   _current_size->set_value(_virtual_space->committed_size());
diff --git a/src/hotspot/share/gc/shared/generationCounters.hpp b/src/hotspot/share/gc/shared/generationCounters.hpp
index 2b53549d575..71c42bb318c 100644
--- a/src/hotspot/share/gc/shared/generationCounters.hpp
+++ b/src/hotspot/share/gc/shared/generationCounters.hpp
@@ -68,9 +68,7 @@ private:
   GenerationCounters(const char* name, int ordinal, int spaces,
                      size_t min_capacity, size_t max_capacity, VirtualSpace* v);
 
-  ~GenerationCounters() {
-    if (_name_space != NULL) FREE_C_HEAP_ARRAY(char, _name_space);
-  }
+  ~GenerationCounters();
 
   virtual void update_all();
 
diff --git a/src/hotspot/share/gc/g1/hSpaceCounters.cpp b/src/hotspot/share/gc/shared/hSpaceCounters.cpp
similarity index 69%
rename from src/hotspot/share/gc/g1/hSpaceCounters.cpp
rename to src/hotspot/share/gc/shared/hSpaceCounters.cpp
index 7a5afddfedf..351c5cd920f 100644
--- a/src/hotspot/share/gc/g1/hSpaceCounters.cpp
+++ b/src/hotspot/share/gc/shared/hSpaceCounters.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2017, 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
@@ -23,22 +23,23 @@
  */
 
 #include "precompiled.hpp"
-#include "gc/g1/hSpaceCounters.hpp"
-#include "gc/shared/generation.hpp"
+#include "gc/shared/hSpaceCounters.hpp"
+#include "memory/allocation.inline.hpp"
 #include "memory/resourceArea.hpp"
+#include "runtime/perfData.hpp"
 
-HSpaceCounters::HSpaceCounters(const char* name,
+HSpaceCounters::HSpaceCounters(const char* name_space,
+                               const char* name,
                                int ordinal,
                                size_t max_size,
-                               size_t initial_capacity,
-                               GenerationCounters* gc) {
+                               size_t initial_capacity) {
 
   if (UsePerfData) {
     EXCEPTION_MARK;
     ResourceMark rm;
 
     const char* cns =
-      PerfDataManager::name_space(gc->name_space(), "space", ordinal);
+      PerfDataManager::name_space(name_space, "space", ordinal);
 
     _name_space = NEW_C_HEAP_ARRAY(char, strlen(cns)+1, mtGC);
     strcpy(_name_space, cns);
@@ -64,3 +65,33 @@ HSpaceCounters::HSpaceCounters(const char* name,
                                      initial_capacity, CHECK);
   }
 }
+
+HSpaceCounters::~HSpaceCounters() {
+  if (_name_space != NULL) {
+    FREE_C_HEAP_ARRAY(char, _name_space);
+  }
+}
+
+void HSpaceCounters::update_capacity(size_t v) {
+  _capacity->set_value(v);
+}
+
+void HSpaceCounters::update_used(size_t v) {
+  _used->set_value(v);
+}
+
+void HSpaceCounters::update_all(size_t capacity, size_t used) {
+  update_capacity(capacity);
+  update_used(used);
+}
+
+debug_only(
+  // for security reasons, we do not allow arbitrary reads from
+  // the counters as they may live in shared memory.
+  jlong HSpaceCounters::used() {
+    return _used->get_value();
+  }
+  jlong HSpaceCounters::capacity() {
+    return _used->get_value();
+  }
+)
diff --git a/src/hotspot/share/gc/g1/hSpaceCounters.hpp b/src/hotspot/share/gc/shared/hSpaceCounters.hpp
similarity index 60%
rename from src/hotspot/share/gc/g1/hSpaceCounters.hpp
rename to src/hotspot/share/gc/shared/hSpaceCounters.hpp
index fd7ed263415..36873fd8f70 100644
--- a/src/hotspot/share/gc/g1/hSpaceCounters.hpp
+++ b/src/hotspot/share/gc/shared/hSpaceCounters.hpp
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2017, 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
@@ -22,65 +22,47 @@
  *
  */
 
-#ifndef SHARE_VM_GC_G1_HSPACECOUNTERS_HPP
-#define SHARE_VM_GC_G1_HSPACECOUNTERS_HPP
+#ifndef SHARE_VM_GC_SHARED_HSPACECOUNTERS_HPP
+#define SHARE_VM_GC_SHARED_HSPACECOUNTERS_HPP
 
-#include "gc/shared/generation.hpp"
-#include "gc/shared/generationCounters.hpp"
+#include "memory/allocation.hpp"
 #include "runtime/perfData.hpp"
 #include "utilities/macros.hpp"
 
 // A HSpaceCounter is a holder class for performance counters
 // that track a collections (logical spaces) in a heap;
 
-class HeapSpaceUsedHelper;
-class G1SpaceMonitoringSupport;
-
 class HSpaceCounters: public CHeapObj<mtGC> {
   friend class VMStructs;
 
  private:
-  PerfVariable*        _capacity;
-  PerfVariable*        _used;
+  PerfVariable* _capacity;
+  PerfVariable* _used;
 
   // Constant PerfData types don't need to retain a reference.
   // However, it's a good idea to document them here.
 
-  char*             _name_space;
+  char*         _name_space;
 
  public:
 
-  HSpaceCounters(const char* name, int ordinal, size_t max_size,
-                 size_t initial_capacity, GenerationCounters* gc);
+  HSpaceCounters(const char* name_space, const char* name, int ordinal,
+                 size_t max_size, size_t initial_capacity);
 
-  ~HSpaceCounters() {
-    if (_name_space != NULL) FREE_C_HEAP_ARRAY(char, _name_space);
-  }
+  ~HSpaceCounters();
 
-  inline void update_capacity(size_t v) {
-    _capacity->set_value(v);
-  }
+  void update_capacity(size_t v);
+  void update_used(size_t v);
 
-  inline void update_used(size_t v) {
-    _used->set_value(v);
-  }
+  void update_all(size_t capacity, size_t used);
 
   debug_only(
     // for security reasons, we do not allow arbitrary reads from
     // the counters as they may live in shared memory.
-    jlong used() {
-      return _used->get_value();
-    }
-    jlong capacity() {
-      return _used->get_value();
-    }
+    jlong used();
+    jlong capacity();
   )
 
-  inline void update_all(size_t capacity, size_t used) {
-    update_capacity(capacity);
-    update_used(used);
-  }
-
   const char* name_space() const        { return _name_space; }
 };
-#endif // SHARE_VM_GC_G1_HSPACECOUNTERS_HPP
+#endif // SHARE_VM_GC_SHARED_HSPACECOUNTERS_HPP
diff --git a/src/hotspot/share/gc/shared/taskqueue.hpp b/src/hotspot/share/gc/shared/taskqueue.hpp
index ef1e9688bd4..ca0e1a02a58 100644
--- a/src/hotspot/share/gc/shared/taskqueue.hpp
+++ b/src/hotspot/share/gc/shared/taskqueue.hpp
@@ -26,6 +26,8 @@
 #define SHARE_VM_GC_SHARED_TASKQUEUE_HPP
 
 #include "memory/allocation.hpp"
+#include "oops/oopsHierarchy.hpp"
+#include "utilities/ostream.hpp"
 #include "utilities/stack.hpp"
 
 // Simple TaskQueue stats that are collected by default in debug builds.
@@ -425,7 +427,7 @@ class ParallelTaskTerminator: public StackObj {
 private:
   uint _n_threads;
   TaskQueueSetSuper* _queue_set;
-  uint _offered_termination;
+  volatile uint _offered_termination;
 
 #ifdef TRACESPINNING
   static uint _total_yields;
diff --git a/src/hotspot/share/gc/shared/threadLocalAllocBuffer.cpp b/src/hotspot/share/gc/shared/threadLocalAllocBuffer.cpp
index e8828abbe40..6634a0cc906 100644
--- a/src/hotspot/share/gc/shared/threadLocalAllocBuffer.cpp
+++ b/src/hotspot/share/gc/shared/threadLocalAllocBuffer.cpp
@@ -30,6 +30,7 @@
 #include "memory/universe.inline.hpp"
 #include "oops/oop.inline.hpp"
 #include "runtime/thread.inline.hpp"
+#include "runtime/threadSMR.hpp"
 #include "utilities/copy.hpp"
 
 // Thread-Local Edens support
@@ -48,7 +49,7 @@ void ThreadLocalAllocBuffer::clear_before_allocation() {
 void ThreadLocalAllocBuffer::accumulate_statistics_before_gc() {
   global_stats()->initialize();
 
-  for (JavaThread *thread = Threads::first(); thread != NULL; thread = thread->next()) {
+  for (JavaThreadIteratorWithHandle jtiwh; JavaThread *thread = jtiwh.next(); ) {
     thread->tlab().accumulate_statistics();
     thread->tlab().initialize_statistics();
   }
@@ -130,7 +131,7 @@ void ThreadLocalAllocBuffer::make_parsable(bool retire, bool zap) {
 
 void ThreadLocalAllocBuffer::resize_all_tlabs() {
   if (ResizeTLAB) {
-    for (JavaThread *thread = Threads::first(); thread != NULL; thread = thread->next()) {
+    for (JavaThreadIteratorWithHandle jtiwh; JavaThread *thread = jtiwh.next(); ) {
       thread->tlab().resize();
     }
   }
diff --git a/src/hotspot/share/gc/shared/workgroup.cpp b/src/hotspot/share/gc/shared/workgroup.cpp
index 73b3a2a55ab..2538e701fc9 100644
--- a/src/hotspot/share/gc/shared/workgroup.cpp
+++ b/src/hotspot/share/gc/shared/workgroup.cpp
@@ -261,6 +261,10 @@ WorkGang::WorkGang(const char* name,
     _dispatcher(create_dispatcher())
 { }
 
+WorkGang::~WorkGang() {
+  delete _dispatcher;
+}
+
 AbstractGangWorker* WorkGang::allocate_worker(uint worker_id) {
   return new GangWorker(this, worker_id);
 }
diff --git a/src/hotspot/share/gc/shared/workgroup.hpp b/src/hotspot/share/gc/shared/workgroup.hpp
index 320652a993c..c1b50f3885d 100644
--- a/src/hotspot/share/gc/shared/workgroup.hpp
+++ b/src/hotspot/share/gc/shared/workgroup.hpp
@@ -122,6 +122,8 @@ class AbstractWorkGang : public CHeapObj<mtInternal> {
   // Printing support.
   const char* _name;
 
+  ~AbstractWorkGang() {}
+
  private:
   // Initialize only instance data.
   const bool _are_GC_task_threads;
@@ -206,9 +208,6 @@ class WorkGang: public AbstractWorkGang {
   // To get access to the GangTaskDispatcher instance.
   friend class GangWorker;
 
-  // Never deleted.
-  ~WorkGang();
-
   GangTaskDispatcher* const _dispatcher;
   GangTaskDispatcher* dispatcher() const {
     return _dispatcher;
@@ -220,6 +219,8 @@ public:
            bool are_GC_task_threads,
            bool are_ConcurrentGC_threads);
 
+  ~WorkGang();
+
   // Run a task using the current active number of workers, returns when the task is done.
   virtual void run_task(AbstractGangTask* task);
   // Run a task with the given number of workers, returns
diff --git a/src/hotspot/share/jvmci/jvmciRuntime.cpp b/src/hotspot/share/jvmci/jvmciRuntime.cpp
index 134ea685c83..ab5e3542fa9 100644
--- a/src/hotspot/share/jvmci/jvmciRuntime.cpp
+++ b/src/hotspot/share/jvmci/jvmciRuntime.cpp
@@ -42,6 +42,7 @@
 #include "runtime/interfaceSupport.hpp"
 #include "runtime/reflection.hpp"
 #include "runtime/sharedRuntime.hpp"
+#include "runtime/threadSMR.hpp"
 #include "utilities/debug.hpp"
 #include "utilities/defaultStream.hpp"
 #include "utilities/macros.hpp"
@@ -598,12 +599,13 @@ JRT_ENTRY(jint, JVMCIRuntime::identity_hash_code(JavaThread* thread, oopDesc* ob
 JRT_END
 
 JRT_ENTRY(jboolean, JVMCIRuntime::thread_is_interrupted(JavaThread* thread, oopDesc* receiver, jboolean clear_interrupted))
-  // Ensure that the C++ Thread and OSThread structures aren't freed before we operate.
-  // This locking requires thread_in_vm which is why this method cannot be JRT_LEAF.
   Handle receiverHandle(thread, receiver);
-  MutexLockerEx ml(thread->threadObj() == (void*)receiver ? NULL : Threads_lock);
+  // A nested ThreadsListHandle may require the Threads_lock which
+  // requires thread_in_vm which is why this method cannot be JRT_LEAF.
+  ThreadsListHandle tlh;
+
   JavaThread* receiverThread = java_lang_Thread::thread(receiverHandle());
-  if (receiverThread == NULL) {
+  if (receiverThread == NULL || (EnableThreadSMRExtraValidityChecks && !tlh.includes(receiverThread))) {
     // The other thread may exit during this process, which is ok so return false.
     return JNI_FALSE;
   } else {
diff --git a/src/hotspot/share/jvmci/vmStructs_jvmci.cpp b/src/hotspot/share/jvmci/vmStructs_jvmci.cpp
index 66a9d859ed0..0f4ffa27113 100644
--- a/src/hotspot/share/jvmci/vmStructs_jvmci.cpp
+++ b/src/hotspot/share/jvmci/vmStructs_jvmci.cpp
@@ -317,6 +317,7 @@
                                                                                                                                      \
   nonstatic_field(Thread,                   _tlab,                                            ThreadLocalAllocBuffer)                \
   nonstatic_field(Thread,                   _allocated_bytes,                                 jlong)                                 \
+  nonstatic_field(Thread,                   _polling_page,                                    address)                               \
                                                                                                                                      \
   nonstatic_field(ThreadLocalAllocBuffer,   _start,                                           HeapWord*)                             \
   nonstatic_field(ThreadLocalAllocBuffer,   _top,                                             HeapWord*)                             \
diff --git a/src/hotspot/share/logging/logTag.hpp b/src/hotspot/share/logging/logTag.hpp
index e53e7cb4774..1956f27a078 100644
--- a/src/hotspot/share/logging/logTag.hpp
+++ b/src/hotspot/share/logging/logTag.hpp
@@ -121,6 +121,7 @@
   LOG_TAG(safepoint) \
   LOG_TAG(scavenge) \
   LOG_TAG(scrub) \
+  LOG_TAG(smr) \
   LOG_TAG(stacktrace) \
   LOG_TAG(stackwalk) \
   LOG_TAG(start) \
diff --git a/src/hotspot/share/memory/metaspace.cpp b/src/hotspot/share/memory/metaspace.cpp
index 5671e12c2eb..18de6beaef9 100644
--- a/src/hotspot/share/memory/metaspace.cpp
+++ b/src/hotspot/share/memory/metaspace.cpp
@@ -785,7 +785,10 @@ class SpaceManager : public CHeapObj<mtClass> {
   Mutex* const _lock;
 
   // Type of metadata allocated.
-  Metaspace::MetadataType _mdtype;
+  const Metaspace::MetadataType   _mdtype;
+
+  // Type of metaspace
+  const Metaspace::MetaspaceType  _space_type;
 
   // List of chunks in use by this SpaceManager.  Allocations
   // are done from the current chunk.  The list is used for deallocating
@@ -796,6 +799,10 @@ class SpaceManager : public CHeapObj<mtClass> {
   // Maximum number of small chunks to allocate to a SpaceManager
   static uint const _small_chunk_limit;
 
+  // Maximum number of specialize chunks to allocate for anonymous
+  // metadata space to a SpaceManager
+  static uint const _anon_metadata_specialize_chunk_limit;
+
   // Sum of all space in allocated chunks
   size_t _allocated_blocks_words;
 
@@ -846,6 +853,7 @@ class SpaceManager : public CHeapObj<mtClass> {
 
  public:
   SpaceManager(Metaspace::MetadataType mdtype,
+               Metaspace::MetaspaceType space_type,
                Mutex* lock);
   ~SpaceManager();
 
@@ -963,6 +971,7 @@ class SpaceManager : public CHeapObj<mtClass> {
 };
 
 uint const SpaceManager::_small_chunk_limit = 4;
+uint const SpaceManager::_anon_metadata_specialize_chunk_limit = 4;
 
 const char* SpaceManager::_expand_lock_name =
   "SpaceManager chunk allocation lock";
@@ -2400,6 +2409,20 @@ size_t SpaceManager::calc_chunk_size(size_t word_size) {
   // _small_chunk_limit small chunks can be allocated.
   // After that a medium chunk is preferred.
   size_t chunk_word_size;
+
+  // Special case for anonymous metadata space.
+  // Anonymous metadata space is usually small, with majority within 1K - 2K range and
+  // rarely about 4K (64-bits JVM).
+  // Instead of jumping to SmallChunk after initial chunk exhausted, keeping allocation
+  // from SpecializeChunk up to _anon_metadata_specialize_chunk_limit (4) reduces space waste
+  // from 60+% to around 30%.
+  if (_space_type == Metaspace::AnonymousMetaspaceType &&
+      _mdtype == Metaspace::NonClassType &&
+      sum_count_in_chunks_in_use(SpecializedIndex) < _anon_metadata_specialize_chunk_limit &&
+      word_size + Metachunk::overhead() <= SpecializedChunk) {
+    return SpecializedChunk;
+  }
+
   if (chunks_in_use(MediumIndex) == NULL &&
       sum_count_in_chunks_in_use(SmallIndex) < _small_chunk_limit) {
     chunk_word_size = (size_t) small_chunk_size();
@@ -2504,8 +2527,10 @@ void SpaceManager::print_on(outputStream* st) const {
 }
 
 SpaceManager::SpaceManager(Metaspace::MetadataType mdtype,
+                           Metaspace::MetaspaceType space_type,
                            Mutex* lock) :
   _mdtype(mdtype),
+  _space_type(space_type),
   _allocated_blocks_words(0),
   _allocated_chunks_words(0),
   _allocated_chunks_count(0),
@@ -3781,11 +3806,11 @@ void Metaspace::initialize(Mutex* lock, MetaspaceType type) {
   verify_global_initialization();
 
   // Allocate SpaceManager for metadata objects.
-  _vsm = new SpaceManager(NonClassType, lock);
+  _vsm = new SpaceManager(NonClassType, type, lock);
 
   if (using_class_space()) {
     // Allocate SpaceManager for classes.
-    _class_vsm = new SpaceManager(ClassType, lock);
+    _class_vsm = new SpaceManager(ClassType, type, lock);
   }
 
   MutexLockerEx cl(SpaceManager::expand_lock(), Mutex::_no_safepoint_check_flag);
diff --git a/src/hotspot/share/memory/resourceArea.cpp b/src/hotspot/share/memory/resourceArea.cpp
index 3995e6335db..aca27c0b56a 100644
--- a/src/hotspot/share/memory/resourceArea.cpp
+++ b/src/hotspot/share/memory/resourceArea.cpp
@@ -24,7 +24,7 @@
 
 #include "precompiled.hpp"
 #include "memory/allocation.inline.hpp"
-#include "memory/resourceArea.hpp"
+#include "memory/resourceArea.inline.hpp"
 #include "runtime/mutexLocker.hpp"
 #include "runtime/thread.inline.hpp"
 #include "services/memTracker.hpp"
diff --git a/src/hotspot/share/memory/resourceArea.hpp b/src/hotspot/share/memory/resourceArea.hpp
index 5fc13ac9243..32d57fd6e61 100644
--- a/src/hotspot/share/memory/resourceArea.hpp
+++ b/src/hotspot/share/memory/resourceArea.hpp
@@ -57,18 +57,7 @@ public:
     debug_only(_nesting = 0;);
   }
 
-  char* allocate_bytes(size_t size, AllocFailType alloc_failmode = AllocFailStrategy::EXIT_OOM) {
-#ifdef ASSERT
-    if (_nesting < 1 && !_warned++)
-      fatal("memory leak: allocating without ResourceMark");
-    if (UseMallocOnly) {
-      // use malloc, but save pointer in res. area for later freeing
-      char** save = (char**)internal_malloc_4(sizeof(char*));
-      return (*save = (char*)os::malloc(size, mtThread, CURRENT_PC));
-    }
-#endif
-    return (char*)Amalloc(size, alloc_failmode);
-  }
+  char* allocate_bytes(size_t size, AllocFailType alloc_failmode = AllocFailStrategy::EXIT_OOM);
 
   // Bias this resource area to specific memory type
   // (by default, ResourceArea is tagged as mtThread, per-thread general purpose storage)
diff --git a/src/hotspot/share/memory/resourceArea.inline.hpp b/src/hotspot/share/memory/resourceArea.inline.hpp
new file mode 100644
index 00000000000..895c0b71bbf
--- /dev/null
+++ b/src/hotspot/share/memory/resourceArea.inline.hpp
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 1997, 2017, 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_MEMORY_RESOURCEAREA_INLINE_HPP
+#define SHARE_VM_MEMORY_RESOURCEAREA_INLINE_HPP
+
+#include "memory/resourceArea.hpp"
+
+inline char* ResourceArea::allocate_bytes(size_t size, AllocFailType alloc_failmode) {
+#ifdef ASSERT
+  if (_nesting < 1 && !_warned++)
+    fatal("memory leak: allocating without ResourceMark");
+  if (UseMallocOnly) {
+    // use malloc, but save pointer in res. area for later freeing
+    char** save = (char**)internal_malloc_4(sizeof(char*));
+    return (*save = (char*)os::malloc(size, mtThread, CURRENT_PC));
+  }
+#endif
+  return (char*)Amalloc(size, alloc_failmode);
+}
+
+#endif // SHARE_VM_MEMORY_RESOURCEAREA_INLINE_HPP
diff --git a/src/hotspot/share/memory/universe.cpp b/src/hotspot/share/memory/universe.cpp
index 32da59f6053..0dbad080700 100644
--- a/src/hotspot/share/memory/universe.cpp
+++ b/src/hotspot/share/memory/universe.cpp
@@ -687,6 +687,10 @@ jint universe_init() {
 
   Metaspace::global_initialize();
 
+  // Initialize performance counters for metaspaces
+  MetaspaceCounters::initialize_performance_counters();
+  CompressedClassSpaceCounters::initialize_performance_counters();
+
   AOTLoader::universe_init();
 
   // Checks 'AfterMemoryInit' constraints.
@@ -764,6 +768,7 @@ jint Universe::initialize_heap() {
   }
   log_info(gc)("Using %s", _collectedHeap->name());
 
+  GCArguments::arguments()->post_heap_initialize();
   ThreadLocalAllocBuffer::set_max_size(Universe::heap()->max_tlab_size());
 
 #ifdef _LP64
@@ -852,7 +857,7 @@ ReservedSpace Universe::reserve_heap(size_t heap_size, size_t alignment) {
       || use_large_pages, "Wrong alignment to use large pages");
 
   // Now create the space.
-  ReservedHeapSpace total_rs(total_reserved, alignment, use_large_pages);
+  ReservedHeapSpace total_rs(total_reserved, alignment, use_large_pages, AllocateHeapAt);
 
   if (total_rs.is_reserved()) {
     assert((total_reserved == total_rs.size()) && ((uintptr_t)total_rs.base() % alignment == 0),
@@ -866,6 +871,9 @@ ReservedSpace Universe::reserve_heap(size_t heap_size, size_t alignment) {
       Universe::set_narrow_oop_base((address)total_rs.compressed_oop_base());
     }
 
+    if (AllocateHeapAt != NULL) {
+      log_info(gc,heap)("Successfully allocated Java heap at location %s", AllocateHeapAt);
+    }
     return total_rs;
   }
 
@@ -1085,10 +1093,6 @@ bool universe_post_init() {
   // ("weak") refs processing infrastructure initialization
   Universe::heap()->post_initialize();
 
-  // Initialize performance counters for metaspaces
-  MetaspaceCounters::initialize_performance_counters();
-  CompressedClassSpaceCounters::initialize_performance_counters();
-
   MemoryService::add_metaspace_memory_pools();
 
   MemoryService::set_universe_heap(Universe::heap());
diff --git a/src/hotspot/share/memory/virtualspace.cpp b/src/hotspot/share/memory/virtualspace.cpp
index a3289d9b043..759210aace1 100644
--- a/src/hotspot/share/memory/virtualspace.cpp
+++ b/src/hotspot/share/memory/virtualspace.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2017, 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
@@ -35,10 +35,10 @@
 
 // Dummy constructor
 ReservedSpace::ReservedSpace() : _base(NULL), _size(0), _noaccess_prefix(0),
-    _alignment(0), _special(false), _executable(false) {
+    _alignment(0), _special(false), _executable(false), _fd_for_heap(-1) {
 }
 
-ReservedSpace::ReservedSpace(size_t size, size_t preferred_page_size) {
+ReservedSpace::ReservedSpace(size_t size, size_t preferred_page_size) : _fd_for_heap(-1) {
   bool has_preferred_page_size = preferred_page_size != 0;
   // Want to use large pages where possible and pad with small pages.
   size_t page_size = has_preferred_page_size ? preferred_page_size : os::page_size_for_region_unaligned(size, 1);
@@ -59,19 +59,30 @@ ReservedSpace::ReservedSpace(size_t size, size_t preferred_page_size) {
 
 ReservedSpace::ReservedSpace(size_t size, size_t alignment,
                              bool large,
-                             char* requested_address) {
+                             char* requested_address) : _fd_for_heap(-1) {
   initialize(size, alignment, large, requested_address, false);
 }
 
 ReservedSpace::ReservedSpace(size_t size, size_t alignment,
                              bool large,
-                             bool executable) {
+                             bool executable) : _fd_for_heap(-1) {
   initialize(size, alignment, large, NULL, executable);
 }
 
+// Helper method
+static void unmap_or_release_memory(char* base, size_t size, bool is_file_mapped) {
+  if (is_file_mapped) {
+    if (!os::unmap_memory(base, size)) {
+      fatal("os::unmap_memory failed");
+    }
+  } else if (!os::release_memory(base, size)) {
+    fatal("os::release_memory failed");
+  }
+}
+
 // Helper method.
 static bool failed_to_reserve_as_requested(char* base, char* requested_address,
-                                           const size_t size, bool special)
+                                           const size_t size, bool special, bool is_file_mapped = false)
 {
   if (base == requested_address || requested_address == NULL)
     return false; // did not fail
@@ -87,9 +98,7 @@ static bool failed_to_reserve_as_requested(char* base, char* requested_address,
         fatal("os::release_memory_special failed");
       }
     } else {
-      if (!os::release_memory(base, size)) {
-        fatal("os::release_memory failed");
-      }
+      unmap_or_release_memory(base, size, is_file_mapped);
     }
   }
   return true;
@@ -120,7 +129,18 @@ void ReservedSpace::initialize(size_t size, size_t alignment, bool large,
 
   // If OS doesn't support demand paging for large page memory, we need
   // to use reserve_memory_special() to reserve and pin the entire region.
+  // If there is a backing file directory for this space then whether
+  // large pages are allocated is up to the filesystem of the backing file.
+  // So we ignore the UseLargePages flag in this case.
   bool special = large && !os::can_commit_large_page_memory();
+  if (special && _fd_for_heap != -1) {
+    special = false;
+    if (UseLargePages && (!FLAG_IS_DEFAULT(UseLargePages) ||
+      !FLAG_IS_DEFAULT(LargePageSizeInBytes))) {
+      log_debug(gc, heap)("Ignoring UseLargePages since large page support is up to the file system of the backing file for Java heap");
+    }
+  }
+
   char* base = NULL;
 
   if (special) {
@@ -157,13 +177,13 @@ void ReservedSpace::initialize(size_t size, size_t alignment, bool large,
     // important.  If available space is not detected, return NULL.
 
     if (requested_address != 0) {
-      base = os::attempt_reserve_memory_at(size, requested_address);
-      if (failed_to_reserve_as_requested(base, requested_address, size, false)) {
+      base = os::attempt_reserve_memory_at(size, requested_address, _fd_for_heap);
+      if (failed_to_reserve_as_requested(base, requested_address, size, false, _fd_for_heap != -1)) {
         // OS ignored requested address. Try different address.
         base = NULL;
       }
     } else {
-      base = os::reserve_memory(size, NULL, alignment);
+      base = os::reserve_memory(size, NULL, alignment, _fd_for_heap);
     }
 
     if (base == NULL) return;
@@ -171,13 +191,14 @@ void ReservedSpace::initialize(size_t size, size_t alignment, bool large,
     // Check alignment constraints
     if ((((size_t)base) & (alignment - 1)) != 0) {
       // Base not aligned, retry
-      if (!os::release_memory(base, size)) fatal("os::release_memory failed");
+      unmap_or_release_memory(base, size, _fd_for_heap != -1 /*is_file_mapped*/);
+
       // Make sure that size is aligned
       size = align_up(size, alignment);
-      base = os::reserve_memory_aligned(size, alignment);
+      base = os::reserve_memory_aligned(size, alignment, _fd_for_heap);
 
       if (requested_address != 0 &&
-          failed_to_reserve_as_requested(base, requested_address, size, false)) {
+          failed_to_reserve_as_requested(base, requested_address, size, false, _fd_for_heap != -1)) {
         // As a result of the alignment constraints, the allocated base differs
         // from the requested address. Return back to the caller who can
         // take remedial action (like try again without a requested address).
@@ -190,6 +211,10 @@ void ReservedSpace::initialize(size_t size, size_t alignment, bool large,
   _base = base;
   _size = size;
   _alignment = alignment;
+  // If heap is reserved with a backing file, the entire space has been committed. So set the _special flag to true
+  if (_fd_for_heap != -1) {
+    _special = true;
+  }
 }
 
 
@@ -252,7 +277,11 @@ void ReservedSpace::release() {
     char *real_base = _base - _noaccess_prefix;
     const size_t real_size = _size + _noaccess_prefix;
     if (special()) {
-      os::release_memory_special(real_base, real_size);
+      if (_fd_for_heap != -1) {
+        os::unmap_memory(real_base, real_size);
+      } else {
+        os::release_memory_special(real_base, real_size);
+      }
     } else{
       os::release_memory(real_base, real_size);
     }
@@ -313,7 +342,17 @@ void ReservedHeapSpace::try_reserve_heap(size_t size,
 
   // If OS doesn't support demand paging for large page memory, we need
   // to use reserve_memory_special() to reserve and pin the entire region.
+  // If there is a backing file directory for this space then whether
+  // large pages are allocated is up to the filesystem of the backing file.
+  // So we ignore the UseLargePages flag in this case.
   bool special = large && !os::can_commit_large_page_memory();
+  if (special && _fd_for_heap != -1) {
+    special = false;
+    if (UseLargePages && (!FLAG_IS_DEFAULT(UseLargePages) ||
+                          !FLAG_IS_DEFAULT(LargePageSizeInBytes))) {
+      log_debug(gc, heap)("Cannot allocate large pages for Java Heap when AllocateHeapAt option is set.");
+    }
+  }
   char* base = NULL;
 
   log_trace(gc, heap, coops)("Trying to allocate at address " PTR_FORMAT
@@ -350,9 +389,9 @@ void ReservedHeapSpace::try_reserve_heap(size_t size,
     // important.  If available space is not detected, return NULL.
 
     if (requested_address != 0) {
-      base = os::attempt_reserve_memory_at(size, requested_address);
+      base = os::attempt_reserve_memory_at(size, requested_address, _fd_for_heap);
     } else {
-      base = os::reserve_memory(size, NULL, alignment);
+      base = os::reserve_memory(size, NULL, alignment, _fd_for_heap);
     }
   }
   if (base == NULL) { return; }
@@ -362,6 +401,11 @@ void ReservedHeapSpace::try_reserve_heap(size_t size,
   _size = size;
   _alignment = alignment;
 
+  // If heap is reserved with a backing file, the entire space has been committed. So set the _special flag to true
+  if (_fd_for_heap != -1) {
+    _special = true;
+  }
+
   // Check alignment constraints
   if ((((size_t)base) & (alignment - 1)) != 0) {
     // Base not aligned, retry.
@@ -556,12 +600,20 @@ void ReservedHeapSpace::initialize_compressed_heap(const size_t size, size_t ali
   }
 }
 
-ReservedHeapSpace::ReservedHeapSpace(size_t size, size_t alignment, bool large) : ReservedSpace() {
+ReservedHeapSpace::ReservedHeapSpace(size_t size, size_t alignment, bool large, const char* heap_allocation_directory) : ReservedSpace() {
 
   if (size == 0) {
     return;
   }
 
+  if (heap_allocation_directory != NULL) {
+    _fd_for_heap = os::create_file_for_heap(heap_allocation_directory);
+    if (_fd_for_heap == -1) {
+      vm_exit_during_initialization(
+        err_msg("Could not create file for Heap at location %s", heap_allocation_directory));
+    }
+  }
+
   // Heap size should be aligned to alignment, too.
   guarantee(is_aligned(size, alignment), "set by caller");
 
@@ -585,6 +637,10 @@ ReservedHeapSpace::ReservedHeapSpace(size_t size, size_t alignment, bool large)
   if (base() != NULL) {
     MemTracker::record_virtual_memory_type((address)base(), mtJavaHeap);
   }
+
+  if (_fd_for_heap != -1) {
+    os::close(_fd_for_heap);
+  }
 }
 
 // Reserve space for code segment.  Same as Java heap only we mark this as
diff --git a/src/hotspot/share/memory/virtualspace.hpp b/src/hotspot/share/memory/virtualspace.hpp
index 2475f09d156..5041ce145ec 100644
--- a/src/hotspot/share/memory/virtualspace.hpp
+++ b/src/hotspot/share/memory/virtualspace.hpp
@@ -37,6 +37,7 @@ class ReservedSpace VALUE_OBJ_CLASS_SPEC {
   size_t _noaccess_prefix;
   size_t _alignment;
   bool   _special;
+  int    _fd_for_heap;
  private:
   bool   _executable;
 
@@ -115,7 +116,9 @@ class ReservedHeapSpace : public ReservedSpace {
   void establish_noaccess_prefix();
  public:
   // Constructor. Tries to find a heap that is good for compressed oops.
-  ReservedHeapSpace(size_t size, size_t forced_base_alignment, bool large);
+  // heap_allocation_directory is the path to the backing memory for Java heap. When set, Java heap will be allocated
+  // on the device which is managed by the file system where the directory resides.
+  ReservedHeapSpace(size_t size, size_t forced_base_alignment, bool large, const char* heap_allocation_directory = NULL);
   // Returns the base to be used for compression, i.e. so that null can be
   // encoded safely and implicit null checks can work.
   char *compressed_oop_base() { return _base - _noaccess_prefix; }
diff --git a/src/hotspot/share/oops/array.hpp b/src/hotspot/share/oops/array.hpp
index 225331e0521..f56da8e26e0 100644
--- a/src/hotspot/share/oops/array.hpp
+++ b/src/hotspot/share/oops/array.hpp
@@ -26,7 +26,6 @@
 #define SHARE_VM_OOPS_ARRAY_HPP
 
 #include "memory/allocation.hpp"
-#include "memory/allocation.inline.hpp"
 #include "memory/metaspace.hpp"
 #include "runtime/orderAccess.hpp"
 #include "utilities/align.hpp"
diff --git a/src/hotspot/share/oops/constantPool.cpp b/src/hotspot/share/oops/constantPool.cpp
index 9e1eefd5251..4dd2e35e2cf 100644
--- a/src/hotspot/share/oops/constantPool.cpp
+++ b/src/hotspot/share/oops/constantPool.cpp
@@ -31,6 +31,7 @@
 #include "classfile/systemDictionary.hpp"
 #include "classfile/vmSymbols.hpp"
 #include "interpreter/linkResolver.hpp"
+#include "memory/allocation.inline.hpp"
 #include "memory/heapInspection.hpp"
 #include "memory/metadataFactory.hpp"
 #include "memory/metaspaceClosure.hpp"
@@ -2300,3 +2301,11 @@ SymbolHashMapEntry* SymbolHashMap::find_entry(Symbol* sym) {
   }
   return NULL;
 }
+
+void SymbolHashMap::initialize_table(int table_size) {
+  _table_size = table_size;
+  _buckets = NEW_C_HEAP_ARRAY(SymbolHashMapBucket, table_size, mtSymbol);
+  for (int index = 0; index < table_size; index++) {
+    _buckets[index].clear();
+  }
+}
diff --git a/src/hotspot/share/oops/constantPool.hpp b/src/hotspot/share/oops/constantPool.hpp
index 681935edae5..8dc75922419 100644
--- a/src/hotspot/share/oops/constantPool.hpp
+++ b/src/hotspot/share/oops/constantPool.hpp
@@ -982,13 +982,7 @@ class SymbolHashMap: public CHeapObj<mtSymbol> {
   int                   _table_size;
   SymbolHashMapBucket*  _buckets;
 
-  void initialize_table(int table_size) {
-    _table_size = table_size;
-    _buckets = NEW_C_HEAP_ARRAY(SymbolHashMapBucket, table_size, mtSymbol);
-    for (int index = 0; index < table_size; index++) {
-      _buckets[index].clear();
-    }
-  }
+  void initialize_table(int table_size);
 
  public:
 
diff --git a/src/hotspot/share/oops/generateOopMap.cpp b/src/hotspot/share/oops/generateOopMap.cpp
index e8edb2c0030..50d4de01f4f 100644
--- a/src/hotspot/share/oops/generateOopMap.cpp
+++ b/src/hotspot/share/oops/generateOopMap.cpp
@@ -27,6 +27,7 @@
 #include "interpreter/bytecodeStream.hpp"
 #include "logging/log.hpp"
 #include "logging/logStream.hpp"
+#include "memory/allocation.inline.hpp"
 #include "oops/generateOopMap.hpp"
 #include "oops/oop.inline.hpp"
 #include "oops/symbol.hpp"
@@ -217,6 +218,12 @@ public:
 int RetTable::_init_nof_entries = 10;
 int RetTableEntry::_init_nof_jsrs = 5;
 
+RetTableEntry::RetTableEntry(int target, RetTableEntry *next) {
+  _target_bci = target;
+  _jsrs = new GrowableArray<intptr_t>(_init_nof_jsrs);
+  _next = next;
+}
+
 void RetTableEntry::add_delta(int bci, int delta) {
   if (_target_bci > bci) _target_bci += delta;
 
diff --git a/src/hotspot/share/oops/generateOopMap.hpp b/src/hotspot/share/oops/generateOopMap.hpp
index e401746240a..3fa44525146 100644
--- a/src/hotspot/share/oops/generateOopMap.hpp
+++ b/src/hotspot/share/oops/generateOopMap.hpp
@@ -26,7 +26,7 @@
 #define SHARE_VM_OOPS_GENERATEOOPMAP_HPP
 
 #include "interpreter/bytecodeStream.hpp"
-#include "memory/allocation.inline.hpp"
+#include "memory/allocation.hpp"
 #include "memory/universe.inline.hpp"
 #include "oops/method.hpp"
 #include "oops/oopsHierarchy.hpp"
@@ -57,7 +57,7 @@ class RetTableEntry : public ResourceObj {
   GrowableArray<intptr_t> * _jsrs;                     // List of return addresses  (bytecode index)
   RetTableEntry *_next;                           // Link to next entry
  public:
-   RetTableEntry(int target, RetTableEntry *next)  { _target_bci=target; _jsrs = new GrowableArray<intptr_t>(_init_nof_jsrs); _next = next;  }
+   RetTableEntry(int target, RetTableEntry *next);
 
   // Query
   int target_bci() const                      { return _target_bci; }
diff --git a/src/hotspot/share/oops/instanceMirrorKlass.inline.hpp b/src/hotspot/share/oops/instanceMirrorKlass.inline.hpp
index cb416918d9e..a00d5b14951 100644
--- a/src/hotspot/share/oops/instanceMirrorKlass.inline.hpp
+++ b/src/hotspot/share/oops/instanceMirrorKlass.inline.hpp
@@ -71,10 +71,15 @@ void InstanceMirrorKlass::oop_oop_iterate(oop obj, OopClosureType* closure) {
         Devirtualizer<nv>::do_klass(closure, klass);
       }
     } else {
-      // If klass is NULL then this a mirror for a primitive type.
-      // We don't have to follow them, since they are handled as strong
-      // roots in Universe::oops_do.
-      assert(java_lang_Class::is_primitive(obj), "Sanity check");
+      // We would like to assert here (as below) that if klass has been NULL, then
+      // this has been a mirror for a primitive type that we do not need to follow
+      // as they are always strong roots.
+      // However, we might get across a klass that just changed during CMS concurrent
+      // marking if allocation occurred in the old generation.
+      // This is benign here, as we keep alive all CLDs that were loaded during the
+      // CMS concurrent phase in the class loading, i.e. they will be iterated over
+      // and kept alive during remark.
+      // assert(java_lang_Class::is_primitive(obj), "Sanity check");
     }
   }
 
diff --git a/src/hotspot/share/opto/c2_globals.hpp b/src/hotspot/share/opto/c2_globals.hpp
index e1c859c3af9..9f105299402 100644
--- a/src/hotspot/share/opto/c2_globals.hpp
+++ b/src/hotspot/share/opto/c2_globals.hpp
@@ -740,6 +740,14 @@
                                                                             \
   develop(bool, RenumberLiveNodes, true,                                    \
           "Renumber live nodes")                                            \
+                                                                            \
+  product(uintx, LoopStripMiningIter, 0,                                    \
+          "Number of iterations in strip mined loop")                       \
+          range(0, max_juint)                                               \
+                                                                            \
+  product(uintx, LoopStripMiningIterShortLoop, 0,                           \
+          "Loop with fewer iterations are not strip mined")                 \
+          range(0, max_juint)                                               \
 
 C2_FLAGS(DECLARE_DEVELOPER_FLAG, \
          DECLARE_PD_DEVELOPER_FLAG, \
diff --git a/src/hotspot/share/opto/callGenerator.cpp b/src/hotspot/share/opto/callGenerator.cpp
index a5aaa552fee..3a0dd830ac1 100644
--- a/src/hotspot/share/opto/callGenerator.cpp
+++ b/src/hotspot/share/opto/callGenerator.cpp
@@ -362,6 +362,20 @@ void LateInlineCallGenerator::do_late_inline() {
     return;
   }
 
+  // check for unreachable loop
+  CallProjections callprojs;
+  call->extract_projections(&callprojs, true);
+  if (callprojs.fallthrough_catchproj == call->in(0) ||
+      callprojs.catchall_catchproj == call->in(0) ||
+      callprojs.fallthrough_memproj == call->in(TypeFunc::Memory) ||
+      callprojs.catchall_memproj == call->in(TypeFunc::Memory) ||
+      callprojs.fallthrough_ioproj == call->in(TypeFunc::I_O) ||
+      callprojs.catchall_ioproj == call->in(TypeFunc::I_O) ||
+      (callprojs.resproj != NULL && call->find_edge(callprojs.resproj) != -1) ||
+      (callprojs.exobj != NULL && call->find_edge(callprojs.exobj) != -1)) {
+    return;
+  }
+
   Compile* C = Compile::current();
   // Remove inlined methods from Compiler's lists.
   if (call->is_macro()) {
diff --git a/src/hotspot/share/opto/cfgnode.cpp b/src/hotspot/share/opto/cfgnode.cpp
index 272c1340fbb..e2269104bf7 100644
--- a/src/hotspot/share/opto/cfgnode.cpp
+++ b/src/hotspot/share/opto/cfgnode.cpp
@@ -571,6 +571,18 @@ Node *RegionNode::Ideal(PhaseGVN *phase, bool can_reshape) {
       return NULL;
     } else if (can_reshape) {   // Optimization phase - remove the node
       PhaseIterGVN *igvn = phase->is_IterGVN();
+      // Strip mined (inner) loop is going away, remove outer loop.
+      if (is_CountedLoop() &&
+          as_Loop()->is_strip_mined()) {
+        Node* outer_sfpt = as_CountedLoop()->outer_safepoint();
+        Node* outer_out = as_CountedLoop()->outer_loop_exit();
+        if (outer_sfpt != NULL && outer_out != NULL) {
+          Node* in = outer_sfpt->in(0);
+          igvn->replace_node(outer_out, in);
+          LoopNode* outer = as_CountedLoop()->outer_loop();
+          igvn->replace_input_of(outer, LoopNode::LoopBackControl, igvn->C->top());
+        }
+      }
       Node *parent_ctrl;
       if( cnt == 0 ) {
         assert( req() == 1, "no inputs expected" );
diff --git a/src/hotspot/share/opto/classes.hpp b/src/hotspot/share/opto/classes.hpp
index 123ea3e0f28..235021f8660 100644
--- a/src/hotspot/share/opto/classes.hpp
+++ b/src/hotspot/share/opto/classes.hpp
@@ -133,6 +133,8 @@ macro(ConvL2F)
 macro(ConvL2I)
 macro(CountedLoop)
 macro(CountedLoopEnd)
+macro(OuterStripMinedLoop)
+macro(OuterStripMinedLoopEnd)
 macro(CountLeadingZerosI)
 macro(CountLeadingZerosL)
 macro(CountTrailingZerosI)
@@ -252,6 +254,7 @@ macro(SafePoint)
 macro(SafePointScalarObject)
 macro(SCMemProj)
 macro(SqrtD)
+macro(SqrtF)
 macro(Start)
 macro(StartOSR)
 macro(StoreB)
@@ -320,6 +323,7 @@ macro(AbsVD)
 macro(NegVF)
 macro(NegVD)
 macro(SqrtVD)
+macro(SqrtVF)
 macro(LShiftCntV)
 macro(RShiftCntV)
 macro(LShiftVB)
diff --git a/src/hotspot/share/opto/compile.cpp b/src/hotspot/share/opto/compile.cpp
index 6fe374640b6..8e5da8a4028 100644
--- a/src/hotspot/share/opto/compile.cpp
+++ b/src/hotspot/share/opto/compile.cpp
@@ -2751,27 +2751,28 @@ void Compile::final_graph_reshaping_impl( Node *n, Final_Reshape_Counts &frc) {
   case Op_CallRuntime:
   case Op_CallLeaf:
   case Op_CallLeafNoFP: {
-    assert( n->is_Call(), "" );
+    assert (n->is_Call(), "");
     CallNode *call = n->as_Call();
     // Count call sites where the FP mode bit would have to be flipped.
     // Do not count uncommon runtime calls:
     // uncommon_trap, _complete_monitor_locking, _complete_monitor_unlocking,
     // _new_Java, _new_typeArray, _new_objArray, _rethrow_Java, ...
-    if( !call->is_CallStaticJava() || !call->as_CallStaticJava()->_name ) {
+    if (!call->is_CallStaticJava() || !call->as_CallStaticJava()->_name) {
       frc.inc_call_count();   // Count the call site
     } else {                  // See if uncommon argument is shared
       Node *n = call->in(TypeFunc::Parms);
       int nop = n->Opcode();
       // Clone shared simple arguments to uncommon calls, item (1).
-      if( n->outcnt() > 1 &&
+      if (n->outcnt() > 1 &&
           !n->is_Proj() &&
           nop != Op_CreateEx &&
           nop != Op_CheckCastPP &&
           nop != Op_DecodeN &&
           nop != Op_DecodeNKlass &&
-          !n->is_Mem() ) {
+          !n->is_Mem() &&
+          !n->is_Phi()) {
         Node *x = n->clone();
-        call->set_req( TypeFunc::Parms, x );
+        call->set_req(TypeFunc::Parms, x);
       }
     }
     break;
@@ -3244,9 +3245,11 @@ void Compile::final_graph_reshaping_impl( Node *n, Final_Reshape_Counts &frc) {
     break;
   case Op_Loop:
   case Op_CountedLoop:
+  case Op_OuterStripMinedLoop:
     if (n->as_Loop()->is_inner_loop()) {
       frc.inc_inner_loop_count();
     }
+    n->as_Loop()->verify_strip_mined(0);
     break;
   case Op_LShiftI:
   case Op_RShiftI:
@@ -3525,6 +3528,14 @@ bool Compile::final_graph_reshaping() {
         record_method_not_compilable("infinite loop");
         return true;            // Found unvisited kid; must be unreach
       }
+
+    // Here so verification code in final_graph_reshaping_walk()
+    // always see an OuterStripMinedLoopEnd
+    if (n->is_OuterStripMinedLoopEnd()) {
+      IfNode* init_iff = n->as_If();
+      Node* iff = new IfNode(init_iff->in(0), init_iff->in(1), init_iff->_prob, init_iff->_fcnt);
+      n->subsume_by(iff, this);
+    }
   }
 
   // If original bytecodes contained a mixture of floats and doubles
diff --git a/src/hotspot/share/opto/convertnode.cpp b/src/hotspot/share/opto/convertnode.cpp
index 5d97a7330f2..aaf0cc2fc61 100644
--- a/src/hotspot/share/opto/convertnode.cpp
+++ b/src/hotspot/share/opto/convertnode.cpp
@@ -73,6 +73,21 @@ const Type* ConvD2FNode::Value(PhaseGVN* phase) const {
   return TypeF::make( (float)td->getd() );
 }
 
+//------------------------------Ideal------------------------------------------
+// If we see pattern ConvF2D SomeDoubleOp ConvD2F, do operation as float.
+Node *ConvD2FNode::Ideal(PhaseGVN *phase, bool can_reshape) {
+  if ( in(1)->Opcode() == Op_SqrtD ) {
+    Node* sqrtd = in(1);
+    if ( sqrtd->in(1)->Opcode() == Op_ConvF2D ) {
+      if ( Matcher::match_rule_supported(Op_SqrtF) ) {
+        Node* convf2d = sqrtd->in(1);
+        return new SqrtFNode(phase->C, sqrtd->in(0), convf2d->in(1));
+      }
+    }
+  }
+  return NULL;
+}
+
 //------------------------------Identity---------------------------------------
 // Float's can be converted to doubles with no loss of bits.  Hence
 // converting a float to a double and back to a float is a NOP.
diff --git a/src/hotspot/share/opto/convertnode.hpp b/src/hotspot/share/opto/convertnode.hpp
index 0a3e78b18dc..6ae7de5882f 100644
--- a/src/hotspot/share/opto/convertnode.hpp
+++ b/src/hotspot/share/opto/convertnode.hpp
@@ -51,6 +51,7 @@ class ConvD2FNode : public Node {
   virtual const Type *bottom_type() const { return Type::FLOAT; }
   virtual const Type* Value(PhaseGVN* phase) const;
   virtual Node* Identity(PhaseGVN* phase);
+  virtual Node *Ideal(PhaseGVN *phase, bool can_reshape);
   virtual uint  ideal_reg() const { return Op_RegF; }
 };
 
diff --git a/src/hotspot/share/opto/idealGraphPrinter.cpp b/src/hotspot/share/opto/idealGraphPrinter.cpp
index 0d5fbdda39c..cb78db147b2 100644
--- a/src/hotspot/share/opto/idealGraphPrinter.cpp
+++ b/src/hotspot/share/opto/idealGraphPrinter.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2007, 2017, 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
@@ -29,6 +29,7 @@
 #include "opto/machnode.hpp"
 #include "opto/parse.hpp"
 #include "runtime/threadCritical.hpp"
+#include "runtime/threadSMR.hpp"
 
 #ifndef PRODUCT
 
@@ -91,8 +92,7 @@ IdealGraphPrinter *IdealGraphPrinter::printer() {
 }
 
 void IdealGraphPrinter::clean_up() {
-  JavaThread *p;
-  for (p = Threads::first(); p; p = p->next()) {
+  for (JavaThreadIteratorWithHandle jtiwh; JavaThread *p = jtiwh.next(); ) {
     if (p->is_Compiler_thread()) {
       CompilerThread *c = (CompilerThread *)p;
       IdealGraphPrinter *printer = c->ideal_graph_printer();
diff --git a/src/hotspot/share/opto/ifnode.cpp b/src/hotspot/share/opto/ifnode.cpp
index 7865e3ca882..3437dfed372 100644
--- a/src/hotspot/share/opto/ifnode.cpp
+++ b/src/hotspot/share/opto/ifnode.cpp
@@ -117,6 +117,7 @@ static Node* split_if(IfNode *iff, PhaseIterGVN *igvn) {
   // No intervening control, like a simple Call
   Node *r = iff->in(0);
   if( !r->is_Region() ) return NULL;
+  if (r->is_Loop() && r->in(LoopNode::LoopBackControl)->is_top()) return NULL; // going away anyway
   if( phi->region() != r ) return NULL;
   // No other users of the cmp/bool
   if (b->outcnt() != 1 || cmp->outcnt() != 1) {
diff --git a/src/hotspot/share/opto/loopPredicate.cpp b/src/hotspot/share/opto/loopPredicate.cpp
index 0de17405ccc..9de54bb372a 100644
--- a/src/hotspot/share/opto/loopPredicate.cpp
+++ b/src/hotspot/share/opto/loopPredicate.cpp
@@ -515,8 +515,8 @@ class Invariance : public StackObj {
     _visited(area), _invariant(area), _stack(area, 10 /* guess */),
     _clone_visited(area), _old_new(area)
   {
-    Node* head = _lpt->_head;
-    Node* entry = head->in(LoopNode::EntryControl);
+    LoopNode* head = _lpt->_head->as_Loop();
+    Node* entry = head->skip_strip_mined()->in(LoopNode::EntryControl);
     if (entry->outcnt() != 1) {
       // If a node is pinned between the predicates and the loop
       // entry, we won't be able to move any node in the loop that
@@ -801,6 +801,10 @@ bool PhaseIdealLoop::loop_predication_impl(IdealLoopTree *loop) {
     return false;
   }
 
+  if (head->is_OuterStripMinedLoop()) {
+    return false;
+  }
+
   CountedLoopNode *cl = NULL;
   if (head->is_valid_counted_loop()) {
     cl = head->as_CountedLoop();
@@ -812,7 +816,7 @@ bool PhaseIdealLoop::loop_predication_impl(IdealLoopTree *loop) {
       cl = NULL;
   }
 
-  Node* entry = head->in(LoopNode::EntryControl);
+  Node* entry = head->skip_strip_mined()->in(LoopNode::EntryControl);
   ProjNode *predicate_proj = NULL;
   // Loop limit check predicate should be near the loop.
   predicate_proj = find_predicate_insertion_point(entry, Deoptimization::Reason_loop_limit_check);
@@ -1007,6 +1011,8 @@ bool PhaseIdealLoop::loop_predication_impl(IdealLoopTree *loop) {
   }
 #endif
 
+  head->verify_strip_mined(1);
+
   return hoisted;
 }
 
diff --git a/src/hotspot/share/opto/loopTransform.cpp b/src/hotspot/share/opto/loopTransform.cpp
index 2bad280129a..28a270ef4c4 100644
--- a/src/hotspot/share/opto/loopTransform.cpp
+++ b/src/hotspot/share/opto/loopTransform.cpp
@@ -67,6 +67,16 @@ void IdealLoopTree::record_for_igvn() {
     Node *n = _body.at(i);
     _phase->_igvn._worklist.push(n);
   }
+  // put body of outer strip mined loop on igvn work list as well
+  if (_head->is_CountedLoop() && _head->as_Loop()->is_strip_mined()) {
+    CountedLoopNode* l = _head->as_CountedLoop();
+    _phase->_igvn._worklist.push(l->outer_loop());
+    _phase->_igvn._worklist.push(l->outer_loop_tail());
+    _phase->_igvn._worklist.push(l->outer_loop_end());
+    _phase->_igvn._worklist.push(l->outer_safepoint());
+    Node* cle_out = _head->as_CountedLoop()->loopexit()->proj_out(false);
+    _phase->_igvn._worklist.push(cle_out);
+  }
 }
 
 //------------------------------compute_exact_trip_count-----------------------
@@ -494,7 +504,7 @@ void PhaseIdealLoop::do_peeling( IdealLoopTree *loop, Node_List &old_new ) {
     loop->dump_head();
   }
 #endif
-  Node* head = loop->_head;
+  LoopNode* head = loop->_head->as_Loop();
   bool counted_loop = head->is_CountedLoop();
   if (counted_loop) {
     CountedLoopNode *cl = head->as_CountedLoop();
@@ -514,7 +524,7 @@ void PhaseIdealLoop::do_peeling( IdealLoopTree *loop, Node_List &old_new ) {
 
   // Step 1: Clone the loop body.  The clone becomes the peeled iteration.
   //         The pre-loop illegally has 2 control users (old & new loops).
-  clone_loop( loop, old_new, dom_depth(head) );
+  clone_loop(loop, old_new, dom_depth(head->skip_strip_mined()), ControlAroundStripMined);
 
   // Step 2: Make the old-loop fall-in edges point to the peeled iteration.
   //         Do this by making the old-loop fall-in edges act as if they came
@@ -523,8 +533,8 @@ void PhaseIdealLoop::do_peeling( IdealLoopTree *loop, Node_List &old_new ) {
   //         the pre-loop with only 1 user (the new peeled iteration), but the
   //         peeled-loop backedge has 2 users.
   Node* new_entry = old_new[head->in(LoopNode::LoopBackControl)->_idx];
-  _igvn.hash_delete(head);
-  head->set_req(LoopNode::EntryControl, new_entry);
+  _igvn.hash_delete(head->skip_strip_mined());
+  head->skip_strip_mined()->set_req(LoopNode::EntryControl, new_entry);
   for (DUIterator_Fast jmax, j = head->fast_outs(jmax); j < jmax; j++) {
     Node* old = head->fast_out(j);
     if (old->in(0) == loop->_head && old->req() == 3 && old->is_Phi()) {
@@ -1009,8 +1019,6 @@ void PhaseIdealLoop::insert_pre_post_loops( IdealLoopTree *loop, Node_List &old_
   CountedLoopEndNode *main_end = main_head->loopexit();
   guarantee(main_end != NULL, "no loop exit node");
   assert( main_end->outcnt() == 2, "1 true, 1 false path only" );
-  uint dd_main_head = dom_depth(main_head);
-  uint max = main_head->outcnt();
 
   Node *pre_header= main_head->in(LoopNode::EntryControl);
   Node *init      = main_head->init_trip();
@@ -1043,7 +1051,16 @@ void PhaseIdealLoop::insert_pre_post_loops( IdealLoopTree *loop, Node_List &old_
 
   // Step B1: Clone the loop body.  The clone becomes the pre-loop.  The main
   // loop pre-header illegally has 2 control users (old & new loops).
-  clone_loop( loop, old_new, dd_main_head );
+  LoopNode* outer_main_head = main_head;
+  IdealLoopTree* outer_loop = loop;
+  if (main_head->is_strip_mined()) {
+    main_head->verify_strip_mined(1);
+    outer_main_head = main_head->outer_loop();
+    outer_loop = loop->_parent;
+    assert(outer_loop->_head == outer_main_head, "broken loop tree");
+  }
+  uint dd_main_head = dom_depth(outer_main_head);
+  clone_loop(loop, old_new, dd_main_head, ControlAroundStripMined);
   CountedLoopNode*    pre_head = old_new[main_head->_idx]->as_CountedLoop();
   CountedLoopEndNode* pre_end  = old_new[main_end ->_idx]->as_CountedLoopEnd();
   pre_head->set_pre_loop(main_head);
@@ -1058,7 +1075,7 @@ void PhaseIdealLoop::insert_pre_post_loops( IdealLoopTree *loop, Node_List &old_
   IfFalseNode *new_pre_exit = new IfFalseNode(pre_end);
   _igvn.register_new_node_with_optimizer( new_pre_exit );
   set_idom(new_pre_exit, pre_end, dd_main_head);
-  set_loop(new_pre_exit, loop->_parent);
+  set_loop(new_pre_exit, outer_loop->_parent);
 
   // Step B2: Build a zero-trip guard for the main-loop.  After leaving the
   // pre-loop, the main-loop may not execute at all.  Later in life this
@@ -1075,22 +1092,22 @@ void PhaseIdealLoop::insert_pre_post_loops( IdealLoopTree *loop, Node_List &old_
   IfNode *min_iff = new IfNode( new_pre_exit, min_bol, PROB_ALWAYS, COUNT_UNKNOWN );
   _igvn.register_new_node_with_optimizer( min_iff );
   set_idom(min_iff, new_pre_exit, dd_main_head);
-  set_loop(min_iff, loop->_parent);
+  set_loop(min_iff, outer_loop->_parent);
 
   // Plug in the false-path, taken if we need to skip main-loop
   _igvn.hash_delete( pre_exit );
   pre_exit->set_req(0, min_iff);
   set_idom(pre_exit, min_iff, dd_main_head);
-  set_idom(pre_exit->unique_out(), min_iff, dd_main_head);
+  set_idom(pre_exit->unique_ctrl_out(), min_iff, dd_main_head);
   // Make the true-path, must enter the main loop
   Node *min_taken = new IfTrueNode( min_iff );
   _igvn.register_new_node_with_optimizer( min_taken );
   set_idom(min_taken, min_iff, dd_main_head);
-  set_loop(min_taken, loop->_parent);
+  set_loop(min_taken, outer_loop->_parent);
   // Plug in the true path
-  _igvn.hash_delete( main_head );
-  main_head->set_req(LoopNode::EntryControl, min_taken);
-  set_idom(main_head, min_taken, dd_main_head);
+  _igvn.hash_delete(outer_main_head);
+  outer_main_head->set_req(LoopNode::EntryControl, min_taken);
+  set_idom(outer_main_head, min_taken, dd_main_head);
 
   Arena *a = Thread::current()->resource_area();
   VectorSet visited(a);
@@ -1102,7 +1119,7 @@ void PhaseIdealLoop::insert_pre_post_loops( IdealLoopTree *loop, Node_List &old_
     if( main_phi->is_Phi() && main_phi->in(0) == main_head && main_phi->outcnt() > 0 ) {
       Node *pre_phi = old_new[main_phi->_idx];
       Node *fallpre  = clone_up_backedge_goo(pre_head->back_control(),
-                                             main_head->init_control(),
+                                             main_head->skip_strip_mined()->in(LoopNode::EntryControl),
                                              pre_phi->in(LoopNode::LoopBackControl),
                                              visited, clones);
       _igvn.hash_delete(main_phi);
@@ -1305,16 +1322,24 @@ void PhaseIdealLoop::insert_scalar_rced_post_loop(IdealLoopTree *loop, Node_List
 Node *PhaseIdealLoop::insert_post_loop(IdealLoopTree *loop, Node_List &old_new,
                                        CountedLoopNode *main_head, CountedLoopEndNode *main_end,
                                        Node *incr, Node *limit, CountedLoopNode *&post_head) {
+  IfNode* outer_main_end = main_end;
+  IdealLoopTree* outer_loop = loop;
+  if (main_head->is_strip_mined()) {
+    main_head->verify_strip_mined(1);
+    outer_main_end = main_head->outer_loop_end();
+    outer_loop = loop->_parent;
+    assert(outer_loop->_head == main_head->in(LoopNode::EntryControl), "broken loop tree");
+  }
 
   //------------------------------
   // Step A: Create a new post-Loop.
-  Node* main_exit = main_end->proj_out(false);
+  Node* main_exit = outer_main_end->proj_out(false);
   assert(main_exit->Opcode() == Op_IfFalse, "");
   int dd_main_exit = dom_depth(main_exit);
 
   // Step A1: Clone the loop body of main. The clone becomes the post-loop.
   // The main loop pre-header illegally has 2 control users (old & new loops).
-  clone_loop(loop, old_new, dd_main_exit);
+  clone_loop(loop, old_new, dd_main_exit, ControlAroundStripMined);
   assert(old_new[main_end->_idx]->Opcode() == Op_CountedLoopEnd, "");
   post_head = old_new[main_head->_idx]->as_CountedLoop();
   post_head->set_normal_loop();
@@ -1325,10 +1350,10 @@ Node *PhaseIdealLoop::insert_post_loop(IdealLoopTree *loop, Node_List &old_new,
   post_end->_prob = PROB_FAIR;
 
   // Build the main-loop normal exit.
-  IfFalseNode *new_main_exit = new IfFalseNode(main_end);
+  IfFalseNode *new_main_exit = new IfFalseNode(outer_main_end);
   _igvn.register_new_node_with_optimizer(new_main_exit);
-  set_idom(new_main_exit, main_end, dd_main_exit);
-  set_loop(new_main_exit, loop->_parent);
+  set_idom(new_main_exit, outer_main_end, dd_main_exit);
+  set_loop(new_main_exit, outer_loop->_parent);
 
   // Step A2: Build a zero-trip guard for the post-loop.  After leaving the
   // main-loop, the post-loop may not execute at all.  We 'opaque' the incr
@@ -1346,7 +1371,7 @@ Node *PhaseIdealLoop::insert_post_loop(IdealLoopTree *loop, Node_List &old_new,
   IfNode *zer_iff = new IfNode(new_main_exit, zer_bol, PROB_FAIR, COUNT_UNKNOWN);
   _igvn.register_new_node_with_optimizer(zer_iff);
   set_idom(zer_iff, new_main_exit, dd_main_exit);
-  set_loop(zer_iff, loop->_parent);
+  set_loop(zer_iff, outer_loop->_parent);
 
   // Plug in the false-path, taken if we need to skip this post-loop
   _igvn.replace_input_of(main_exit, 0, zer_iff);
@@ -1356,7 +1381,7 @@ Node *PhaseIdealLoop::insert_post_loop(IdealLoopTree *loop, Node_List &old_new,
   Node *zer_taken = new IfTrueNode(zer_iff);
   _igvn.register_new_node_with_optimizer(zer_taken);
   set_idom(zer_taken, zer_iff, dd_main_exit);
-  set_loop(zer_taken, loop->_parent);
+  set_loop(zer_taken, outer_loop->_parent);
   // Plug in the true path
   _igvn.hash_delete(post_head);
   post_head->set_req(LoopNode::EntryControl, zer_taken);
@@ -1431,7 +1456,7 @@ void PhaseIdealLoop::do_unroll( IdealLoopTree *loop, Node_List &old_new, bool ad
   // if rounds of unroll,optimize are making progress
   loop_head->set_node_count_before_unroll(loop->_body.size());
 
-  Node *ctrl  = loop_head->in(LoopNode::EntryControl);
+  Node *ctrl  = loop_head->skip_strip_mined()->in(LoopNode::EntryControl);
   Node *limit = loop_head->limit();
   Node *init  = loop_head->init_trip();
   Node *stride = loop_head->stride();
@@ -1610,7 +1635,7 @@ void PhaseIdealLoop::do_unroll( IdealLoopTree *loop, Node_List &old_new, bool ad
   // represents the odd iterations; since the loop trips an even number of
   // times its backedge is never taken.  Kill the backedge.
   uint dd = dom_depth(loop_head);
-  clone_loop( loop, old_new, dd );
+  clone_loop(loop, old_new, dd, IgnoreStripMined);
 
   // Make backedges of the clone equal to backedges of the original.
   // Make the fall-in from the original come from the fall-out of the clone.
@@ -1653,6 +1678,7 @@ void PhaseIdealLoop::do_unroll( IdealLoopTree *loop, Node_List &old_new, bool ad
   }
 
   loop->record_for_igvn();
+  loop_head->clear_strip_mined();
 
 #ifndef PRODUCT
   if (C->do_vector_loop() && (PrintOpto && (VerifyLoopOptimizations || TraceLoopOpts))) {
@@ -2047,7 +2073,7 @@ int PhaseIdealLoop::do_range_check( IdealLoopTree *loop, Node_List &old_new ) {
   }
 
   // Need to find the main-loop zero-trip guard
-  Node *ctrl  = cl->in(LoopNode::EntryControl);
+  Node *ctrl  = cl->skip_strip_mined()->in(LoopNode::EntryControl);
   Node *iffm = ctrl->in(0);
   Node *opqzm = iffm->in(1)->in(1)->in(2);
   assert(opqzm->in(1) == main_limit, "do not understand situation");
@@ -2413,7 +2439,6 @@ bool PhaseIdealLoop::multi_version_post_loops(IdealLoopTree *rce_loop, IdealLoop
     _igvn.register_new_node_with_optimizer(cur_min);
     Node *cmp_node = rce_loop_end->cmp_node();
     _igvn.replace_input_of(cmp_node, 2, cur_min);
-    set_idom(cmp_node, cur_min, dom_depth(ctrl));
     set_ctrl(cur_min, ctrl);
     set_loop(cur_min, rce_loop->_parent);
 
@@ -2519,7 +2544,7 @@ void IdealLoopTree::adjust_loop_exit_prob( PhaseIdealLoop *phase ) {
 
 #ifdef ASSERT
 static CountedLoopNode* locate_pre_from_main(CountedLoopNode *cl) {
-  Node *ctrl  = cl->in(LoopNode::EntryControl);
+  Node *ctrl  = cl->skip_strip_mined()->in(LoopNode::EntryControl);
   assert(ctrl->Opcode() == Op_IfTrue || ctrl->Opcode() == Op_IfFalse, "");
   Node *iffm = ctrl->in(0);
   assert(iffm->Opcode() == Op_If, "");
@@ -2558,7 +2583,7 @@ void IdealLoopTree::remove_main_post_loops(CountedLoopNode *cl, PhaseIdealLoop *
   }
 
   assert(locate_pre_from_main(main_head) == cl, "bad main loop");
-  Node* main_iff = main_head->in(LoopNode::EntryControl)->in(0);
+  Node* main_iff = main_head->skip_strip_mined()->in(LoopNode::EntryControl)->in(0);
 
   // Remove the Opaque1Node of the pre loop and make it execute all iterations
   phase->_igvn.replace_input_of(pre_cmp, 2, pre_cmp->in(2)->in(2));
@@ -2619,7 +2644,7 @@ bool IdealLoopTree::policy_do_remove_empty_loop( PhaseIdealLoop *phase ) {
   }
   if (needs_guard) {
     // Check for an obvious zero trip guard.
-    Node* inctrl = PhaseIdealLoop::skip_loop_predicates(cl->in(LoopNode::EntryControl));
+    Node* inctrl = PhaseIdealLoop::skip_loop_predicates(cl->skip_strip_mined()->in(LoopNode::EntryControl));
     if (inctrl->Opcode() == Op_IfTrue || inctrl->Opcode() == Op_IfFalse) {
       bool maybe_swapped = (inctrl->Opcode() == Op_IfFalse);
       // The test should look like just the backedge of a CountedLoop
@@ -3167,6 +3192,8 @@ bool PhaseIdealLoop::intrinsify_fill(IdealLoopTree* lpt) {
     return false;
   }
 
+  head->verify_strip_mined(1);
+
   // Check that the body only contains a store of a loop invariant
   // value that is indexed by the loop phi.
   Node* store = NULL;
@@ -3288,6 +3315,16 @@ bool PhaseIdealLoop::intrinsify_fill(IdealLoopTree* lpt) {
   }
 */
 
+  if (head->is_strip_mined()) {
+    // Inner strip mined loop goes away so get rid of outer strip
+    // mined loop
+    Node* outer_sfpt = head->outer_safepoint();
+    Node* in = outer_sfpt->in(0);
+    Node* outer_out = head->outer_loop_exit();
+    lazy_replace(outer_out, in);
+    _igvn.replace_input_of(outer_sfpt, 0, C->top());
+  }
+
   // Redirect the old control and memory edges that are outside the loop.
   // Sometimes the memory phi of the head is used as the outgoing
   // state of the loop.  It's safe in this case to replace it with the
diff --git a/src/hotspot/share/opto/loopUnswitch.cpp b/src/hotspot/share/opto/loopUnswitch.cpp
index 72201c2e282..9940b333fe0 100644
--- a/src/hotspot/share/opto/loopUnswitch.cpp
+++ b/src/hotspot/share/opto/loopUnswitch.cpp
@@ -132,11 +132,11 @@ void PhaseIdealLoop::do_unswitching (IdealLoopTree *loop, Node_List &old_new) {
     head->as_CountedLoop()->set_normal_loop();
   }
 
-  ProjNode* proj_true = create_slow_version_of_loop(loop, old_new, unswitch_iff->Opcode());
+  ProjNode* proj_true = create_slow_version_of_loop(loop, old_new, unswitch_iff->Opcode(), CloneIncludesStripMined);
 
 #ifdef ASSERT
   Node* uniqc = proj_true->unique_ctrl_out();
-  Node* entry = head->in(LoopNode::EntryControl);
+  Node* entry = head->skip_strip_mined()->in(LoopNode::EntryControl);
   Node* predicate = find_predicate(entry);
   if (predicate != NULL && UseLoopPredicate) {
     // We may have two predicates, find first.
@@ -145,7 +145,8 @@ void PhaseIdealLoop::do_unswitching (IdealLoopTree *loop, Node_List &old_new) {
   }
   if (predicate != NULL) predicate = predicate->in(0);
   assert(proj_true->is_IfTrue() &&
-         (predicate == NULL && uniqc == head ||
+         (predicate == NULL && uniqc == head && !head->is_strip_mined() ||
+          predicate == NULL && uniqc == head->in(LoopNode::EntryControl) && head->is_strip_mined() ||
           predicate != NULL && uniqc == predicate), "by construction");
 #endif
   // Increment unswitch count
@@ -223,13 +224,16 @@ void PhaseIdealLoop::do_unswitching (IdealLoopTree *loop, Node_List &old_new) {
 // Return control projection of the entry to the fast version.
 ProjNode* PhaseIdealLoop::create_slow_version_of_loop(IdealLoopTree *loop,
                                                       Node_List &old_new,
-                                                      int opcode) {
+                                                      int opcode,
+                                                      CloneLoopMode mode) {
   LoopNode* head  = loop->_head->as_Loop();
   bool counted_loop = head->is_CountedLoop();
-  Node*     entry = head->in(LoopNode::EntryControl);
+  Node*     entry = head->skip_strip_mined()->in(LoopNode::EntryControl);
   _igvn.rehash_node_delayed(entry);
   IdealLoopTree* outer_loop = loop->_parent;
 
+  head->verify_strip_mined(1);
+
   Node *cont      = _igvn.intcon(1);
   set_ctrl(cont, C->root());
   Node* opq       = new Opaque1Node(C, cont);
@@ -247,19 +251,21 @@ ProjNode* PhaseIdealLoop::create_slow_version_of_loop(IdealLoopTree *loop,
   // Clone the loop body.  The clone becomes the fast loop.  The
   // original pre-header will (illegally) have 3 control users
   // (old & new loops & new if).
-  clone_loop(loop, old_new, dom_depth(head), iff);
+  clone_loop(loop, old_new, dom_depth(head->skip_strip_mined()), mode, iff);
   assert(old_new[head->_idx]->is_Loop(), "" );
 
   // Fast (true) control
   Node* iffast_pred = clone_loop_predicates(entry, iffast, !counted_loop);
-  _igvn.replace_input_of(head, LoopNode::EntryControl, iffast_pred);
-  set_idom(head, iffast_pred, dom_depth(head));
 
   // Slow (false) control
   Node* ifslow_pred = clone_loop_predicates(entry, ifslow, !counted_loop);
-  LoopNode* slow_head = old_new[head->_idx]->as_Loop();
-  _igvn.replace_input_of(slow_head, LoopNode::EntryControl, ifslow_pred);
-  set_idom(slow_head, ifslow_pred, dom_depth(slow_head));
+
+  Node* l = head->skip_strip_mined();
+  _igvn.replace_input_of(l, LoopNode::EntryControl, iffast_pred);
+  set_idom(l, iffast_pred, dom_depth(l));
+  LoopNode* slow_l = old_new[head->_idx]->as_Loop()->skip_strip_mined();
+  _igvn.replace_input_of(slow_l, LoopNode::EntryControl, ifslow_pred);
+  set_idom(slow_l, ifslow_pred, dom_depth(l));
 
   recompute_dom_depth();
 
@@ -270,9 +276,9 @@ LoopNode* PhaseIdealLoop::create_reserve_version_of_loop(IdealLoopTree *loop, Co
   Node_List old_new;
   LoopNode* head  = loop->_head->as_Loop();
   bool counted_loop = head->is_CountedLoop();
-  Node*     entry = head->in(LoopNode::EntryControl);
+  Node*     entry = head->skip_strip_mined()->in(LoopNode::EntryControl);
   _igvn.rehash_node_delayed(entry);
-  IdealLoopTree* outer_loop = loop->_parent;
+  IdealLoopTree* outer_loop = head->is_strip_mined() ? loop->_parent->_parent : loop->_parent;
 
   ConINode* const_1 = _igvn.intcon(1);
   set_ctrl(const_1, C->root());
@@ -286,7 +292,7 @@ LoopNode* PhaseIdealLoop::create_reserve_version_of_loop(IdealLoopTree *loop, Co
   // Clone the loop body.  The clone becomes the fast loop.  The
   // original pre-header will (illegally) have 3 control users
   // (old & new loops & new if).
-  clone_loop(loop, old_new, dom_depth(head), iff);
+  clone_loop(loop, old_new, dom_depth(head), CloneIncludesStripMined, iff);
   assert(old_new[head->_idx]->is_Loop(), "" );
 
   LoopNode* slow_head = old_new[head->_idx]->as_Loop();
@@ -303,9 +309,9 @@ LoopNode* PhaseIdealLoop::create_reserve_version_of_loop(IdealLoopTree *loop, Co
 #endif
 
   // Fast (true) control
-  _igvn.replace_input_of(head, LoopNode::EntryControl, iffast);
+  _igvn.replace_input_of(head->skip_strip_mined(), LoopNode::EntryControl, iffast);
   // Slow (false) control
-  _igvn.replace_input_of(slow_head, LoopNode::EntryControl, ifslow);
+  _igvn.replace_input_of(slow_head->skip_strip_mined(), LoopNode::EntryControl, ifslow);
 
   recompute_dom_depth();
 
@@ -394,7 +400,7 @@ bool CountedLoopReserveKit::create_reserve() {
     return false;
   }
 
-  Node* ifslow_pred = _lp_reserved->as_CountedLoop()->in(LoopNode::EntryControl);
+  Node* ifslow_pred = _lp_reserved->skip_strip_mined()->in(LoopNode::EntryControl);
 
   if (!ifslow_pred->is_IfFalse()) {
     return false;
diff --git a/src/hotspot/share/opto/loopnode.cpp b/src/hotspot/share/opto/loopnode.cpp
index 5f003d304ff..1e5af607642 100644
--- a/src/hotspot/share/opto/loopnode.cpp
+++ b/src/hotspot/share/opto/loopnode.cpp
@@ -261,8 +261,68 @@ void PhaseIdealLoop::set_subtree_ctrl( Node *n ) {
   set_early_ctrl( n );
 }
 
+// Create a skeleton strip mined outer loop: a Loop head before the
+// inner strip mined loop, a safepoint and an exit condition guarded
+// by an opaque node after the inner strip mined loop with a backedge
+// to the loop head. The inner strip mined loop is left as it is. Only
+// once loop optimizations are over, do we adjust the inner loop exit
+// condition to limit its number of iterations, set the outer loop
+// exit condition and add Phis to the outer loop head. Some loop
+// optimizations that operate on the inner strip mined loop need to be
+// aware of the outer strip mined loop: loop unswitching needs to
+// clone the outer loop as well as the inner, unrolling needs to only
+// clone the inner loop etc. No optimizations need to change the outer
+// strip mined loop as it is only a skeleton.
+IdealLoopTree* PhaseIdealLoop::create_outer_strip_mined_loop(BoolNode *test, Node *cmp, Node *init_control,
+                                                             IdealLoopTree* loop, float cl_prob, float le_fcnt,
+                                                             Node*& entry_control, Node*& iffalse) {
+  Node* outer_test = _igvn.intcon(0);
+  set_ctrl(outer_test, C->root());
+  Node *orig = iffalse;
+  iffalse = iffalse->clone();
+  _igvn.register_new_node_with_optimizer(iffalse);
+  set_idom(iffalse, idom(orig), dom_depth(orig));
+
+  IfNode *outer_le = new OuterStripMinedLoopEndNode(iffalse, outer_test, cl_prob, le_fcnt);
+  Node *outer_ift = new IfTrueNode (outer_le);
+  Node* outer_iff = orig;
+  _igvn.replace_input_of(outer_iff, 0, outer_le);
+
+  LoopNode *outer_l = new OuterStripMinedLoopNode(C, init_control, outer_ift);
+  entry_control = outer_l;
+
+  IdealLoopTree* outer_ilt = new IdealLoopTree(this, outer_l, outer_ift);
+  IdealLoopTree* parent = loop->_parent;
+  IdealLoopTree* sibling = parent->_child;
+  if (sibling == loop) {
+    parent->_child = outer_ilt;
+  } else {
+    while (sibling->_next != loop) {
+      sibling = sibling->_next;
+    }
+    sibling->_next = outer_ilt;
+  }
+  outer_ilt->_next = loop->_next;
+  outer_ilt->_parent = parent;
+  outer_ilt->_child = loop;
+  outer_ilt->_nest = loop->_nest;
+  loop->_parent = outer_ilt;
+  loop->_next = NULL;
+  loop->_nest++;
+
+  set_loop(iffalse, outer_ilt);
+  register_control(outer_le, outer_ilt, iffalse);
+  register_control(outer_ift, outer_ilt, outer_le);
+  set_idom(outer_iff, outer_le, dom_depth(outer_le));
+  _igvn.register_new_node_with_optimizer(outer_l);
+  set_loop(outer_l, outer_ilt);
+  set_idom(outer_l, init_control, dom_depth(init_control)+1);
+
+  return outer_ilt;
+}
+
 //------------------------------is_counted_loop--------------------------------
-bool PhaseIdealLoop::is_counted_loop( Node *x, IdealLoopTree *loop ) {
+bool PhaseIdealLoop::is_counted_loop(Node* x, IdealLoopTree*& loop) {
   PhaseGVN *gvn = &_igvn;
 
   // Counted loop head must be a good RegionNode with only 3 not NULL
@@ -280,7 +340,7 @@ bool PhaseIdealLoop::is_counted_loop( Node *x, IdealLoopTree *loop ) {
 
   // Allow funny placement of Safepoint
   if (back_control->Opcode() == Op_SafePoint) {
-    if (UseCountedLoopSafepoints) {
+    if (LoopStripMiningIter != 0) {
       // Leaving the safepoint on the backedge and creating a
       // CountedLoop will confuse optimizations. We can't move the
       // safepoint around because its jvm state wouldn't match a new
@@ -600,7 +660,7 @@ bool PhaseIdealLoop::is_counted_loop( Node *x, IdealLoopTree *loop ) {
   }
   set_subtree_ctrl( limit );
 
-  if (!UseCountedLoopSafepoints) {
+  if (LoopStripMiningIter == 0) {
     // Check for SafePoint on backedge and remove
     Node *sfpt = x->in(LoopNode::LoopBackControl);
     if (sfpt->Opcode() == Op_SafePoint && is_deleteable_safept(sfpt)) {
@@ -683,8 +743,20 @@ bool PhaseIdealLoop::is_counted_loop( Node *x, IdealLoopTree *loop ) {
   assert(iff->outcnt() == 0, "should be dead now");
   lazy_replace( iff, le ); // fix 'get_ctrl'
 
+  Node *sfpt2 = le->in(0);
+
+  Node* entry_control = init_control;
+  bool strip_mine_loop = LoopStripMiningIter > 1 && loop->_child == NULL &&
+    sfpt2->Opcode() == Op_SafePoint && !loop->_has_call;
+  IdealLoopTree* outer_ilt = NULL;
+  if (strip_mine_loop) {
+    outer_ilt = create_outer_strip_mined_loop(test, cmp, init_control, loop,
+                                              cl_prob, le->_fcnt, entry_control,
+                                              iffalse);
+  }
+
   // Now setup a new CountedLoopNode to replace the existing LoopNode
-  CountedLoopNode *l = new CountedLoopNode(init_control, back_control);
+  CountedLoopNode *l = new CountedLoopNode(entry_control, back_control);
   l->set_unswitch_count(x->as_Loop()->unswitch_count()); // Preserve
   // The following assert is approximately true, and defines the intention
   // of can_be_counted_loop.  It fails, however, because phase->type
@@ -696,12 +768,19 @@ bool PhaseIdealLoop::is_counted_loop( Node *x, IdealLoopTree *loop ) {
   // Fix all data nodes placed at the old loop head.
   // Uses the lazy-update mechanism of 'get_ctrl'.
   lazy_replace( x, l );
-  set_idom(l, init_control, dom_depth(x));
+  set_idom(l, entry_control, dom_depth(entry_control) + 1);
 
-  if (!UseCountedLoopSafepoints) {
+  if (LoopStripMiningIter == 0 || strip_mine_loop) {
     // Check for immediately preceding SafePoint and remove
-    Node *sfpt2 = le->in(0);
-    if (sfpt2->Opcode() == Op_SafePoint && is_deleteable_safept(sfpt2)) {
+    if (sfpt2->Opcode() == Op_SafePoint && (LoopStripMiningIter != 0 || is_deleteable_safept(sfpt2))) {
+      if (strip_mine_loop) {
+        Node* outer_le = outer_ilt->_tail->in(0);
+        Node* sfpt = sfpt2->clone();
+        sfpt->set_req(0, iffalse);
+        outer_le->set_req(0, sfpt);
+        register_control(sfpt, outer_ilt, iffalse);
+        set_idom(outer_le, sfpt, dom_depth(sfpt));
+      }
       lazy_replace( sfpt2, sfpt2->in(TypeFunc::Control));
       if (loop->_safepts != NULL) {
         loop->_safepts->yank(sfpt2);
@@ -730,6 +809,13 @@ bool PhaseIdealLoop::is_counted_loop( Node *x, IdealLoopTree *loop ) {
   // bounds
   l->phi()->as_Phi()->set_type(l->phi()->Value(&_igvn));
 
+  if (strip_mine_loop) {
+    l->mark_strip_mined();
+    l->verify_strip_mined(1);
+    outer_ilt->_head->as_Loop()->verify_strip_mined(1);
+    loop = outer_ilt;
+  }
+
   return true;
 }
 
@@ -776,12 +862,93 @@ Node* PhaseIdealLoop::exact_limit( IdealLoopTree *loop ) {
 // Return a node which is more "ideal" than the current node.
 // Attempt to convert into a counted-loop.
 Node *LoopNode::Ideal(PhaseGVN *phase, bool can_reshape) {
-  if (!can_be_counted_loop(phase)) {
+  if (!can_be_counted_loop(phase) && !is_OuterStripMinedLoop()) {
     phase->C->set_major_progress();
   }
   return RegionNode::Ideal(phase, can_reshape);
 }
 
+void LoopNode::verify_strip_mined(int expect_skeleton) const {
+#ifdef ASSERT
+  const OuterStripMinedLoopNode* outer = NULL;
+  const CountedLoopNode* inner = NULL;
+  if (is_strip_mined()) {
+    assert(is_CountedLoop(), "no Loop should be marked strip mined");
+    inner = as_CountedLoop();
+    outer = inner->in(LoopNode::EntryControl)->as_OuterStripMinedLoop();
+  } else if (is_OuterStripMinedLoop()) {
+    outer = this->as_OuterStripMinedLoop();
+    inner = outer->unique_ctrl_out()->as_CountedLoop();
+    assert(!is_strip_mined(), "outer loop shouldn't be marked strip mined");
+  }
+  if (inner != NULL || outer != NULL) {
+    assert(inner != NULL && outer != NULL, "missing loop in strip mined nest");
+    Node* outer_tail = outer->in(LoopNode::LoopBackControl);
+    Node* outer_le = outer_tail->in(0);
+    assert(outer_le->Opcode() == Op_OuterStripMinedLoopEnd, "tail of outer loop should be an If");
+    Node* sfpt = outer_le->in(0);
+    assert(sfpt->Opcode() == Op_SafePoint, "where's the safepoint?");
+    Node* inner_out = sfpt->in(0);
+    if (inner_out->outcnt() != 1) {
+      ResourceMark rm;
+      Unique_Node_List wq;
+
+      for (DUIterator_Fast imax, i = inner_out->fast_outs(imax); i < imax; i++) {
+        Node* u = inner_out->fast_out(i);
+        if (u == sfpt) {
+          continue;
+        }
+        wq.clear();
+        wq.push(u);
+        bool found_sfpt = false;
+        for (uint next = 0; next < wq.size() && !found_sfpt; next++) {
+          Node *n = wq.at(next);
+          for (DUIterator_Fast imax, i = n->fast_outs(imax); i < imax && !found_sfpt; i++) {
+            Node* u = n->fast_out(i);
+            if (u == sfpt) {
+              found_sfpt = true;
+            }
+            if (!u->is_CFG()) {
+              wq.push(u);
+            }
+          }
+        }
+        assert(found_sfpt, "no node in loop that's not input to safepoint");
+      }
+    }
+    CountedLoopEndNode* cle = inner_out->in(0)->as_CountedLoopEnd();
+    assert(cle == inner->loopexit(), "mismatch");
+    bool has_skeleton = outer_le->in(1)->bottom_type()->singleton() && outer_le->in(1)->bottom_type()->is_int()->get_con() == 0;
+    if (has_skeleton) {
+      assert(expect_skeleton == 1 || expect_skeleton == -1, "unexpected skeleton node");
+      assert(outer->outcnt() == 2, "only phis");
+    } else {
+      assert(expect_skeleton == 0 || expect_skeleton == -1, "no skeleton node?");
+      uint phis = 0;
+      for (DUIterator_Fast imax, i = inner->fast_outs(imax); i < imax; i++) {
+        Node* u = inner->fast_out(i);
+        if (u->is_Phi()) {
+          phis++;
+        }
+      }
+      for (DUIterator_Fast imax, i = outer->fast_outs(imax); i < imax; i++) {
+        Node* u = outer->fast_out(i);
+        assert(u == outer || u == inner || u->is_Phi(), "nothing between inner and outer loop");
+      }
+      uint stores = 0;
+      for (DUIterator_Fast imax, i = inner_out->fast_outs(imax); i < imax; i++) {
+        Node* u = inner_out->fast_out(i);
+        if (u->is_Store()) {
+          stores++;
+        }
+      }
+      assert(outer->outcnt() >= phis + 2 && outer->outcnt() <= phis + 2 + stores + 1, "only phis");
+    }
+    assert(sfpt->outcnt() == 1, "no data node");
+    assert(outer_tail->outcnt() == 1 || !has_skeleton, "no data node");
+  }
+#endif
+}
 
 //=============================================================================
 //------------------------------Ideal------------------------------------------
@@ -802,6 +969,7 @@ void CountedLoopNode::dump_spec(outputStream *st) const {
   if (is_pre_loop ()) st->print("pre of N%d" , _main_idx);
   if (is_main_loop()) st->print("main of N%d", _idx);
   if (is_post_loop()) st->print("post of N%d", _main_idx);
+  if (is_strip_mined()) st->print(" strip mined");
 }
 #endif
 
@@ -990,6 +1158,365 @@ Node* CountedLoopNode::match_incr_with_optional_truncation(
   return NULL;
 }
 
+LoopNode* CountedLoopNode::skip_strip_mined(int expect_opaq) {
+  if (is_strip_mined()) {
+    verify_strip_mined(expect_opaq);
+    return in(EntryControl)->as_Loop();
+  }
+  return this;
+}
+
+OuterStripMinedLoopNode* CountedLoopNode::outer_loop() const {
+  assert(is_strip_mined(), "not a strip mined loop");
+  Node* c = in(EntryControl);
+  if (c == NULL || c->is_top() || !c->is_OuterStripMinedLoop()) {
+    return NULL;
+  }
+  return c->as_OuterStripMinedLoop();
+}
+
+IfTrueNode* OuterStripMinedLoopNode::outer_loop_tail() const {
+  Node* c = in(LoopBackControl);
+  if (c == NULL || c->is_top()) {
+    return NULL;
+  }
+  return c->as_IfTrue();
+}
+
+IfTrueNode* CountedLoopNode::outer_loop_tail() const {
+  LoopNode* l = outer_loop();
+  if (l == NULL) {
+    return NULL;
+  }
+  return l->outer_loop_tail();
+}
+
+OuterStripMinedLoopEndNode* OuterStripMinedLoopNode::outer_loop_end() const {
+  IfTrueNode* proj = outer_loop_tail();
+  if (proj == NULL) {
+    return NULL;
+  }
+  Node* c = proj->in(0);
+  if (c == NULL || c->is_top() || c->outcnt() != 2) {
+    return NULL;
+  }
+  return c->as_OuterStripMinedLoopEnd();
+}
+
+OuterStripMinedLoopEndNode* CountedLoopNode::outer_loop_end() const {
+  LoopNode* l = outer_loop();
+  if (l == NULL) {
+    return NULL;
+  }
+  return l->outer_loop_end();
+}
+
+IfFalseNode* OuterStripMinedLoopNode::outer_loop_exit() const {
+  IfNode* le = outer_loop_end();
+  if (le == NULL) {
+    return NULL;
+  }
+  Node* c = le->proj_out(false);
+  if (c == NULL) {
+    return NULL;
+  }
+  return c->as_IfFalse();
+}
+
+IfFalseNode* CountedLoopNode::outer_loop_exit() const {
+  LoopNode* l = outer_loop();
+  if (l == NULL) {
+    return NULL;
+  }
+  return l->outer_loop_exit();
+}
+
+SafePointNode* OuterStripMinedLoopNode::outer_safepoint() const {
+  IfNode* le = outer_loop_end();
+  if (le == NULL) {
+    return NULL;
+  }
+  Node* c = le->in(0);
+  if (c == NULL || c->is_top()) {
+    return NULL;
+  }
+  assert(c->Opcode() == Op_SafePoint, "broken outer loop");
+  return c->as_SafePoint();
+}
+
+SafePointNode* CountedLoopNode::outer_safepoint() const {
+  LoopNode* l = outer_loop();
+  if (l == NULL) {
+    return NULL;
+  }
+  return l->outer_safepoint();
+}
+
+void OuterStripMinedLoopNode::adjust_strip_mined_loop(PhaseIterGVN* igvn) {
+  // Look for the outer & inner strip mined loop, reduce number of
+  // iterations of the inner loop, set exit condition of outer loop,
+  // construct required phi nodes for outer loop.
+  CountedLoopNode* inner_cl = unique_ctrl_out()->as_CountedLoop();
+  assert(inner_cl->is_strip_mined(), "inner loop should be strip mined");
+  Node* inner_iv_phi = inner_cl->phi();
+  if (inner_iv_phi == NULL) {
+    return;
+  }
+  CountedLoopEndNode* inner_cle = inner_cl->loopexit();
+
+  int stride = inner_cl->stride_con();
+  jlong scaled_iters_long = ((jlong)LoopStripMiningIter) * ABS(stride);
+  int scaled_iters = (int)scaled_iters_long;
+  int short_scaled_iters = LoopStripMiningIterShortLoop* ABS(stride);
+  const TypeInt* inner_iv_t = igvn->type(inner_iv_phi)->is_int();
+  jlong iter_estimate = (jlong)inner_iv_t->_hi - (jlong)inner_iv_t->_lo;
+  assert(iter_estimate > 0, "broken");
+  if ((jlong)scaled_iters != scaled_iters_long || iter_estimate <= short_scaled_iters) {
+    // Remove outer loop and safepoint (too few iterations)
+    Node* outer_sfpt = outer_safepoint();
+    Node* outer_out = outer_loop_exit();
+    igvn->replace_node(outer_out, outer_sfpt->in(0));
+    igvn->replace_input_of(outer_sfpt, 0, igvn->C->top());
+    inner_cl->clear_strip_mined();
+    return;
+  }
+  if (iter_estimate <= scaled_iters_long) {
+    // We would only go through one iteration of
+    // the outer loop: drop the outer loop but
+    // keep the safepoint so we don't run for
+    // too long without a safepoint
+    IfNode* outer_le = outer_loop_end();
+    Node* iff = igvn->transform(new IfNode(outer_le->in(0), outer_le->in(1), outer_le->_prob, outer_le->_fcnt));
+    igvn->replace_node(outer_le, iff);
+    inner_cl->clear_strip_mined();
+    return;
+  }
+
+  Node* cle_tail = inner_cle->proj_out(true);
+  ResourceMark rm;
+  Node_List old_new;
+  if (cle_tail->outcnt() > 1) {
+    // Look for nodes on backedge of inner loop and clone them
+    Unique_Node_List backedge_nodes;
+    for (DUIterator_Fast imax, i = cle_tail->fast_outs(imax); i < imax; i++) {
+      Node* u = cle_tail->fast_out(i);
+      if (u != inner_cl) {
+        assert(!u->is_CFG(), "control flow on the backedge?");
+        backedge_nodes.push(u);
+      }
+    }
+    uint last = igvn->C->unique();
+    for (uint next = 0; next < backedge_nodes.size(); next++) {
+      Node* n = backedge_nodes.at(next);
+      old_new.map(n->_idx, n->clone());
+      for (DUIterator_Fast imax, i = n->fast_outs(imax); i < imax; i++) {
+        Node* u = n->fast_out(i);
+        assert(!u->is_CFG(), "broken");
+        if (u->_idx >= last) {
+          continue;
+        }
+        if (!u->is_Phi()) {
+          backedge_nodes.push(u);
+        } else {
+          assert(u->in(0) == inner_cl, "strange phi on the backedge");
+        }
+      }
+    }
+    // Put the clones on the outer loop backedge
+    Node* le_tail = outer_loop_tail();
+    for (uint next = 0; next < backedge_nodes.size(); next++) {
+      Node *n = old_new[backedge_nodes.at(next)->_idx];
+      for (uint i = 1; i < n->req(); i++) {
+        if (n->in(i) != NULL && old_new[n->in(i)->_idx] != NULL) {
+          n->set_req(i, old_new[n->in(i)->_idx]);
+        }
+      }
+      if (n->in(0) != NULL) {
+        assert(n->in(0) == cle_tail, "node not on backedge?");
+        n->set_req(0, le_tail);
+      }
+      igvn->register_new_node_with_optimizer(n);
+    }
+  }
+
+  Node* iv_phi = NULL;
+  // Make a clone of each phi in the inner loop
+  // for the outer loop
+  for (uint i = 0; i < inner_cl->outcnt(); i++) {
+    Node* u = inner_cl->raw_out(i);
+    if (u->is_Phi()) {
+      assert(u->in(0) == inner_cl, "inconsistent");
+      Node* phi = u->clone();
+      phi->set_req(0, this);
+      Node* be = old_new[phi->in(LoopNode::LoopBackControl)->_idx];
+      if (be != NULL) {
+        phi->set_req(LoopNode::LoopBackControl, be);
+      }
+      phi = igvn->transform(phi);
+      igvn->replace_input_of(u, LoopNode::EntryControl, phi);
+      if (u == inner_iv_phi) {
+        iv_phi = phi;
+      }
+    }
+  }
+  Node* cle_out = inner_cle->proj_out(false);
+  if (cle_out->outcnt() > 1) {
+    // Look for chains of stores that were sunk
+    // out of the inner loop and are in the outer loop
+    for (DUIterator_Fast imax, i = cle_out->fast_outs(imax); i < imax; i++) {
+      Node* u = cle_out->fast_out(i);
+      if (u->is_Store()) {
+        Node* first = u;
+        for(;;) {
+          Node* next = first->in(MemNode::Memory);
+          if (!next->is_Store() || next->in(0) != cle_out) {
+            break;
+          }
+          first = next;
+        }
+        Node* last = u;
+        for(;;) {
+          Node* next = NULL;
+          for (DUIterator_Fast jmax, j = last->fast_outs(jmax); j < jmax; j++) {
+            Node* uu = last->fast_out(j);
+            if (uu->is_Store() && uu->in(0) == cle_out) {
+              assert(next == NULL, "only one in the outer loop");
+              next = uu;
+            }
+          }
+          if (next == NULL) {
+            break;
+          }
+          last = next;
+        }
+        Node* phi = NULL;
+        for (DUIterator_Fast jmax, j = fast_outs(jmax); j < jmax; j++) {
+          Node* uu = fast_out(j);
+          if (uu->is_Phi()) {
+            Node* be = uu->in(LoopNode::LoopBackControl);
+            while (be->is_Store() && old_new[be->_idx] != NULL) {
+              ShouldNotReachHere();
+              be = be->in(MemNode::Memory);
+            }
+            if (be == last || be == first->in(MemNode::Memory)) {
+              assert(phi == NULL, "only one phi");
+              phi = uu;
+            }
+          }
+        }
+#ifdef ASSERT
+        for (DUIterator_Fast jmax, j = fast_outs(jmax); j < jmax; j++) {
+          Node* uu = fast_out(j);
+          if (uu->is_Phi() && uu->bottom_type() == Type::MEMORY) {
+            if (uu->adr_type() == igvn->C->get_adr_type(igvn->C->get_alias_index(u->adr_type()))) {
+              assert(phi == uu, "what's that phi?");
+            } else if (uu->adr_type() == TypePtr::BOTTOM) {
+              Node* n = uu->in(LoopNode::LoopBackControl);
+              uint limit = igvn->C->live_nodes();
+              uint i = 0;
+              while (n != uu) {
+                i++;
+                assert(i < limit, "infinite loop");
+                if (n->is_Proj()) {
+                  n = n->in(0);
+                } else if (n->is_SafePoint() || n->is_MemBar()) {
+                  n = n->in(TypeFunc::Memory);
+                } else if (n->is_Phi()) {
+                  n = n->in(1);
+                } else if (n->is_MergeMem()) {
+                  n = n->as_MergeMem()->memory_at(igvn->C->get_alias_index(u->adr_type()));
+                } else if (n->is_Store() || n->is_LoadStore() || n->is_ClearArray()) {
+                  n = n->in(MemNode::Memory);
+                } else {
+                  n->dump();
+                  ShouldNotReachHere();
+                }
+              }
+            }
+          }
+        }
+#endif
+        if (phi == NULL) {
+          // If the an entire chains was sunk, the
+          // inner loop has no phi for that memory
+          // slice, create one for the outer loop
+          phi = PhiNode::make(this, first->in(MemNode::Memory), Type::MEMORY,
+                              igvn->C->get_adr_type(igvn->C->get_alias_index(u->adr_type())));
+          phi->set_req(LoopNode::LoopBackControl, last);
+          phi = igvn->transform(phi);
+          igvn->replace_input_of(first, MemNode::Memory, phi);
+        } else {
+          // Or fix the outer loop fix to include
+          // that chain of stores.
+          Node* be = phi->in(LoopNode::LoopBackControl);
+          while (be->is_Store() && old_new[be->_idx] != NULL) {
+            ShouldNotReachHere();
+            be = be->in(MemNode::Memory);
+          }
+          if (be == first->in(MemNode::Memory)) {
+            if (be == phi->in(LoopNode::LoopBackControl)) {
+              igvn->replace_input_of(phi, LoopNode::LoopBackControl, last);
+            } else {
+              igvn->replace_input_of(be, MemNode::Memory, last);
+            }
+          } else {
+#ifdef ASSERT
+            if (be == phi->in(LoopNode::LoopBackControl)) {
+              assert(phi->in(LoopNode::LoopBackControl) == last, "");
+            } else {
+              assert(be->in(MemNode::Memory) == last, "");
+            }
+#endif
+          }
+        }
+      }
+    }
+  }
+
+  if (iv_phi != NULL) {
+    // Now adjust the inner loop's exit condition
+    Node* limit = inner_cl->limit();
+    Node* sub = NULL;
+    if (stride > 0) {
+      sub = igvn->transform(new SubINode(limit, iv_phi));
+    } else {
+      sub = igvn->transform(new SubINode(iv_phi, limit));
+    }
+    Node* min = igvn->transform(new MinINode(sub, igvn->intcon(scaled_iters)));
+    Node* new_limit = NULL;
+    if (stride > 0) {
+      new_limit = igvn->transform(new AddINode(min, iv_phi));
+    } else {
+      new_limit = igvn->transform(new SubINode(iv_phi, min));
+    }
+    igvn->replace_input_of(inner_cle->cmp_node(), 2, new_limit);
+    Node* cmp = inner_cle->cmp_node()->clone();
+    Node* bol = inner_cle->in(CountedLoopEndNode::TestValue)->clone();
+    cmp->set_req(2, limit);
+    bol->set_req(1, igvn->transform(cmp));
+    igvn->replace_input_of(outer_loop_end(), 1, igvn->transform(bol));
+  } else {
+    assert(false, "should be able to adjust outer loop");
+    IfNode* outer_le = outer_loop_end();
+    Node* iff = igvn->transform(new IfNode(outer_le->in(0), outer_le->in(1), outer_le->_prob, outer_le->_fcnt));
+    igvn->replace_node(outer_le, iff);
+    inner_cl->clear_strip_mined();
+  }
+}
+
+const Type* OuterStripMinedLoopEndNode::Value(PhaseGVN* phase) const {
+  if (!in(0)) return Type::TOP;
+  if (phase->type(in(0)) == Type::TOP)
+    return Type::TOP;
+
+  return TypeTuple::IFBOTH;
+}
+
+Node *OuterStripMinedLoopEndNode::Ideal(PhaseGVN *phase, bool can_reshape) {
+  if (remove_dead_region(phase, can_reshape))  return this;
+
+  return NULL;
+}
 
 //------------------------------filtered_type--------------------------------
 // Return a type based on condition control flow
@@ -1778,10 +2305,11 @@ void IdealLoopTree::counted_loop( PhaseIdealLoop *phase ) {
     if (_head->is_Loop()) _head->as_Loop()->set_inner_loop();
   }
 
+  IdealLoopTree* loop = this;
   if (_head->is_CountedLoop() ||
-      phase->is_counted_loop(_head, this)) {
+      phase->is_counted_loop(_head, loop)) {
 
-    if (!UseCountedLoopSafepoints) {
+    if (LoopStripMiningIter == 0 || (LoopStripMiningIter > 1 && _child == NULL)) {
       // Indicate we do not need a safepoint here
       _has_sfpt = 1;
     }
@@ -1800,8 +2328,10 @@ void IdealLoopTree::counted_loop( PhaseIdealLoop *phase ) {
   }
 
   // Recursively
-  if (_child) _child->counted_loop( phase );
-  if (_next)  _next ->counted_loop( phase );
+  assert(loop->_child != this || (loop->_head->as_Loop()->is_OuterStripMinedLoop() && _head->as_CountedLoop()->is_strip_mined()), "what kind of loop was added?");
+  assert(loop->_child != this || (loop->_child->_child == NULL && loop->_child->_next == NULL), "would miss some loops");
+  if (loop->_child && loop->_child != this) loop->_child->counted_loop(phase);
+  if (loop->_next)  loop->_next ->counted_loop(phase);
 }
 
 #ifndef PRODUCT
@@ -1812,7 +2342,7 @@ void IdealLoopTree::dump_head( ) const {
     tty->print("  ");
   tty->print("Loop: N%d/N%d ",_head->_idx,_tail->_idx);
   if (_irreducible) tty->print(" IRREDUCIBLE");
-  Node* entry = _head->in(LoopNode::EntryControl);
+  Node* entry = _head->as_Loop()->skip_strip_mined(-1)->in(LoopNode::EntryControl);
   Node* predicate = PhaseIdealLoop::find_predicate_insertion_point(entry, Deoptimization::Reason_loop_limit_check);
   if (predicate != NULL ) {
     tty->print(" limit_check");
@@ -1863,6 +2393,9 @@ void IdealLoopTree::dump_head( ) const {
   if (Verbose) {
     tty->print(" body={"); _body.dump_simple(); tty->print(" }");
   }
+  if (_head->as_Loop()->is_strip_mined()) {
+    tty->print(" strip_mined");
+  }
   tty->cr();
 }
 
@@ -3232,7 +3765,7 @@ bool PhaseIdealLoop::is_canonical_loop_entry(CountedLoopNode* cl) {
   if (!cl->is_main_loop() && !cl->is_post_loop()) {
     return false;
   }
-  Node* ctrl = cl->in(LoopNode::EntryControl);
+  Node* ctrl = cl->skip_strip_mined()->in(LoopNode::EntryControl);
   if (ctrl == NULL || (!ctrl->is_IfTrue() && !ctrl->is_IfFalse())) {
     return false;
   }
@@ -3292,7 +3825,7 @@ Node *PhaseIdealLoop::get_late_ctrl( Node *n, Node *early ) {
     }
     while(worklist.size() != 0 && LCA != early) {
       Node* s = worklist.pop();
-      if (s->is_Load()) {
+      if (s->is_Load() || s->Opcode() == Op_SafePoint) {
         continue;
       } else if (s->is_MergeMem()) {
         for (DUIterator_Fast imax, i = s->fast_outs(imax); i < imax; i++) {
@@ -3471,6 +4004,38 @@ void PhaseIdealLoop::build_loop_late( VectorSet &visited, Node_List &worklist, N
   }
 }
 
+// Verify that no data node is schedules in the outer loop of a strip
+// mined loop.
+void PhaseIdealLoop::verify_strip_mined_scheduling(Node *n, Node* least) {
+#ifdef ASSERT
+  if (get_loop(least)->_nest == 0) {
+    return;
+  }
+  IdealLoopTree* loop = get_loop(least);
+  Node* head = loop->_head;
+  if (head->is_OuterStripMinedLoop()) {
+    Node* sfpt = head->as_Loop()->outer_safepoint();
+    ResourceMark rm;
+    Unique_Node_List wq;
+    wq.push(sfpt);
+    for (uint i = 0; i < wq.size(); i++) {
+      Node *m = wq.at(i);
+      for (uint i = 1; i < m->req(); i++) {
+        Node* nn = m->in(i);
+        if (nn == n) {
+          return;
+        }
+        if (nn != NULL && has_ctrl(nn) && get_loop(get_ctrl(nn)) == loop) {
+          wq.push(nn);
+        }
+      }
+    }
+    ShouldNotReachHere();
+  }
+#endif
+}
+
+
 //------------------------------build_loop_late_post---------------------------
 // Put Data nodes into some loop nest, by setting the _nodes[]->loop mapping.
 // Second pass finds latest legal placement, and ideal loop placement.
@@ -3580,8 +4145,9 @@ void PhaseIdealLoop::build_loop_late_post( Node *n ) {
   // which can inhibit range check elimination.
   if (least != early) {
     Node* ctrl_out = least->unique_ctrl_out();
-    if (ctrl_out && ctrl_out->is_CountedLoop() &&
-        least == ctrl_out->in(LoopNode::EntryControl)) {
+    if (ctrl_out && ctrl_out->is_Loop() &&
+        least == ctrl_out->in(LoopNode::EntryControl) &&
+        (ctrl_out->is_CountedLoop() || ctrl_out->is_OuterStripMinedLoop())) {
       Node* least_dom = idom(least);
       if (get_loop(least_dom)->is_member(get_loop(least))) {
         least = least_dom;
@@ -3606,6 +4172,7 @@ void PhaseIdealLoop::build_loop_late_post( Node *n ) {
 
   // Assign discovered "here or above" point
   least = find_non_split_ctrl(least);
+  verify_strip_mined_scheduling(n, least);
   set_ctrl(n, least);
 
   // Collect inner loop bodies
diff --git a/src/hotspot/share/opto/loopnode.hpp b/src/hotspot/share/opto/loopnode.hpp
index 70168186cca..7e615de7e29 100644
--- a/src/hotspot/share/opto/loopnode.hpp
+++ b/src/hotspot/share/opto/loopnode.hpp
@@ -37,6 +37,7 @@ class CountedLoopNode;
 class IdealLoopTree;
 class LoopNode;
 class Node;
+class OuterStripMinedLoopEndNode;
 class PhaseIdealLoop;
 class CountedLoopReserveKit;
 class VectorSet;
@@ -71,7 +72,8 @@ protected:
          VectorizedLoop=2048,
          HasAtomicPostLoop=4096,
          HasRangeChecks=8192,
-         IsMultiversioned=16384};
+         IsMultiversioned=16384,
+         StripMined=32768};
   char _unswitch_count;
   enum { _unswitch_max=3 };
   char _postloop_flags;
@@ -90,6 +92,7 @@ public:
   int is_partial_peel_loop() const { return _loop_flags & PartialPeelLoop; }
   void set_partial_peel_loop() { _loop_flags |= PartialPeelLoop; }
   int partial_peel_has_failed() const { return _loop_flags & PartialPeelFailed; }
+  int is_strip_mined() const { return _loop_flags & StripMined; }
 
   void mark_partial_peel_failed() { _loop_flags |= PartialPeelFailed; }
   void mark_has_reductions() { _loop_flags |= HasReductions; }
@@ -100,6 +103,8 @@ public:
   void mark_has_atomic_post_loop() { _loop_flags |= HasAtomicPostLoop; }
   void mark_has_range_checks() { _loop_flags |=  HasRangeChecks; }
   void mark_is_multiversioned() { _loop_flags |= IsMultiversioned; }
+  void mark_strip_mined() { _loop_flags |= StripMined; }
+  void clear_strip_mined() { _loop_flags &= ~StripMined; }
 
   int unswitch_max() { return _unswitch_max; }
   int unswitch_count() { return _unswitch_count; }
@@ -131,6 +136,13 @@ public:
 #ifndef PRODUCT
   virtual void dump_spec(outputStream *st) const;
 #endif
+
+  void verify_strip_mined(int expect_skeleton) const;
+  virtual LoopNode* skip_strip_mined(int expect_opaq = 1) { return this; }
+  virtual IfTrueNode* outer_loop_tail() const { ShouldNotReachHere(); return NULL; }
+  virtual OuterStripMinedLoopEndNode* outer_loop_end() const { ShouldNotReachHere(); return NULL; }
+  virtual IfFalseNode* outer_loop_exit() const { ShouldNotReachHere(); return NULL; }
+  virtual SafePointNode* outer_safepoint() const { ShouldNotReachHere(); return NULL; }
 };
 
 //------------------------------Counted Loops----------------------------------
@@ -278,6 +290,13 @@ public:
   void set_slp_max_unroll(int unroll_factor) { _slp_maximum_unroll_factor = unroll_factor; }
   int  slp_max_unroll() const                { return _slp_maximum_unroll_factor; }
 
+  virtual LoopNode* skip_strip_mined(int expect_opaq = 1);
+  OuterStripMinedLoopNode* outer_loop() const;
+  virtual IfTrueNode* outer_loop_tail() const;
+  virtual OuterStripMinedLoopEndNode* outer_loop_end() const;
+  virtual IfFalseNode* outer_loop_exit() const;
+  virtual SafePointNode* outer_safepoint() const;
+
 #ifndef PRODUCT
   virtual void dump_spec(outputStream *st) const;
 #endif
@@ -374,6 +393,40 @@ class LoopLimitNode : public Node {
   virtual Node* Identity(PhaseGVN* phase);
 };
 
+// Support for strip mining
+class OuterStripMinedLoopNode : public LoopNode {
+private:
+  CountedLoopNode* inner_loop() const;
+public:
+  OuterStripMinedLoopNode(Compile* C, Node *entry, Node *backedge)
+    : LoopNode(entry, backedge) {
+    init_class_id(Class_OuterStripMinedLoop);
+    init_flags(Flag_is_macro);
+    C->add_macro_node(this);
+  }
+
+  virtual int Opcode() const;
+
+  virtual IfTrueNode* outer_loop_tail() const;
+  virtual OuterStripMinedLoopEndNode* outer_loop_end() const;
+  virtual IfFalseNode* outer_loop_exit() const;
+  virtual SafePointNode* outer_safepoint() const;
+  void adjust_strip_mined_loop(PhaseIterGVN* igvn);
+};
+
+class OuterStripMinedLoopEndNode : public IfNode {
+public:
+  OuterStripMinedLoopEndNode(Node *control, Node *test, float prob, float cnt)
+    : IfNode(control, test, prob, cnt) {
+    init_class_id(Class_OuterStripMinedLoopEnd);
+  }
+
+  virtual int Opcode() const;
+
+  virtual const Type* Value(PhaseGVN* phase) const;
+  virtual Node *Ideal(PhaseGVN *phase, bool can_reshape);
+};
+
 // -----------------------------IdealLoopTree----------------------------------
 class IdealLoopTree : public ResourceObj {
 public:
@@ -780,6 +833,7 @@ private:
   void build_loop_early( VectorSet &visited, Node_List &worklist, Node_Stack &nstack );
   void build_loop_late ( VectorSet &visited, Node_List &worklist, Node_Stack &nstack );
   void build_loop_late_post ( Node* n );
+  void verify_strip_mined_scheduling(Node *n, Node* least);
 
   // Array of immediate dominance info for each CFG node indexed by node idx
 private:
@@ -877,7 +931,10 @@ public:
   // Per-Node transform
   virtual Node *transform( Node *a_node ) { return 0; }
 
-  bool is_counted_loop( Node *x, IdealLoopTree *loop );
+  bool is_counted_loop(Node* x, IdealLoopTree*& loop);
+  IdealLoopTree* create_outer_strip_mined_loop(BoolNode *test, Node *cmp, Node *init_control,
+                                               IdealLoopTree* loop, float cl_prob, float le_fcnt,
+                                               Node*& entry_control, Node*& iffalse);
 
   Node* exact_limit( IdealLoopTree *loop );
 
@@ -908,8 +965,24 @@ public:
   //   When nonnull, the clone and original are side-by-side, both are
   //      dominated by the passed in side_by_side_idom node.  Used in
   //      construction of unswitched loops.
+  enum CloneLoopMode {
+    IgnoreStripMined = 0,        // Only clone inner strip mined loop
+    CloneIncludesStripMined = 1, // clone both inner and outer strip mined loops
+    ControlAroundStripMined = 2  // Only clone inner strip mined loop,
+                                 // result control flow branches
+                                 // either to inner clone or outer
+                                 // strip mined loop.
+  };
   void clone_loop( IdealLoopTree *loop, Node_List &old_new, int dom_depth,
-                   Node* side_by_side_idom = NULL);
+                  CloneLoopMode mode, Node* side_by_side_idom = NULL);
+  void clone_loop_handle_data_uses(Node* old, Node_List &old_new,
+                                   IdealLoopTree* loop, IdealLoopTree* companion_loop,
+                                   Node_List*& split_if_set, Node_List*& split_bool_set,
+                                   Node_List*& split_cex_set, Node_List& worklist,
+                                   uint new_counter, CloneLoopMode mode);
+  void clone_outer_loop(LoopNode* head, CloneLoopMode mode, IdealLoopTree *loop,
+                        IdealLoopTree* outer_loop, int dd, Node_List &old_new,
+                        Node_List& extra_data_nodes);
 
   // If we got the effect of peeling, either by actually peeling or by
   // making a pre-loop which must execute at least once, we can remove
@@ -1020,7 +1093,8 @@ public:
   // and inserting an if to select fast-slow versions.
   ProjNode* create_slow_version_of_loop(IdealLoopTree *loop,
                                         Node_List &old_new,
-                                        int opcode);
+                                        int opcode,
+                                        CloneLoopMode mode);
 
   // Clone a loop and return the clone head (clone_loop_head).
   // Added nodes include int(1), int(0) - disconnected, If, IfTrue, IfFalse,
@@ -1098,7 +1172,7 @@ public:
   // "Nearly" because all Nodes have been cloned from the original in the loop,
   // but the fall-in edges to the Cmp are different.  Clone bool/Cmp pairs
   // through the Phi recursively, and return a Bool.
-  BoolNode *clone_iff( PhiNode *phi, IdealLoopTree *loop );
+  Node *clone_iff( PhiNode *phi, IdealLoopTree *loop );
   CmpNode *clone_bool( PhiNode *phi, IdealLoopTree *loop );
 
 
diff --git a/src/hotspot/share/opto/loopopts.cpp b/src/hotspot/share/opto/loopopts.cpp
index 45acc2fcae2..d6ddc5723fe 100644
--- a/src/hotspot/share/opto/loopopts.cpp
+++ b/src/hotspot/share/opto/loopopts.cpp
@@ -26,6 +26,7 @@
 #include "memory/allocation.inline.hpp"
 #include "memory/resourceArea.hpp"
 #include "opto/addnode.hpp"
+#include "opto/callnode.hpp"
 #include "opto/castnode.hpp"
 #include "opto/connode.hpp"
 #include "opto/castnode.hpp"
@@ -306,7 +307,12 @@ Node *PhaseIdealLoop::has_local_phi_input( Node *n ) {
           get_ctrl(m->in(2)) != n_ctrl &&
           get_ctrl(m->in(3)) != n_ctrl) {
         // Move the AddP up to dominating point
-        set_ctrl_and_loop(m, find_non_split_ctrl(idom(n_ctrl)));
+        Node* c = find_non_split_ctrl(idom(n_ctrl));
+        if (c->is_OuterStripMinedLoop()) {
+          c->as_Loop()->verify_strip_mined(1);
+          c = c->in(LoopNode::EntryControl);
+        }
+        set_ctrl_and_loop(m, c);
         continue;
       }
       return NULL;
@@ -750,14 +756,13 @@ Node* PhaseIdealLoop::try_move_store_before_loop(Node* n, Node *n_ctrl) {
       if (ctrl_ok) {
         // move the Store
         _igvn.replace_input_of(mem, LoopNode::LoopBackControl, mem);
-        _igvn.replace_input_of(n, 0, n_loop->_head->in(LoopNode::EntryControl));
+        _igvn.replace_input_of(n, 0, n_loop->_head->as_Loop()->skip_strip_mined()->in(LoopNode::EntryControl));
         _igvn.replace_input_of(n, MemNode::Memory, mem->in(LoopNode::EntryControl));
         // Disconnect the phi now. An empty phi can confuse other
         // optimizations in this pass of loop opts.
         _igvn.replace_node(mem, mem->in(LoopNode::EntryControl));
         n_loop->_body.yank(mem);
 
-        IdealLoopTree* new_loop = get_loop(n->in(0));
         set_ctrl_and_loop(n, n->in(0));
 
         return n;
@@ -840,6 +845,16 @@ void PhaseIdealLoop::try_move_store_after_loop(Node* n) {
               _igvn.replace_node(hook, n);
               return;
             }
+#ifdef ASSERT
+            if (n_loop->_head->is_Loop() && n_loop->_head->as_Loop()->is_strip_mined()) {
+              assert(n_loop->_head->Opcode() == Op_CountedLoop, "outer loop is a strip mined");
+              n_loop->_head->as_Loop()->verify_strip_mined(1);
+              Node* outer = n_loop->_head->as_CountedLoop()->outer_loop();
+              IdealLoopTree* outer_loop = get_loop(outer);
+              assert(n_loop->_parent == outer_loop, "broken loop tree");
+              assert(get_loop(lca) == outer_loop, "safepoint in outer loop consume all memory state");
+            }
+#endif
 
             // Move store out of the loop
             _igvn.replace_node(hook, n->in(MemNode::Memory));
@@ -1016,7 +1031,7 @@ Node *PhaseIdealLoop::place_near_use( Node *useblock ) const {
   IdealLoopTree *u_loop = get_loop( useblock );
   return (u_loop->_irreducible || u_loop->_child)
     ? useblock
-    : u_loop->_head->in(LoopNode::EntryControl);
+    : u_loop->_head->as_Loop()->skip_strip_mined()->in(LoopNode::EntryControl);
 }
 
 
@@ -1407,47 +1422,56 @@ void PhaseIdealLoop::split_if_with_blocks( VectorSet &visited, Node_Stack &nstac
 // "Nearly" because all Nodes have been cloned from the original in the loop,
 // but the fall-in edges to the Cmp are different.  Clone bool/Cmp pairs
 // through the Phi recursively, and return a Bool.
-BoolNode *PhaseIdealLoop::clone_iff( PhiNode *phi, IdealLoopTree *loop ) {
+Node* PhaseIdealLoop::clone_iff(PhiNode *phi, IdealLoopTree *loop) {
 
   // Convert this Phi into a Phi merging Bools
   uint i;
-  for( i = 1; i < phi->req(); i++ ) {
+  for (i = 1; i < phi->req(); i++) {
     Node *b = phi->in(i);
-    if( b->is_Phi() ) {
-      _igvn.replace_input_of(phi, i, clone_iff( b->as_Phi(), loop ));
+    if (b->is_Phi()) {
+      _igvn.replace_input_of(phi, i, clone_iff(b->as_Phi(), loop));
     } else {
-      assert( b->is_Bool(), "" );
+      assert(b->is_Bool() || b->Opcode() == Op_Opaque4, "");
     }
   }
 
-  Node *sample_bool = phi->in(1);
-  Node *sample_cmp  = sample_bool->in(1);
+  Node* n = phi->in(1);
+  Node* sample_opaque = NULL;
+  Node *sample_bool = NULL;
+  if (n->Opcode() == Op_Opaque4) {
+    sample_opaque = n;
+    sample_bool = n->in(1);
+    assert(sample_bool->is_Bool(), "wrong type");
+  } else {
+    sample_bool = n;
+  }
+  Node *sample_cmp = sample_bool->in(1);
 
   // Make Phis to merge the Cmp's inputs.
-  PhiNode *phi1 = new PhiNode( phi->in(0), Type::TOP );
-  PhiNode *phi2 = new PhiNode( phi->in(0), Type::TOP );
-  for( i = 1; i < phi->req(); i++ ) {
-    Node *n1 = phi->in(i)->in(1)->in(1);
-    Node *n2 = phi->in(i)->in(1)->in(2);
-    phi1->set_req( i, n1 );
-    phi2->set_req( i, n2 );
-    phi1->set_type( phi1->type()->meet_speculative(n1->bottom_type()));
-    phi2->set_type( phi2->type()->meet_speculative(n2->bottom_type()));
+  PhiNode *phi1 = new PhiNode(phi->in(0), Type::TOP);
+  PhiNode *phi2 = new PhiNode(phi->in(0), Type::TOP);
+  for (i = 1; i < phi->req(); i++) {
+    Node *n1 = sample_opaque == NULL ? phi->in(i)->in(1)->in(1) : phi->in(i)->in(1)->in(1)->in(1);
+    Node *n2 = sample_opaque == NULL ? phi->in(i)->in(1)->in(2) : phi->in(i)->in(1)->in(1)->in(2);
+    phi1->set_req(i, n1);
+    phi2->set_req(i, n2);
+    phi1->set_type(phi1->type()->meet_speculative(n1->bottom_type()));
+    phi2->set_type(phi2->type()->meet_speculative(n2->bottom_type()));
   }
   // See if these Phis have been made before.
   // Register with optimizer
   Node *hit1 = _igvn.hash_find_insert(phi1);
-  if( hit1 ) {                  // Hit, toss just made Phi
+  if (hit1) {                   // Hit, toss just made Phi
     _igvn.remove_dead_node(phi1); // Remove new phi
-    assert( hit1->is_Phi(), "" );
+    assert(hit1->is_Phi(), "" );
     phi1 = (PhiNode*)hit1;      // Use existing phi
   } else {                      // Miss
     _igvn.register_new_node_with_optimizer(phi1);
   }
   Node *hit2 = _igvn.hash_find_insert(phi2);
-  if( hit2 ) {                  // Hit, toss just made Phi
+  if (hit2) {                   // Hit, toss just made Phi
     _igvn.remove_dead_node(phi2); // Remove new phi
-    assert( hit2->is_Phi(), "" );
+    assert(hit2->is_Phi(), "" );
     phi2 = (PhiNode*)hit2;      // Use existing phi
   } else {                      // Miss
     _igvn.register_new_node_with_optimizer(phi2);
@@ -1457,8 +1481,8 @@ BoolNode *PhaseIdealLoop::clone_iff( PhiNode *phi, IdealLoopTree *loop ) {
   set_ctrl(phi2, phi->in(0));
   // Make a new Cmp
   Node *cmp = sample_cmp->clone();
-  cmp->set_req( 1, phi1 );
-  cmp->set_req( 2, phi2 );
+  cmp->set_req(1, phi1);
+  cmp->set_req(2, phi2);
   _igvn.register_new_node_with_optimizer(cmp);
   set_ctrl(cmp, phi->in(0));
 
@@ -1468,8 +1492,16 @@ BoolNode *PhaseIdealLoop::clone_iff( PhiNode *phi, IdealLoopTree *loop ) {
   _igvn.register_new_node_with_optimizer(b);
   set_ctrl(b, phi->in(0));
 
-  assert( b->is_Bool(), "" );
-  return (BoolNode*)b;
+  if (sample_opaque != NULL) {
+    Node* opaque = sample_opaque->clone();
+    opaque->set_req(1, b);
+    _igvn.register_new_node_with_optimizer(opaque);
+    set_ctrl(opaque, phi->in(0));
+    return opaque;
+  }
+
+  assert(b->is_Bool(), "");
+  return b;
 }
 
 //------------------------------clone_bool-------------------------------------
@@ -1552,6 +1584,252 @@ void PhaseIdealLoop::sink_use( Node *use, Node *post_loop ) {
   }
 }
 
+void PhaseIdealLoop::clone_loop_handle_data_uses(Node* old, Node_List &old_new,
+                                                 IdealLoopTree* loop, IdealLoopTree* outer_loop,
+                                                 Node_List*& split_if_set, Node_List*& split_bool_set,
+                                                 Node_List*& split_cex_set, Node_List& worklist,
+                                                 uint new_counter, CloneLoopMode mode) {
+  Node* nnn = old_new[old->_idx];
+  // Copy uses to a worklist, so I can munge the def-use info
+  // with impunity.
+  for (DUIterator_Fast jmax, j = old->fast_outs(jmax); j < jmax; j++)
+    worklist.push(old->fast_out(j));
+
+  while( worklist.size() ) {
+    Node *use = worklist.pop();
+    if (!has_node(use))  continue; // Ignore dead nodes
+    if (use->in(0) == C->top())  continue;
+    IdealLoopTree *use_loop = get_loop( has_ctrl(use) ? get_ctrl(use) : use );
+    // Check for data-use outside of loop - at least one of OLD or USE
+    // must not be a CFG node.
+#ifdef ASSERT
+    if (loop->_head->as_Loop()->is_strip_mined() && outer_loop->is_member(use_loop) && !loop->is_member(use_loop) && old_new[use->_idx] == NULL) {
+      Node* sfpt = loop->_head->as_CountedLoop()->outer_safepoint();
+      assert(mode == ControlAroundStripMined && use == sfpt, "missed a node");
+    }
+#endif
+    if (!loop->is_member(use_loop) && !outer_loop->is_member(use_loop) && (!old->is_CFG() || !use->is_CFG())) {
+
+      // If the Data use is an IF, that means we have an IF outside of the
+      // loop that is switching on a condition that is set inside of the
+      // loop.  Happens if people set a loop-exit flag; then test the flag
+      // in the loop to break the loop, then test is again outside of the
+      // loop to determine which way the loop exited.
+      // Loop predicate If node connects to Bool node through Opaque1 node.
+      if (use->is_If() || use->is_CMove() || C->is_predicate_opaq(use) || use->Opcode() == Op_Opaque4) {
+        // Since this code is highly unlikely, we lazily build the worklist
+        // of such Nodes to go split.
+        if (!split_if_set) {
+          ResourceArea *area = Thread::current()->resource_area();
+          split_if_set = new Node_List(area);
+        }
+        split_if_set->push(use);
+      }
+      if (use->is_Bool()) {
+        if (!split_bool_set) {
+          ResourceArea *area = Thread::current()->resource_area();
+          split_bool_set = new Node_List(area);
+        }
+        split_bool_set->push(use);
+      }
+      if (use->Opcode() == Op_CreateEx) {
+        if (!split_cex_set) {
+          ResourceArea *area = Thread::current()->resource_area();
+          split_cex_set = new Node_List(area);
+        }
+        split_cex_set->push(use);
+      }
+
+
+      // Get "block" use is in
+      uint idx = 0;
+      while( use->in(idx) != old ) idx++;
+      Node *prev = use->is_CFG() ? use : get_ctrl(use);
+      assert(!loop->is_member(get_loop(prev)) && !outer_loop->is_member(get_loop(prev)), "" );
+      Node *cfg = prev->_idx >= new_counter
+        ? prev->in(2)
+        : idom(prev);
+      if( use->is_Phi() )     // Phi use is in prior block
+        cfg = prev->in(idx);  // NOT in block of Phi itself
+      if (cfg->is_top()) {    // Use is dead?
+        _igvn.replace_input_of(use, idx, C->top());
+        continue;
+      }
+
+      while(!outer_loop->is_member(get_loop(cfg))) {
+        prev = cfg;
+        cfg = cfg->_idx >= new_counter ? cfg->in(2) : idom(cfg);
+      }
+      // If the use occurs after merging several exits from the loop, then
+      // old value must have dominated all those exits.  Since the same old
+      // value was used on all those exits we did not need a Phi at this
+      // merge point.  NOW we do need a Phi here.  Each loop exit value
+      // is now merged with the peeled body exit; each exit gets its own
+      // private Phi and those Phis need to be merged here.
+      Node *phi;
+      if( prev->is_Region() ) {
+        if( idx == 0 ) {      // Updating control edge?
+          phi = prev;         // Just use existing control
+        } else {              // Else need a new Phi
+          phi = PhiNode::make( prev, old );
+          // Now recursively fix up the new uses of old!
+          for( uint i = 1; i < prev->req(); i++ ) {
+            worklist.push(phi); // Onto worklist once for each 'old' input
+          }
+        }
+      } else {
+        // Get new RegionNode merging old and new loop exits
+        prev = old_new[prev->_idx];
+        assert( prev, "just made this in step 7" );
+        if( idx == 0) {      // Updating control edge?
+          phi = prev;         // Just use existing control
+        } else {              // Else need a new Phi
+          // Make a new Phi merging data values properly
+          phi = PhiNode::make( prev, old );
+          phi->set_req( 1, nnn );
+        }
+      }
+      // If inserting a new Phi, check for prior hits
+      if( idx != 0 ) {
+        Node *hit = _igvn.hash_find_insert(phi);
+        if( hit == NULL ) {
+          _igvn.register_new_node_with_optimizer(phi); // Register new phi
+        } else {                                      // or
+          // Remove the new phi from the graph and use the hit
+          _igvn.remove_dead_node(phi);
+          phi = hit;                                  // Use existing phi
+        }
+        set_ctrl(phi, prev);
+      }
+      // Make 'use' use the Phi instead of the old loop body exit value
+      _igvn.replace_input_of(use, idx, phi);
+      if( use->_idx >= new_counter ) { // If updating new phis
+        // Not needed for correctness, but prevents a weak assert
+        // in AddPNode from tripping (when we end up with different
+        // base & derived Phis that will become the same after
+        // IGVN does CSE).
+        Node *hit = _igvn.hash_find_insert(use);
+        if( hit )             // Go ahead and re-hash for hits.
+          _igvn.replace_node( use, hit );
+      }
+
+      // If 'use' was in the loop-exit block, it now needs to be sunk
+      // below the post-loop merge point.
+      sink_use( use, prev );
+    }
+  }
+}
+
+void PhaseIdealLoop::clone_outer_loop(LoopNode* head, CloneLoopMode mode, IdealLoopTree *loop,
+                                      IdealLoopTree* outer_loop, int dd, Node_List &old_new,
+                                      Node_List& extra_data_nodes) {
+  if (head->is_strip_mined() && mode != IgnoreStripMined) {
+    CountedLoopNode* cl = head->as_CountedLoop();
+    Node* l = cl->outer_loop();
+    Node* tail = cl->outer_loop_tail();
+    IfNode* le = cl->outer_loop_end();
+    Node* sfpt = cl->outer_safepoint();
+    CountedLoopEndNode* cle = cl->loopexit();
+    CountedLoopNode* new_cl = old_new[cl->_idx]->as_CountedLoop();
+    CountedLoopEndNode* new_cle = new_cl->as_CountedLoop()->loopexit();
+    Node* cle_out = cle->proj_out(false);
+
+    Node* new_sfpt = NULL;
+    Node* new_cle_out = cle_out->clone();
+    old_new.map(cle_out->_idx, new_cle_out);
+    if (mode == CloneIncludesStripMined) {
+      // clone outer loop body
+      Node* new_l = l->clone();
+      Node* new_tail = tail->clone();
+      IfNode* new_le = le->clone()->as_If();
+      new_sfpt = sfpt->clone();
+
+      set_loop(new_l, outer_loop->_parent);
+      set_idom(new_l, new_l->in(LoopNode::EntryControl), dd);
+      set_loop(new_cle_out, outer_loop->_parent);
+      set_idom(new_cle_out, new_cle, dd);
+      set_loop(new_sfpt, outer_loop->_parent);
+      set_idom(new_sfpt, new_cle_out, dd);
+      set_loop(new_le, outer_loop->_parent);
+      set_idom(new_le, new_sfpt, dd);
+      set_loop(new_tail, outer_loop->_parent);
+      set_idom(new_tail, new_le, dd);
+      set_idom(new_cl, new_l, dd);
+
+      old_new.map(l->_idx, new_l);
+      old_new.map(tail->_idx, new_tail);
+      old_new.map(le->_idx, new_le);
+      old_new.map(sfpt->_idx, new_sfpt);
+
+      new_l->set_req(LoopNode::LoopBackControl, new_tail);
+      new_l->set_req(0, new_l);
+      new_tail->set_req(0, new_le);
+      new_le->set_req(0, new_sfpt);
+      new_sfpt->set_req(0, new_cle_out);
+      new_cle_out->set_req(0, new_cle);
+      new_cl->set_req(LoopNode::EntryControl, new_l);
+
+      _igvn.register_new_node_with_optimizer(new_l);
+      _igvn.register_new_node_with_optimizer(new_tail);
+      _igvn.register_new_node_with_optimizer(new_le);
+    } else {
+      Node *newhead = old_new[loop->_head->_idx];
+      newhead->as_Loop()->clear_strip_mined();
+      _igvn.replace_input_of(newhead, LoopNode::EntryControl, newhead->in(LoopNode::EntryControl)->in(LoopNode::EntryControl));
+      set_idom(newhead, newhead->in(LoopNode::EntryControl), dd);
+    }
+    // Look at data node that were assigned a control in the outer
+    // loop: they are kept in the outer loop by the safepoint so start
+    // from the safepoint node's inputs.
+    IdealLoopTree* outer_loop = get_loop(l);
+    Node_Stack stack(2);
+    stack.push(sfpt, 1);
+    uint new_counter = C->unique();
+    while (stack.size() > 0) {
+      Node* n = stack.node();
+      uint i = stack.index();
+      while (i < n->req() &&
+             (n->in(i) == NULL ||
+              !has_ctrl(n->in(i)) ||
+              get_loop(get_ctrl(n->in(i))) != outer_loop ||
+              (old_new[n->in(i)->_idx] != NULL && old_new[n->in(i)->_idx]->_idx >= new_counter))) {
+        i++;
+      }
+      if (i < n->req()) {
+        stack.set_index(i+1);
+        stack.push(n->in(i), 0);
+      } else {
+        assert(old_new[n->_idx] == NULL || n == sfpt || old_new[n->_idx]->_idx < new_counter, "no clone yet");
+        Node* m = n == sfpt ? new_sfpt : n->clone();
+        if (m != NULL) {
+          for (uint i = 0; i < n->req(); i++) {
+            if (m->in(i) != NULL && old_new[m->in(i)->_idx] != NULL) {
+              m->set_req(i, old_new[m->in(i)->_idx]);
+            }
+          }
+        } else {
+          assert(n == sfpt && mode != CloneIncludesStripMined, "where's the safepoint clone?");
+        }
+        if (n != sfpt) {
+          extra_data_nodes.push(n);
+          _igvn.register_new_node_with_optimizer(m);
+          assert(get_ctrl(n) == cle_out, "what other control?");
+          set_ctrl(m, new_cle_out);
+          old_new.map(n->_idx, m);
+        }
+        stack.pop();
+      }
+    }
+    if (mode == CloneIncludesStripMined) {
+      _igvn.register_new_node_with_optimizer(new_sfpt);
+      _igvn.register_new_node_with_optimizer(new_cle_out);
+    }
+  } else {
+    Node *newhead = old_new[loop->_head->_idx];
+    set_idom(newhead, newhead->in(LoopNode::EntryControl), dd);
+  }
+}
+
 //------------------------------clone_loop-------------------------------------
 //
 //                   C L O N E   A   L O O P   B O D Y
@@ -1580,7 +1858,10 @@ void PhaseIdealLoop::sink_use( Node *use, Node *post_loop ) {
 //      dominated by the side_by_side_idom node.  Used in construction of
 //      unswitched loops.
 void PhaseIdealLoop::clone_loop( IdealLoopTree *loop, Node_List &old_new, int dd,
-                                 Node* side_by_side_idom) {
+                                CloneLoopMode mode, Node* side_by_side_idom) {
+
+  LoopNode* head = loop->_head->as_Loop();
+  head->verify_strip_mined(1);
 
   if (C->do_vector_loop() && PrintOpto) {
     const char* mname = C->method()->name()->as_quoted_ascii();
@@ -1613,6 +1894,7 @@ void PhaseIdealLoop::clone_loop( IdealLoopTree *loop, Node_List &old_new, int dd
     _igvn.register_new_node_with_optimizer(nnn);
   }
 
+  IdealLoopTree* outer_loop = (head->is_strip_mined() && mode != IgnoreStripMined) ? get_loop(head->as_CountedLoop()->outer_loop()) : loop;
 
   // Step 2: Fix the edges in the new body.  If the old input is outside the
   // loop use it.  If the old input is INside the loop, use the corresponding
@@ -1624,7 +1906,7 @@ void PhaseIdealLoop::clone_loop( IdealLoopTree *loop, Node_List &old_new, int dd
     if (has_ctrl(old)) {
       set_ctrl(nnn, old_new[get_ctrl(old)->_idx]);
     } else {
-      set_loop(nnn, loop->_parent);
+      set_loop(nnn, outer_loop->_parent);
       if (old->outcnt() > 0) {
         set_idom( nnn, old_new[idom(old)->_idx], dd );
       }
@@ -1640,22 +1922,21 @@ void PhaseIdealLoop::clone_loop( IdealLoopTree *loop, Node_List &old_new, int dd
     }
     _igvn.hash_find_insert(nnn);
   }
-  Node *newhead = old_new[loop->_head->_idx];
-  set_idom(newhead, newhead->in(LoopNode::EntryControl), dd);
 
+  ResourceArea *area = Thread::current()->resource_area();
+  Node_List extra_data_nodes(area);
+  clone_outer_loop(head, mode, loop, outer_loop, dd, old_new, extra_data_nodes);
 
   // Step 3: Now fix control uses.  Loop varying control uses have already
   // been fixed up (as part of all input edges in Step 2).  Loop invariant
   // control uses must be either an IfFalse or an IfTrue.  Make a merge
   // point to merge the old and new IfFalse/IfTrue nodes; make the use
   // refer to this.
-  ResourceArea *area = Thread::current()->resource_area();
   Node_List worklist(area);
   uint new_counter = C->unique();
   for( i = 0; i < loop->_body.size(); i++ ) {
     Node* old = loop->_body.at(i);
     if( !old->is_CFG() ) continue;
-    Node* nnn = old_new[old->_idx];
 
     // Copy uses to a worklist, so I can munge the def-use info
     // with impunity.
@@ -1669,9 +1950,29 @@ void PhaseIdealLoop::clone_loop( IdealLoopTree *loop, Node_List &old_new, int dd
       if( !loop->is_member( use_loop ) && use->is_CFG() ) {
         // Both OLD and USE are CFG nodes here.
         assert( use->is_Proj(), "" );
+        Node* nnn = old_new[old->_idx];
+
+        Node* newuse = NULL;
+        if (head->is_strip_mined() && mode != IgnoreStripMined) {
+          CountedLoopNode* cl = head->as_CountedLoop();
+          CountedLoopEndNode* cle = cl->loopexit();
+          Node* cle_out = cle->proj_out(false);
+          if (use == cle_out) {
+            IfNode* le = cl->outer_loop_end();
+            use = le->proj_out(false);
+            use_loop = get_loop(use);
+            if (mode == CloneIncludesStripMined) {
+              nnn = old_new[le->_idx];
+            } else {
+              newuse = old_new[cle_out->_idx];
+            }
+          }
+        }
+        if (newuse == NULL) {
+          newuse = use->clone();
+        }
 
         // Clone the loop exit control projection
-        Node *newuse = use->clone();
         if (C->do_vector_loop()) {
           cm.verify_insert_and_clone(use, newuse, cm.clone_idx());
         }
@@ -1705,6 +2006,10 @@ void PhaseIdealLoop::clone_loop( IdealLoopTree *loop, Node_List &old_new, int dd
             if( useuse->in(k) == use ) {
               useuse->set_req(k, r);
               uses_found++;
+              if (useuse->is_Loop() && k == LoopNode::EntryControl) {
+                assert(dom_depth(useuse) > dd_r , "");
+                set_idom(useuse, r, dom_depth(useuse));
+              }
             }
           }
           l -= uses_found;    // we deleted 1 or more copies of this edge
@@ -1728,123 +2033,16 @@ void PhaseIdealLoop::clone_loop( IdealLoopTree *loop, Node_List &old_new, int dd
   Node_List *split_cex_set = NULL;
   for( i = 0; i < loop->_body.size(); i++ ) {
     Node* old = loop->_body.at(i);
-    Node* nnn = old_new[old->_idx];
-    // Copy uses to a worklist, so I can munge the def-use info
-    // with impunity.
-    for (DUIterator_Fast jmax, j = old->fast_outs(jmax); j < jmax; j++)
-      worklist.push(old->fast_out(j));
+    clone_loop_handle_data_uses(old, old_new, loop, outer_loop, split_if_set,
+                                split_bool_set, split_cex_set, worklist, new_counter,
+                                mode);
+  }
 
-    while( worklist.size() ) {
-      Node *use = worklist.pop();
-      if (!has_node(use))  continue; // Ignore dead nodes
-      if (use->in(0) == C->top())  continue;
-      IdealLoopTree *use_loop = get_loop( has_ctrl(use) ? get_ctrl(use) : use );
-      // Check for data-use outside of loop - at least one of OLD or USE
-      // must not be a CFG node.
-      if( !loop->is_member( use_loop ) && (!old->is_CFG() || !use->is_CFG())) {
-
-        // If the Data use is an IF, that means we have an IF outside of the
-        // loop that is switching on a condition that is set inside of the
-        // loop.  Happens if people set a loop-exit flag; then test the flag
-        // in the loop to break the loop, then test is again outside of the
-        // loop to determine which way the loop exited.
-        // Loop predicate If node connects to Bool node through Opaque1 node.
-        if (use->is_If() || use->is_CMove() || C->is_predicate_opaq(use)) {
-          // Since this code is highly unlikely, we lazily build the worklist
-          // of such Nodes to go split.
-          if( !split_if_set )
-            split_if_set = new Node_List(area);
-          split_if_set->push(use);
-        }
-        if( use->is_Bool() ) {
-          if( !split_bool_set )
-            split_bool_set = new Node_List(area);
-          split_bool_set->push(use);
-        }
-        if( use->Opcode() == Op_CreateEx ) {
-          if( !split_cex_set )
-            split_cex_set = new Node_List(area);
-          split_cex_set->push(use);
-        }
-
-
-        // Get "block" use is in
-        uint idx = 0;
-        while( use->in(idx) != old ) idx++;
-        Node *prev = use->is_CFG() ? use : get_ctrl(use);
-        assert( !loop->is_member( get_loop( prev ) ), "" );
-        Node *cfg = prev->_idx >= new_counter
-          ? prev->in(2)
-          : idom(prev);
-        if( use->is_Phi() )     // Phi use is in prior block
-          cfg = prev->in(idx);  // NOT in block of Phi itself
-        if (cfg->is_top()) {    // Use is dead?
-          _igvn.replace_input_of(use, idx, C->top());
-          continue;
-        }
-
-        while( !loop->is_member( get_loop( cfg ) ) ) {
-          prev = cfg;
-          cfg = cfg->_idx >= new_counter ? cfg->in(2) : idom(cfg);
-        }
-        // If the use occurs after merging several exits from the loop, then
-        // old value must have dominated all those exits.  Since the same old
-        // value was used on all those exits we did not need a Phi at this
-        // merge point.  NOW we do need a Phi here.  Each loop exit value
-        // is now merged with the peeled body exit; each exit gets its own
-        // private Phi and those Phis need to be merged here.
-        Node *phi;
-        if( prev->is_Region() ) {
-          if( idx == 0 ) {      // Updating control edge?
-            phi = prev;         // Just use existing control
-          } else {              // Else need a new Phi
-            phi = PhiNode::make( prev, old );
-            // Now recursively fix up the new uses of old!
-            for( uint i = 1; i < prev->req(); i++ ) {
-              worklist.push(phi); // Onto worklist once for each 'old' input
-            }
-          }
-        } else {
-          // Get new RegionNode merging old and new loop exits
-          prev = old_new[prev->_idx];
-          assert( prev, "just made this in step 7" );
-          if( idx == 0 ) {      // Updating control edge?
-            phi = prev;         // Just use existing control
-          } else {              // Else need a new Phi
-            // Make a new Phi merging data values properly
-            phi = PhiNode::make( prev, old );
-            phi->set_req( 1, nnn );
-          }
-        }
-        // If inserting a new Phi, check for prior hits
-        if( idx != 0 ) {
-          Node *hit = _igvn.hash_find_insert(phi);
-          if( hit == NULL ) {
-           _igvn.register_new_node_with_optimizer(phi); // Register new phi
-          } else {                                      // or
-            // Remove the new phi from the graph and use the hit
-            _igvn.remove_dead_node(phi);
-            phi = hit;                                  // Use existing phi
-          }
-          set_ctrl(phi, prev);
-        }
-        // Make 'use' use the Phi instead of the old loop body exit value
-        _igvn.replace_input_of(use, idx, phi);
-        if( use->_idx >= new_counter ) { // If updating new phis
-          // Not needed for correctness, but prevents a weak assert
-          // in AddPNode from tripping (when we end up with different
-          // base & derived Phis that will become the same after
-          // IGVN does CSE).
-          Node *hit = _igvn.hash_find_insert(use);
-          if( hit )             // Go ahead and re-hash for hits.
-            _igvn.replace_node( use, hit );
-        }
-
-        // If 'use' was in the loop-exit block, it now needs to be sunk
-        // below the post-loop merge point.
-        sink_use( use, prev );
-      }
-    }
+  for (i = 0; i < extra_data_nodes.size(); i++) {
+    Node* old = extra_data_nodes.at(i);
+    clone_loop_handle_data_uses(old, old_new, loop, outer_loop, split_if_set,
+                                split_bool_set, split_cex_set, worklist, new_counter,
+                                mode);
   }
 
   // Check for IFs that need splitting/cloning.  Happens if an IF outside of
@@ -1852,31 +2050,31 @@ void PhaseIdealLoop::clone_loop( IdealLoopTree *loop, Node_List &old_new, int dd
   // takes control from one or more OLD Regions (which in turn get from NEW
   // Regions).  In any case, there will be a set of Phis for each merge point
   // from the IF up to where the original BOOL def exists the loop.
-  if( split_if_set ) {
-    while( split_if_set->size() ) {
+  if (split_if_set) {
+    while (split_if_set->size()) {
       Node *iff = split_if_set->pop();
-      if( iff->in(1)->is_Phi() ) {
-        BoolNode *b = clone_iff( iff->in(1)->as_Phi(), loop );
+      if (iff->in(1)->is_Phi()) {
+        Node *b = clone_iff(iff->in(1)->as_Phi(), loop);
         _igvn.replace_input_of(iff, 1, b);
       }
     }
   }
-  if( split_bool_set ) {
-    while( split_bool_set->size() ) {
+  if (split_bool_set) {
+    while (split_bool_set->size()) {
       Node *b = split_bool_set->pop();
       Node *phi = b->in(1);
-      assert( phi->is_Phi(), "" );
-      CmpNode *cmp = clone_bool( (PhiNode*)phi, loop );
+      assert(phi->is_Phi(), "");
+      CmpNode *cmp = clone_bool((PhiNode*)phi, loop);
       _igvn.replace_input_of(b, 1, cmp);
     }
   }
-  if( split_cex_set ) {
-    while( split_cex_set->size() ) {
+  if (split_cex_set) {
+    while (split_cex_set->size()) {
       Node *b = split_cex_set->pop();
-      assert( b->in(0)->is_Region(), "" );
-      assert( b->in(1)->is_Phi(), "" );
-      assert( b->in(0)->in(0) == b->in(1)->in(0), "" );
-      split_up( b, b->in(0), NULL );
+      assert(b->in(0)->is_Region(), "");
+      assert(b->in(1)->is_Phi(), "");
+      assert(b->in(0)->in(0) == b->in(1)->in(0), "");
+      split_up(b, b->in(0), NULL);
     }
   }
 
@@ -2936,7 +3134,7 @@ bool PhaseIdealLoop::partial_peel( IdealLoopTree *loop, Node_List &old_new ) {
 
   assert(is_valid_loop_partition(loop, peel, peel_list, not_peel), "bad partition");
 
-  clone_loop( loop, old_new, dd );
+  clone_loop(loop, old_new, dd, IgnoreStripMined);
 
   const uint clone_exit_idx = 1;
   const uint orig_exit_idx  = 2;
diff --git a/src/hotspot/share/opto/macro.cpp b/src/hotspot/share/opto/macro.cpp
index a6ec2ff4267..8e8737f65c6 100644
--- a/src/hotspot/share/opto/macro.cpp
+++ b/src/hotspot/share/opto/macro.cpp
@@ -282,7 +282,8 @@ void PhaseMacroExpand::eliminate_card_mark(Node* p2x) {
         if (!this_region->in(ind)->is_IfFalse()) {
           ind = 2;
         }
-        if (this_region->in(ind)->is_IfFalse()) {
+        if (this_region->in(ind)->is_IfFalse() &&
+            this_region->in(ind)->in(0)->Opcode() == Op_If) {
           Node* bol = this_region->in(ind)->in(0)->in(1);
           assert(bol->is_Bool(), "");
           cmpx = bol->in(1);
@@ -2660,6 +2661,8 @@ void PhaseMacroExpand::eliminate_macro_nodes() {
         break;
       case Node::Class_ArrayCopy:
         break;
+      case Node::Class_OuterStripMinedLoop:
+        break;
       default:
         assert(n->Opcode() == Op_LoopLimit ||
                n->Opcode() == Op_Opaque1   ||
@@ -2733,6 +2736,10 @@ bool PhaseMacroExpand::expand_macro_nodes() {
       } else if (n->Opcode() == Op_Opaque4) {
         _igvn.replace_node(n, n->in(2));
         success = true;
+      } else if (n->Opcode() == Op_OuterStripMinedLoop) {
+        n->as_OuterStripMinedLoop()->adjust_strip_mined_loop(&_igvn);
+        C->remove_macro_node(n);
+        success = true;
       }
       assert(success == (C->macro_count() < old_macro_count), "elimination reduces macro count");
       progress = progress || success;
diff --git a/src/hotspot/share/opto/node.hpp b/src/hotspot/share/opto/node.hpp
index 658b54fd358..17942bac08b 100644
--- a/src/hotspot/share/opto/node.hpp
+++ b/src/hotspot/share/opto/node.hpp
@@ -111,6 +111,8 @@ class MulNode;
 class MultiNode;
 class MultiBranchNode;
 class NeverBranchNode;
+class OuterStripMinedLoopNode;
+class OuterStripMinedLoopEndNode;
 class Node;
 class Node_Array;
 class Node_List;
@@ -623,8 +625,9 @@ public:
           DEFINE_CLASS_ID(Catch,       PCTable, 0)
           DEFINE_CLASS_ID(Jump,        PCTable, 1)
         DEFINE_CLASS_ID(If,          MultiBranch, 1)
-          DEFINE_CLASS_ID(CountedLoopEnd, If, 0)
-          DEFINE_CLASS_ID(RangeCheck, If, 1)
+          DEFINE_CLASS_ID(CountedLoopEnd,         If, 0)
+          DEFINE_CLASS_ID(RangeCheck,             If, 1)
+          DEFINE_CLASS_ID(OuterStripMinedLoopEnd, If, 2)
         DEFINE_CLASS_ID(NeverBranch, MultiBranch, 2)
       DEFINE_CLASS_ID(Start,       Multi, 2)
       DEFINE_CLASS_ID(MemBar,      Multi, 3)
@@ -684,8 +687,9 @@ public:
 
     DEFINE_CLASS_ID(Region, Node, 5)
       DEFINE_CLASS_ID(Loop, Region, 0)
-        DEFINE_CLASS_ID(Root,        Loop, 0)
-        DEFINE_CLASS_ID(CountedLoop, Loop, 1)
+        DEFINE_CLASS_ID(Root,                Loop, 0)
+        DEFINE_CLASS_ID(CountedLoop,         Loop, 1)
+        DEFINE_CLASS_ID(OuterStripMinedLoop, Loop, 2)
 
     DEFINE_CLASS_ID(Sub,   Node, 6)
       DEFINE_CLASS_ID(Cmp,   Sub, 0)
@@ -841,6 +845,8 @@ public:
   DEFINE_CLASS_QUERY(Mul)
   DEFINE_CLASS_QUERY(Multi)
   DEFINE_CLASS_QUERY(MultiBranch)
+  DEFINE_CLASS_QUERY(OuterStripMinedLoop)
+  DEFINE_CLASS_QUERY(OuterStripMinedLoopEnd)
   DEFINE_CLASS_QUERY(Parm)
   DEFINE_CLASS_QUERY(PCTable)
   DEFINE_CLASS_QUERY(Phi)
diff --git a/src/hotspot/share/opto/reg_split.cpp b/src/hotspot/share/opto/reg_split.cpp
index d0c72c3a5b1..05f42d5a343 100644
--- a/src/hotspot/share/opto/reg_split.cpp
+++ b/src/hotspot/share/opto/reg_split.cpp
@@ -25,7 +25,7 @@
 #include "precompiled.hpp"
 #include "libadt/vectset.hpp"
 #include "memory/allocation.inline.hpp"
-#include "memory/resourceArea.hpp"
+#include "memory/resourceArea.inline.hpp"
 #include "opto/addnode.hpp"
 #include "opto/c2compiler.hpp"
 #include "opto/callnode.hpp"
diff --git a/src/hotspot/share/opto/split_if.cpp b/src/hotspot/share/opto/split_if.cpp
index 0634b5efb5e..c3a9965bdf3 100644
--- a/src/hotspot/share/opto/split_if.cpp
+++ b/src/hotspot/share/opto/split_if.cpp
@@ -169,7 +169,7 @@ bool PhaseIdealLoop::split_up( Node *n, Node *blk1, Node *blk2 ) {
               assert(u->in(1) == bol, "");
               // Get control block of either the CMove or the If input
               Node *u_ctrl = u->is_If() ? u->in(0) : get_ctrl(u);
-              assert(u_ctrl != blk1 && u_ctrl != blk2, "won't converge");
+              assert((u_ctrl != blk1 && u_ctrl != blk2) || u->is_CMove(), "won't converge");
               Node *x = bol->clone();
               register_new_node(x, u_ctrl);
               _igvn.replace_input_of(u, 1, x);
diff --git a/src/hotspot/share/opto/subnode.cpp b/src/hotspot/share/opto/subnode.cpp
index cc1d881f10d..f53c9eb14f8 100644
--- a/src/hotspot/share/opto/subnode.cpp
+++ b/src/hotspot/share/opto/subnode.cpp
@@ -1595,3 +1595,12 @@ const Type* SqrtDNode::Value(PhaseGVN* phase) const {
   if( d < 0.0 ) return Type::DOUBLE;
   return TypeD::make( sqrt( d ) );
 }
+
+const Type* SqrtFNode::Value(PhaseGVN* phase) const {
+  const Type *t1 = phase->type( in(1) );
+  if( t1 == Type::TOP ) return Type::TOP;
+  if( t1->base() != Type::FloatCon ) return Type::FLOAT;
+  float f = t1->getf();
+  if( f < 0.0f ) return Type::FLOAT;
+  return TypeF::make( (float)sqrt( (double)f ) );
+}
diff --git a/src/hotspot/share/opto/subnode.hpp b/src/hotspot/share/opto/subnode.hpp
index a4adbcf5a47..68aa1a3c0b8 100644
--- a/src/hotspot/share/opto/subnode.hpp
+++ b/src/hotspot/share/opto/subnode.hpp
@@ -442,6 +442,20 @@ public:
   virtual const Type* Value(PhaseGVN* phase) const;
 };
 
+//------------------------------SqrtFNode--------------------------------------
+// square root a float
+class SqrtFNode : public Node {
+public:
+  SqrtFNode(Compile* C, Node *c, Node *in1) : Node(c, in1) {
+    init_flags(Flag_is_expensive);
+    C->add_expensive_node(this);
+  }
+  virtual int Opcode() const;
+  const Type *bottom_type() const { return Type::FLOAT; }
+  virtual uint ideal_reg() const { return Op_RegF; }
+  virtual const Type* Value(PhaseGVN* phase) const;
+};
+
 //-------------------------------ReverseBytesINode--------------------------------
 // reverse bytes of an integer
 class ReverseBytesINode : public Node {
diff --git a/src/hotspot/share/opto/superword.cpp b/src/hotspot/share/opto/superword.cpp
index b0afe8e5c74..39a525a1955 100644
--- a/src/hotspot/share/opto/superword.cpp
+++ b/src/hotspot/share/opto/superword.cpp
@@ -1102,7 +1102,7 @@ bool SuperWord::stmts_can_pack(Node* s1, Node* s2, int align) {
   }
 
   if (isomorphic(s1, s2)) {
-    if (independent(s1, s2) || reduction(s1, s2)) {
+    if ((independent(s1, s2) && have_similar_inputs(s1, s2)) || reduction(s1, s2)) {
       if (!exists_at(s1, 0) && !exists_at(s2, 1)) {
         if (!s1->is_Mem() || are_adjacent_refs(s1, s2)) {
           int s1_align = alignment(s1);
@@ -1180,6 +1180,20 @@ bool SuperWord::independent(Node* s1, Node* s2) {
   return independent_path(shallow, deep);
 }
 
+//--------------------------have_similar_inputs-----------------------
+// For a node pair (s1, s2) which is isomorphic and independent,
+// do s1 and s2 have similar input edges?
+bool SuperWord::have_similar_inputs(Node* s1, Node* s2) {
+  // assert(isomorphic(s1, s2) == true, "check isomorphic");
+  // assert(independent(s1, s2) == true, "check independent");
+  if (s1->req() > 1 && !s1->is_Store() && !s1->is_Load()) {
+    for (uint i = 1; i < s1->req(); i++) {
+      if (s1->in(i)->Opcode() != s2->in(i)->Opcode()) return false;
+    }
+  }
+  return true;
+}
+
 //------------------------------reduction---------------------------
 // Is there a data path between s1 and s2 and the nodes reductions?
 bool SuperWord::reduction(Node* s1, Node* s2) {
@@ -1339,6 +1353,7 @@ bool SuperWord::follow_def_uses(Node_List* p) {
     for (DUIterator_Fast jmax, j = s2->fast_outs(jmax); j < jmax; j++) {
       Node* t2 = s2->fast_out(j);
       if (!in_bb(t2)) continue;
+      if (t2->Opcode() == Op_AddI && t2 == _lp->as_CountedLoop()->incr()) continue; // don't mess with the iv
       if (!opnd_positions_match(s1, t1, s2, t2))
         continue;
       if (stmts_can_pack(t1, t2, align)) {
@@ -2307,7 +2322,7 @@ void SuperWord::output() {
           vn = VectorNode::make(opc, in1, in2, vlen, velt_basic_type(n));
           vlen_in_bytes = vn->as_Vector()->length_in_bytes();
         }
-      } else if (opc == Op_SqrtD || opc == Op_AbsF || opc == Op_AbsD || opc == Op_NegF || opc == Op_NegD) {
+      } else if (opc == Op_SqrtF || opc == Op_SqrtD || opc == Op_AbsF || opc == Op_AbsD || opc == Op_NegF || opc == Op_NegD) {
         // Promote operand to vector (Sqrt/Abs/Neg are 2 address instructions)
         Node* in = vector_opd(p, 1);
         vn = VectorNode::make(opc, in, NULL, vlen, velt_basic_type(n));
@@ -3299,7 +3314,7 @@ CountedLoopEndNode* SuperWord::get_pre_loop_end(CountedLoopNode* cl) {
     return NULL;
   }
 
-  Node* p_f = cl->in(LoopNode::EntryControl)->in(0)->in(0);
+  Node* p_f = cl->skip_strip_mined()->in(LoopNode::EntryControl)->in(0)->in(0);
   if (!p_f->is_IfFalse()) return NULL;
   if (!p_f->in(0)->is_CountedLoopEnd()) return NULL;
   CountedLoopEndNode* pre_end = p_f->in(0)->as_CountedLoopEnd();
diff --git a/src/hotspot/share/opto/superword.hpp b/src/hotspot/share/opto/superword.hpp
index 28a4f3c28de..8a4241afdf4 100644
--- a/src/hotspot/share/opto/superword.hpp
+++ b/src/hotspot/share/opto/superword.hpp
@@ -442,6 +442,9 @@ class SuperWord : public ResourceObj {
   bool isomorphic(Node* s1, Node* s2);
   // Is there no data path from s1 to s2 or s2 to s1?
   bool independent(Node* s1, Node* s2);
+  // For a node pair (s1, s2) which is isomorphic and independent,
+  // do s1 and s2 have similar input edges?
+  bool have_similar_inputs(Node* s1, Node* s2);
   // Is there a data path between s1 and s2 and both are reductions?
   bool reduction(Node* s1, Node* s2);
   // Helper for independent
diff --git a/src/hotspot/share/opto/vectornode.cpp b/src/hotspot/share/opto/vectornode.cpp
index ecf8247825e..57b0ecf0e7b 100644
--- a/src/hotspot/share/opto/vectornode.cpp
+++ b/src/hotspot/share/opto/vectornode.cpp
@@ -113,6 +113,9 @@ int VectorNode::opcode(int sopc, BasicType bt) {
   case Op_NegD:
     assert(bt == T_DOUBLE, "must be");
     return Op_NegVD;
+  case Op_SqrtF:
+    assert(bt == T_FLOAT, "must be");
+    return Op_SqrtVF;
   case Op_SqrtD:
     assert(bt == T_DOUBLE, "must be");
     return Op_SqrtVD;
@@ -316,7 +319,7 @@ VectorNode* VectorNode::make(int opc, Node* n1, Node* n2, uint vlen, BasicType b
   case Op_NegVF: return new NegVFNode(n1, vt);
   case Op_NegVD: return new NegVDNode(n1, vt);
 
-  // Currently only supports double precision sqrt
+  case Op_SqrtVF: return new SqrtVFNode(n1, vt);
   case Op_SqrtVD: return new SqrtVDNode(n1, vt);
 
   case Op_LShiftVB: return new LShiftVBNode(n1, n2, vt);
diff --git a/src/hotspot/share/opto/vectornode.hpp b/src/hotspot/share/opto/vectornode.hpp
index 7b65aa9a91d..93da7bc4b7c 100644
--- a/src/hotspot/share/opto/vectornode.hpp
+++ b/src/hotspot/share/opto/vectornode.hpp
@@ -373,6 +373,14 @@ class NegVDNode : public VectorNode {
   virtual int Opcode() const;
 };
 
+//------------------------------SqrtVFNode--------------------------------------
+// Vector Sqrt float
+class SqrtVFNode : public VectorNode {
+ public:
+  SqrtVFNode(Node* in, const TypeVect* vt) : VectorNode(in,vt) {}
+  virtual int Opcode() const;
+};
+
 //------------------------------SqrtVDNode--------------------------------------
 // Vector Sqrt double
 class SqrtVDNode : public VectorNode {
diff --git a/src/hotspot/share/precompiled/precompiled.hpp b/src/hotspot/share/precompiled/precompiled.hpp
index dd76a0b9c93..728a75535c0 100644
--- a/src/hotspot/share/precompiled/precompiled.hpp
+++ b/src/hotspot/share/precompiled/precompiled.hpp
@@ -131,7 +131,6 @@
 # include "jvmtifiles/jvmti.h"
 # include "logging/log.hpp"
 # include "memory/allocation.hpp"
-# include "memory/allocation.inline.hpp"
 # include "memory/arena.hpp"
 # include "memory/heap.hpp"
 # include "memory/iterator.hpp"
diff --git a/src/hotspot/share/prims/cdsoffsets.cpp b/src/hotspot/share/prims/cdsoffsets.cpp
new file mode 100644
index 00000000000..d38b7efbfff
--- /dev/null
+++ b/src/hotspot/share/prims/cdsoffsets.cpp
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+#include "precompiled.hpp"
+#include "utilities/macros.hpp"
+#if INCLUDE_CDS
+#include "runtime/os.hpp"
+#include "memory/filemap.hpp"
+#include "memory/allocation.hpp"
+#include "memory/allocation.inline.hpp"
+#include "prims/cdsoffsets.hpp"
+
+CDSOffsets* CDSOffsets::_all = NULL;
+#define ADD_NEXT(list, name, value) \
+  list->add_end(new CDSOffsets(name, value, NULL))
+
+#define CREATE_OFFSET_MAPS                                                                                  \
+    _all = new CDSOffsets("size_t_size", sizeof(size_t), NULL);                                             \
+    ADD_NEXT(_all, "FileMapHeader::_magic", offset_of(FileMapInfo::FileMapHeader, _magic));                 \
+    ADD_NEXT(_all, "FileMapHeader::_crc", offset_of(FileMapInfo::FileMapHeader, _crc));                     \
+    ADD_NEXT(_all, "FileMapHeader::_version", offset_of(FileMapInfo::FileMapHeader, _version));             \
+    ADD_NEXT(_all, "FileMapHeader::_space[0]", offset_of(FileMapInfo::FileMapHeader, _space));              \
+    ADD_NEXT(_all, "space_info::_crc", offset_of(FileMapInfo::FileMapHeader::space_info, _crc));            \
+    ADD_NEXT(_all, "space_info::_used", offset_of(FileMapInfo::FileMapHeader::space_info, _used));          \
+    ADD_NEXT(_all, "FileMapHeader::_paths_misc_info_size", offset_of(FileMapInfo::FileMapHeader, _paths_misc_info_size)); \
+    ADD_NEXT(_all, "file_header_size", sizeof(FileMapInfo::FileMapHeader));                                 \
+    ADD_NEXT(_all, "space_info_size", sizeof(FileMapInfo::FileMapHeader::space_info));
+
+int CDSOffsets::find_offset(const char* name) {
+  if (_all == NULL) {
+    CREATE_OFFSET_MAPS
+  }
+  CDSOffsets* it = _all;
+  while(it) {
+    if (!strcmp(name, it->get_name())) {
+      return it->get_offset();
+    }
+    it = it->next();
+  }
+  return -1; // not found
+}
+
+void CDSOffsets::add_end(CDSOffsets* n) {
+  CDSOffsets* p = this;
+  while(p && p->_next) { p = p->_next; }
+  p->_next = n;
+}
+#endif // INCLUDE_CDS
diff --git a/src/hotspot/share/prims/cdsoffsets.hpp b/src/hotspot/share/prims/cdsoffsets.hpp
new file mode 100644
index 00000000000..aa147cc70a0
--- /dev/null
+++ b/src/hotspot/share/prims/cdsoffsets.hpp
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2014, 2017, 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_PRIMS_CDSOFFSETS_HPP
+#define SHARE_PRIMS_CDSOFFSETS_HPP
+class CDSOffsets: public CHeapObj<mtInternal> {
+ private:
+  char* _name;
+  int   _offset;
+  CDSOffsets* _next;
+  static CDSOffsets* _all;  // sole list for cds
+ public:
+  CDSOffsets(const char* name, int offset, CDSOffsets* next) {
+     _name = NEW_C_HEAP_ARRAY(char, strlen(name) + 1, mtInternal);
+     strcpy(_name, name);
+     _offset = offset;
+     _next = next;
+  }
+
+  char* get_name() const { return _name; }
+  int   get_offset() const { return _offset; }
+  CDSOffsets* next() const { return _next; }
+  void add_end(CDSOffsets* n);
+
+  static int find_offset(const char* name);
+};
+#endif // SHARE_PRIMS_CDSOFFSETS_HPP
diff --git a/src/hotspot/share/prims/jni.cpp b/src/hotspot/share/prims/jni.cpp
index 26aedaaebe5..06d0f91a6b1 100644
--- a/src/hotspot/share/prims/jni.cpp
+++ b/src/hotspot/share/prims/jni.cpp
@@ -4119,7 +4119,7 @@ static jint attach_current_thread(JavaVM *vm, void **penv, void *_args, bool dae
   thread->initialize_thread_current();
 
   if (!os::create_attached_thread(thread)) {
-    delete thread;
+    thread->smr_delete();
     return JNI_ERR;
   }
   // Enable stack overflow checks
@@ -4250,7 +4250,7 @@ jint JNICALL jni_DetachCurrentThread(JavaVM *vm)  {
   // (platform-dependent) methods where we do alternate stack
   // maintenance work?)
   thread->exit(false, JavaThread::jni_detach);
-  delete thread;
+  thread->smr_delete();
 
   HOTSPOT_JNI_DETACHCURRENTTHREAD_RETURN(JNI_OK);
   return JNI_OK;
diff --git a/src/hotspot/share/prims/jvm.cpp b/src/hotspot/share/prims/jvm.cpp
index 368696e5d08..ad3a1cedb46 100644
--- a/src/hotspot/share/prims/jvm.cpp
+++ b/src/hotspot/share/prims/jvm.cpp
@@ -66,6 +66,7 @@
 #include "runtime/perfData.hpp"
 #include "runtime/reflection.hpp"
 #include "runtime/thread.inline.hpp"
+#include "runtime/threadSMR.hpp"
 #include "runtime/vframe.hpp"
 #include "runtime/vm_operations.hpp"
 #include "runtime/vm_version.hpp"
@@ -2737,16 +2738,12 @@ void jio_print(const char* s) {
 
 // java.lang.Thread //////////////////////////////////////////////////////////////////////////////
 
-// In most of the JVM Thread support functions we need to be sure to lock the Threads_lock
-// to prevent the target thread from exiting after we have a pointer to the C++ Thread or
-// OSThread objects.  The exception to this rule is when the target object is the thread
-// doing the operation, in which case we know that the thread won't exit until the
-// operation is done (all exits being voluntary).  There are a few cases where it is
-// rather silly to do operations on yourself, like resuming yourself or asking whether
-// you are alive.  While these can still happen, they are not subject to deadlocks if
-// the lock is held while the operation occurs (this is not the case for suspend, for
-// instance), and are very unlikely.  Because IsAlive needs to be fast and its
-// implementation is local to this file, we always lock Threads_lock for that one.
+// In most of the JVM thread support functions we need to access the
+// thread through a ThreadsListHandle to prevent it from exiting and
+// being reclaimed while we try to operate on it. The exceptions to this
+// rule are when operating on the current thread, or if the monitor of
+// the target java.lang.Thread is locked at the Java level - in both
+// cases the target cannot exit.
 
 static void thread_entry(JavaThread* thread, TRAPS) {
   HandleMark hm(THREAD);
@@ -2821,7 +2818,7 @@ JVM_ENTRY(void, JVM_StartThread(JNIEnv* env, jobject jthread))
 
   if (native_thread->osthread() == NULL) {
     // No one should hold a reference to the 'native_thread'.
-    delete native_thread;
+    native_thread->smr_delete();
     if (JvmtiExport::should_post_resource_exhausted()) {
       JvmtiExport::post_resource_exhausted(
         JVMTI_RESOURCE_EXHAUSTED_OOM_ERROR | JVMTI_RESOURCE_EXHAUSTED_THREADS,
@@ -2835,41 +2832,45 @@ JVM_ENTRY(void, JVM_StartThread(JNIEnv* env, jobject jthread))
 
 JVM_END
 
+
 // JVM_Stop is implemented using a VM_Operation, so threads are forced to safepoints
 // before the quasi-asynchronous exception is delivered.  This is a little obtrusive,
 // but is thought to be reliable and simple. In the case, where the receiver is the
-// same thread as the sender, no safepoint is needed.
+// same thread as the sender, no VM_Operation is needed.
 JVM_ENTRY(void, JVM_StopThread(JNIEnv* env, jobject jthread, jobject throwable))
   JVMWrapper("JVM_StopThread");
 
+  // A nested ThreadsListHandle will grab the Threads_lock so create
+  // tlh before we resolve throwable.
+  ThreadsListHandle tlh(thread);
   oop java_throwable = JNIHandles::resolve(throwable);
   if (java_throwable == NULL) {
     THROW(vmSymbols::java_lang_NullPointerException());
   }
-  oop java_thread = JNIHandles::resolve_non_null(jthread);
-  JavaThread* receiver = java_lang_Thread::thread(java_thread);
-  Events::log_exception(JavaThread::current(),
+  oop java_thread = NULL;
+  JavaThread* receiver = NULL;
+  bool is_alive = tlh.cv_internal_thread_to_JavaThread(jthread, &receiver, &java_thread);
+  Events::log_exception(thread,
                         "JVM_StopThread thread JavaThread " INTPTR_FORMAT " as oop " INTPTR_FORMAT " [exception " INTPTR_FORMAT "]",
                         p2i(receiver), p2i((address)java_thread), p2i(throwable));
-  // First check if thread is alive
-  if (receiver != NULL) {
-    // Check if exception is getting thrown at self (use oop equality, since the
-    // target object might exit)
-    if (java_thread == thread->threadObj()) {
+
+  if (is_alive) {
+    // jthread refers to a live JavaThread.
+    if (thread == receiver) {
+      // Exception is getting thrown at self so no VM_Operation needed.
       THROW_OOP(java_throwable);
     } else {
-      // Enques a VM_Operation to stop all threads and then deliver the exception...
-      Thread::send_async_exception(java_thread, JNIHandles::resolve(throwable));
+      // Use a VM_Operation to throw the exception.
+      Thread::send_async_exception(java_thread, java_throwable);
     }
-  }
-  else {
+  } else {
     // Either:
     // - target thread has not been started before being stopped, or
     // - target thread already terminated
     // We could read the threadStatus to determine which case it is
     // but that is overkill as it doesn't matter. We must set the
     // stillborn flag for the first case, and if the thread has already
-    // exited setting this flag has no affect
+    // exited setting this flag has no effect.
     java_lang_Thread::set_stillborn(java_thread);
   }
 JVM_END
@@ -2885,12 +2886,12 @@ JVM_END
 
 JVM_ENTRY(void, JVM_SuspendThread(JNIEnv* env, jobject jthread))
   JVMWrapper("JVM_SuspendThread");
-  oop java_thread = JNIHandles::resolve_non_null(jthread);
-  JavaThread* receiver = java_lang_Thread::thread(java_thread);
-
-  if (receiver != NULL) {
-    // thread has run and has not exited (still on threads list)
 
+  ThreadsListHandle tlh(thread);
+  JavaThread* receiver = NULL;
+  bool is_alive = tlh.cv_internal_thread_to_JavaThread(jthread, &receiver, NULL);
+  if (is_alive) {
+    // jthread refers to a live JavaThread.
     {
       MutexLockerEx ml(receiver->SR_lock(), Mutex::_no_safepoint_check_flag);
       if (receiver->is_external_suspend()) {
@@ -2922,30 +2923,49 @@ JVM_END
 
 JVM_ENTRY(void, JVM_ResumeThread(JNIEnv* env, jobject jthread))
   JVMWrapper("JVM_ResumeThread");
-  // Ensure that the C++ Thread and OSThread structures aren't freed before we operate.
-  // We need to *always* get the threads lock here, since this operation cannot be allowed during
-  // a safepoint. The safepoint code relies on suspending a thread to examine its state. If other
-  // threads randomly resumes threads, then a thread might not be suspended when the safepoint code
-  // looks at it.
-  MutexLocker ml(Threads_lock);
-  JavaThread* thr = java_lang_Thread::thread(JNIHandles::resolve_non_null(jthread));
-  if (thr != NULL) {
-    // the thread has run and is not in the process of exiting
-    thr->java_resume();
+
+  ThreadsListHandle tlh(thread);
+  JavaThread* receiver = NULL;
+  bool is_alive = tlh.cv_internal_thread_to_JavaThread(jthread, &receiver, NULL);
+  if (is_alive) {
+    // jthread refers to a live JavaThread.
+
+    // This is the original comment for this Threads_lock grab:
+    //   We need to *always* get the threads lock here, since this operation cannot be allowed during
+    //   a safepoint. The safepoint code relies on suspending a thread to examine its state. If other
+    //   threads randomly resumes threads, then a thread might not be suspended when the safepoint code
+    //   looks at it.
+    //
+    // The above comment dates back to when we had both internal and
+    // external suspend APIs that shared a common underlying mechanism.
+    // External suspend is now entirely cooperative and doesn't share
+    // anything with internal suspend. That said, there are some
+    // assumptions in the VM that an external resume grabs the
+    // Threads_lock. We can't drop the Threads_lock grab here until we
+    // resolve the assumptions that exist elsewhere.
+    //
+    MutexLocker ml(Threads_lock);
+    receiver->java_resume();
   }
 JVM_END
 
 
 JVM_ENTRY(void, JVM_SetThreadPriority(JNIEnv* env, jobject jthread, jint prio))
   JVMWrapper("JVM_SetThreadPriority");
-  // Ensure that the C++ Thread and OSThread structures aren't freed before we operate
-  MutexLocker ml(Threads_lock);
-  oop java_thread = JNIHandles::resolve_non_null(jthread);
+
+  ThreadsListHandle tlh(thread);
+  oop java_thread = NULL;
+  JavaThread* receiver = NULL;
+  bool is_alive = tlh.cv_internal_thread_to_JavaThread(jthread, &receiver, &java_thread);
   java_lang_Thread::set_priority(java_thread, (ThreadPriority)prio);
-  JavaThread* thr = java_lang_Thread::thread(java_thread);
-  if (thr != NULL) {                  // Thread not yet started; priority pushed down when it is
-    Thread::set_priority(thr, (ThreadPriority)prio);
+
+  if (is_alive) {
+    // jthread refers to a live JavaThread.
+    Thread::set_priority(receiver, (ThreadPriority)prio);
   }
+  // Implied else: If the JavaThread hasn't started yet, then the
+  // priority set in the java.lang.Thread object above will be pushed
+  // down when it does start.
 JVM_END
 
 
@@ -3016,67 +3036,39 @@ JVM_END
 JVM_ENTRY(jint, JVM_CountStackFrames(JNIEnv* env, jobject jthread))
   JVMWrapper("JVM_CountStackFrames");
 
-  // Ensure that the C++ Thread and OSThread structures aren't freed before we operate
-  oop java_thread = JNIHandles::resolve_non_null(jthread);
-  bool throw_illegal_thread_state = false;
+  uint32_t debug_bits = 0;
+  ThreadsListHandle tlh(thread);
+  JavaThread* receiver = NULL;
+  bool is_alive = tlh.cv_internal_thread_to_JavaThread(jthread, &receiver, NULL);
   int count = 0;
-
-  {
-    MutexLockerEx ml(thread->threadObj() == java_thread ? NULL : Threads_lock);
-    // We need to re-resolve the java_thread, since a GC might have happened during the
-    // acquire of the lock
-    JavaThread* thr = java_lang_Thread::thread(JNIHandles::resolve_non_null(jthread));
-
-    if (thr == NULL) {
-      // do nothing
-    } else if(! thr->is_external_suspend() || ! thr->frame_anchor()->walkable()) {
-      // Check whether this java thread has been suspended already. If not, throws
-      // IllegalThreadStateException. We defer to throw that exception until
-      // Threads_lock is released since loading exception class has to leave VM.
-      // The correct way to test a thread is actually suspended is
-      // wait_for_ext_suspend_completion(), but we can't call that while holding
-      // the Threads_lock. The above tests are sufficient for our purposes
-      // provided the walkability of the stack is stable - which it isn't
-      // 100% but close enough for most practical purposes.
-      throw_illegal_thread_state = true;
-    } else {
-      // Count all java activation, i.e., number of vframes
-      for(vframeStream vfst(thr); !vfst.at_end(); vfst.next()) {
-        // Native frames are not counted
+  if (is_alive) {
+    // jthread refers to a live JavaThread.
+    if (receiver->is_thread_fully_suspended(true /* wait for suspend completion */, &debug_bits)) {
+      // Count all java activation, i.e., number of vframes.
+      for (vframeStream vfst(receiver); !vfst.at_end(); vfst.next()) {
+        // Native frames are not counted.
         if (!vfst.method()->is_native()) count++;
-       }
+      }
+    } else {
+      THROW_MSG_0(vmSymbols::java_lang_IllegalThreadStateException(),
+                  "this thread is not suspended");
     }
   }
+  // Implied else: if JavaThread is not alive simply return a count of 0.
 
-  if (throw_illegal_thread_state) {
-    THROW_MSG_0(vmSymbols::java_lang_IllegalThreadStateException(),
-                "this thread is not suspended");
-  }
   return count;
 JVM_END
 
-// Consider: A better way to implement JVM_Interrupt() is to acquire
-// Threads_lock to resolve the jthread into a Thread pointer, fetch
-// Thread->platformevent, Thread->native_thr, Thread->parker, etc.,
-// drop Threads_lock, and the perform the unpark() and thr_kill() operations
-// outside the critical section.  Threads_lock is hot so we want to minimize
-// the hold-time.  A cleaner interface would be to decompose interrupt into
-// two steps.  The 1st phase, performed under Threads_lock, would return
-// a closure that'd be invoked after Threads_lock was dropped.
-// This tactic is safe as PlatformEvent and Parkers are type-stable (TSM) and
-// admit spurious wakeups.
 
 JVM_ENTRY(void, JVM_Interrupt(JNIEnv* env, jobject jthread))
   JVMWrapper("JVM_Interrupt");
 
-  // Ensure that the C++ Thread and OSThread structures aren't freed before we operate
-  oop java_thread = JNIHandles::resolve_non_null(jthread);
-  MutexLockerEx ml(thread->threadObj() == java_thread ? NULL : Threads_lock);
-  // We need to re-resolve the java_thread, since a GC might have happened during the
-  // acquire of the lock
-  JavaThread* thr = java_lang_Thread::thread(JNIHandles::resolve_non_null(jthread));
-  if (thr != NULL) {
-    Thread::interrupt(thr);
+  ThreadsListHandle tlh(thread);
+  JavaThread* receiver = NULL;
+  bool is_alive = tlh.cv_internal_thread_to_JavaThread(jthread, &receiver, NULL);
+  if (is_alive) {
+    // jthread refers to a live JavaThread.
+    Thread::interrupt(receiver);
   }
 JVM_END
 
@@ -3084,16 +3076,14 @@ JVM_END
 JVM_QUICK_ENTRY(jboolean, JVM_IsInterrupted(JNIEnv* env, jobject jthread, jboolean clear_interrupted))
   JVMWrapper("JVM_IsInterrupted");
 
-  // Ensure that the C++ Thread and OSThread structures aren't freed before we operate
-  oop java_thread = JNIHandles::resolve_non_null(jthread);
-  MutexLockerEx ml(thread->threadObj() == java_thread ? NULL : Threads_lock);
-  // We need to re-resolve the java_thread, since a GC might have happened during the
-  // acquire of the lock
-  JavaThread* thr = java_lang_Thread::thread(JNIHandles::resolve_non_null(jthread));
-  if (thr == NULL) {
-    return JNI_FALSE;
+  ThreadsListHandle tlh(thread);
+  JavaThread* receiver = NULL;
+  bool is_alive = tlh.cv_internal_thread_to_JavaThread(jthread, &receiver, NULL);
+  if (is_alive) {
+    // jthread refers to a live JavaThread.
+    return (jboolean) Thread::is_interrupted(receiver, clear_interrupted != 0);
   } else {
-    return (jboolean) Thread::is_interrupted(thr, clear_interrupted != 0);
+    return JNI_FALSE;
   }
 JVM_END
 
@@ -3122,14 +3112,16 @@ JVM_END
 
 JVM_ENTRY(void, JVM_SetNativeThreadName(JNIEnv* env, jobject jthread, jstring name))
   JVMWrapper("JVM_SetNativeThreadName");
-  ResourceMark rm(THREAD);
+
+  // We don't use a ThreadsListHandle here because the current thread
+  // must be alive.
   oop java_thread = JNIHandles::resolve_non_null(jthread);
   JavaThread* thr = java_lang_Thread::thread(java_thread);
-  // Thread naming only supported for the current thread, doesn't work for
-  // target threads.
-  if (Thread::current() == thr && !thr->has_attached_via_jni()) {
+  if (thread == thr && !thr->has_attached_via_jni()) {
+    // Thread naming is only supported for the current thread and
     // we don't set the name of an attached thread to avoid stepping
-    // on other programs
+    // on other programs.
+    ResourceMark rm(thread);
     const char *thread_name = java_lang_String::as_utf8_string(JNIHandles::resolve_non_null(name));
     os::set_native_thread_name(thread_name);
   }
@@ -3561,6 +3553,8 @@ JVM_ENTRY(jobjectArray, JVM_DumpThreads(JNIEnv *env, jclass threadClass, jobject
     thread_handle_array->append(h);
   }
 
+  // The JavaThread references in thread_handle_array are validated
+  // in VM_ThreadDump::doit().
   Handle stacktraces = ThreadService::dump_stack_traces(thread_handle_array, num_threads, CHECK_NULL);
   return (jobjectArray)JNIHandles::make_local(env, stacktraces());
 
diff --git a/src/hotspot/share/prims/jvmtiEnter.xsl b/src/hotspot/share/prims/jvmtiEnter.xsl
index fbb8724c7ab..406ddc642c0 100644
--- a/src/hotspot/share/prims/jvmtiEnter.xsl
+++ b/src/hotspot/share/prims/jvmtiEnter.xsl
@@ -45,6 +45,7 @@
 # include "prims/jvmtiEnter.hpp"
 # include "prims/jvmtiRawMonitor.hpp"
 # include "prims/jvmtiUtil.hpp"
+# include "runtime/threadSMR.hpp"
 
 </xsl:text>
 
@@ -769,47 +770,27 @@ static jvmtiError JNICALL
 
 <xsl:template match="jthread" mode="dochecksbody">
   <xsl:param name="name"/>
-    <xsl:text>    oop thread_oop = JNIHandles::resolve_external_guard(</xsl:text>
+    <xsl:text>    err = JvmtiExport::cv_external_thread_to_JavaThread(tlh.list(), </xsl:text>
     <xsl:value-of select="$name"/>
-    <xsl:text>);
-    if (thread_oop == NULL) {
+    <xsl:text>, &amp;java_thread, NULL);
+    if (err != JVMTI_ERROR_NONE) {
 </xsl:text>
     <xsl:apply-templates select=".." mode="traceError">     
-      <xsl:with-param name="err">JVMTI_ERROR_INVALID_THREAD</xsl:with-param>
-      <xsl:with-param name="comment"> - jthread resolved to NULL - jthread = " PTR_FORMAT "</xsl:with-param>
+      <xsl:with-param name="err">err</xsl:with-param>
+      <xsl:with-param name="comment"> - jthread did not convert to a JavaThread - jthread = " PTR_FORMAT "</xsl:with-param>
       <xsl:with-param name="extraValue">, p2i(<xsl:value-of select="$name"/>)</xsl:with-param>
     </xsl:apply-templates>
     <xsl:text>
     }
-    if (!thread_oop-&gt;is_a(SystemDictionary::Thread_klass())) {
 </xsl:text>
-    <xsl:apply-templates select=".." mode="traceError">     
-      <xsl:with-param name="err">JVMTI_ERROR_INVALID_THREAD</xsl:with-param>
-      <xsl:with-param name="comment"> - oop is not a thread - jthread = " PTR_FORMAT "</xsl:with-param>
-      <xsl:with-param name="extraValue">, p2i(<xsl:value-of select="$name"/>)</xsl:with-param>
-    </xsl:apply-templates>
-    <xsl:text>
-    }
-    java_thread = java_lang_Thread::thread(thread_oop); 
-    if (java_thread == NULL) {
-</xsl:text>
-    <xsl:apply-templates select=".." mode="traceError">     
-      <xsl:with-param name="err">
-        <xsl:text>JVMTI_ERROR_THREAD_NOT_ALIVE</xsl:text>
-      </xsl:with-param>
-      <xsl:with-param name="comment"> - not a Java thread - jthread = " PTR_FORMAT "</xsl:with-param>
-      <xsl:with-param name="extraValue">, p2i(<xsl:value-of select="$name"/>)</xsl:with-param>
-    </xsl:apply-templates>
-    <xsl:text>
-    }
-</xsl:text>  
 </xsl:template>
 
 <xsl:template match="jthread" mode="dochecks">
   <xsl:param name="name"/>
   <!-- If we convert and test threads -->
   <xsl:if test="count(@impl)=0 or not(contains(@impl,'noconvert'))">
-    <xsl:text>  JavaThread* java_thread;
+    <xsl:text>  JavaThread* java_thread = NULL;
+  ThreadsListHandle tlh(this_thread);
 </xsl:text>
     <xsl:choose>
       <xsl:when test="count(@null)=0">
diff --git a/src/hotspot/share/prims/jvmtiEnv.cpp b/src/hotspot/share/prims/jvmtiEnv.cpp
index b67e23f45ad..bff8274a3ea 100644
--- a/src/hotspot/share/prims/jvmtiEnv.cpp
+++ b/src/hotspot/share/prims/jvmtiEnv.cpp
@@ -62,6 +62,7 @@
 #include "runtime/reflectionUtils.hpp"
 #include "runtime/signature.hpp"
 #include "runtime/thread.inline.hpp"
+#include "runtime/threadSMR.hpp"
 #include "runtime/timerTrace.hpp"
 #include "runtime/vframe.hpp"
 #include "runtime/vmThread.hpp"
@@ -162,7 +163,6 @@ JvmtiEnv::GetThreadLocalStorage(jthread thread, void** data_ptr) {
     *data_ptr = (state == NULL) ? NULL :
       state->env_thread_state(this)->get_agent_thread_local_storage_data();
   } else {
-
     // jvmti_GetThreadLocalStorage is "in native" and doesn't transition
     // the thread to _thread_in_vm. However, when the TLS for a thread
     // other than the current thread is required we need to transition
@@ -172,17 +172,13 @@ JvmtiEnv::GetThreadLocalStorage(jthread thread, void** data_ptr) {
     VM_ENTRY_BASE(jvmtiError, JvmtiEnv::GetThreadLocalStorage , current_thread)
     debug_only(VMNativeEntryWrapper __vew;)
 
-    oop thread_oop = JNIHandles::resolve_external_guard(thread);
-    if (thread_oop == NULL) {
-      return JVMTI_ERROR_INVALID_THREAD;
-    }
-    if (!thread_oop->is_a(SystemDictionary::Thread_klass())) {
-      return JVMTI_ERROR_INVALID_THREAD;
-    }
-    JavaThread* java_thread = java_lang_Thread::thread(thread_oop);
-    if (java_thread == NULL) {
-      return JVMTI_ERROR_THREAD_NOT_ALIVE;
+    JavaThread* java_thread = NULL;
+    ThreadsListHandle tlh(current_thread);
+    jvmtiError err = JvmtiExport::cv_external_thread_to_JavaThread(tlh.list(), thread, &java_thread, NULL);
+    if (err != JVMTI_ERROR_NONE) {
+      return err;
     }
+
     JvmtiThreadState* state = java_thread->jvmti_thread_state();
     *data_ptr = (state == NULL) ? NULL :
       state->env_thread_state(this)->get_agent_thread_local_storage_data();
@@ -518,42 +514,60 @@ JvmtiEnv::SetEventCallbacks(const jvmtiEventCallbacks* callbacks, jint size_of_c
 // event_thread - NULL is a valid value, must be checked
 jvmtiError
 JvmtiEnv::SetEventNotificationMode(jvmtiEventMode mode, jvmtiEvent event_type, jthread event_thread,   ...) {
-  JavaThread* java_thread = NULL;
-  if (event_thread != NULL) {
-    oop thread_oop = JNIHandles::resolve_external_guard(event_thread);
-    if (thread_oop == NULL) {
-      return JVMTI_ERROR_INVALID_THREAD;
+  if (event_thread == NULL) {
+    // Can be called at Agent_OnLoad() time with event_thread == NULL
+    // when Thread::current() does not work yet so we cannot create a
+    // ThreadsListHandle that is common to both thread-specific and
+    // global code paths.
+
+    // event_type must be valid
+    if (!JvmtiEventController::is_valid_event_type(event_type)) {
+      return JVMTI_ERROR_INVALID_EVENT_TYPE;
     }
-    if (!thread_oop->is_a(SystemDictionary::Thread_klass())) {
-      return JVMTI_ERROR_INVALID_THREAD;
+
+    bool enabled = (mode == JVMTI_ENABLE);
+
+    // assure that needed capabilities are present
+    if (enabled && !JvmtiUtil::has_event_capability(event_type, get_capabilities())) {
+      return JVMTI_ERROR_MUST_POSSESS_CAPABILITY;
     }
-    java_thread = java_lang_Thread::thread(thread_oop);
-    if (java_thread == NULL) {
-      return JVMTI_ERROR_THREAD_NOT_ALIVE;
+
+    if (event_type == JVMTI_EVENT_CLASS_FILE_LOAD_HOOK && enabled) {
+      record_class_file_load_hook_enabled();
     }
-  }
+    JvmtiEventController::set_user_enabled(this, (JavaThread*) NULL, event_type, enabled);
+  } else {
+    // We have a specified event_thread.
 
-  // event_type must be valid
-  if (!JvmtiEventController::is_valid_event_type(event_type)) {
-    return JVMTI_ERROR_INVALID_EVENT_TYPE;
-  }
+    JavaThread* java_thread = NULL;
+    ThreadsListHandle tlh;
+    jvmtiError err = JvmtiExport::cv_external_thread_to_JavaThread(tlh.list(), event_thread, &java_thread, NULL);
+    if (err != JVMTI_ERROR_NONE) {
+      return err;
+    }
 
-  // global events cannot be controlled at thread level.
-  if (java_thread != NULL && JvmtiEventController::is_global_event(event_type)) {
-    return JVMTI_ERROR_ILLEGAL_ARGUMENT;
-  }
+    // event_type must be valid
+    if (!JvmtiEventController::is_valid_event_type(event_type)) {
+      return JVMTI_ERROR_INVALID_EVENT_TYPE;
+    }
 
-  bool enabled = (mode == JVMTI_ENABLE);
+    // global events cannot be controlled at thread level.
+    if (JvmtiEventController::is_global_event(event_type)) {
+      return JVMTI_ERROR_ILLEGAL_ARGUMENT;
+    }
 
-  // assure that needed capabilities are present
-  if (enabled && !JvmtiUtil::has_event_capability(event_type, get_capabilities())) {
-    return JVMTI_ERROR_MUST_POSSESS_CAPABILITY;
-  }
+    bool enabled = (mode == JVMTI_ENABLE);
 
-  if (event_type == JVMTI_EVENT_CLASS_FILE_LOAD_HOOK && enabled) {
-    record_class_file_load_hook_enabled();
+    // assure that needed capabilities are present
+    if (enabled && !JvmtiUtil::has_event_capability(event_type, get_capabilities())) {
+      return JVMTI_ERROR_MUST_POSSESS_CAPABILITY;
+    }
+
+    if (event_type == JVMTI_EVENT_CLASS_FILE_LOAD_HOOK && enabled) {
+      record_class_file_load_hook_enabled();
+    }
+    JvmtiEventController::set_user_enabled(this, java_thread, event_type, enabled);
   }
-  JvmtiEventController::set_user_enabled(this, java_thread, event_type, enabled);
 
   return JVMTI_ERROR_NONE;
 } /* end SetEventNotificationMode */
@@ -817,35 +831,45 @@ JvmtiEnv::GetJLocationFormat(jvmtiJlocationFormat* format_ptr) {
 // thread_state_ptr - pre-checked for NULL
 jvmtiError
 JvmtiEnv::GetThreadState(jthread thread, jint* thread_state_ptr) {
-  jint state;
-  oop thread_oop;
-  JavaThread* thr;
+  JavaThread* current_thread = JavaThread::current();
+  JavaThread* java_thread = NULL;
+  oop thread_oop = NULL;
+  ThreadsListHandle tlh(current_thread);
 
   if (thread == NULL) {
-    thread_oop = JavaThread::current()->threadObj();
-  } else {
-    thread_oop = JNIHandles::resolve_external_guard(thread);
-  }
+    java_thread = current_thread;
+    thread_oop = java_thread->threadObj();
 
-  if (thread_oop == NULL || !thread_oop->is_a(SystemDictionary::Thread_klass())) {
-    return JVMTI_ERROR_INVALID_THREAD;
+    if (thread_oop == NULL || !thread_oop->is_a(SystemDictionary::Thread_klass())) {
+      return JVMTI_ERROR_INVALID_THREAD;
+    }
+  } else {
+    jvmtiError err = JvmtiExport::cv_external_thread_to_JavaThread(tlh.list(), thread, &java_thread, &thread_oop);
+    if (err != JVMTI_ERROR_NONE) {
+      // We got an error code so we don't have a JavaThread *, but
+      // only return an error from here if we didn't get a valid
+      // thread_oop.
+      if (thread_oop == NULL) {
+        return err;
+      }
+      // We have a valid thread_oop so we can return some thread state.
+    }
   }
 
   // get most state bits
-  state = (jint)java_lang_Thread::get_thread_status(thread_oop);
+  jint state = (jint)java_lang_Thread::get_thread_status(thread_oop);
 
-  // add more state bits
-  thr = java_lang_Thread::thread(thread_oop);
-  if (thr != NULL) {
-    JavaThreadState jts = thr->thread_state();
+  if (java_thread != NULL) {
+    // We have a JavaThread* so add more state bits.
+    JavaThreadState jts = java_thread->thread_state();
 
-    if (thr->is_being_ext_suspended()) {
+    if (java_thread->is_being_ext_suspended()) {
       state |= JVMTI_THREAD_STATE_SUSPENDED;
     }
     if (jts == _thread_in_native) {
       state |= JVMTI_THREAD_STATE_IN_NATIVE;
     }
-    OSThread* osThread = thr->osthread();
+    OSThread* osThread = java_thread->osthread();
     if (osThread != NULL && osThread->interrupted()) {
       state |= JVMTI_THREAD_STATE_INTERRUPTED;
     }
@@ -891,7 +915,6 @@ JvmtiEnv::GetAllThreads(jint* threads_count_ptr, jthread** threads_ptr) {
     thread_objs[i] = Handle(tle.get_threadObj(i));
   }
 
-  // have to make global handles outside of Threads_lock
   jthread *jthreads  = new_jthreadArray(nthreads, thread_objs);
   NULL_CHECK(jthreads, JVMTI_ERROR_OUT_OF_MEMORY);
 
@@ -935,19 +958,12 @@ JvmtiEnv::SuspendThread(JavaThread* java_thread) {
 jvmtiError
 JvmtiEnv::SuspendThreadList(jint request_count, const jthread* request_list, jvmtiError* results) {
   int needSafepoint = 0;  // > 0 if we need a safepoint
+  ThreadsListHandle tlh;
   for (int i = 0; i < request_count; i++) {
-    JavaThread *java_thread = get_JavaThread(request_list[i]);
-    if (java_thread == NULL) {
-      results[i] = JVMTI_ERROR_INVALID_THREAD;
-      continue;
-    }
-    // the thread has not yet run or has exited (not on threads list)
-    if (java_thread->threadObj() == NULL) {
-      results[i] = JVMTI_ERROR_THREAD_NOT_ALIVE;
-      continue;
-    }
-    if (java_lang_Thread::thread(java_thread->threadObj()) == NULL) {
-      results[i] = JVMTI_ERROR_THREAD_NOT_ALIVE;
+    JavaThread *java_thread = NULL;
+    jvmtiError err = JvmtiExport::cv_external_thread_to_JavaThread(tlh.list(), request_list[i], &java_thread, NULL);
+    if (err != JVMTI_ERROR_NONE) {
+      results[i] = err;
       continue;
     }
     // don't allow hidden thread suspend request.
@@ -1018,10 +1034,12 @@ JvmtiEnv::ResumeThread(JavaThread* java_thread) {
 // results - pre-checked for NULL
 jvmtiError
 JvmtiEnv::ResumeThreadList(jint request_count, const jthread* request_list, jvmtiError* results) {
+  ThreadsListHandle tlh;
   for (int i = 0; i < request_count; i++) {
-    JavaThread *java_thread = get_JavaThread(request_list[i]);
-    if (java_thread == NULL) {
-      results[i] = JVMTI_ERROR_INVALID_THREAD;
+    JavaThread* java_thread = NULL;
+    jvmtiError err = JvmtiExport::cv_external_thread_to_JavaThread(tlh.list(), request_list[i], &java_thread, NULL);
+    if (err != JVMTI_ERROR_NONE) {
+      results[i] = err;
       continue;
     }
     // don't allow hidden thread resume request.
@@ -1039,7 +1057,7 @@ JvmtiEnv::ResumeThreadList(jint request_count, const jthread* request_list, jvmt
       continue;
     }
 
-    results[i] = JVMTI_ERROR_NONE;  // indicate successful suspend
+    results[i] = JVMTI_ERROR_NONE;  // indicate successful resume
   }
   // per-thread resume results returned via results parameter
   return JVMTI_ERROR_NONE;
@@ -1064,20 +1082,14 @@ JvmtiEnv::StopThread(JavaThread* java_thread, jobject exception) {
 // thread - NOT pre-checked
 jvmtiError
 JvmtiEnv::InterruptThread(jthread thread) {
-  oop thread_oop = JNIHandles::resolve_external_guard(thread);
-  if (thread_oop == NULL || !thread_oop->is_a(SystemDictionary::Thread_klass()))
-    return JVMTI_ERROR_INVALID_THREAD;
-
+  // TODO: this is very similar to JVM_Interrupt(); share code in future
   JavaThread* current_thread  = JavaThread::current();
-
-  // Todo: this is a duplicate of JVM_Interrupt; share code in future
-  // Ensure that the C++ Thread and OSThread structures aren't freed before we operate
-  MutexLockerEx ml(current_thread->threadObj() == thread_oop ? NULL : Threads_lock);
-  // We need to re-resolve the java_thread, since a GC might have happened during the
-  // acquire of the lock
-
-  JavaThread* java_thread = java_lang_Thread::thread(JNIHandles::resolve_external_guard(thread));
-  NULL_CHECK(java_thread, JVMTI_ERROR_THREAD_NOT_ALIVE);
+  JavaThread* java_thread = NULL;
+  ThreadsListHandle tlh(current_thread);
+  jvmtiError err = JvmtiExport::cv_external_thread_to_JavaThread(tlh.list(), thread, &java_thread, NULL);
+  if (err != JVMTI_ERROR_NONE) {
+    return err;
+  }
 
   Thread::interrupt(java_thread);
 
@@ -1094,16 +1106,28 @@ JvmtiEnv::GetThreadInfo(jthread thread, jvmtiThreadInfo* info_ptr) {
   HandleMark hm;
 
   JavaThread* current_thread = JavaThread::current();
+  ThreadsListHandle tlh(current_thread);
 
   // if thread is NULL the current thread is used
-  oop thread_oop;
+  oop thread_oop = NULL;
   if (thread == NULL) {
     thread_oop = current_thread->threadObj();
+    if (thread_oop == NULL || !thread_oop->is_a(SystemDictionary::Thread_klass())) {
+      return JVMTI_ERROR_INVALID_THREAD;
+    }
   } else {
-    thread_oop = JNIHandles::resolve_external_guard(thread);
+    JavaThread* java_thread = NULL;
+    jvmtiError err = JvmtiExport::cv_external_thread_to_JavaThread(tlh.list(), thread, &java_thread, &thread_oop);
+    if (err != JVMTI_ERROR_NONE) {
+      // We got an error code so we don't have a JavaThread *, but
+      // only return an error from here if we didn't get a valid
+      // thread_oop.
+      if (thread_oop == NULL) {
+        return err;
+      }
+      // We have a valid thread_oop so we can return some thread info.
+    }
   }
-  if (thread_oop == NULL || !thread_oop->is_a(SystemDictionary::Thread_klass()))
-    return JVMTI_ERROR_INVALID_THREAD;
 
   Handle thread_obj(current_thread, thread_oop);
   Handle name;
@@ -1272,17 +1296,31 @@ JvmtiEnv::GetCurrentContendedMonitor(JavaThread* java_thread, jobject* monitor_p
 // arg - NULL is a valid value, must be checked
 jvmtiError
 JvmtiEnv::RunAgentThread(jthread thread, jvmtiStartFunction proc, const void* arg, jint priority) {
-  oop thread_oop = JNIHandles::resolve_external_guard(thread);
-  if (thread_oop == NULL || !thread_oop->is_a(SystemDictionary::Thread_klass())) {
+  JavaThread* current_thread = JavaThread::current();
+
+  JavaThread* java_thread = NULL;
+  oop thread_oop = NULL;
+  ThreadsListHandle tlh(current_thread);
+  jvmtiError err = JvmtiExport::cv_external_thread_to_JavaThread(tlh.list(), thread, &java_thread, &thread_oop);
+  if (err != JVMTI_ERROR_NONE) {
+    // We got an error code so we don't have a JavaThread *, but
+    // only return an error from here if we didn't get a valid
+    // thread_oop.
+    if (thread_oop == NULL) {
+      return err;
+    }
+    // We have a valid thread_oop.
+  }
+
+  if (java_thread != NULL) {
+    // 'thread' refers to an existing JavaThread.
     return JVMTI_ERROR_INVALID_THREAD;
   }
+
   if (priority < JVMTI_THREAD_MIN_PRIORITY || priority > JVMTI_THREAD_MAX_PRIORITY) {
     return JVMTI_ERROR_INVALID_PRIORITY;
   }
 
-  //Thread-self
-  JavaThread* current_thread = JavaThread::current();
-
   Handle thread_hndl(current_thread, thread_oop);
   {
     MutexLocker mu(Threads_lock); // grab Threads_lock
@@ -1292,7 +1330,9 @@ JvmtiEnv::RunAgentThread(jthread thread, jvmtiStartFunction proc, const void* ar
     // At this point it may be possible that no osthread was created for the
     // JavaThread due to lack of memory.
     if (new_thread == NULL || new_thread->osthread() == NULL) {
-      if (new_thread) delete new_thread;
+      if (new_thread != NULL) {
+        new_thread->smr_delete();
+      }
       return JVMTI_ERROR_OUT_OF_MEMORY;
     }
 
@@ -1394,36 +1434,53 @@ JvmtiEnv::GetThreadGroupChildren(jthreadGroup group, jint* thread_count_ptr, jth
   int ngroups = 0;
   int hidden_threads = 0;
 
-  ResourceMark rm;
-  HandleMark hm;
+  ResourceMark rm(current_thread);
+  HandleMark hm(current_thread);
 
   Handle group_hdl(current_thread, group_obj);
 
-  { MutexLocker mu(Threads_lock);
+  { // Cannot allow thread or group counts to change.
+    MutexLocker mu(Threads_lock);
 
     nthreads = java_lang_ThreadGroup::nthreads(group_hdl());
     ngroups  = java_lang_ThreadGroup::ngroups(group_hdl());
 
     if (nthreads > 0) {
+      ThreadsListHandle tlh(current_thread);
       objArrayOop threads = java_lang_ThreadGroup::threads(group_hdl());
       assert(nthreads <= threads->length(), "too many threads");
       thread_objs = NEW_RESOURCE_ARRAY(Handle,nthreads);
       for (int i=0, j=0; i<nthreads; i++) {
         oop thread_obj = threads->obj_at(i);
         assert(thread_obj != NULL, "thread_obj is NULL");
-        JavaThread *javathread = java_lang_Thread::thread(thread_obj);
-        // Filter out hidden java threads.
-        if (javathread != NULL && javathread->is_hidden_from_external_view()) {
-          hidden_threads++;
-          continue;
+        JavaThread *java_thread = NULL;
+        jvmtiError err = JvmtiExport::cv_oop_to_JavaThread(tlh.list(), thread_obj, &java_thread);
+        if (err == JVMTI_ERROR_NONE) {
+          // Have a valid JavaThread*.
+          if (java_thread->is_hidden_from_external_view()) {
+            // Filter out hidden java threads.
+            hidden_threads++;
+            continue;
+          }
+        } else {
+          // We couldn't convert thread_obj into a JavaThread*.
+          if (err == JVMTI_ERROR_INVALID_THREAD) {
+            // The thread_obj does not refer to a java.lang.Thread object
+            // so skip it.
+            hidden_threads++;
+            continue;
+          }
+          // We have a valid thread_obj, but no JavaThread*; the caller
+          // can still have limited use for the thread_obj.
         }
         thread_objs[j++] = Handle(current_thread, thread_obj);
       }
       nthreads -= hidden_threads;
-    }
+    } // ThreadsListHandle is destroyed here.
+
     if (ngroups > 0) {
       objArrayOop groups = java_lang_ThreadGroup::groups(group_hdl());
-      assert(ngroups <= groups->length(), "too many threads");
+      assert(ngroups <= groups->length(), "too many groups");
       group_objs = NEW_RESOURCE_ARRAY(Handle,ngroups);
       for (int i=0; i<ngroups; i++) {
         oop group_obj = groups->obj_at(i);
@@ -1556,7 +1613,7 @@ JvmtiEnv::PopFrame(JavaThread* java_thread) {
   }
 
   // Check if java_thread is fully suspended
-  if (!is_thread_fully_suspended(java_thread, true /* wait for suspend completion */, &debug_bits)) {
+  if (!java_thread->is_thread_fully_suspended(true /* wait for suspend completion */, &debug_bits)) {
     return JVMTI_ERROR_THREAD_NOT_SUSPENDED;
   }
   // Check to see if a PopFrame was already in progress
@@ -1686,8 +1743,8 @@ JvmtiEnv::NotifyFramePop(JavaThread* java_thread, jint depth) {
     return JVMTI_ERROR_THREAD_NOT_ALIVE;
   }
 
-  if (!JvmtiEnv::is_thread_fully_suspended(java_thread, true, &debug_bits)) {
-      return JVMTI_ERROR_THREAD_NOT_SUSPENDED;
+  if (!java_thread->is_thread_fully_suspended(true, &debug_bits)) {
+    return JVMTI_ERROR_THREAD_NOT_SUSPENDED;
   }
 
   if (TraceJVMTICalls) {
diff --git a/src/hotspot/share/prims/jvmtiEnvBase.cpp b/src/hotspot/share/prims/jvmtiEnvBase.cpp
index 2e8918c57e1..18d59fbc2e8 100644
--- a/src/hotspot/share/prims/jvmtiEnvBase.cpp
+++ b/src/hotspot/share/prims/jvmtiEnvBase.cpp
@@ -44,6 +44,7 @@
 #include "runtime/objectMonitor.inline.hpp"
 #include "runtime/signature.hpp"
 #include "runtime/thread.inline.hpp"
+#include "runtime/threadSMR.hpp"
 #include "runtime/vframe.hpp"
 #include "runtime/vframe_hp.hpp"
 #include "runtime/vmThread.hpp"
@@ -487,37 +488,6 @@ JvmtiEnvBase::set_event_callbacks(const jvmtiEventCallbacks* callbacks,
   }
 }
 
-// Called from JVMTI entry points which perform stack walking. If the
-// associated JavaThread is the current thread, then wait_for_suspend
-// is not used. Otherwise, it determines if we should wait for the
-// "other" thread to complete external suspension. (NOTE: in future
-// releases the suspension mechanism should be reimplemented so this
-// is not necessary.)
-//
-bool
-JvmtiEnvBase::is_thread_fully_suspended(JavaThread* thr, bool wait_for_suspend, uint32_t *bits) {
-  // "other" threads require special handling
-  if (thr != JavaThread::current()) {
-    if (wait_for_suspend) {
-      // We are allowed to wait for the external suspend to complete
-      // so give the other thread a chance to get suspended.
-      if (!thr->wait_for_ext_suspend_completion(SuspendRetryCount,
-          SuspendRetryDelay, bits)) {
-        // didn't make it so let the caller know
-        return false;
-      }
-    }
-    // We aren't allowed to wait for the external suspend to complete
-    // so if the other thread isn't externally suspended we need to
-    // let the caller know.
-    else if (!thr->is_ext_suspend_completed_with_lock(bits)) {
-      return false;
-    }
-  }
-
-  return true;
-}
-
 
 // In the fullness of time, all users of the method should instead
 // directly use allocate, besides being cleaner and faster, this will
@@ -560,19 +530,6 @@ JvmtiEnvBase::new_jthreadGroupArray(int length, Handle *handles) {
   return (jthreadGroup *) new_jobjectArray(length,handles);
 }
 
-
-JavaThread *
-JvmtiEnvBase::get_JavaThread(jthread jni_thread) {
-  oop t = JNIHandles::resolve_external_guard(jni_thread);
-  if (t == NULL || !t->is_a(SystemDictionary::Thread_klass())) {
-    return NULL;
-  }
-  // The following returns NULL if the thread has not yet run or is in
-  // process of exiting
-  return java_lang_Thread::thread(t);
-}
-
-
 // return the vframe on the specified thread and depth, NULL if no such frame
 vframe*
 JvmtiEnvBase::vframeFor(JavaThread* java_thread, jint depth) {
@@ -670,7 +627,7 @@ JvmtiEnvBase::get_current_contended_monitor(JavaThread *calling_thread, JavaThre
   uint32_t debug_bits = 0;
 #endif
   assert((SafepointSynchronize::is_at_safepoint() ||
-          is_thread_fully_suspended(java_thread, false, &debug_bits)),
+          java_thread->is_thread_fully_suspended(false, &debug_bits)),
          "at safepoint or target thread is suspended");
   oop obj = NULL;
   ObjectMonitor *mon = java_thread->current_waiting_monitor();
@@ -709,7 +666,7 @@ JvmtiEnvBase::get_owned_monitors(JavaThread *calling_thread, JavaThread* java_th
   uint32_t debug_bits = 0;
 #endif
   assert((SafepointSynchronize::is_at_safepoint() ||
-          is_thread_fully_suspended(java_thread, false, &debug_bits)),
+          java_thread->is_thread_fully_suspended(false, &debug_bits)),
          "at safepoint or target thread is suspended");
 
   if (java_thread->has_last_Java_frame()) {
@@ -831,7 +788,7 @@ JvmtiEnvBase::get_stack_trace(JavaThread *java_thread,
   uint32_t debug_bits = 0;
 #endif
   assert((SafepointSynchronize::is_at_safepoint() ||
-          is_thread_fully_suspended(java_thread, false, &debug_bits)),
+          java_thread->is_thread_fully_suspended(false, &debug_bits)),
          "at safepoint or target thread is suspended");
   int count = 0;
   if (java_thread->has_last_Java_frame()) {
@@ -914,7 +871,7 @@ JvmtiEnvBase::get_frame_location(JavaThread *java_thread, jint depth,
   uint32_t debug_bits = 0;
 #endif
   assert((SafepointSynchronize::is_at_safepoint() ||
-          is_thread_fully_suspended(java_thread, false, &debug_bits)),
+          java_thread->is_thread_fully_suspended(false, &debug_bits)),
          "at safepoint or target thread is suspended");
   Thread* current_thread = Thread::current();
   ResourceMark rm(current_thread);
@@ -976,7 +933,7 @@ JvmtiEnvBase::get_object_monitor_usage(JavaThread* calling_thread, jobject objec
   // first derive the object's owner and entry_count (if any)
   {
     // Revoke any biases before querying the mark word
-    if (SafepointSynchronize::is_at_safepoint()) {
+    if (at_safepoint) {
       BiasedLocking::revoke_at_safepoint(hobj);
     } else {
       BiasedLocking::revoke_and_rebias(hobj, false, calling_thread);
@@ -1008,11 +965,11 @@ JvmtiEnvBase::get_object_monitor_usage(JavaThread* calling_thread, jobject objec
     }
 
     if (owner != NULL) {
+      // Use current thread since function can be called from a
+      // JavaThread or the VMThread.
+      ThreadsListHandle tlh;
       // This monitor is owned so we have to find the owning JavaThread.
-      // Since owning_thread_from_monitor_owner() grabs a lock, GC can
-      // move our object at this point. However, our owner value is safe
-      // since it is either the Lock word on a stack or a JavaThread *.
-      owning_thread = Threads::owning_thread_from_monitor_owner(owner, !at_safepoint);
+      owning_thread = Threads::owning_thread_from_monitor_owner(tlh.list(), owner);
       // Cannot assume (owning_thread != NULL) here because this function
       // may not have been called at a safepoint and the owning_thread
       // might not be suspended.
@@ -1021,7 +978,7 @@ JvmtiEnvBase::get_object_monitor_usage(JavaThread* calling_thread, jobject objec
         // or it has to be suspended. Any of these conditions will prevent both
         // contending and waiting threads from modifying the state of
         // the monitor.
-        if (!at_safepoint && !JvmtiEnv::is_thread_fully_suspended(owning_thread, true, &debug_bits)) {
+        if (!at_safepoint && !owning_thread->is_thread_fully_suspended(true, &debug_bits)) {
           // Don't worry! This return of JVMTI_ERROR_THREAD_NOT_SUSPENDED
           // will not make it back to the JVM/TI agent. The error code will
           // get intercepted in JvmtiEnv::GetObjectMonitorUsage() which
@@ -1033,7 +990,7 @@ JvmtiEnvBase::get_object_monitor_usage(JavaThread* calling_thread, jobject objec
         ret.owner = (jthread)jni_reference(calling_thread, th);
       }
       // implied else: no owner
-    }
+    } // ThreadsListHandle is destroyed here.
 
     if (owning_thread != NULL) {  // monitor is owned
       // The recursions field of a monitor does not reflect recursions
@@ -1084,13 +1041,15 @@ JvmtiEnvBase::get_object_monitor_usage(JavaThread* calling_thread, jobject objec
     if (ret.waiter_count > 0) {
       // we have contending and/or waiting threads
       HandleMark hm;
+      // Use current thread since function can be called from a
+      // JavaThread or the VMThread.
+      ThreadsListHandle tlh;
       if (nWant > 0) {
         // we have contending threads
         ResourceMark rm;
         // get_pending_threads returns only java thread so we do not need to
-        // check for  non java threads.
-        GrowableArray<JavaThread*>* wantList = Threads::get_pending_threads(
-          nWant, (address)mon, !at_safepoint);
+        // check for non java threads.
+        GrowableArray<JavaThread*>* wantList = Threads::get_pending_threads(tlh.list(), nWant, (address)mon);
         if (wantList->length() < nWant) {
           // robustness: the pending list has gotten smaller
           nWant = wantList->length();
@@ -1101,7 +1060,7 @@ JvmtiEnvBase::get_object_monitor_usage(JavaThread* calling_thread, jobject objec
           // thread could potentially change the state of the monitor by
           // entering it. The JVM/TI spec doesn't allow this.
           if (owning_thread == NULL && !at_safepoint &
-              !JvmtiEnv::is_thread_fully_suspended(pending_thread, true, &debug_bits)) {
+              !pending_thread->is_thread_fully_suspended(true, &debug_bits)) {
             if (ret.owner != NULL) {
               destroy_jni_reference(calling_thread, ret.owner);
             }
@@ -1139,7 +1098,7 @@ JvmtiEnvBase::get_object_monitor_usage(JavaThread* calling_thread, jobject objec
           waiter = mon->next_waiter(waiter);
         }
       }
-    }
+    } // ThreadsListHandle is destroyed here.
 
     // Adjust count. nWant and nWait count values may be less than original.
     ret.waiter_count = nWant + nWait;
@@ -1291,14 +1250,23 @@ VM_GetThreadListStackTraces::doit() {
   assert(SafepointSynchronize::is_at_safepoint(), "must be at safepoint");
 
   ResourceMark rm;
+  ThreadsListHandle tlh;
   for (int i = 0; i < _thread_count; ++i) {
     jthread jt = _thread_list[i];
-    oop thread_oop = JNIHandles::resolve_external_guard(jt);
-    if (thread_oop == NULL || !thread_oop->is_a(SystemDictionary::Thread_klass())) {
-      set_result(JVMTI_ERROR_INVALID_THREAD);
-      return;
+    JavaThread* java_thread = NULL;
+    oop thread_oop = NULL;
+    jvmtiError err = JvmtiExport::cv_external_thread_to_JavaThread(tlh.list(), jt, &java_thread, &thread_oop);
+    if (err != JVMTI_ERROR_NONE) {
+      // We got an error code so we don't have a JavaThread *, but
+      // only return an error from here if we didn't get a valid
+      // thread_oop.
+      if (thread_oop == NULL) {
+        set_result(err);
+        return;
+      }
+      // We have a valid thread_oop.
     }
-    fill_frames(jt, java_lang_Thread::thread(thread_oop), thread_oop);
+    fill_frames(jt, java_thread, thread_oop);
   }
   allocate_and_fill_stacks(_thread_count);
 }
@@ -1309,7 +1277,7 @@ VM_GetAllStackTraces::doit() {
 
   ResourceMark rm;
   _final_thread_count = 0;
-  for (JavaThread *jt = Threads::first(); jt != NULL; jt = jt->next()) {
+  for (JavaThreadIteratorWithHandle jtiwh; JavaThread *jt = jtiwh.next(); ) {
     oop thread_oop = jt->threadObj();
     if (thread_oop != NULL &&
         !jt->is_exiting() &&
@@ -1404,9 +1372,7 @@ JvmtiEnvBase::force_early_return(JavaThread* java_thread, jvalue value, TosState
   }
 
   // Check if java_thread is fully suspended
-  if (!is_thread_fully_suspended(java_thread,
-                                 true /* wait for suspend completion */,
-                                 &debug_bits)) {
+  if (!java_thread->is_thread_fully_suspended(true /* wait for suspend completion */, &debug_bits)) {
     return JVMTI_ERROR_THREAD_NOT_SUSPENDED;
   }
 
@@ -1521,3 +1487,79 @@ JvmtiModuleClosure::get_all_modules(JvmtiEnv* env, jint* module_count_ptr, jobje
   return JVMTI_ERROR_NONE;
 }
 
+void
+VM_UpdateForPopTopFrame::doit() {
+  JavaThread* jt = _state->get_thread();
+  ThreadsListHandle tlh;
+  if (jt != NULL && tlh.includes(jt) && !jt->is_exiting() && jt->threadObj() != NULL) {
+    _state->update_for_pop_top_frame();
+  } else {
+    _result = JVMTI_ERROR_THREAD_NOT_ALIVE;
+  }
+}
+
+void
+VM_SetFramePop::doit() {
+  JavaThread* jt = _state->get_thread();
+  ThreadsListHandle tlh;
+  if (jt != NULL && tlh.includes(jt) && !jt->is_exiting() && jt->threadObj() != NULL) {
+    int frame_number = _state->count_frames() - _depth;
+    _state->env_thread_state((JvmtiEnvBase*)_env)->set_frame_pop(frame_number);
+  } else {
+    _result = JVMTI_ERROR_THREAD_NOT_ALIVE;
+  }
+}
+
+void
+VM_GetOwnedMonitorInfo::doit() {
+  _result = JVMTI_ERROR_THREAD_NOT_ALIVE;
+  ThreadsListHandle tlh;
+  if (_java_thread != NULL && tlh.includes(_java_thread)
+      && !_java_thread->is_exiting() && _java_thread->threadObj() != NULL) {
+    _result = ((JvmtiEnvBase *)_env)->get_owned_monitors(_calling_thread, _java_thread,
+                                                          _owned_monitors_list);
+  }
+}
+
+void
+VM_GetCurrentContendedMonitor::doit() {
+  _result = JVMTI_ERROR_THREAD_NOT_ALIVE;
+  ThreadsListHandle tlh;
+  if (_java_thread != NULL && tlh.includes(_java_thread)
+      && !_java_thread->is_exiting() && _java_thread->threadObj() != NULL) {
+    _result = ((JvmtiEnvBase *)_env)->get_current_contended_monitor(_calling_thread,_java_thread,_owned_monitor_ptr);
+  }
+}
+
+void
+VM_GetStackTrace::doit() {
+  _result = JVMTI_ERROR_THREAD_NOT_ALIVE;
+  ThreadsListHandle tlh;
+  if (_java_thread != NULL && tlh.includes(_java_thread)
+      && !_java_thread->is_exiting() && _java_thread->threadObj() != NULL) {
+    _result = ((JvmtiEnvBase *)_env)->get_stack_trace(_java_thread,
+                                                      _start_depth, _max_count,
+                                                      _frame_buffer, _count_ptr);
+  }
+}
+
+void
+VM_GetFrameCount::doit() {
+  _result = JVMTI_ERROR_THREAD_NOT_ALIVE;
+  JavaThread* jt = _state->get_thread();
+  ThreadsListHandle tlh;
+  if (jt != NULL && tlh.includes(jt) && !jt->is_exiting() && jt->threadObj() != NULL) {
+    _result = ((JvmtiEnvBase*)_env)->get_frame_count(_state, _count_ptr);
+  }
+}
+
+void
+VM_GetFrameLocation::doit() {
+  _result = JVMTI_ERROR_THREAD_NOT_ALIVE;
+  ThreadsListHandle tlh;
+  if (_java_thread != NULL && tlh.includes(_java_thread)
+      && !_java_thread->is_exiting() && _java_thread->threadObj() != NULL) {
+    _result = ((JvmtiEnvBase*)_env)->get_frame_location(_java_thread, _depth,
+                                                        _method_ptr, _location_ptr);
+  }
+}
diff --git a/src/hotspot/share/prims/jvmtiEnvBase.hpp b/src/hotspot/share/prims/jvmtiEnvBase.hpp
index c5786aca4af..84e3f54ae6d 100644
--- a/src/hotspot/share/prims/jvmtiEnvBase.hpp
+++ b/src/hotspot/share/prims/jvmtiEnvBase.hpp
@@ -280,9 +280,6 @@ class JvmtiEnvBase : public CHeapObj<mtInternal> {
   jthread * new_jthreadArray(int length, Handle *handles);
   jthreadGroup * new_jthreadGroupArray(int length, Handle *handles);
 
-  // convert from JNIHandle to JavaThread *
-  JavaThread  * get_JavaThread(jthread jni_thread);
-
   // convert to a jni jclass from a non-null Klass*
   jclass get_jni_class_non_null(Klass* k);
 
@@ -297,11 +294,6 @@ class JvmtiEnvBase : public CHeapObj<mtInternal> {
  public:
   // get a field descriptor for the specified class and field
   static bool get_field_descriptor(Klass* k, jfieldID field, fieldDescriptor* fd);
-  // test for suspend - most (all?) of these should go away
-  static bool is_thread_fully_suspended(JavaThread *thread,
-                                        bool wait_for_suspend,
-                                        uint32_t *bits);
-
 
   // JVMTI API helper functions which are called at safepoint or thread is suspended.
   jvmtiError get_frame_count(JvmtiThreadState *state, jint *count_ptr);
@@ -360,14 +352,7 @@ public:
   }
   VMOp_Type type() const { return VMOp_UpdateForPopTopFrame; }
   jvmtiError result() { return _result; }
-  void doit() {
-    JavaThread* jt = _state->get_thread();
-    if (Threads::includes(jt) && !jt->is_exiting() && jt->threadObj() != NULL) {
-      _state->update_for_pop_top_frame();
-    } else {
-      _result = JVMTI_ERROR_THREAD_NOT_ALIVE;
-    }
-  }
+  void doit();
 };
 
 // VM operation to set frame pop.
@@ -390,15 +375,7 @@ public:
   bool allow_nested_vm_operations() const { return true; }
   VMOp_Type type() const { return VMOp_SetFramePop; }
   jvmtiError result() { return _result; }
-  void doit() {
-    JavaThread* jt = _state->get_thread();
-    if (Threads::includes(jt) && !jt->is_exiting() && jt->threadObj() != NULL) {
-      int frame_number = _state->count_frames() - _depth;
-      _state->env_thread_state((JvmtiEnvBase*)_env)->set_frame_pop(frame_number);
-    } else {
-      _result = JVMTI_ERROR_THREAD_NOT_ALIVE;
-    }
-  }
+  void doit();
 };
 
 
@@ -422,14 +399,7 @@ public:
     _result = JVMTI_ERROR_NONE;
   }
   VMOp_Type type() const { return VMOp_GetOwnedMonitorInfo; }
-  void doit() {
-    _result = JVMTI_ERROR_THREAD_NOT_ALIVE;
-    if (Threads::includes(_java_thread) && !_java_thread->is_exiting()
-                                        && _java_thread->threadObj() != NULL) {
-      _result = ((JvmtiEnvBase *)_env)->get_owned_monitors(_calling_thread, _java_thread,
-                                                            _owned_monitors_list);
-    }
-  }
+  void doit();
   jvmtiError result() { return _result; }
 };
 
@@ -476,13 +446,7 @@ public:
   }
   VMOp_Type type() const { return VMOp_GetCurrentContendedMonitor; }
   jvmtiError result() { return _result; }
-  void doit() {
-    _result = JVMTI_ERROR_THREAD_NOT_ALIVE;
-    if (Threads::includes(_java_thread) && !_java_thread->is_exiting() &&
-        _java_thread->threadObj() != NULL) {
-      _result = ((JvmtiEnvBase *)_env)->get_current_contended_monitor(_calling_thread,_java_thread,_owned_monitor_ptr);
-    }
-  }
+  void doit();
 };
 
 // VM operation to get stack trace at safepoint.
@@ -509,15 +473,7 @@ public:
   }
   jvmtiError result() { return _result; }
   VMOp_Type type() const { return VMOp_GetStackTrace; }
-  void doit() {
-    _result = JVMTI_ERROR_THREAD_NOT_ALIVE;
-    if (Threads::includes(_java_thread) && !_java_thread->is_exiting()
-                                        && _java_thread->threadObj() != NULL) {
-      _result = ((JvmtiEnvBase *)_env)->get_stack_trace(_java_thread,
-                                                        _start_depth, _max_count,
-                                                        _frame_buffer, _count_ptr);
-    }
-  }
+  void doit();
 };
 
 // forward declaration
@@ -607,13 +563,7 @@ public:
   }
   VMOp_Type type() const { return VMOp_GetFrameCount; }
   jvmtiError result()    { return _result; }
-  void doit() {
-    _result = JVMTI_ERROR_THREAD_NOT_ALIVE;
-    JavaThread* jt = _state->get_thread();
-    if (Threads::includes(jt) && !jt->is_exiting() && jt->threadObj() != NULL) {
-      _result = ((JvmtiEnvBase*)_env)->get_frame_count(_state, _count_ptr);
-    }
-  }
+  void doit();
 };
 
 // VM operation to frame location at safepoint.
@@ -637,14 +587,7 @@ public:
   }
   VMOp_Type type() const { return VMOp_GetFrameLocation; }
   jvmtiError result()    { return _result; }
-  void doit() {
-    _result = JVMTI_ERROR_THREAD_NOT_ALIVE;
-    if (Threads::includes(_java_thread) && !_java_thread->is_exiting() &&
-        _java_thread->threadObj() != NULL) {
-      _result = ((JvmtiEnvBase*)_env)->get_frame_location(_java_thread, _depth,
-                                                          _method_ptr, _location_ptr);
-    }
-  }
+  void doit();
 };
 
 
diff --git a/src/hotspot/share/prims/jvmtiEnvThreadState.cpp b/src/hotspot/share/prims/jvmtiEnvThreadState.cpp
index d7ffd105596..643c4409274 100644
--- a/src/hotspot/share/prims/jvmtiEnvThreadState.cpp
+++ b/src/hotspot/share/prims/jvmtiEnvThreadState.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2003, 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2017, 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
@@ -35,6 +35,7 @@
 #include "runtime/interfaceSupport.hpp"
 #include "runtime/javaCalls.hpp"
 #include "runtime/signature.hpp"
+#include "runtime/thread.inline.hpp"
 #include "runtime/vframe.hpp"
 #include "runtime/vm_operations.hpp"
 
diff --git a/src/hotspot/share/prims/jvmtiEnvThreadState.hpp b/src/hotspot/share/prims/jvmtiEnvThreadState.hpp
index c25e5e34547..9b947f93e94 100644
--- a/src/hotspot/share/prims/jvmtiEnvThreadState.hpp
+++ b/src/hotspot/share/prims/jvmtiEnvThreadState.hpp
@@ -27,7 +27,6 @@
 
 #include "jvmtifiles/jvmti.h"
 #include "memory/allocation.hpp"
-#include "memory/allocation.inline.hpp"
 #include "oops/instanceKlass.hpp"
 #include "prims/jvmtiEventController.hpp"
 #include "utilities/globalDefinitions.hpp"
diff --git a/src/hotspot/share/prims/jvmtiEventController.cpp b/src/hotspot/share/prims/jvmtiEventController.cpp
index cdad3264c82..3c919c61106 100644
--- a/src/hotspot/share/prims/jvmtiEventController.cpp
+++ b/src/hotspot/share/prims/jvmtiEventController.cpp
@@ -33,7 +33,8 @@
 #include "prims/jvmtiImpl.hpp"
 #include "prims/jvmtiThreadState.inline.hpp"
 #include "runtime/frame.hpp"
-#include "runtime/thread.hpp"
+#include "runtime/thread.inline.hpp"
+#include "runtime/threadSMR.hpp"
 #include "runtime/vframe.hpp"
 #include "runtime/vframe_hp.hpp"
 #include "runtime/vmThread.hpp"
@@ -580,13 +581,10 @@ JvmtiEventControllerPrivate::recompute_enabled() {
   // filtered events and there weren't last time
   if (    (any_env_thread_enabled & THREAD_FILTERED_EVENT_BITS) != 0 &&
       (was_any_env_thread_enabled & THREAD_FILTERED_EVENT_BITS) == 0) {
-    {
-      MutexLocker mu(Threads_lock);   //hold the Threads_lock for the iteration
-      for (JavaThread *tp = Threads::first(); tp != NULL; tp = tp->next()) {
-        // state_for_while_locked() makes tp->is_exiting() check
-        JvmtiThreadState::state_for_while_locked(tp);  // create the thread state if missing
-      }
-    }// release Threads_lock
+    for (JavaThreadIteratorWithHandle jtiwh; JavaThread *tp = jtiwh.next(); ) {
+      // state_for_while_locked() makes tp->is_exiting() check
+      JvmtiThreadState::state_for_while_locked(tp);  // create the thread state if missing
+    }
   }
 
   // compute and set thread-filtered events
diff --git a/src/hotspot/share/prims/jvmtiEventController.hpp b/src/hotspot/share/prims/jvmtiEventController.hpp
index 90b000ccfb3..c250c2f4772 100644
--- a/src/hotspot/share/prims/jvmtiEventController.hpp
+++ b/src/hotspot/share/prims/jvmtiEventController.hpp
@@ -27,7 +27,6 @@
 
 #include "jvmtifiles/jvmti.h"
 #include "memory/allocation.hpp"
-#include "memory/allocation.inline.hpp"
 #include "utilities/globalDefinitions.hpp"
 
 // forward declaration
diff --git a/src/hotspot/share/prims/jvmtiExport.cpp b/src/hotspot/share/prims/jvmtiExport.cpp
index 7d8de627808..631ddcdabaa 100644
--- a/src/hotspot/share/prims/jvmtiExport.cpp
+++ b/src/hotspot/share/prims/jvmtiExport.cpp
@@ -53,6 +53,7 @@
 #include "runtime/objectMonitor.inline.hpp"
 #include "runtime/os.inline.hpp"
 #include "runtime/thread.inline.hpp"
+#include "runtime/threadSMR.hpp"
 #include "runtime/vframe.hpp"
 #include "services/serviceUtil.hpp"
 #include "utilities/macros.hpp"
@@ -721,6 +722,108 @@ JvmtiExport::get_all_native_method_prefixes(int* count_ptr) {
   }
 }
 
+// Convert an external thread reference to a JavaThread found on the
+// specified ThreadsList. The ThreadsListHandle in the caller "protects"
+// the returned JavaThread *.
+//
+// If thread_oop_p is not NULL, then the caller wants to use the oop
+// after this call so the oop is returned. On success, *jt_pp is set
+// to the converted JavaThread * and JVMTI_ERROR_NONE is returned.
+// On error, returns various JVMTI_ERROR_* values.
+//
+jvmtiError
+JvmtiExport::cv_external_thread_to_JavaThread(ThreadsList * t_list,
+                                              jthread thread,
+                                              JavaThread ** jt_pp,
+                                              oop * thread_oop_p) {
+  assert(t_list != NULL, "must have a ThreadsList");
+  assert(jt_pp != NULL, "must have a return JavaThread pointer");
+  // thread_oop_p is optional so no assert()
+
+  oop thread_oop = JNIHandles::resolve_external_guard(thread);
+  if (thread_oop == NULL) {
+    // NULL jthread, GC'ed jthread or a bad JNI handle.
+    return JVMTI_ERROR_INVALID_THREAD;
+  }
+  // Looks like an oop at this point.
+
+  if (!thread_oop->is_a(SystemDictionary::Thread_klass())) {
+    // The oop is not a java.lang.Thread.
+    return JVMTI_ERROR_INVALID_THREAD;
+  }
+  // Looks like a java.lang.Thread oop at this point.
+
+  if (thread_oop_p != NULL) {
+    // Return the oop to the caller; the caller may still want
+    // the oop even if this function returns an error.
+    *thread_oop_p = thread_oop;
+  }
+
+  JavaThread * java_thread = java_lang_Thread::thread(thread_oop);
+  if (java_thread == NULL) {
+    // The java.lang.Thread does not contain a JavaThread * so it has
+    // not yet run or it has died.
+    return JVMTI_ERROR_THREAD_NOT_ALIVE;
+  }
+  // Looks like a live JavaThread at this point.
+
+  // We do not check the EnableThreadSMRExtraValidityChecks option
+  // for this includes() call because JVM/TI's spec is tighter.
+  if (!t_list->includes(java_thread)) {
+    // Not on the JavaThreads list so it is not alive.
+    return JVMTI_ERROR_THREAD_NOT_ALIVE;
+  }
+
+  // Return a live JavaThread that is "protected" by the
+  // ThreadsListHandle in the caller.
+  *jt_pp = java_thread;
+
+  return JVMTI_ERROR_NONE;
+}
+
+// Convert an oop to a JavaThread found on the specified ThreadsList.
+// The ThreadsListHandle in the caller "protects" the returned
+// JavaThread *.
+//
+// On success, *jt_pp is set to the converted JavaThread * and
+// JVMTI_ERROR_NONE is returned. On error, returns various
+// JVMTI_ERROR_* values.
+//
+jvmtiError
+JvmtiExport::cv_oop_to_JavaThread(ThreadsList * t_list, oop thread_oop,
+                                  JavaThread ** jt_pp) {
+  assert(t_list != NULL, "must have a ThreadsList");
+  assert(thread_oop != NULL, "must have an oop");
+  assert(jt_pp != NULL, "must have a return JavaThread pointer");
+
+  if (!thread_oop->is_a(SystemDictionary::Thread_klass())) {
+    // The oop is not a java.lang.Thread.
+    return JVMTI_ERROR_INVALID_THREAD;
+  }
+  // Looks like a java.lang.Thread oop at this point.
+
+  JavaThread * java_thread = java_lang_Thread::thread(thread_oop);
+  if (java_thread == NULL) {
+    // The java.lang.Thread does not contain a JavaThread * so it has
+    // not yet run or it has died.
+    return JVMTI_ERROR_THREAD_NOT_ALIVE;
+  }
+  // Looks like a live JavaThread at this point.
+
+  // We do not check the EnableThreadSMRExtraValidityChecks option
+  // for this includes() call because JVM/TI's spec is tighter.
+  if (!t_list->includes(java_thread)) {
+    // Not on the JavaThreads list so it is not alive.
+    return JVMTI_ERROR_THREAD_NOT_ALIVE;
+  }
+
+  // Return a live JavaThread that is "protected" by the
+  // ThreadsListHandle in the caller.
+  *jt_pp = java_thread;
+
+  return JVMTI_ERROR_NONE;
+}
+
 class JvmtiClassFileLoadHookPoster : public StackObj {
  private:
   Symbol*            _h_name;
@@ -2475,7 +2578,7 @@ extern "C" {
 
 jint JvmtiExport::load_agent_library(const char *agent, const char *absParam,
                                      const char *options, outputStream* st) {
-  char ebuf[1024];
+  char ebuf[1024] = {0};
   char buffer[JVM_MAXPATHLEN];
   void* library = NULL;
   jint result = JNI_ERR;
@@ -2525,6 +2628,8 @@ jint JvmtiExport::load_agent_library(const char *agent, const char *absParam,
       if (!agent_lib->is_static_lib()) {
         os::dll_unload(library);
       }
+      st->print_cr("%s is not available in %s",
+                   on_attach_symbols[0], agent_lib->name());
       delete agent_lib;
     } else {
       // Invoke the Agent_OnAttach function
@@ -2551,9 +2656,14 @@ jint JvmtiExport::load_agent_library(const char *agent, const char *absParam,
       }
 
       // Agent_OnAttach executed so completion status is JNI_OK
-      st->print_cr("%d", result);
+      st->print_cr("return code: %d", result);
       result = JNI_OK;
     }
+  } else {
+    st->print_cr("%s was not loaded.", agent);
+    if (*ebuf != '\0') {
+      st->print_cr("%s", ebuf);
+    }
   }
   return result;
 }
@@ -2685,8 +2795,7 @@ void JvmtiVMObjectAllocEventCollector::oops_do_for_all_threads(OopClosure* f) {
     return;
   }
 
-  // Runs at safepoint. So no need to acquire Threads_lock.
-  for (JavaThread *jthr = Threads::first(); jthr != NULL; jthr = jthr->next()) {
+  for (JavaThreadIteratorWithHandle jtiwh; JavaThread *jthr = jtiwh.next(); ) {
     JvmtiThreadState *state = jthr->jvmti_thread_state();
     if (state != NULL) {
       JvmtiVMObjectAllocEventCollector *collector;
diff --git a/src/hotspot/share/prims/jvmtiExport.hpp b/src/hotspot/share/prims/jvmtiExport.hpp
index 7bae70b626e..3421150cab2 100644
--- a/src/hotspot/share/prims/jvmtiExport.hpp
+++ b/src/hotspot/share/prims/jvmtiExport.hpp
@@ -399,6 +399,14 @@ class JvmtiExport : public AllStatic {
 
   // SetNativeMethodPrefix support
   static char** get_all_native_method_prefixes(int* count_ptr) NOT_JVMTI_RETURN_(NULL);
+
+  // JavaThread lifecycle support:
+  static jvmtiError cv_external_thread_to_JavaThread(ThreadsList * t_list,
+                                                     jthread thread,
+                                                     JavaThread ** jt_pp,
+                                                     oop * thread_oop_p);
+  static jvmtiError cv_oop_to_JavaThread(ThreadsList * t_list, oop thread_oop,
+                                         JavaThread ** jt_pp);
 };
 
 // Support class used by JvmtiDynamicCodeEventCollector and others. It
diff --git a/src/hotspot/share/prims/jvmtiImpl.cpp b/src/hotspot/share/prims/jvmtiImpl.cpp
index 4869a653144..82cdabdbd9b 100644
--- a/src/hotspot/share/prims/jvmtiImpl.cpp
+++ b/src/hotspot/share/prims/jvmtiImpl.cpp
@@ -46,6 +46,7 @@
 #include "runtime/serviceThread.hpp"
 #include "runtime/signature.hpp"
 #include "runtime/thread.inline.hpp"
+#include "runtime/threadSMR.hpp"
 #include "runtime/vframe.hpp"
 #include "runtime/vframe_hp.hpp"
 #include "runtime/vm_operations.hpp"
@@ -878,10 +879,9 @@ bool JvmtiSuspendControl::resume(JavaThread *java_thread) {
 
 void JvmtiSuspendControl::print() {
 #ifndef PRODUCT
-  MutexLocker mu(Threads_lock);
   LogStreamHandle(Trace, jvmti) log_stream;
   log_stream.print("Suspended Threads: [");
-  for (JavaThread *thread = Threads::first(); thread != NULL; thread = thread->next()) {
+  for (JavaThreadIteratorWithHandle jtiwh; JavaThread *thread = jtiwh.next(); ) {
 #ifdef JVMTI_TRACE
     const char *name   = JvmtiTrace::safe_get_thread_name(thread);
 #else
diff --git a/src/hotspot/share/prims/jvmtiRedefineClasses.cpp b/src/hotspot/share/prims/jvmtiRedefineClasses.cpp
index c73842e0500..abca485f97b 100644
--- a/src/hotspot/share/prims/jvmtiRedefineClasses.cpp
+++ b/src/hotspot/share/prims/jvmtiRedefineClasses.cpp
@@ -43,6 +43,7 @@
 #include "oops/oop.inline.hpp"
 #include "prims/jvmtiImpl.hpp"
 #include "prims/jvmtiRedefineClasses.hpp"
+#include "prims/jvmtiThreadState.inline.hpp"
 #include "prims/resolvedMethodTable.hpp"
 #include "prims/methodComparator.hpp"
 #include "runtime/deoptimization.hpp"
diff --git a/src/hotspot/share/prims/jvmtiTagMap.cpp b/src/hotspot/share/prims/jvmtiTagMap.cpp
index 3a45b2c8eff..6ea720a1e44 100644
--- a/src/hotspot/share/prims/jvmtiTagMap.cpp
+++ b/src/hotspot/share/prims/jvmtiTagMap.cpp
@@ -45,6 +45,8 @@
 #include "runtime/mutex.hpp"
 #include "runtime/mutexLocker.hpp"
 #include "runtime/reflectionUtils.hpp"
+#include "runtime/thread.inline.hpp"
+#include "runtime/threadSMR.hpp"
 #include "runtime/vframe.hpp"
 #include "runtime/vmThread.hpp"
 #include "runtime/vm_operations.hpp"
@@ -3174,7 +3176,7 @@ inline bool VM_HeapWalkOperation::collect_stack_roots(JavaThread* java_thread,
 // stack to find all references and local JNI refs.
 inline bool VM_HeapWalkOperation::collect_stack_roots() {
   JNILocalRootsClosure blk;
-  for (JavaThread* thread = Threads::first(); thread != NULL ; thread = thread->next()) {
+  for (JavaThreadIteratorWithHandle jtiwh; JavaThread *thread = jtiwh.next(); ) {
     oop threadObj = thread->threadObj();
     if (threadObj != NULL && !thread->is_exiting() && !thread->is_hidden_from_external_view()) {
       // Collect the simple root for this thread before we
diff --git a/src/hotspot/share/prims/jvmtiThreadState.hpp b/src/hotspot/share/prims/jvmtiThreadState.hpp
index 9dfb9b85309..16a0a3455c7 100644
--- a/src/hotspot/share/prims/jvmtiThreadState.hpp
+++ b/src/hotspot/share/prims/jvmtiThreadState.hpp
@@ -27,7 +27,6 @@
 
 #include "jvmtifiles/jvmti.h"
 #include "memory/allocation.hpp"
-#include "memory/allocation.inline.hpp"
 #include "prims/jvmtiEventController.hpp"
 #include "runtime/thread.hpp"
 #include "utilities/growableArray.hpp"
@@ -336,34 +335,10 @@ class JvmtiThreadState : public CHeapObj<mtInternal> {
 
   // already holding JvmtiThreadState_lock - retrieve or create JvmtiThreadState
   // Can return NULL if JavaThread is exiting.
-  inline static JvmtiThreadState *state_for_while_locked(JavaThread *thread) {
-    assert(JvmtiThreadState_lock->is_locked(), "sanity check");
-
-    JvmtiThreadState *state = thread->jvmti_thread_state();
-    if (state == NULL) {
-      if (thread->is_exiting()) {
-        // don't add a JvmtiThreadState to a thread that is exiting
-        return NULL;
-      }
-
-      state = new JvmtiThreadState(thread);
-    }
-    return state;
-  }
-
+  static JvmtiThreadState *state_for_while_locked(JavaThread *thread);
   // retrieve or create JvmtiThreadState
   // Can return NULL if JavaThread is exiting.
-  inline static JvmtiThreadState *state_for(JavaThread *thread) {
-    JvmtiThreadState *state = thread->jvmti_thread_state();
-    if (state == NULL) {
-      MutexLocker mu(JvmtiThreadState_lock);
-      // check again with the lock held
-      state = state_for_while_locked(thread);
-    } else {
-      CHECK_UNHANDLED_OOPS_ONLY(Thread::current()->clear_unhandled_oops());
-    }
-    return state;
-  }
+  static JvmtiThreadState *state_for(JavaThread *thread);
 
   // JVMTI ForceEarlyReturn support
 
diff --git a/src/hotspot/share/prims/jvmtiThreadState.inline.hpp b/src/hotspot/share/prims/jvmtiThreadState.inline.hpp
index 1b9926fb1da..e3859a3334d 100644
--- a/src/hotspot/share/prims/jvmtiThreadState.inline.hpp
+++ b/src/hotspot/share/prims/jvmtiThreadState.inline.hpp
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2006, 2017, 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
@@ -68,4 +68,31 @@ void JvmtiThreadState::set_head_env_thread_state(JvmtiEnvThreadState* ets) {
   _head_env_thread_state = ets;
 }
 
+inline JvmtiThreadState* JvmtiThreadState::state_for_while_locked(JavaThread *thread) {
+  assert(JvmtiThreadState_lock->is_locked(), "sanity check");
+
+  JvmtiThreadState *state = thread->jvmti_thread_state();
+  if (state == NULL) {
+    if (thread->is_exiting()) {
+      // don't add a JvmtiThreadState to a thread that is exiting
+      return NULL;
+    }
+
+    state = new JvmtiThreadState(thread);
+  }
+  return state;
+}
+
+inline JvmtiThreadState* JvmtiThreadState::state_for(JavaThread *thread) {
+  JvmtiThreadState *state = thread->jvmti_thread_state();
+  if (state == NULL) {
+    MutexLocker mu(JvmtiThreadState_lock);
+    // check again with the lock held
+    state = state_for_while_locked(thread);
+  } else {
+    CHECK_UNHANDLED_OOPS_ONLY(Thread::current()->clear_unhandled_oops());
+  }
+  return state;
+}
+
 #endif // SHARE_VM_PRIMS_JVMTITHREADSTATE_INLINE_HPP
diff --git a/src/hotspot/share/prims/methodHandles.cpp b/src/hotspot/share/prims/methodHandles.cpp
index 8374755490e..2da189889fb 100644
--- a/src/hotspot/share/prims/methodHandles.cpp
+++ b/src/hotspot/share/prims/methodHandles.cpp
@@ -1029,6 +1029,26 @@ void MethodHandles::flush_dependent_nmethods(Handle call_site, Handle target) {
   }
 }
 
+void MethodHandles::trace_method_handle_interpreter_entry(MacroAssembler* _masm, vmIntrinsics::ID iid) {
+  if (TraceMethodHandles) {
+    const char* name = vmIntrinsics::name_at(iid);
+    if (*name == '_')  name += 1;
+    const size_t len = strlen(name) + 50;
+    char* qname = NEW_C_HEAP_ARRAY(char, len, mtInternal);
+    const char* suffix = "";
+    if (is_signature_polymorphic(iid)) {
+      if (is_signature_polymorphic_static(iid))
+        suffix = "/static";
+      else
+        suffix = "/private";
+    }
+    jio_snprintf(qname, len, "MethodHandle::interpreter_entry::%s%s", name, suffix);
+    trace_method_handle(_masm, qname);
+    // Note:  Don't free the allocated char array because it's used
+    // during runtime.
+  }
+}
+
 //
 // Here are the native methods in java.lang.invoke.MethodHandleNatives
 // They are the private interface between this JVM and the HotSpot-specific
diff --git a/src/hotspot/share/prims/methodHandles.hpp b/src/hotspot/share/prims/methodHandles.hpp
index df83f23daf3..2be115636af 100644
--- a/src/hotspot/share/prims/methodHandles.hpp
+++ b/src/hotspot/share/prims/methodHandles.hpp
@@ -195,25 +195,7 @@ public:
 
   // Tracing
   static void trace_method_handle(MacroAssembler* _masm, const char* adaptername) PRODUCT_RETURN;
-  static void trace_method_handle_interpreter_entry(MacroAssembler* _masm, vmIntrinsics::ID iid) {
-    if (TraceMethodHandles) {
-      const char* name = vmIntrinsics::name_at(iid);
-      if (*name == '_')  name += 1;
-      const size_t len = strlen(name) + 50;
-      char* qname = NEW_C_HEAP_ARRAY(char, len, mtInternal);
-      const char* suffix = "";
-      if (is_signature_polymorphic(iid)) {
-        if (is_signature_polymorphic_static(iid))
-          suffix = "/static";
-        else
-          suffix = "/private";
-      }
-      jio_snprintf(qname, len, "MethodHandle::interpreter_entry::%s%s", name, suffix);
-      trace_method_handle(_masm, qname);
-      // Note:  Don't free the allocated char array because it's used
-      // during runtime.
-    }
-  }
+  static void trace_method_handle_interpreter_entry(MacroAssembler* _masm, vmIntrinsics::ID iid);
 };
 
 //------------------------------------------------------------------------------
diff --git a/src/hotspot/share/prims/perf.cpp b/src/hotspot/share/prims/perf.cpp
index ba019e04cb2..cf93aaeb99b 100644
--- a/src/hotspot/share/prims/perf.cpp
+++ b/src/hotspot/share/prims/perf.cpp
@@ -30,7 +30,7 @@
 #include "memory/resourceArea.hpp"
 #include "oops/oop.inline.hpp"
 #include "runtime/interfaceSupport.hpp"
-#include "runtime/perfData.hpp"
+#include "runtime/perfData.inline.hpp"
 #include "runtime/perfMemory.hpp"
 
 /*
diff --git a/src/hotspot/share/prims/unsafe.cpp b/src/hotspot/share/prims/unsafe.cpp
index c6796d53a3a..a8d13138507 100644
--- a/src/hotspot/share/prims/unsafe.cpp
+++ b/src/hotspot/share/prims/unsafe.cpp
@@ -39,6 +39,8 @@
 #include "runtime/interfaceSupport.hpp"
 #include "runtime/orderAccess.inline.hpp"
 #include "runtime/reflection.hpp"
+#include "runtime/thread.hpp"
+#include "runtime/threadSMR.hpp"
 #include "runtime/vm_version.hpp"
 #include "services/threadService.hpp"
 #include "trace/tracing.hpp"
@@ -144,18 +146,25 @@ jlong Unsafe_field_offset_from_byte_offset(jlong byte_offset) {
  * Normalizes values and wraps accesses in
  * JavaThread::doing_unsafe_access() if needed.
  */
+template <typename T>
 class MemoryAccess : StackObj {
   JavaThread* _thread;
   oop _obj;
   ptrdiff_t _offset;
 
-  // Resolves and returns the address of the memory access
-  void* addr() {
-    return index_oop_from_field_offset_long(_obj, _offset);
+  // Resolves and returns the address of the memory access.
+  // This raw memory access may fault, so we make sure it happens within the
+  // guarded scope by making the access volatile at least. Since the store
+  // of Thread::set_doing_unsafe_access() is also volatile, these accesses
+  // can not be reordered by the compiler. Therefore, if the access triggers
+  // a fault, we will know that Thread::doing_unsafe_access() returns true.
+  volatile T* addr() {
+    void* addr = index_oop_from_field_offset_long(_obj, _offset);
+    return static_cast<volatile T*>(addr);
   }
 
-  template <typename T>
-  T normalize_for_write(T x) {
+  template <typename U>
+  U normalize_for_write(U x) {
     return x;
   }
 
@@ -163,8 +172,8 @@ class MemoryAccess : StackObj {
     return x & 1;
   }
 
-  template <typename T>
-  T normalize_for_read(T x) {
+  template <typename U>
+  U normalize_for_read(U x) {
     return x;
   }
 
@@ -197,11 +206,10 @@ public:
     assert_field_offset_sane(_obj, offset);
   }
 
-  template <typename T>
   T get() {
     if (oopDesc::is_null(_obj)) {
       GuardUnsafeAccess guard(_thread);
-      T ret = RawAccess<>::load((T*)addr());
+      T ret = RawAccess<>::load(addr());
       return normalize_for_read(ret);
     } else {
       T ret = HeapAccess<>::load_at(_obj, _offset);
@@ -209,22 +217,20 @@ public:
     }
   }
 
-  template <typename T>
   void put(T x) {
     if (oopDesc::is_null(_obj)) {
       GuardUnsafeAccess guard(_thread);
-      RawAccess<>::store((T*)addr(), normalize_for_write(x));
+      RawAccess<>::store(addr(), normalize_for_write(x));
     } else {
       HeapAccess<>::store_at(_obj, _offset, normalize_for_write(x));
     }
   }
 
 
-  template <typename T>
   T get_volatile() {
     if (oopDesc::is_null(_obj)) {
       GuardUnsafeAccess guard(_thread);
-      volatile T ret = RawAccess<MO_SEQ_CST>::load((volatile T*)addr());
+      volatile T ret = RawAccess<MO_SEQ_CST>::load(addr());
       return normalize_for_read(ret);
     } else {
       T ret = HeapAccess<MO_SEQ_CST>::load_at(_obj, _offset);
@@ -232,11 +238,10 @@ public:
     }
   }
 
-  template <typename T>
   void put_volatile(T x) {
     if (oopDesc::is_null(_obj)) {
       GuardUnsafeAccess guard(_thread);
-      RawAccess<MO_SEQ_CST>::store((volatile T*)addr(), normalize_for_write(x));
+      RawAccess<MO_SEQ_CST>::store(addr(), normalize_for_write(x));
     } else {
       HeapAccess<MO_SEQ_CST>::store_at(_obj, _offset, normalize_for_write(x));
     }
@@ -294,11 +299,11 @@ UNSAFE_LEAF(jint, Unsafe_unalignedAccess0(JNIEnv *env, jobject unsafe)) {
 #define DEFINE_GETSETOOP(java_type, Type) \
  \
 UNSAFE_ENTRY(java_type, Unsafe_Get##Type(JNIEnv *env, jobject unsafe, jobject obj, jlong offset)) { \
-  return MemoryAccess(thread, obj, offset).get<java_type>(); \
+  return MemoryAccess<java_type>(thread, obj, offset).get(); \
 } UNSAFE_END \
  \
 UNSAFE_ENTRY(void, Unsafe_Put##Type(JNIEnv *env, jobject unsafe, jobject obj, jlong offset, java_type x)) { \
-  MemoryAccess(thread, obj, offset).put<java_type>(x); \
+  MemoryAccess<java_type>(thread, obj, offset).put(x); \
 } UNSAFE_END \
  \
 // END DEFINE_GETSETOOP.
@@ -317,11 +322,11 @@ DEFINE_GETSETOOP(jdouble, Double);
 #define DEFINE_GETSETOOP_VOLATILE(java_type, Type) \
  \
 UNSAFE_ENTRY(java_type, Unsafe_Get##Type##Volatile(JNIEnv *env, jobject unsafe, jobject obj, jlong offset)) { \
-  return MemoryAccess(thread, obj, offset).get_volatile<java_type>(); \
+  return MemoryAccess<java_type>(thread, obj, offset).get_volatile(); \
 } UNSAFE_END \
  \
 UNSAFE_ENTRY(void, Unsafe_Put##Type##Volatile(JNIEnv *env, jobject unsafe, jobject obj, jlong offset, java_type x)) { \
-  MemoryAccess(thread, obj, offset).put_volatile<java_type>(x); \
+  MemoryAccess<java_type>(thread, obj, offset).put_volatile(x); \
 } UNSAFE_END \
  \
 // END DEFINE_GETSETOOP_VOLATILE.
@@ -937,8 +942,12 @@ UNSAFE_ENTRY(void, Unsafe_Unpark(JNIEnv *env, jobject unsafe, jobject jthread))
   Parker* p = NULL;
 
   if (jthread != NULL) {
-    oop java_thread = JNIHandles::resolve_non_null(jthread);
+    ThreadsListHandle tlh;
+    JavaThread* thr = NULL;
+    oop java_thread = NULL;
+    (void) tlh.cv_internal_thread_to_JavaThread(jthread, &thr, &java_thread);
     if (java_thread != NULL) {
+      // This is a valid oop.
       jlong lp = java_lang_Thread::park_event(java_thread);
       if (lp != 0) {
         // This cast is OK even though the jlong might have been read
@@ -946,22 +955,19 @@ UNSAFE_ENTRY(void, Unsafe_Unpark(JNIEnv *env, jobject unsafe, jobject jthread))
         // always be zero anyway and the value set is always the same
         p = (Parker*)addr_from_java(lp);
       } else {
-        // Grab lock if apparently null or using older version of library
-        MutexLocker mu(Threads_lock);
-        java_thread = JNIHandles::resolve_non_null(jthread);
-
-        if (java_thread != NULL) {
-          JavaThread* thr = java_lang_Thread::thread(java_thread);
-          if (thr != NULL) {
-            p = thr->parker();
-            if (p != NULL) { // Bind to Java thread for next time.
-              java_lang_Thread::set_park_event(java_thread, addr_to_java(p));
-            }
+        // Not cached in the java.lang.Thread oop yet (could be an
+        // older version of library).
+        if (thr != NULL) {
+          // The JavaThread is alive.
+          p = thr->parker();
+          if (p != NULL) {
+            // Cache the Parker in the java.lang.Thread oop for next time.
+            java_lang_Thread::set_park_event(java_thread, addr_to_java(p));
           }
         }
       }
     }
-  }
+  } // ThreadsListHandle is destroyed here.
 
   if (p != NULL) {
     HOTSPOT_THREAD_UNPARK((uintptr_t) p);
diff --git a/src/hotspot/share/prims/whitebox.cpp b/src/hotspot/share/prims/whitebox.cpp
index 84510d0da19..28d8851ff08 100644
--- a/src/hotspot/share/prims/whitebox.cpp
+++ b/src/hotspot/share/prims/whitebox.cpp
@@ -55,11 +55,15 @@
 #include "runtime/os.hpp"
 #include "runtime/sweeper.hpp"
 #include "runtime/thread.hpp"
+#include "runtime/threadSMR.hpp"
 #include "runtime/vm_version.hpp"
 #include "utilities/align.hpp"
 #include "utilities/debug.hpp"
 #include "utilities/exceptions.hpp"
 #include "utilities/macros.hpp"
+#if INCLUDE_CDS
+#include "prims/cdsoffsets.hpp"
+#endif // INCLUDE_CDS
 #if INCLUDE_ALL_GCS
 #include "gc/g1/concurrentMarkThread.hpp"
 #include "gc/g1/g1CollectedHeap.inline.hpp"
@@ -665,7 +669,7 @@ class VM_WhiteBoxDeoptimizeFrames : public VM_WhiteBoxOperation {
   int  result() const { return _result; }
 
   void doit() {
-    for (JavaThread* t = Threads::first(); t != NULL; t = t->next()) {
+    for (JavaThreadIteratorWithHandle jtiwh; JavaThread *t = jtiwh.next(); ) {
       if (t->has_last_Java_frame()) {
         for (StackFrameStream fst(t, UseBiasedLocking); !fst.is_done(); fst.next()) {
           frame* f = fst.current();
@@ -1729,6 +1733,18 @@ WB_ENTRY(jboolean, WB_IsCDSIncludedInVmBuild(JNIEnv* env))
 #endif
 WB_END
 
+
+#if INCLUDE_CDS
+
+WB_ENTRY(jint, WB_GetOffsetForName(JNIEnv* env, jobject o, jstring name))
+  ResourceMark rm;
+  char* c_name = java_lang_String::as_utf8_string(JNIHandles::resolve_non_null(name));
+  int result = CDSOffsets::find_offset(c_name);
+  return (jint)result;
+WB_END
+
+#endif // INCLUDE_CDS
+
 WB_ENTRY(jint, WB_HandshakeWalkStack(JNIEnv* env, jobject wb, jobject thread_handle, jboolean all_threads))
   class TraceSelfClosure : public ThreadClosure {
     jint _num_threads_completed;
@@ -1917,6 +1933,9 @@ static JNINativeMethod methods[] = {
   {CC"runMemoryUnitTests", CC"()V",                   (void*)&WB_RunMemoryUnitTests},
   {CC"readFromNoaccessArea",CC"()V",                  (void*)&WB_ReadFromNoaccessArea},
   {CC"stressVirtualSpaceResize",CC"(JJJ)I",           (void*)&WB_StressVirtualSpaceResize},
+#if INCLUDE_CDS
+  {CC"getOffsetForName0", CC"(Ljava/lang/String;)I",  (void*)&WB_GetOffsetForName},
+#endif
 #if INCLUDE_ALL_GCS
   {CC"g1InConcurrentMark", CC"()Z",                   (void*)&WB_G1InConcurrentMark},
   {CC"g1IsHumongous0",      CC"(Ljava/lang/Object;)Z", (void*)&WB_G1IsHumongous     },
diff --git a/src/hotspot/share/runtime/arguments.cpp b/src/hotspot/share/runtime/arguments.cpp
index de4adedf8f2..2540390b5c9 100644
--- a/src/hotspot/share/runtime/arguments.cpp
+++ b/src/hotspot/share/runtime/arguments.cpp
@@ -114,6 +114,108 @@ bool Arguments::_has_jimage = false;
 
 char* Arguments::_ext_dirs = NULL;
 
+bool PathString::set_value(const char *value) {
+  if (_value != NULL) {
+    FreeHeap(_value);
+  }
+  _value = AllocateHeap(strlen(value)+1, mtArguments);
+  assert(_value != NULL, "Unable to allocate space for new path value");
+  if (_value != NULL) {
+    strcpy(_value, value);
+  } else {
+    // not able to allocate
+    return false;
+  }
+  return true;
+}
+
+void PathString::append_value(const char *value) {
+  char *sp;
+  size_t len = 0;
+  if (value != NULL) {
+    len = strlen(value);
+    if (_value != NULL) {
+      len += strlen(_value);
+    }
+    sp = AllocateHeap(len+2, mtArguments);
+    assert(sp != NULL, "Unable to allocate space for new append path value");
+    if (sp != NULL) {
+      if (_value != NULL) {
+        strcpy(sp, _value);
+        strcat(sp, os::path_separator());
+        strcat(sp, value);
+        FreeHeap(_value);
+      } else {
+        strcpy(sp, value);
+      }
+      _value = sp;
+    }
+  }
+}
+
+PathString::PathString(const char* value) {
+  if (value == NULL) {
+    _value = NULL;
+  } else {
+    _value = AllocateHeap(strlen(value)+1, mtArguments);
+    strcpy(_value, value);
+  }
+}
+
+PathString::~PathString() {
+  if (_value != NULL) {
+    FreeHeap(_value);
+    _value = NULL;
+  }
+}
+
+ModulePatchPath::ModulePatchPath(const char* module_name, const char* path) {
+  assert(module_name != NULL && path != NULL, "Invalid module name or path value");
+  size_t len = strlen(module_name) + 1;
+  _module_name = AllocateHeap(len, mtInternal);
+  strncpy(_module_name, module_name, len); // copy the trailing null
+  _path =  new PathString(path);
+}
+
+ModulePatchPath::~ModulePatchPath() {
+  if (_module_name != NULL) {
+    FreeHeap(_module_name);
+    _module_name = NULL;
+  }
+  if (_path != NULL) {
+    delete _path;
+    _path = NULL;
+  }
+}
+
+SystemProperty::SystemProperty(const char* key, const char* value, bool writeable, bool internal) : PathString(value) {
+  if (key == NULL) {
+    _key = NULL;
+  } else {
+    _key = AllocateHeap(strlen(key)+1, mtArguments);
+    strcpy(_key, key);
+  }
+  _next = NULL;
+  _internal = internal;
+  _writeable = writeable;
+}
+
+AgentLibrary::AgentLibrary(const char* name, const char* options, bool is_absolute_path, void* os_lib) {
+  _name = AllocateHeap(strlen(name)+1, mtArguments);
+  strcpy(_name, name);
+  if (options == NULL) {
+    _options = NULL;
+  } else {
+    _options = AllocateHeap(strlen(options)+1, mtArguments);
+    strcpy(_options, options);
+  }
+  _is_absolute_path = is_absolute_path;
+  _os_lib = os_lib;
+  _next = NULL;
+  _state = agent_invalid;
+  _is_static_lib = false;
+}
+
 // Check if head of 'option' matches 'name', and sets 'tail' to the remaining
 // part of the option string.
 static bool match_option(const JavaVMOption *option, const char* name,
@@ -180,6 +282,23 @@ bool needs_module_property_warning = false;
 #define UPGRADE_PATH "upgrade.path"
 #define UPGRADE_PATH_LEN 12
 
+void Arguments::add_init_library(const char* name, char* options) {
+  _libraryList.add(new AgentLibrary(name, options, false, NULL));
+}
+
+void Arguments::add_init_agent(const char* name, char* options, bool absolute_path) {
+  _agentList.add(new AgentLibrary(name, options, absolute_path, NULL));
+}
+
+// Late-binding agents not started via arguments
+void Arguments::add_loaded_agent(AgentLibrary *agentLib) {
+  _agentList.add(agentLib);
+}
+
+void Arguments::add_loaded_agent(const char* name, char* options, bool absolute_path, void* os_lib) {
+  _agentList.add(new AgentLibrary(name, options, absolute_path, os_lib));
+}
+
 // Return TRUE if option matches 'property', or 'property=', or 'property.'.
 static bool matches_property_suffix(const char* option, const char* property, size_t len) {
   return ((strncmp(option, property, len) == 0) &&
@@ -2152,12 +2271,7 @@ bool Arguments::check_vm_args_consistency() {
   // Check lower bounds of the code cache
   // Template Interpreter code is approximately 3X larger in debug builds.
   uint min_code_cache_size = CodeCacheMinimumUseSpace DEBUG_ONLY(* 3);
-  if (InitialCodeCacheSize < (uintx)os::vm_page_size()) {
-    jio_fprintf(defaultStream::error_stream(),
-                "Invalid InitialCodeCacheSize=%dK. Must be at least %dK.\n", InitialCodeCacheSize/K,
-                os::vm_page_size()/K);
-    status = false;
-  } else if (ReservedCodeCacheSize < InitialCodeCacheSize) {
+  if (ReservedCodeCacheSize < InitialCodeCacheSize) {
     jio_fprintf(defaultStream::error_stream(),
                 "Invalid ReservedCodeCacheSize: %dK. Must be at least InitialCodeCacheSize=%dK.\n",
                 ReservedCodeCacheSize/K, InitialCodeCacheSize/K);
@@ -2212,7 +2326,27 @@ bool Arguments::check_vm_args_consistency() {
     }
     FLAG_SET_CMDLINE(bool, PostLoopMultiversioning, false);
   }
+  if (UseCountedLoopSafepoints && LoopStripMiningIter == 0) {
+    if (!FLAG_IS_DEFAULT(UseCountedLoopSafepoints) || !FLAG_IS_DEFAULT(LoopStripMiningIter)) {
+      warning("When counted loop safepoints are enabled, LoopStripMiningIter must be at least 1 (a safepoint every 1 iteration): setting it to 1");
+    }
+    LoopStripMiningIter = 1;
+  } else if (!UseCountedLoopSafepoints && LoopStripMiningIter > 0) {
+    if (!FLAG_IS_DEFAULT(UseCountedLoopSafepoints) || !FLAG_IS_DEFAULT(LoopStripMiningIter)) {
+      warning("Disabling counted safepoints implies no loop strip mining: setting LoopStripMiningIter to 0");
+    }
+    LoopStripMiningIter = 0;
+  }
+  if (FLAG_IS_DEFAULT(LoopStripMiningIterShortLoop)) {
+    // blind guess
+    LoopStripMiningIterShortLoop = LoopStripMiningIter / 10;
+  }
 #endif
+  if (!FLAG_IS_DEFAULT(AllocateHeapAt)) {
+    if ((UseNUMAInterleaving && !FLAG_IS_DEFAULT(UseNUMAInterleaving)) || (UseNUMA && !FLAG_IS_DEFAULT(UseNUMA))) {
+      log_warning(arguments) ("NUMA support for Heap depends on the file system when AllocateHeapAt option is used.\n");
+    }
+  }
   return status;
 }
 
@@ -2770,18 +2904,6 @@ jint Arguments::parse_each_vm_init_arg(const JavaVMInitArgs* args, bool* patch_m
       if (FLAG_SET_CMDLINE(intx, ThreadStackSize, value) != Flag::SUCCESS) {
         return JNI_EINVAL;
       }
-    } else if (match_option(option, "-XX:CodeCacheExpansionSize=", &tail)) {
-      julong long_CodeCacheExpansionSize = 0;
-      ArgsRange errcode = parse_memory_size(tail, &long_CodeCacheExpansionSize, os::vm_page_size());
-      if (errcode != arg_in_range) {
-        jio_fprintf(defaultStream::error_stream(),
-                   "Invalid argument: %s. Must be at least %luK.\n", option->optionString,
-                   os::vm_page_size()/K);
-        return JNI_EINVAL;
-      }
-      if (FLAG_SET_CMDLINE(uintx, CodeCacheExpansionSize, (uintx)long_CodeCacheExpansionSize) != Flag::SUCCESS) {
-        return JNI_EINVAL;
-      }
     } else if (match_option(option, "-Xmaxjitcodesize", &tail) ||
                match_option(option, "-XX:ReservedCodeCacheSize=", &tail)) {
       julong long_ReservedCodeCacheSize = 0;
@@ -2795,45 +2917,6 @@ jint Arguments::parse_each_vm_init_arg(const JavaVMInitArgs* args, bool* patch_m
       if (FLAG_SET_CMDLINE(uintx, ReservedCodeCacheSize, (uintx)long_ReservedCodeCacheSize) != Flag::SUCCESS) {
         return JNI_EINVAL;
       }
-      // -XX:NonNMethodCodeHeapSize=
-    } else if (match_option(option, "-XX:NonNMethodCodeHeapSize=", &tail)) {
-      julong long_NonNMethodCodeHeapSize = 0;
-
-      ArgsRange errcode = parse_memory_size(tail, &long_NonNMethodCodeHeapSize, 1);
-      if (errcode != arg_in_range) {
-        jio_fprintf(defaultStream::error_stream(),
-                    "Invalid maximum non-nmethod code heap size: %s.\n", option->optionString);
-        return JNI_EINVAL;
-      }
-      if (FLAG_SET_CMDLINE(uintx, NonNMethodCodeHeapSize, (uintx)long_NonNMethodCodeHeapSize) != Flag::SUCCESS) {
-        return JNI_EINVAL;
-      }
-      // -XX:ProfiledCodeHeapSize=
-    } else if (match_option(option, "-XX:ProfiledCodeHeapSize=", &tail)) {
-      julong long_ProfiledCodeHeapSize = 0;
-
-      ArgsRange errcode = parse_memory_size(tail, &long_ProfiledCodeHeapSize, 1);
-      if (errcode != arg_in_range) {
-        jio_fprintf(defaultStream::error_stream(),
-                    "Invalid maximum profiled code heap size: %s.\n", option->optionString);
-        return JNI_EINVAL;
-      }
-      if (FLAG_SET_CMDLINE(uintx, ProfiledCodeHeapSize, (uintx)long_ProfiledCodeHeapSize) != Flag::SUCCESS) {
-        return JNI_EINVAL;
-      }
-      // -XX:NonProfiledCodeHeapSizee=
-    } else if (match_option(option, "-XX:NonProfiledCodeHeapSize=", &tail)) {
-      julong long_NonProfiledCodeHeapSize = 0;
-
-      ArgsRange errcode = parse_memory_size(tail, &long_NonProfiledCodeHeapSize, 1);
-      if (errcode != arg_in_range) {
-        jio_fprintf(defaultStream::error_stream(),
-                    "Invalid maximum non-profiled code heap size: %s.\n", option->optionString);
-        return JNI_EINVAL;
-      }
-      if (FLAG_SET_CMDLINE(uintx, NonProfiledCodeHeapSize, (uintx)long_NonProfiledCodeHeapSize) != Flag::SUCCESS) {
-        return JNI_EINVAL;
-      }
     // -green
     } else if (match_option(option, "-green")) {
       jio_fprintf(defaultStream::error_stream(),
@@ -3936,6 +4019,14 @@ jint Arguments::match_special_option_and_act(const JavaVMInitArgs* args,
       vm_exit(0);
     }
 #endif
+
+    if (match_option(option, "-XX:+UseAppCDS")) {
+      Flag* flag = Flag::find_flag("SharedArchiveFile", 17, true, true);
+      if (flag->is_diagnostic()) {
+        flag->clear_diagnostic();
+      }
+      continue;
+    }
   }
   return JNI_OK;
 }
@@ -4306,26 +4397,7 @@ jint Arguments::apply_ergo() {
   }
 #endif
 
-  bool aot_enabled = UseAOT && AOTLibrary != NULL;
-  bool jvmci_enabled = NOT_JVMCI(false) JVMCI_ONLY(EnableJVMCI || UseJVMCICompiler);
-  bool handshakes_supported = SafepointMechanism::supports_thread_local_poll() && !aot_enabled && !jvmci_enabled && ThreadLocalHandshakes;
   // ThreadLocalHandshakesConstraintFunc handles the constraints.
-  // Here we try to figure out if a mutual exclusive option have been set that conflict with a default.
-  if (handshakes_supported) {
-    FLAG_SET_DEFAULT(UseAOT, false); // Clear the AOT flag to make sure it doesn't try to initialize.
-  } else {
-    if (FLAG_IS_DEFAULT(ThreadLocalHandshakes) && ThreadLocalHandshakes) {
-      if (aot_enabled) {
-        // If user enabled AOT but ThreadLocalHandshakes is at default set it to false.
-        log_debug(ergo)("Disabling ThreadLocalHandshakes for UseAOT.");
-        FLAG_SET_DEFAULT(ThreadLocalHandshakes, false);
-      } else if (jvmci_enabled){
-        // If user enabled JVMCI but ThreadLocalHandshakes is at default set it to false.
-        log_debug(ergo)("Disabling ThreadLocalHandshakes for EnableJVMCI/UseJVMCICompiler.");
-        FLAG_SET_DEFAULT(ThreadLocalHandshakes, false);
-      }
-    }
-  }
   if (FLAG_IS_DEFAULT(ThreadLocalHandshakes) || !SafepointMechanism::supports_thread_local_poll()) {
     log_debug(ergo)("ThreadLocalHandshakes %s", ThreadLocalHandshakes ? "enabled." : "disabled.");
   } else {
@@ -4337,7 +4409,9 @@ jint Arguments::apply_ergo() {
 
 jint Arguments::adjust_after_os() {
   if (UseNUMA) {
-    if (UseParallelGC || UseParallelOldGC) {
+    if (!FLAG_IS_DEFAULT(AllocateHeapAt)) {
+      FLAG_SET_ERGO(bool, UseNUMA, false);
+    } else if (UseParallelGC || UseParallelOldGC) {
       if (FLAG_IS_DEFAULT(MinHeapDeltaBytes)) {
          FLAG_SET_DEFAULT(MinHeapDeltaBytes, 64*M);
       }
diff --git a/src/hotspot/share/runtime/arguments.hpp b/src/hotspot/share/runtime/arguments.hpp
index 43c0ff37d33..222f4b25ad6 100644
--- a/src/hotspot/share/runtime/arguments.hpp
+++ b/src/hotspot/share/runtime/arguments.hpp
@@ -27,6 +27,7 @@
 
 #include "logging/logLevel.hpp"
 #include "logging/logTag.hpp"
+#include "memory/allocation.hpp"
 #include "runtime/java.hpp"
 #include "runtime/os.hpp"
 #include "runtime/perfData.hpp"
@@ -59,60 +60,11 @@ class PathString : public CHeapObj<mtArguments> {
  public:
   char* value() const { return _value; }
 
-  bool set_value(const char *value) {
-    if (_value != NULL) {
-      FreeHeap(_value);
-    }
-    _value = AllocateHeap(strlen(value)+1, mtArguments);
-    assert(_value != NULL, "Unable to allocate space for new path value");
-    if (_value != NULL) {
-      strcpy(_value, value);
-    } else {
-      // not able to allocate
-      return false;
-    }
-    return true;
-  }
+  bool set_value(const char *value);
+  void append_value(const char *value);
 
-  void append_value(const char *value) {
-    char *sp;
-    size_t len = 0;
-    if (value != NULL) {
-      len = strlen(value);
-      if (_value != NULL) {
-        len += strlen(_value);
-      }
-      sp = AllocateHeap(len+2, mtArguments);
-      assert(sp != NULL, "Unable to allocate space for new append path value");
-      if (sp != NULL) {
-        if (_value != NULL) {
-          strcpy(sp, _value);
-          strcat(sp, os::path_separator());
-          strcat(sp, value);
-          FreeHeap(_value);
-        } else {
-          strcpy(sp, value);
-        }
-        _value = sp;
-      }
-    }
-  }
-
-  PathString(const char* value) {
-    if (value == NULL) {
-      _value = NULL;
-    } else {
-      _value = AllocateHeap(strlen(value)+1, mtArguments);
-      strcpy(_value, value);
-    }
-  }
-
-  ~PathString() {
-    if (_value != NULL) {
-      FreeHeap(_value);
-      _value = NULL;
-    }
-  }
+  PathString(const char* value);
+  ~PathString();
 };
 
 // ModulePatchPath records the module/path pair as specified to --patch-module.
@@ -121,24 +73,8 @@ private:
   char* _module_name;
   PathString* _path;
 public:
-  ModulePatchPath(const char* module_name, const char* path) {
-    assert(module_name != NULL && path != NULL, "Invalid module name or path value");
-    size_t len = strlen(module_name) + 1;
-    _module_name = AllocateHeap(len, mtInternal);
-    strncpy(_module_name, module_name, len); // copy the trailing null
-    _path =  new PathString(path);
-  }
-
-  ~ModulePatchPath() {
-    if (_module_name != NULL) {
-      FreeHeap(_module_name);
-      _module_name = NULL;
-    }
-    if (_path != NULL) {
-      delete _path;
-      _path = NULL;
-    }
-  }
+  ModulePatchPath(const char* module_name, const char* path);
+  ~ModulePatchPath();
 
   inline void set_path(const char* path) { _path->set_value(path); }
   inline const char* module_name() const { return _module_name; }
@@ -185,17 +121,7 @@ class SystemProperty : public PathString {
   }
 
   // Constructor
-  SystemProperty(const char* key, const char* value, bool writeable, bool internal = false) : PathString(value) {
-    if (key == NULL) {
-      _key = NULL;
-    } else {
-      _key = AllocateHeap(strlen(key)+1, mtArguments);
-      strcpy(_key, key);
-    }
-    _next = NULL;
-    _internal = internal;
-    _writeable = writeable;
-  }
+  SystemProperty(const char* key, const char* value, bool writeable, bool internal = false);
 };
 
 
@@ -234,21 +160,7 @@ public:
   void set_invalid()                        { _state = agent_invalid; }
 
   // Constructor
-  AgentLibrary(const char* name, const char* options, bool is_absolute_path, void* os_lib) {
-    _name = AllocateHeap(strlen(name)+1, mtArguments);
-    strcpy(_name, name);
-    if (options == NULL) {
-      _options = NULL;
-    } else {
-      _options = AllocateHeap(strlen(options)+1, mtArguments);
-      strcpy(_options, options);
-    }
-    _is_absolute_path = is_absolute_path;
-    _os_lib = os_lib;
-    _next = NULL;
-    _state = agent_invalid;
-    _is_static_lib = false;
-  }
+  AgentLibrary(const char* name, const char* options, bool is_absolute_path, void* os_lib);
 };
 
 // maintain an order of entry list of AgentLibrary
@@ -420,19 +332,15 @@ class Arguments : AllStatic {
 
   // -Xrun arguments
   static AgentLibraryList _libraryList;
-  static void add_init_library(const char* name, char* options)
-    { _libraryList.add(new AgentLibrary(name, options, false, NULL)); }
+  static void add_init_library(const char* name, char* options);
 
   // -agentlib and -agentpath arguments
   static AgentLibraryList _agentList;
-  static void add_init_agent(const char* name, char* options, bool absolute_path)
-    { _agentList.add(new AgentLibrary(name, options, absolute_path, NULL)); }
+  static void add_init_agent(const char* name, char* options, bool absolute_path);
 
   // Late-binding agents not started via arguments
-  static void add_loaded_agent(AgentLibrary *agentLib)
-    { _agentList.add(agentLib); }
-  static void add_loaded_agent(const char* name, char* options, bool absolute_path, void* os_lib)
-    { _agentList.add(new AgentLibrary(name, options, absolute_path, os_lib)); }
+  static void add_loaded_agent(AgentLibrary *agentLib);
+  static void add_loaded_agent(const char* name, char* options, bool absolute_path, void* os_lib);
 
   // Operation modi
   static Mode _mode;
diff --git a/src/hotspot/share/runtime/arguments_ext.hpp b/src/hotspot/share/runtime/arguments_ext.hpp
index d1c9f183e8e..3ae21e1267f 100644
--- a/src/hotspot/share/runtime/arguments_ext.hpp
+++ b/src/hotspot/share/runtime/arguments_ext.hpp
@@ -36,7 +36,6 @@ public:
   // Otherwise returns false.
   static inline bool process_options(const JavaVMOption *option) { return false; }
   static inline void report_unsupported_options() { }
-  static inline bool using_AppCDS() { return false; }
 };
 
 void ArgumentsExt::set_gc_specific_flags() {
diff --git a/src/hotspot/share/runtime/biasedLocking.cpp b/src/hotspot/share/runtime/biasedLocking.cpp
index de93a4b370a..a1e70a97478 100644
--- a/src/hotspot/share/runtime/biasedLocking.cpp
+++ b/src/hotspot/share/runtime/biasedLocking.cpp
@@ -32,6 +32,7 @@
 #include "runtime/basicLock.hpp"
 #include "runtime/biasedLocking.hpp"
 #include "runtime/task.hpp"
+#include "runtime/threadSMR.hpp"
 #include "runtime/vframe.hpp"
 #include "runtime/vmThread.hpp"
 #include "runtime/vm_operations.hpp"
@@ -214,12 +215,8 @@ static BiasedLocking::Condition revoke_bias(oop obj, bool allow_rebias, bool is_
   if (requesting_thread == biased_thread) {
     thread_is_alive = true;
   } else {
-    for (JavaThread* cur_thread = Threads::first(); cur_thread != NULL; cur_thread = cur_thread->next()) {
-      if (cur_thread == biased_thread) {
-        thread_is_alive = true;
-        break;
-      }
-    }
+    ThreadsListHandle tlh;
+    thread_is_alive = tlh.includes(biased_thread);
   }
   if (!thread_is_alive) {
     if (allow_rebias) {
@@ -390,72 +387,76 @@ static BiasedLocking::Condition bulk_revoke_or_rebias_at_safepoint(oop o,
   Klass* k_o = o->klass();
   Klass* klass = k_o;
 
-  if (bulk_rebias) {
-    // Use the epoch in the klass of the object to implicitly revoke
-    // all biases of objects of this data type and force them to be
-    // reacquired. However, we also need to walk the stacks of all
-    // threads and update the headers of lightweight locked objects
-    // with biases to have the current epoch.
+  {
+    JavaThreadIteratorWithHandle jtiwh;
 
-    // If the prototype header doesn't have the bias pattern, don't
-    // try to update the epoch -- assume another VM operation came in
-    // and reset the header to the unbiased state, which will
-    // implicitly cause all existing biases to be revoked
-    if (klass->prototype_header()->has_bias_pattern()) {
-      int prev_epoch = klass->prototype_header()->bias_epoch();
-      klass->set_prototype_header(klass->prototype_header()->incr_bias_epoch());
-      int cur_epoch = klass->prototype_header()->bias_epoch();
+    if (bulk_rebias) {
+      // Use the epoch in the klass of the object to implicitly revoke
+      // all biases of objects of this data type and force them to be
+      // reacquired. However, we also need to walk the stacks of all
+      // threads and update the headers of lightweight locked objects
+      // with biases to have the current epoch.
 
-      // Now walk all threads' stacks and adjust epochs of any biased
-      // and locked objects of this data type we encounter
-      for (JavaThread* thr = Threads::first(); thr != NULL; thr = thr->next()) {
+      // If the prototype header doesn't have the bias pattern, don't
+      // try to update the epoch -- assume another VM operation came in
+      // and reset the header to the unbiased state, which will
+      // implicitly cause all existing biases to be revoked
+      if (klass->prototype_header()->has_bias_pattern()) {
+        int prev_epoch = klass->prototype_header()->bias_epoch();
+        klass->set_prototype_header(klass->prototype_header()->incr_bias_epoch());
+        int cur_epoch = klass->prototype_header()->bias_epoch();
+
+        // Now walk all threads' stacks and adjust epochs of any biased
+        // and locked objects of this data type we encounter
+        for (; JavaThread *thr = jtiwh.next(); ) {
+          GrowableArray<MonitorInfo*>* cached_monitor_info = get_or_compute_monitor_info(thr);
+          for (int i = 0; i < cached_monitor_info->length(); i++) {
+            MonitorInfo* mon_info = cached_monitor_info->at(i);
+            oop owner = mon_info->owner();
+            markOop mark = owner->mark();
+            if ((owner->klass() == k_o) && mark->has_bias_pattern()) {
+              // We might have encountered this object already in the case of recursive locking
+              assert(mark->bias_epoch() == prev_epoch || mark->bias_epoch() == cur_epoch, "error in bias epoch adjustment");
+              owner->set_mark(mark->set_bias_epoch(cur_epoch));
+            }
+          }
+        }
+      }
+
+      // At this point we're done. All we have to do is potentially
+      // adjust the header of the given object to revoke its bias.
+      revoke_bias(o, attempt_rebias_of_object && klass->prototype_header()->has_bias_pattern(), true, requesting_thread, NULL);
+    } else {
+      if (log_is_enabled(Info, biasedlocking)) {
+        ResourceMark rm;
+        log_info(biasedlocking)("* Disabling biased locking for type %s", klass->external_name());
+      }
+
+      // Disable biased locking for this data type. Not only will this
+      // cause future instances to not be biased, but existing biased
+      // instances will notice that this implicitly caused their biases
+      // to be revoked.
+      klass->set_prototype_header(markOopDesc::prototype());
+
+      // Now walk all threads' stacks and forcibly revoke the biases of
+      // any locked and biased objects of this data type we encounter.
+      for (; JavaThread *thr = jtiwh.next(); ) {
         GrowableArray<MonitorInfo*>* cached_monitor_info = get_or_compute_monitor_info(thr);
         for (int i = 0; i < cached_monitor_info->length(); i++) {
           MonitorInfo* mon_info = cached_monitor_info->at(i);
           oop owner = mon_info->owner();
           markOop mark = owner->mark();
           if ((owner->klass() == k_o) && mark->has_bias_pattern()) {
-            // We might have encountered this object already in the case of recursive locking
-            assert(mark->bias_epoch() == prev_epoch || mark->bias_epoch() == cur_epoch, "error in bias epoch adjustment");
-            owner->set_mark(mark->set_bias_epoch(cur_epoch));
+            revoke_bias(owner, false, true, requesting_thread, NULL);
           }
         }
       }
+
+      // Must force the bias of the passed object to be forcibly revoked
+      // as well to ensure guarantees to callers
+      revoke_bias(o, false, true, requesting_thread, NULL);
     }
-
-    // At this point we're done. All we have to do is potentially
-    // adjust the header of the given object to revoke its bias.
-    revoke_bias(o, attempt_rebias_of_object && klass->prototype_header()->has_bias_pattern(), true, requesting_thread, NULL);
-  } else {
-    if (log_is_enabled(Info, biasedlocking)) {
-      ResourceMark rm;
-      log_info(biasedlocking)("* Disabling biased locking for type %s", klass->external_name());
-    }
-
-    // Disable biased locking for this data type. Not only will this
-    // cause future instances to not be biased, but existing biased
-    // instances will notice that this implicitly caused their biases
-    // to be revoked.
-    klass->set_prototype_header(markOopDesc::prototype());
-
-    // Now walk all threads' stacks and forcibly revoke the biases of
-    // any locked and biased objects of this data type we encounter.
-    for (JavaThread* thr = Threads::first(); thr != NULL; thr = thr->next()) {
-      GrowableArray<MonitorInfo*>* cached_monitor_info = get_or_compute_monitor_info(thr);
-      for (int i = 0; i < cached_monitor_info->length(); i++) {
-        MonitorInfo* mon_info = cached_monitor_info->at(i);
-        oop owner = mon_info->owner();
-        markOop mark = owner->mark();
-        if ((owner->klass() == k_o) && mark->has_bias_pattern()) {
-          revoke_bias(owner, false, true, requesting_thread, NULL);
-        }
-      }
-    }
-
-    // Must force the bias of the passed object to be forcibly revoked
-    // as well to ensure guarantees to callers
-    revoke_bias(o, false, true, requesting_thread, NULL);
-  }
+  } // ThreadsListHandle is destroyed here.
 
   log_info(biasedlocking)("* Ending bulk revocation");
 
@@ -481,7 +482,7 @@ static BiasedLocking::Condition bulk_revoke_or_rebias_at_safepoint(oop o,
 
 static void clean_up_cached_monitor_info() {
   // Walk the thread list clearing out the cached monitors
-  for (JavaThread* thr = Threads::first(); thr != NULL; thr = thr->next()) {
+  for (JavaThreadIteratorWithHandle jtiwh; JavaThread *thr = jtiwh.next(); ) {
     thr->set_cached_monitor_info(NULL);
   }
 }
@@ -768,7 +769,7 @@ void BiasedLocking::preserve_marks() {
 
   ResourceMark rm;
   Thread* cur = Thread::current();
-  for (JavaThread* thread = Threads::first(); thread != NULL; thread = thread->next()) {
+  for (JavaThreadIteratorWithHandle jtiwh; JavaThread *thread = jtiwh.next(); ) {
     if (thread->has_last_Java_frame()) {
       RegisterMap rm(thread);
       for (javaVFrame* vf = thread->last_java_vframe(&rm); vf != NULL; vf = vf->java_sender()) {
diff --git a/src/hotspot/share/runtime/commandLineFlagConstraintsRuntime.cpp b/src/hotspot/share/runtime/commandLineFlagConstraintsRuntime.cpp
index 9df5d2bbec8..55483983094 100644
--- a/src/hotspot/share/runtime/commandLineFlagConstraintsRuntime.cpp
+++ b/src/hotspot/share/runtime/commandLineFlagConstraintsRuntime.cpp
@@ -138,10 +138,6 @@ Flag::Error ThreadLocalHandshakesConstraintFunc(bool value, bool verbose) {
       CommandLineError::print(verbose, "ThreadLocalHandshakes not yet supported on this platform\n");
       return Flag::VIOLATES_CONSTRAINT;
     }
-    if (UseAOT JVMCI_ONLY(|| EnableJVMCI || UseJVMCICompiler)) {
-      CommandLineError::print(verbose, "ThreadLocalHandshakes not yet supported in combination with AOT or JVMCI\n");
-      return Flag::VIOLATES_CONSTRAINT;
-    }
   }
   return Flag::SUCCESS;
 }
diff --git a/src/hotspot/share/runtime/deoptimization.cpp b/src/hotspot/share/runtime/deoptimization.cpp
index fb93fbdd712..c61f41d2933 100644
--- a/src/hotspot/share/runtime/deoptimization.cpp
+++ b/src/hotspot/share/runtime/deoptimization.cpp
@@ -50,6 +50,7 @@
 #include "runtime/signature.hpp"
 #include "runtime/stubRoutines.hpp"
 #include "runtime/thread.hpp"
+#include "runtime/threadSMR.hpp"
 #include "runtime/vframe.hpp"
 #include "runtime/vframeArray.hpp"
 #include "runtime/vframe_hp.hpp"
@@ -1297,7 +1298,7 @@ void Deoptimization::revoke_biases_of_monitors(CodeBlob* cb) {
 
   assert(SafepointSynchronize::is_at_safepoint(), "must only be called from safepoint");
   GrowableArray<Handle>* objects_to_revoke = new GrowableArray<Handle>();
-  for (JavaThread* jt = Threads::first(); jt != NULL ; jt = jt->next()) {
+  for (JavaThreadIteratorWithHandle jtiwh; JavaThread *jt = jtiwh.next(); ) {
     if (jt->has_last_Java_frame()) {
       StackFrameStream sfs(jt, true);
       while (!sfs.is_done()) {
diff --git a/src/hotspot/share/runtime/globals.hpp b/src/hotspot/share/runtime/globals.hpp
index bdf69c29da6..ad4b27a3b0b 100644
--- a/src/hotspot/share/runtime/globals.hpp
+++ b/src/hotspot/share/runtime/globals.hpp
@@ -917,9 +917,6 @@ public:
   notproduct(bool, ZapVMHandleArea, trueInDebug,                            \
           "Zap freed VM handle space with 0xBCBCBCBC")                      \
                                                                             \
-  develop(bool, ZapJNIHandleArea, trueInDebug,                              \
-          "Zap freed JNI handle space with 0xFEFEFEFE")                     \
-                                                                            \
   notproduct(bool, ZapStackSegments, trueInDebug,                           \
           "Zap allocated/freed stack segments with 0xFADFADED")             \
                                                                             \
@@ -2271,6 +2268,10 @@ public:
   diagnostic(bool, VerifyDuringGC, false,                                   \
           "Verify memory system during GC (between phases)")                \
                                                                             \
+  diagnostic(ccstrlist, VerifyGCType, "",                                   \
+             "GC type(s) to verify when Verify*GC is enabled."              \
+             "Available types are collector specific.")                     \
+                                                                            \
   diagnostic(ccstrlist, VerifySubSet, "",                                   \
           "Memory sub-systems to verify when Verify*GC flag(s) "            \
           "are enabled. One or more sub-systems can be specified "          \
@@ -2484,6 +2485,12 @@ public:
   LP64_ONLY(range(-1, max_intx/MICROUNITS))                                 \
   NOT_LP64(range(-1, max_intx))                                             \
                                                                             \
+  diagnostic(bool, EnableThreadSMRExtraValidityChecks, true,                \
+             "Enable Thread SMR extra validity checks")                     \
+                                                                            \
+  diagnostic(bool, EnableThreadSMRStatistics, trueInDebug,                  \
+             "Enable Thread SMR Statistics")                                \
+                                                                            \
   product(bool, Inline, true,                                               \
           "Enable inlining")                                                \
                                                                             \
@@ -3359,7 +3366,7 @@ public:
                                                                             \
   product_pd(uintx, InitialCodeCacheSize,                                   \
           "Initial code cache size (in bytes)")                             \
-          range(0, max_uintx)                                               \
+          range(os::vm_page_size(), max_uintx)                              \
                                                                             \
   develop_pd(uintx, CodeCacheMinimumUseSpace,                               \
           "Minimum code cache size (in bytes) required to start VM.")       \
@@ -3370,7 +3377,7 @@ public:
                                                                             \
   product_pd(uintx, ReservedCodeCacheSize,                                  \
           "Reserved code cache size (in bytes) - maximum code cache size")  \
-          range(0, max_uintx)                                               \
+          range(os::vm_page_size(), max_uintx)                              \
                                                                             \
   product_pd(uintx, NonProfiledCodeHeapSize,                                \
           "Size of code heap with non-profiled methods (in bytes)")         \
@@ -3382,11 +3389,11 @@ public:
                                                                             \
   product_pd(uintx, NonNMethodCodeHeapSize,                                 \
           "Size of code heap with non-nmethods (in bytes)")                 \
-          range(0, max_uintx)                                               \
+          range(os::vm_page_size(), max_uintx)                              \
                                                                             \
   product_pd(uintx, CodeCacheExpansionSize,                                 \
           "Code cache expansion size (in bytes)")                           \
-          range(0, max_uintx)                                               \
+          range(32*K, max_uintx)                                            \
                                                                             \
   diagnostic_pd(uintx, CodeCacheMinBlockLength,                             \
           "Minimum number of segments in a code cache block")               \
@@ -3926,6 +3933,13 @@ public:
           "Address to allocate shared memory region for class data")        \
           range(0, SIZE_MAX)                                                \
                                                                             \
+  product(bool, UseAppCDS, false,                                           \
+          "Enable Application Class Data Sharing when using shared spaces") \
+          writeable(CommandLineOnly)                                        \
+                                                                            \
+  product(ccstr, SharedArchiveConfigFile, NULL,                             \
+          "Data to add to the CDS archive file")                            \
+                                                                            \
   product(uintx, SharedSymbolTableBucketSize, 4,                            \
           "Average number of symbols per bucket in shared table")           \
           range(2, 246)                                                     \
@@ -4073,7 +4087,11 @@ public:
   diagnostic(bool, CompilerDirectivesPrint, false,                          \
              "Print compiler directives on installation.")                  \
   diagnostic(int,  CompilerDirectivesLimit, 50,                             \
-             "Limit on number of compiler directives.")
+             "Limit on number of compiler directives.")                     \
+                                                                            \
+  product(ccstr, AllocateHeapAt, NULL,                                      \
+          "Path to the directoy where a temporary file will be created "    \
+          "to use as the backing store for Java Heap.")
 
 
 /*
diff --git a/src/hotspot/share/runtime/handshake.cpp b/src/hotspot/share/runtime/handshake.cpp
index f81c235c13d..0b816b25ed3 100644
--- a/src/hotspot/share/runtime/handshake.cpp
+++ b/src/hotspot/share/runtime/handshake.cpp
@@ -37,8 +37,6 @@
 #include "utilities/formatBuffer.hpp"
 #include "utilities/preserveException.hpp"
 
-#define ALL_JAVA_THREADS(X) for (JavaThread* X = Threads::first(); X; X = X->next())
-
 class HandshakeOperation: public StackObj {
 public:
   virtual void do_handshake(JavaThread* thread) = 0;
@@ -94,8 +92,7 @@ bool VM_Handshake::handshake_has_timed_out(jlong start_time) {
 
 void VM_Handshake::handle_timeout() {
   LogStreamHandle(Warning, handshake) log_stream;
-  MutexLockerEx ml(Threads_lock, Mutex::_no_safepoint_check_flag);
-  ALL_JAVA_THREADS(thr) {
+  for (JavaThreadIteratorWithHandle jtiwh; JavaThread *thr = jtiwh.next(); ) {
     if (thr->has_handshake()) {
       log_stream.print("Thread " PTR_FORMAT " has not cleared its handshake op", p2i(thr));
       thr->print_thread_state_on(&log_stream);
@@ -117,8 +114,8 @@ class VM_HandshakeOneThread: public VM_Handshake {
     TraceTime timer("Performing single-target operation (vmoperation doit)", TRACETIME_LOG(Info, handshake));
 
     {
-      MutexLockerEx ml(Threads_lock, Mutex::_no_safepoint_check_flag);
-      if (Threads::includes(_target)) {
+      ThreadsListHandle tlh;
+      if (tlh.includes(_target)) {
         set_handshake(_target);
         _thread_alive = true;
       }
@@ -139,9 +136,24 @@ class VM_HandshakeOneThread: public VM_Handshake {
         handle_timeout();
       }
 
+      // We need to re-think this with SMR ThreadsList.
+      // There is an assumption in the code that the Threads_lock should be
+      // locked during certain phases.
       MutexLockerEx ml(Threads_lock, Mutex::_no_safepoint_check_flag);
-      _target->handshake_process_by_vmthread();
-
+      ThreadsListHandle tlh;
+      if (tlh.includes(_target)) {
+        // Warning _target's address might be re-used.
+        // handshake_process_by_vmthread will check the semaphore for us again.
+        // Since we can't have more then one handshake in flight a reuse of
+        // _target's address should be okay since the new thread will not have
+        // an operation.
+        _target->handshake_process_by_vmthread();
+      } else {
+        // We can't warn here since the thread does cancel_handshake after
+        // it has been removed from the ThreadsList. So we should just keep
+        // looping here until while below returns false. If we have a bug,
+        // then we hang here, which is good for debugging.
+      }
     } while (!poll_for_completed_thread());
   }
 
@@ -157,15 +169,15 @@ class VM_HandshakeAllThreads: public VM_Handshake {
   void doit() {
     TraceTime timer("Performing operation (vmoperation doit)", TRACETIME_LOG(Info, handshake));
 
-    int number_of_threads_issued = -1;
-    int number_of_threads_completed = 0;
-    {
-      MutexLockerEx ml(Threads_lock, Mutex::_no_safepoint_check_flag);
-      number_of_threads_issued = Threads::number_of_threads();
+    int number_of_threads_issued = 0;
+    for (JavaThreadIteratorWithHandle jtiwh; JavaThread *thr = jtiwh.next(); ) {
+      set_handshake(thr);
+      number_of_threads_issued++;
+    }
 
-      ALL_JAVA_THREADS(thr) {
-        set_handshake(thr);
-      }
+    if (number_of_threads_issued < 1) {
+      log_debug(handshake)("No threads to handshake.");
+      return;
     }
 
     if (!UseMembar) {
@@ -174,6 +186,7 @@ class VM_HandshakeAllThreads: public VM_Handshake {
 
     log_debug(handshake)("Threads signaled, begin processing blocked threads by VMThtread");
     const jlong start_time = os::elapsed_counter();
+    int number_of_threads_completed = 0;
     do {
       // Check if handshake operation has timed out
       if (handshake_has_timed_out(start_time)) {
@@ -184,13 +197,19 @@ class VM_HandshakeAllThreads: public VM_Handshake {
       // Observing a blocked state may of course be transient but the processing is guarded
       // by semaphores and we optimistically begin by working on the blocked threads
       {
+          // We need to re-think this with SMR ThreadsList.
+          // There is an assumption in the code that the Threads_lock should
+          // be locked during certain phases.
           MutexLockerEx ml(Threads_lock, Mutex::_no_safepoint_check_flag);
-          ALL_JAVA_THREADS(thr) {
+          for (JavaThreadIteratorWithHandle jtiwh; JavaThread *thr = jtiwh.next(); ) {
+            // A new thread on the ThreadsList will not have an operation,
+            // hence it is skipped in handshake_process_by_vmthread.
             thr->handshake_process_by_vmthread();
           }
       }
 
       while (poll_for_completed_thread()) {
+        // Includes canceled operations by exiting threads.
         number_of_threads_completed++;
       }
 
@@ -212,7 +231,7 @@ public:
       _thread_cl(cl), _target_thread(target), _all_threads(false), _thread_alive(false) {}
 
   void doit() {
-    ALL_JAVA_THREADS(t) {
+    for (JavaThreadIteratorWithHandle jtiwh; JavaThread *t = jtiwh.next(); ) {
       if (_all_threads || t == _target_thread) {
         if (t == _target_thread) {
           _thread_alive = true;
@@ -298,8 +317,8 @@ void HandshakeState::cancel_inner(JavaThread* thread) {
   assert(thread->thread_state() == _thread_in_vm, "must be in vm state");
 #ifdef DEBUG
   {
-    MutexLockerEx ml(Threads_lock,  Mutex::_no_safepoint_check_flag);
-    assert(!Threads::includes(thread), "java thread must not be on threads list");
+    ThreadsListHandle tlh;
+    assert(!tlh.includes(_target), "java thread must not be on threads list");
   }
 #endif
   HandshakeOperation* op = _operation;
diff --git a/src/hotspot/share/runtime/java.cpp b/src/hotspot/share/runtime/java.cpp
index 11d84f00ee8..9c19095f142 100644
--- a/src/hotspot/share/runtime/java.cpp
+++ b/src/hotspot/share/runtime/java.cpp
@@ -356,6 +356,8 @@ void print_statistics() {
   if (PrintNMTStatistics) {
     MemTracker::final_report(tty);
   }
+
+  Threads::log_smr_statistics();
 }
 
 #else // PRODUCT MODE STATISTICS
@@ -396,6 +398,8 @@ void print_statistics() {
   if (LogTouchedMethods && PrintTouchedMethodsAtExit) {
     Method::print_touched_methods(tty);
   }
+
+  Threads::log_smr_statistics();
 }
 
 #endif
diff --git a/src/hotspot/share/runtime/jniHandles.cpp b/src/hotspot/share/runtime/jniHandles.cpp
index 8819f3c2cfc..483f9a5cb6e 100644
--- a/src/hotspot/share/runtime/jniHandles.cpp
+++ b/src/hotspot/share/runtime/jniHandles.cpp
@@ -279,13 +279,15 @@ JNIHandleBlock* JNIHandleBlock::_block_list           = NULL;
 #endif
 
 
+#ifdef ASSERT
 void JNIHandleBlock::zap() {
   // Zap block values
   _top = 0;
   for (int index = 0; index < block_size_in_oops; index++) {
-    _handles[index] = badJNIHandle;
+    _handles[index] = NULL;
   }
 }
+#endif // ASSERT
 
 JNIHandleBlock* JNIHandleBlock::allocate_block(Thread* thread)  {
   assert(thread == NULL || thread == Thread::current(), "sanity check");
@@ -307,7 +309,7 @@ JNIHandleBlock* JNIHandleBlock::allocate_block(Thread* thread)  {
       // Allocate new block
       block = new JNIHandleBlock();
       _blocks_allocated++;
-      if (ZapJNIHandleArea) block->zap();
+      block->zap();
       #ifndef PRODUCT
       // Link new block to list of all allocated blocks
       block->_block_list_link = _block_list;
@@ -339,7 +341,7 @@ void JNIHandleBlock::release_block(JNIHandleBlock* block, Thread* thread) {
   // we _don't_ want the block to be kept on the free_handle_block.
   // See for instance JavaThread::exit().
   if (thread != NULL ) {
-    if (ZapJNIHandleArea) block->zap();
+    block->zap();
     JNIHandleBlock* freelist = thread->free_handle_block();
     block->_pop_frame_link = NULL;
     thread->set_free_handle_block(block);
@@ -360,7 +362,7 @@ void JNIHandleBlock::release_block(JNIHandleBlock* block, Thread* thread) {
     MutexLockerEx ml(JNIHandleBlockFreeList_lock,
                      Mutex::_no_safepoint_check_flag);
     while (block != NULL) {
-      if (ZapJNIHandleArea) block->zap();
+      block->zap();
       JNIHandleBlock* next = block->_next;
       block->_next = _block_free_list;
       _block_free_list = block;
@@ -453,13 +455,13 @@ jobject JNIHandleBlock::allocate_handle(oop obj) {
         break;
       }
       current->_top = 0;
-      if (ZapJNIHandleArea) current->zap();
+      current->zap();
     }
     // Clear initial block
     _free_list = NULL;
     _allocate_before_rebuild = 0;
     _last = this;
-    if (ZapJNIHandleArea) zap();
+    zap();
   }
 
   // Try last block
diff --git a/src/hotspot/share/runtime/jniHandles.hpp b/src/hotspot/share/runtime/jniHandles.hpp
index b7cd7668549..8bba3e3125b 100644
--- a/src/hotspot/share/runtime/jniHandles.hpp
+++ b/src/hotspot/share/runtime/jniHandles.hpp
@@ -148,7 +148,7 @@ class JNIHandleBlock : public CHeapObj<mtInternal> {
   static int      _blocks_allocated;            // For debugging/printing
 
   // Fill block with bad_handle values
-  void zap();
+  void zap() NOT_DEBUG_RETURN;
 
   // Free list computation
   void rebuild_free_list();
@@ -219,9 +219,8 @@ inline oop& JNIHandles::jweak_ref(jobject handle) {
 template<bool external_guard>
 inline oop JNIHandles::guard_value(oop value) {
   if (!external_guard) {
-    assert(value != badJNIHandle, "Pointing to zapped jni handle area");
     assert(value != deleted_handle(), "Used a deleted global handle");
-  } else if ((value == badJNIHandle) || (value == deleted_handle())) {
+  } else if (value == deleted_handle()) {
     value = NULL;
   }
   return value;
diff --git a/src/hotspot/share/runtime/memprofiler.cpp b/src/hotspot/share/runtime/memprofiler.cpp
index 77a1f183daa..396285ac8c8 100644
--- a/src/hotspot/share/runtime/memprofiler.cpp
+++ b/src/hotspot/share/runtime/memprofiler.cpp
@@ -36,6 +36,7 @@
 #include "runtime/os.hpp"
 #include "runtime/task.hpp"
 #include "runtime/thread.inline.hpp"
+#include "runtime/threadSMR.hpp"
 #include "runtime/vmThread.hpp"
 
 #ifndef PRODUCT
@@ -51,8 +52,6 @@ class MemProfilerTask : public PeriodicTask {
 
 
 void MemProfilerTask::task() {
-  // Get thread lock to provide mutual exclusion, and so we can iterate safely over the thread list.
-  MutexLocker mu(Threads_lock);
   MemProfiler::do_trace();
 }
 
@@ -109,20 +108,21 @@ void MemProfiler::do_trace() {
   // Calculate thread local sizes
   size_t handles_memory_usage    = VMThread::vm_thread()->handle_area()->size_in_bytes();
   size_t resource_memory_usage   = VMThread::vm_thread()->resource_area()->size_in_bytes();
-  JavaThread *cur = Threads::first();
-  while (cur != NULL) {
-    handles_memory_usage  += cur->handle_area()->size_in_bytes();
-    resource_memory_usage += cur->resource_area()->size_in_bytes();
-    cur = cur->next();
-  }
+  {
+    JavaThreadIteratorWithHandle jtiwh;
+    for (; JavaThread *cur = jtiwh.next(); ) {
+      handles_memory_usage  += cur->handle_area()->size_in_bytes();
+      resource_memory_usage += cur->resource_area()->size_in_bytes();
+    }
 
-  // Print trace line in log
-  fprintf(_log_fp, "%6.1f,%5d,%5d," UINTX_FORMAT_W(6) "," UINTX_FORMAT_W(6) ",",
-          os::elapsedTime(),
-          Threads::number_of_threads(),
-          InstanceKlass::number_of_instance_classes(),
-          Universe::heap()->used() / K,
-          Universe::heap()->capacity() / K);
+    // Print trace line in log
+    fprintf(_log_fp, "%6.1f,%5d,%5d," UINTX_FORMAT_W(6) "," UINTX_FORMAT_W(6) ",",
+            os::elapsedTime(),
+            jtiwh.length(),
+            InstanceKlass::number_of_instance_classes(),
+            Universe::heap()->used() / K,
+            Universe::heap()->capacity() / K);
+  }
 
   fprintf(_log_fp, UINTX_FORMAT_W(6) ",", CodeCache::capacity() / K);
 
diff --git a/src/hotspot/share/runtime/objectMonitor.cpp b/src/hotspot/share/runtime/objectMonitor.cpp
index 40037a496a5..7f659bfecb1 100644
--- a/src/hotspot/share/runtime/objectMonitor.cpp
+++ b/src/hotspot/share/runtime/objectMonitor.cpp
@@ -24,6 +24,7 @@
 
 #include "precompiled.hpp"
 #include "classfile/vmSymbols.hpp"
+#include "memory/allocation.inline.hpp"
 #include "memory/resourceArea.hpp"
 #include "oops/markOop.hpp"
 #include "oops/oop.inline.hpp"
@@ -242,6 +243,19 @@ static volatile int InitDone        = 0;
 // * See also http://blogs.sun.com/dave
 
 
+void* ObjectMonitor::operator new (size_t size) throw() {
+  return AllocateHeap(size, mtInternal);
+}
+void* ObjectMonitor::operator new[] (size_t size) throw() {
+  return operator new (size);
+}
+void ObjectMonitor::operator delete(void* p) {
+  FreeHeap(p);
+}
+void ObjectMonitor::operator delete[] (void *p) {
+  operator delete(p);
+}
+
 // -----------------------------------------------------------------------------
 // Enter support
 
@@ -2138,6 +2152,7 @@ ObjectWaiter::ObjectWaiter(Thread* thread) {
   _next     = NULL;
   _prev     = NULL;
   _notified = 0;
+  _notifier_tid = 0;
   TState    = TS_RUN;
   _thread   = thread;
   _event    = thread->_ParkEvent;
diff --git a/src/hotspot/share/runtime/objectMonitor.hpp b/src/hotspot/share/runtime/objectMonitor.hpp
index 484b99aa2da..ee610b1a4db 100644
--- a/src/hotspot/share/runtime/objectMonitor.hpp
+++ b/src/hotspot/share/runtime/objectMonitor.hpp
@@ -25,6 +25,7 @@
 #ifndef SHARE_VM_RUNTIME_OBJECTMONITOR_HPP
 #define SHARE_VM_RUNTIME_OBJECTMONITOR_HPP
 
+#include "memory/allocation.hpp"
 #include "memory/padded.hpp"
 #include "runtime/os.hpp"
 #include "runtime/park.hpp"
@@ -212,18 +213,10 @@ class ObjectMonitor {
   static int Knob_VerifyMatch;
   static int Knob_SpinLimit;
 
-  void* operator new (size_t size) throw() {
-    return AllocateHeap(size, mtInternal);
-  }
-  void* operator new[] (size_t size) throw() {
-    return operator new (size);
-  }
-  void operator delete(void* p) {
-    FreeHeap(p);
-  }
-  void operator delete[] (void *p) {
-    operator delete(p);
-  }
+  void* operator new (size_t size) throw();
+  void* operator new[] (size_t size) throw();
+  void operator delete(void* p);
+  void operator delete[] (void *p);
 
   // TODO-FIXME: the "offset" routines should return a type of off_t instead of int ...
   // ByteSize would also be an appropriate type.
diff --git a/src/hotspot/share/runtime/os.cpp b/src/hotspot/share/runtime/os.cpp
index 0306faa5515..dce176a8a52 100644
--- a/src/hotspot/share/runtime/os.cpp
+++ b/src/hotspot/share/runtime/os.cpp
@@ -54,6 +54,7 @@
 #include "runtime/os.inline.hpp"
 #include "runtime/stubRoutines.hpp"
 #include "runtime/thread.inline.hpp"
+#include "runtime/threadSMR.hpp"
 #include "runtime/vm_version.hpp"
 #include "services/attachListener.hpp"
 #include "services/mallocTracker.hpp"
@@ -197,15 +198,7 @@ char* os::iso8601_time(char* buffer, size_t buffer_length, bool utc) {
 }
 
 OSReturn os::set_priority(Thread* thread, ThreadPriority p) {
-#ifdef ASSERT
-  if (!(!thread->is_Java_thread() ||
-         Thread::current() == thread  ||
-         Threads_lock->owned_by_self()
-         || thread->is_Compiler_thread()
-        )) {
-    assert(false, "possibility of dangling Thread pointer");
-  }
-#endif
+  debug_only(Thread::check_for_dangling_thread_pointer(thread);)
 
   if (p >= MinPriority && p <= MaxPriority) {
     int priority = java_to_os_priority[p];
@@ -1100,7 +1093,7 @@ void os::print_location(outputStream* st, intptr_t x, bool verbose) {
   }
 #endif
 
-  for(JavaThread *thread = Threads::first(); thread; thread = thread->next()) {
+  for (JavaThreadIteratorWithHandle jtiwh; JavaThread *thread = jtiwh.next(); ) {
     // Check for privilege stack
     if (thread->privileged_stack_top() != NULL &&
         thread->privileged_stack_top()->contains(addr)) {
@@ -1126,7 +1119,6 @@ void os::print_location(outputStream* st, intptr_t x, bool verbose) {
       if (verbose) thread->print_on(st);
       return;
     }
-
   }
 
   // Check if in metaspace and print types that have vptrs (only method now)
@@ -1665,7 +1657,6 @@ void os::initialize_initial_active_processor_count() {
 }
 
 void os::SuspendedThreadTask::run() {
-  assert(Threads_lock->owned_by_self() || (_thread == VMThread::vm_thread()), "must have threads lock to call this");
   internal_do_task();
   _done = true;
 }
@@ -1674,10 +1665,21 @@ bool os::create_stack_guard_pages(char* addr, size_t bytes) {
   return os::pd_create_stack_guard_pages(addr, bytes);
 }
 
-char* os::reserve_memory(size_t bytes, char* addr, size_t alignment_hint) {
-  char* result = pd_reserve_memory(bytes, addr, alignment_hint);
-  if (result != NULL) {
-    MemTracker::record_virtual_memory_reserve((address)result, bytes, CALLER_PC);
+char* os::reserve_memory(size_t bytes, char* addr, size_t alignment_hint, int file_desc) {
+  char* result = NULL;
+
+  if (file_desc != -1) {
+    // Could have called pd_reserve_memory() followed by replace_existing_mapping_with_file_mapping(),
+    // but AIX may use SHM in which case its more trouble to detach the segment and remap memory to the file.
+    result = os::map_memory_to_file(addr, bytes, file_desc);
+    if (result != NULL) {
+      MemTracker::record_virtual_memory_reserve_and_commit((address)result, bytes, CALLER_PC);
+    }
+  } else {
+    result = pd_reserve_memory(bytes, addr, alignment_hint);
+    if (result != NULL) {
+      MemTracker::record_virtual_memory_reserve((address)result, bytes, CALLER_PC);
+    }
   }
 
   return result;
@@ -1694,10 +1696,18 @@ char* os::reserve_memory(size_t bytes, char* addr, size_t alignment_hint,
   return result;
 }
 
-char* os::attempt_reserve_memory_at(size_t bytes, char* addr) {
-  char* result = pd_attempt_reserve_memory_at(bytes, addr);
-  if (result != NULL) {
-    MemTracker::record_virtual_memory_reserve((address)result, bytes, CALLER_PC);
+char* os::attempt_reserve_memory_at(size_t bytes, char* addr, int file_desc) {
+  char* result = NULL;
+  if (file_desc != -1) {
+    result = pd_attempt_reserve_memory_at(bytes, addr, file_desc);
+    if (result != NULL) {
+      MemTracker::record_virtual_memory_reserve_and_commit((address)result, bytes, CALLER_PC);
+    }
+  } else {
+    result = pd_attempt_reserve_memory_at(bytes, addr);
+    if (result != NULL) {
+      MemTracker::record_virtual_memory_reserve_and_commit((address)result, bytes, CALLER_PC);
+    }
   }
   return result;
 }
diff --git a/src/hotspot/share/runtime/os.hpp b/src/hotspot/share/runtime/os.hpp
index 61999125339..83b24f574ed 100644
--- a/src/hotspot/share/runtime/os.hpp
+++ b/src/hotspot/share/runtime/os.hpp
@@ -108,8 +108,9 @@ class os: AllStatic {
   }
 
   static char*  pd_reserve_memory(size_t bytes, char* addr = 0,
-                               size_t alignment_hint = 0);
+                                  size_t alignment_hint = 0);
   static char*  pd_attempt_reserve_memory_at(size_t bytes, char* addr);
+  static char*  pd_attempt_reserve_memory_at(size_t bytes, char* addr, int file_desc);
   static void   pd_split_reserved_memory(char *base, size_t size,
                                       size_t split, bool realloc);
   static bool   pd_commit_memory(char* addr, size_t bytes, bool executable);
@@ -310,11 +311,11 @@ class os: AllStatic {
 
   static int    vm_allocation_granularity();
   static char*  reserve_memory(size_t bytes, char* addr = 0,
-                               size_t alignment_hint = 0);
+                               size_t alignment_hint = 0, int file_desc = -1);
   static char*  reserve_memory(size_t bytes, char* addr,
                                size_t alignment_hint, MEMFLAGS flags);
-  static char*  reserve_memory_aligned(size_t size, size_t alignment);
-  static char*  attempt_reserve_memory_at(size_t bytes, char* addr);
+  static char*  reserve_memory_aligned(size_t size, size_t alignment, int file_desc = -1);
+  static char*  attempt_reserve_memory_at(size_t bytes, char* addr, int file_desc = -1);
   static void   split_reserved_memory(char *base, size_t size,
                                       size_t split, bool realloc);
   static bool   commit_memory(char* addr, size_t bytes, bool executable);
@@ -345,6 +346,14 @@ class os: AllStatic {
   static bool   create_stack_guard_pages(char* addr, size_t bytes);
   static bool   pd_create_stack_guard_pages(char* addr, size_t bytes);
   static bool   remove_stack_guard_pages(char* addr, size_t bytes);
+  // Helper function to create a new file with template jvmheap.XXXXXX.
+  // Returns a valid fd on success or else returns -1
+  static int create_file_for_heap(const char* dir);
+  // Map memory to the file referred by fd. This function is slightly different from map_memory()
+  // and is added to be used for implementation of -XX:AllocateHeapAt
+  static char* map_memory_to_file(char* base, size_t size, int fd);
+  // Replace existing reserved memory with file mapping
+  static char* replace_existing_mapping_with_file_mapping(char* base, size_t size, int fd);
 
   static char*  map_memory(int fd, const char* file_name, size_t file_offset,
                            char *addr, size_t bytes, bool read_only = false,
diff --git a/src/hotspot/share/runtime/park.cpp b/src/hotspot/share/runtime/park.cpp
index 6f278dff23a..6314b54bd64 100644
--- a/src/hotspot/share/runtime/park.cpp
+++ b/src/hotspot/share/runtime/park.cpp
@@ -23,10 +23,9 @@
  */
 
 #include "precompiled.hpp"
+#include "memory/allocation.inline.hpp"
 #include "runtime/thread.hpp"
 
-
-
 // Lifecycle management for TSM ParkEvents.
 // ParkEvents are type-stable (TSM).
 // In our particular implementation they happen to be immortal.
diff --git a/src/hotspot/share/runtime/perfData.cpp b/src/hotspot/share/runtime/perfData.cpp
index d79bbc66b48..e6a9045825b 100644
--- a/src/hotspot/share/runtime/perfData.cpp
+++ b/src/hotspot/share/runtime/perfData.cpp
@@ -32,7 +32,7 @@
 #include "runtime/mutex.hpp"
 #include "runtime/mutexLocker.hpp"
 #include "runtime/os.hpp"
-#include "runtime/perfData.hpp"
+#include "runtime/perfData.inline.hpp"
 #include "utilities/exceptions.hpp"
 #include "utilities/globalDefinitions.hpp"
 
@@ -611,3 +611,10 @@ PerfDataList* PerfDataList::clone() {
 
   return copy;
 }
+
+PerfTraceTime::~PerfTraceTime() {
+  if (!UsePerfData || (_recursion_counter != NULL &&
+      --(*_recursion_counter) > 0)) return;
+  _t.stop();
+  _timerp->inc(_t.ticks());
+}
diff --git a/src/hotspot/share/runtime/perfData.hpp b/src/hotspot/share/runtime/perfData.hpp
index 8fa9f14d598..f667ecc9d03 100644
--- a/src/hotspot/share/runtime/perfData.hpp
+++ b/src/hotspot/share/runtime/perfData.hpp
@@ -25,10 +25,11 @@
 #ifndef SHARE_VM_RUNTIME_PERFDATA_HPP
 #define SHARE_VM_RUNTIME_PERFDATA_HPP
 
-#include "memory/allocation.inline.hpp"
+#include "memory/allocation.hpp"
 #include "runtime/perfMemory.hpp"
 #include "runtime/timer.hpp"
-#include "utilities/growableArray.hpp"
+
+template <typename T> class GrowableArray;
 
 /* jvmstat global and subsystem counter name space - enumeration value
  * serve as an index into the PerfDataManager::_name_space[] array
@@ -244,6 +245,7 @@ class PerfData : public CHeapObj<mtInternal> {
 
   friend class StatSampler;      // for access to protected void sample()
   friend class PerfDataManager;  // for access to protected destructor
+  friend class VMStructs;
 
   public:
 
@@ -629,10 +631,10 @@ class PerfDataList : public CHeapObj<mtInternal> {
     bool contains(const char* name) { return find_by_name(name) != NULL; }
 
     // return the number of PerfData items in this list
-    int length() { return _set->length(); }
+    inline int length();
 
     // add a PerfData item to this list
-    void append(PerfData *p) { _set->append(p); }
+    inline void append(PerfData *p);
 
     // remove the given PerfData item from this list. When called
     // while iterating over the list, this method will result in a
@@ -640,7 +642,7 @@ class PerfDataList : public CHeapObj<mtInternal> {
     // method is also impacted by this method as elements with an
     // index greater than the index of the element removed by this
     // method will be shifted down by one.
-    void remove(PerfData *p) { _set->remove(p); }
+    inline void remove(PerfData *p);
 
     // create a new PerfDataList from this list. The new list is
     // a shallow copy of the original list and care should be taken
@@ -651,7 +653,7 @@ class PerfDataList : public CHeapObj<mtInternal> {
     // for backward compatibility with GrowableArray - need to implement
     // some form of iterator to provide a cleaner abstraction for
     // iteration over the container.
-    PerfData* at(int index) { return _set->at(index); }
+    inline PerfData* at(int index);
 };
 
 
@@ -677,23 +679,23 @@ class PerfDataManager : AllStatic {
   protected:
     // return the list of all known PerfData items
     static PerfDataList* all();
-    static int count() { return _all->length(); }
+    static inline int count();
 
     // return the list of all known PerfData items that are to be
     // sampled by the StatSampler.
     static PerfDataList* sampled();
-    static int sampled_count() { return _sampled->length(); }
+    static inline int sampled_count();
 
     // return the list of all known PerfData items that have a
     // variability classification of type Constant
     static PerfDataList* constants();
-    static int constants_count() { return _constants->length(); }
+    static inline int constants_count();
 
   public:
 
     // method to check for the existence of a PerfData item with
     // the given name.
-    static bool exists(const char* name) { return _all->contains(name); }
+    static inline bool exists(const char* name);
 
     // method to search for a instrumentation object by name
     static PerfData* find_by_name(const char* name);
@@ -929,12 +931,7 @@ class PerfTraceTime : public StackObj {
     inline void suspend() { if (!UsePerfData) return; _t.stop(); }
     inline void resume() { if (!UsePerfData) return; _t.start(); }
 
-    inline ~PerfTraceTime() {
-      if (!UsePerfData || (_recursion_counter != NULL &&
-                           --(*_recursion_counter) > 0)) return;
-      _t.stop();
-      _timerp->inc(_t.ticks());
-    }
+    ~PerfTraceTime();
 };
 
 /* The PerfTraceTimedEvent class is responsible for counting the
diff --git a/src/hotspot/share/runtime/perfData.inline.hpp b/src/hotspot/share/runtime/perfData.inline.hpp
new file mode 100644
index 00000000000..b867bbdbb4e
--- /dev/null
+++ b/src/hotspot/share/runtime/perfData.inline.hpp
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2002, 2017, 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_RUNTIME_PERFDATA_INLINE_HPP
+#define SHARE_VM_RUNTIME_PERFDATA_INLINE_HPP
+
+#include "runtime/perfData.hpp"
+#include "utilities/globalDefinitions.hpp"
+#include "utilities/growableArray.hpp"
+
+inline int PerfDataList::length() {
+  return _set->length();
+}
+
+inline void PerfDataList::append(PerfData *p) {
+  _set->append(p);
+}
+
+inline void PerfDataList::remove(PerfData *p) {
+  _set->remove(p);
+}
+
+inline PerfData* PerfDataList::at(int index) {
+  return _set->at(index);
+}
+
+inline int PerfDataManager::count() {
+  return _all->length();
+}
+
+inline int PerfDataManager::sampled_count() {
+  return _sampled->length();
+}
+
+inline int PerfDataManager::constants_count() {
+  return _constants->length();
+}
+
+inline bool PerfDataManager::exists(const char* name) {
+  return _all->contains(name);
+}
+
+#endif // SHARE_VM_RUNTIME_PERFDATA_INLINE_HPP
diff --git a/src/hotspot/share/runtime/safepoint.cpp b/src/hotspot/share/runtime/safepoint.cpp
index 291ee50b170..23d56c8e0d5 100644
--- a/src/hotspot/share/runtime/safepoint.cpp
+++ b/src/hotspot/share/runtime/safepoint.cpp
@@ -59,6 +59,7 @@
 #include "runtime/sweeper.hpp"
 #include "runtime/synchronizer.hpp"
 #include "runtime/thread.inline.hpp"
+#include "runtime/threadSMR.hpp"
 #include "runtime/timerTrace.hpp"
 #include "services/runtimeService.hpp"
 #include "trace/tracing.hpp"
@@ -174,7 +175,7 @@ void SafepointSynchronize::begin() {
     if (SafepointMechanism::uses_thread_local_poll()) {
       // Arming the per thread poll while having _state != _not_synchronized means safepointing
       log_trace(safepoint)("Setting thread local yield flag for threads");
-      for (JavaThread *cur = Threads::first(); cur != NULL; cur = cur->next()) {
+      for (JavaThreadIteratorWithHandle jtiwh; JavaThread *cur = jtiwh.next(); ) {
         // Make sure the threads start polling, it is time to yield.
         SafepointMechanism::arm_local_poll(cur); // release store, global state -> local state
       }
@@ -200,133 +201,137 @@ void SafepointSynchronize::begin() {
 
     // Consider using active_processor_count() ... but that call is expensive.
     int ncpus = os::processor_count() ;
+    unsigned int iterations = 0;
 
+    {
+      JavaThreadIteratorWithHandle jtiwh;
 #ifdef ASSERT
-    for (JavaThread *cur = Threads::first(); cur != NULL; cur = cur->next()) {
-      assert(cur->safepoint_state()->is_running(), "Illegal initial state");
-      // Clear the visited flag to ensure that the critical counts are collected properly.
-      cur->set_visited_for_critical_count(false);
-    }
+      for (; JavaThread *cur = jtiwh.next(); ) {
+        assert(cur->safepoint_state()->is_running(), "Illegal initial state");
+        // Clear the visited flag to ensure that the critical counts are collected properly.
+        cur->set_visited_for_critical_count(false);
+      }
 #endif // ASSERT
 
-    if (SafepointTimeout)
-      safepoint_limit_time = os::javaTimeNanos() + (jlong)SafepointTimeoutDelay * MICROUNITS;
+      if (SafepointTimeout)
+        safepoint_limit_time = os::javaTimeNanos() + (jlong)SafepointTimeoutDelay * MICROUNITS;
 
-    // Iterate through all threads until it have been determined how to stop them all at a safepoint
-    unsigned int iterations = 0;
-    int steps = 0 ;
-    while(still_running > 0) {
-      for (JavaThread *cur = Threads::first(); cur != NULL; cur = cur->next()) {
-        assert(!cur->is_ConcurrentGC_thread(), "A concurrent GC thread is unexpectly being suspended");
-        ThreadSafepointState *cur_state = cur->safepoint_state();
-        if (cur_state->is_running()) {
-          cur_state->examine_state_of_thread();
-          if (!cur_state->is_running()) {
-            still_running--;
-            // consider adjusting steps downward:
-            //   steps = 0
-            //   steps -= NNN
-            //   steps >>= 1
-            //   steps = MIN(steps, 2000-100)
-            //   if (iterations != 0) steps -= NNN
-          }
-          LogTarget(Trace, safepoint) lt;
-          if (lt.is_enabled()) {
-            ResourceMark rm;
-            LogStream ls(lt);
-            cur_state->print_on(&ls);
+      // Iterate through all threads until it have been determined how to stop them all at a safepoint
+      int steps = 0 ;
+      while(still_running > 0) {
+        jtiwh.rewind();
+        for (; JavaThread *cur = jtiwh.next(); ) {
+          assert(!cur->is_ConcurrentGC_thread(), "A concurrent GC thread is unexpectly being suspended");
+          ThreadSafepointState *cur_state = cur->safepoint_state();
+          if (cur_state->is_running()) {
+            cur_state->examine_state_of_thread();
+            if (!cur_state->is_running()) {
+              still_running--;
+              // consider adjusting steps downward:
+              //   steps = 0
+              //   steps -= NNN
+              //   steps >>= 1
+              //   steps = MIN(steps, 2000-100)
+              //   if (iterations != 0) steps -= NNN
+            }
+            LogTarget(Trace, safepoint) lt;
+            if (lt.is_enabled()) {
+              ResourceMark rm;
+              LogStream ls(lt);
+              cur_state->print_on(&ls);
+            }
           }
         }
-      }
 
-      if (iterations == 0) {
-        initial_running = still_running;
-        if (PrintSafepointStatistics) {
-          begin_statistics(nof_threads, still_running);
-        }
-      }
-
-      if (still_running > 0) {
-        // Check for if it takes to long
-        if (SafepointTimeout && safepoint_limit_time < os::javaTimeNanos()) {
-          print_safepoint_timeout(_spinning_timeout);
+        if (iterations == 0) {
+          initial_running = still_running;
+          if (PrintSafepointStatistics) {
+            begin_statistics(nof_threads, still_running);
+          }
         }
 
-        // Spin to avoid context switching.
-        // There's a tension between allowing the mutators to run (and rendezvous)
-        // vs spinning.  As the VM thread spins, wasting cycles, it consumes CPU that
-        // a mutator might otherwise use profitably to reach a safepoint.  Excessive
-        // spinning by the VM thread on a saturated system can increase rendezvous latency.
-        // Blocking or yielding incur their own penalties in the form of context switching
-        // and the resultant loss of $ residency.
-        //
-        // Further complicating matters is that yield() does not work as naively expected
-        // on many platforms -- yield() does not guarantee that any other ready threads
-        // will run.   As such we revert to naked_short_sleep() after some number of iterations.
-        // nakes_short_sleep() is implemented as a short unconditional sleep.
-        // Typical operating systems round a "short" sleep period up to 10 msecs, so sleeping
-        // can actually increase the time it takes the VM thread to detect that a system-wide
-        // stop-the-world safepoint has been reached.  In a pathological scenario such as that
-        // described in CR6415670 the VMthread may sleep just before the mutator(s) become safe.
-        // In that case the mutators will be stalled waiting for the safepoint to complete and the
-        // the VMthread will be sleeping, waiting for the mutators to rendezvous.  The VMthread
-        // will eventually wake up and detect that all mutators are safe, at which point
-        // we'll again make progress.
-        //
-        // Beware too that that the VMThread typically runs at elevated priority.
-        // Its default priority is higher than the default mutator priority.
-        // Obviously, this complicates spinning.
-        //
-        // Note too that on Windows XP SwitchThreadTo() has quite different behavior than Sleep(0).
-        // Sleep(0) will _not yield to lower priority threads, while SwitchThreadTo() will.
-        //
-        // See the comments in synchronizer.cpp for additional remarks on spinning.
-        //
-        // In the future we might:
-        // 1. Modify the safepoint scheme to avoid potentially unbounded spinning.
-        //    This is tricky as the path used by a thread exiting the JVM (say on
-        //    on JNI call-out) simply stores into its state field.  The burden
-        //    is placed on the VM thread, which must poll (spin).
-        // 2. Find something useful to do while spinning.  If the safepoint is GC-related
-        //    we might aggressively scan the stacks of threads that are already safe.
-        // 3. Use Solaris schedctl to examine the state of the still-running mutators.
-        //    If all the mutators are ONPROC there's no reason to sleep or yield.
-        // 4. YieldTo() any still-running mutators that are ready but OFFPROC.
-        // 5. Check system saturation.  If the system is not fully saturated then
-        //    simply spin and avoid sleep/yield.
-        // 6. As still-running mutators rendezvous they could unpark the sleeping
-        //    VMthread.  This works well for still-running mutators that become
-        //    safe.  The VMthread must still poll for mutators that call-out.
-        // 7. Drive the policy on time-since-begin instead of iterations.
-        // 8. Consider making the spin duration a function of the # of CPUs:
-        //    Spin = (((ncpus-1) * M) + K) + F(still_running)
-        //    Alternately, instead of counting iterations of the outer loop
-        //    we could count the # of threads visited in the inner loop, above.
-        // 9. On windows consider using the return value from SwitchThreadTo()
-        //    to drive subsequent spin/SwitchThreadTo()/Sleep(N) decisions.
-
-        if (SafepointMechanism::uses_global_page_poll() && int(iterations) == DeferPollingPageLoopCount) {
-          guarantee (PageArmed == 0, "invariant") ;
-          PageArmed = 1 ;
-          os::make_polling_page_unreadable();
-        }
-
-        // Instead of (ncpus > 1) consider either (still_running < (ncpus + EPSILON)) or
-        // ((still_running + _waiting_to_block - TryingToBlock)) < ncpus)
-        ++steps ;
-        if (ncpus > 1 && steps < SafepointSpinBeforeYield) {
-          SpinPause() ;     // MP-Polite spin
-        } else
-          if (steps < DeferThrSuspendLoopCount) {
-            os::naked_yield() ;
-          } else {
-            os::naked_short_sleep(1);
+        if (still_running > 0) {
+          // Check for if it takes to long
+          if (SafepointTimeout && safepoint_limit_time < os::javaTimeNanos()) {
+            print_safepoint_timeout(_spinning_timeout);
           }
 
-        iterations ++ ;
+          // Spin to avoid context switching.
+          // There's a tension between allowing the mutators to run (and rendezvous)
+          // vs spinning.  As the VM thread spins, wasting cycles, it consumes CPU that
+          // a mutator might otherwise use profitably to reach a safepoint.  Excessive
+          // spinning by the VM thread on a saturated system can increase rendezvous latency.
+          // Blocking or yielding incur their own penalties in the form of context switching
+          // and the resultant loss of $ residency.
+          //
+          // Further complicating matters is that yield() does not work as naively expected
+          // on many platforms -- yield() does not guarantee that any other ready threads
+          // will run.   As such we revert to naked_short_sleep() after some number of iterations.
+          // nakes_short_sleep() is implemented as a short unconditional sleep.
+          // Typical operating systems round a "short" sleep period up to 10 msecs, so sleeping
+          // can actually increase the time it takes the VM thread to detect that a system-wide
+          // stop-the-world safepoint has been reached.  In a pathological scenario such as that
+          // described in CR6415670 the VMthread may sleep just before the mutator(s) become safe.
+          // In that case the mutators will be stalled waiting for the safepoint to complete and the
+          // the VMthread will be sleeping, waiting for the mutators to rendezvous.  The VMthread
+          // will eventually wake up and detect that all mutators are safe, at which point
+          // we'll again make progress.
+          //
+          // Beware too that that the VMThread typically runs at elevated priority.
+          // Its default priority is higher than the default mutator priority.
+          // Obviously, this complicates spinning.
+          //
+          // Note too that on Windows XP SwitchThreadTo() has quite different behavior than Sleep(0).
+          // Sleep(0) will _not yield to lower priority threads, while SwitchThreadTo() will.
+          //
+          // See the comments in synchronizer.cpp for additional remarks on spinning.
+          //
+          // In the future we might:
+          // 1. Modify the safepoint scheme to avoid potentially unbounded spinning.
+          //    This is tricky as the path used by a thread exiting the JVM (say on
+          //    on JNI call-out) simply stores into its state field.  The burden
+          //    is placed on the VM thread, which must poll (spin).
+          // 2. Find something useful to do while spinning.  If the safepoint is GC-related
+          //    we might aggressively scan the stacks of threads that are already safe.
+          // 3. Use Solaris schedctl to examine the state of the still-running mutators.
+          //    If all the mutators are ONPROC there's no reason to sleep or yield.
+          // 4. YieldTo() any still-running mutators that are ready but OFFPROC.
+          // 5. Check system saturation.  If the system is not fully saturated then
+          //    simply spin and avoid sleep/yield.
+          // 6. As still-running mutators rendezvous they could unpark the sleeping
+          //    VMthread.  This works well for still-running mutators that become
+          //    safe.  The VMthread must still poll for mutators that call-out.
+          // 7. Drive the policy on time-since-begin instead of iterations.
+          // 8. Consider making the spin duration a function of the # of CPUs:
+          //    Spin = (((ncpus-1) * M) + K) + F(still_running)
+          //    Alternately, instead of counting iterations of the outer loop
+          //    we could count the # of threads visited in the inner loop, above.
+          // 9. On windows consider using the return value from SwitchThreadTo()
+          //    to drive subsequent spin/SwitchThreadTo()/Sleep(N) decisions.
+
+          if (SafepointMechanism::uses_global_page_poll() && int(iterations) == DeferPollingPageLoopCount) {
+            guarantee (PageArmed == 0, "invariant") ;
+            PageArmed = 1 ;
+            os::make_polling_page_unreadable();
+          }
+
+          // Instead of (ncpus > 1) consider either (still_running < (ncpus + EPSILON)) or
+          // ((still_running + _waiting_to_block - TryingToBlock)) < ncpus)
+          ++steps ;
+          if (ncpus > 1 && steps < SafepointSpinBeforeYield) {
+            SpinPause() ;     // MP-Polite spin
+          } else
+            if (steps < DeferThrSuspendLoopCount) {
+              os::naked_yield() ;
+            } else {
+              os::naked_short_sleep(1);
+            }
+
+          iterations ++ ;
+        }
+        assert(iterations < (uint)max_jint, "We have been iterating in the safepoint loop too long");
       }
-      assert(iterations < (uint)max_jint, "We have been iterating in the safepoint loop too long");
-    }
+    } // ThreadsListHandle destroyed here.
     assert(still_running == 0, "sanity check");
 
     if (PrintSafepointStatistics) {
@@ -341,7 +346,7 @@ void SafepointSynchronize::begin() {
       sync_event.set_iterations(iterations);
       sync_event.commit();
     }
-  } //EventSafepointStateSync
+  } // EventSafepointStateSynchronization destroyed here.
 
   // wait until all threads are stopped
   {
@@ -393,8 +398,8 @@ void SafepointSynchronize::begin() {
   } // EventSafepointWaitBlocked
 
 #ifdef ASSERT
-  for (JavaThread *cur = Threads::first(); cur != NULL; cur = cur->next()) {
-    // make sure all the threads were visited
+  // Make sure all the threads were visited.
+  for (JavaThreadIteratorWithHandle jtiwh; JavaThread *cur = jtiwh.next(); ) {
     assert(cur->was_visited_for_critical_count(), "missed a thread");
   }
 #endif // ASSERT
@@ -452,81 +457,86 @@ void SafepointSynchronize::end() {
     end_statistics(os::javaTimeNanos());
   }
 
+  {
+    JavaThreadIteratorWithHandle jtiwh;
 #ifdef ASSERT
-  // A pending_exception cannot be installed during a safepoint.  The threads
-  // may install an async exception after they come back from a safepoint into
-  // pending_exception after they unblock.  But that should happen later.
-  for (JavaThread *cur = Threads::first(); cur; cur = cur->next()) {
-    assert (!(cur->has_pending_exception() &&
-              cur->safepoint_state()->is_at_poll_safepoint()),
-            "safepoint installed a pending exception");
-  }
+    // A pending_exception cannot be installed during a safepoint.  The threads
+    // may install an async exception after they come back from a safepoint into
+    // pending_exception after they unblock.  But that should happen later.
+    for (; JavaThread *cur = jtiwh.next(); ) {
+      assert (!(cur->has_pending_exception() &&
+                cur->safepoint_state()->is_at_poll_safepoint()),
+              "safepoint installed a pending exception");
+    }
 #endif // ASSERT
 
-  if (PageArmed) {
-    assert(SafepointMechanism::uses_global_page_poll(), "sanity");
-    // Make polling safepoint aware
-    os::make_polling_page_readable();
-    PageArmed = 0 ;
-  }
-
-  if (SafepointMechanism::uses_global_page_poll()) {
-    // Remove safepoint check from interpreter
-    Interpreter::ignore_safepoints();
-  }
-
-  {
-    MutexLocker mu(Safepoint_lock);
-
-    assert(_state == _synchronized, "must be synchronized before ending safepoint synchronization");
-
-    if (SafepointMechanism::uses_thread_local_poll()) {
-      _state = _not_synchronized;
-      OrderAccess::storestore(); // global state -> local state
-      for (JavaThread *current = Threads::first(); current; current = current->next()) {
-        ThreadSafepointState* cur_state = current->safepoint_state();
-        cur_state->restart(); // TSS _running
-        SafepointMechanism::disarm_local_poll(current); // release store, local state -> polling page
-      }
-      log_debug(safepoint)("Leaving safepoint region");
-    } else {
-      // Set to not synchronized, so the threads will not go into the signal_thread_blocked method
-      // when they get restarted.
-      _state = _not_synchronized;
-      OrderAccess::fence();
-
-      log_debug(safepoint)("Leaving safepoint region");
-
-      // Start suspended threads
-      for (JavaThread *current = Threads::first(); current; current = current->next()) {
-        // A problem occurring on Solaris is when attempting to restart threads
-        // the first #cpus - 1 go well, but then the VMThread is preempted when we get
-        // to the next one (since it has been running the longest).  We then have
-        // to wait for a cpu to become available before we can continue restarting
-        // threads.
-        // FIXME: This causes the performance of the VM to degrade when active and with
-        // large numbers of threads.  Apparently this is due to the synchronous nature
-        // of suspending threads.
-        //
-        // TODO-FIXME: the comments above are vestigial and no longer apply.
-        // Furthermore, using solaris' schedctl in this particular context confers no benefit
-        if (VMThreadHintNoPreempt) {
-          os::hint_no_preempt();
-        }
-        ThreadSafepointState* cur_state = current->safepoint_state();
-        assert(cur_state->type() != ThreadSafepointState::_running, "Thread not suspended at safepoint");
-        cur_state->restart();
-        assert(cur_state->is_running(), "safepoint state has not been reset");
-      }
+    if (PageArmed) {
+      assert(SafepointMechanism::uses_global_page_poll(), "sanity");
+      // Make polling safepoint aware
+      os::make_polling_page_readable();
+      PageArmed = 0 ;
     }
 
-    RuntimeService::record_safepoint_end();
+    if (SafepointMechanism::uses_global_page_poll()) {
+      // Remove safepoint check from interpreter
+      Interpreter::ignore_safepoints();
+    }
 
-    // Release threads lock, so threads can be created/destroyed again. It will also starts all threads
-    // blocked in signal_thread_blocked
-    Threads_lock->unlock();
+    {
+      MutexLocker mu(Safepoint_lock);
+
+      assert(_state == _synchronized, "must be synchronized before ending safepoint synchronization");
+
+      if (SafepointMechanism::uses_thread_local_poll()) {
+        _state = _not_synchronized;
+        OrderAccess::storestore(); // global state -> local state
+        jtiwh.rewind();
+        for (; JavaThread *current = jtiwh.next(); ) {
+          ThreadSafepointState* cur_state = current->safepoint_state();
+          cur_state->restart(); // TSS _running
+          SafepointMechanism::disarm_local_poll(current); // release store, local state -> polling page
+        }
+        log_debug(safepoint)("Leaving safepoint region");
+      } else {
+        // Set to not synchronized, so the threads will not go into the signal_thread_blocked method
+        // when they get restarted.
+        _state = _not_synchronized;
+        OrderAccess::fence();
+
+        log_debug(safepoint)("Leaving safepoint region");
+
+        // Start suspended threads
+        jtiwh.rewind();
+        for (; JavaThread *current = jtiwh.next(); ) {
+          // A problem occurring on Solaris is when attempting to restart threads
+          // the first #cpus - 1 go well, but then the VMThread is preempted when we get
+          // to the next one (since it has been running the longest).  We then have
+          // to wait for a cpu to become available before we can continue restarting
+          // threads.
+          // FIXME: This causes the performance of the VM to degrade when active and with
+          // large numbers of threads.  Apparently this is due to the synchronous nature
+          // of suspending threads.
+          //
+          // TODO-FIXME: the comments above are vestigial and no longer apply.
+          // Furthermore, using solaris' schedctl in this particular context confers no benefit
+          if (VMThreadHintNoPreempt) {
+            os::hint_no_preempt();
+          }
+          ThreadSafepointState* cur_state = current->safepoint_state();
+          assert(cur_state->type() != ThreadSafepointState::_running, "Thread not suspended at safepoint");
+          cur_state->restart();
+          assert(cur_state->is_running(), "safepoint state has not been reset");
+        }
+      }
+
+      RuntimeService::record_safepoint_end();
+
+      // Release threads lock, so threads can be created/destroyed again.
+      // It will also release all threads blocked in signal_thread_blocked.
+      Threads_lock->unlock();
+    }
+  } // ThreadsListHandle destroyed here.
 
-  }
   Universe::heap()->safepoint_synchronize_end();
   // record this time so VMThread can keep track how much time has elapsed
   // since last safepoint.
@@ -915,12 +925,11 @@ void SafepointSynchronize::print_safepoint_timeout(SafepointTimeoutReason reason
     tty->print_cr("# SafepointSynchronize::begin: Threads which did not reach the safepoint:");
     ThreadSafepointState *cur_state;
     ResourceMark rm;
-    for (JavaThread *cur_thread = Threads::first(); cur_thread;
-        cur_thread = cur_thread->next()) {
+    for (JavaThreadIteratorWithHandle jtiwh; JavaThread *cur_thread = jtiwh.next(); ) {
       cur_state = cur_thread->safepoint_state();
 
       if (cur_thread->thread_state() != _thread_blocked &&
-          ((reason == _spinning_timeout && cur_state->is_running()) ||
+        ((reason == _spinning_timeout && cur_state->is_running()) ||
            (reason == _blocking_timeout && !cur_state->has_called_back()))) {
         tty->print("# ");
         cur_thread->print();
@@ -1427,7 +1436,7 @@ void SafepointSynchronize::print_state() {
     tty->print_cr("State: %s", (_state == _synchronizing) ? "synchronizing" :
                   "synchronized");
 
-    for (JavaThread *cur = Threads::first(); cur; cur = cur->next()) {
+    for (JavaThreadIteratorWithHandle jtiwh; JavaThread *cur = jtiwh.next(); ) {
        cur->safepoint_state()->print();
     }
   }
diff --git a/src/hotspot/share/runtime/sharedRuntime.cpp b/src/hotspot/share/runtime/sharedRuntime.cpp
index 8e4c90f9199..abb070dffd7 100644
--- a/src/hotspot/share/runtime/sharedRuntime.cpp
+++ b/src/hotspot/share/runtime/sharedRuntime.cpp
@@ -970,7 +970,7 @@ JNI_ENTRY(void*, throw_unsatisfied_link_error(JNIEnv* env, ...))
 {
   // We return a bad value here to make sure that the exception is
   // forwarded before we look at the return value.
-  THROW_(vmSymbols::java_lang_UnsatisfiedLinkError(), (void*)badJNIHandle);
+  THROW_(vmSymbols::java_lang_UnsatisfiedLinkError(), (void*)badAddress);
 }
 JNI_END
 
diff --git a/src/hotspot/share/runtime/statSampler.cpp b/src/hotspot/share/runtime/statSampler.cpp
index 6cdd0743ad1..3f995380468 100644
--- a/src/hotspot/share/runtime/statSampler.cpp
+++ b/src/hotspot/share/runtime/statSampler.cpp
@@ -32,6 +32,7 @@
 #include "runtime/java.hpp"
 #include "runtime/javaCalls.hpp"
 #include "runtime/os.hpp"
+#include "runtime/perfData.inline.hpp"
 #include "runtime/statSampler.hpp"
 #include "runtime/vm_version.hpp"
 
diff --git a/src/hotspot/share/runtime/synchronizer.cpp b/src/hotspot/share/runtime/synchronizer.cpp
index d54b0d8b30a..6b8898959c8 100644
--- a/src/hotspot/share/runtime/synchronizer.cpp
+++ b/src/hotspot/share/runtime/synchronizer.cpp
@@ -894,7 +894,7 @@ ObjectSynchronizer::LockOwnership ObjectSynchronizer::query_lock_ownership
 }
 
 // FIXME: jvmti should call this
-JavaThread* ObjectSynchronizer::get_lock_owner(Handle h_obj, bool doLock) {
+JavaThread* ObjectSynchronizer::get_lock_owner(ThreadsList * t_list, Handle h_obj) {
   if (UseBiasedLocking) {
     if (SafepointSynchronize::is_at_safepoint()) {
       BiasedLocking::revoke_at_safepoint(h_obj);
@@ -923,7 +923,7 @@ JavaThread* ObjectSynchronizer::get_lock_owner(Handle h_obj, bool doLock) {
 
   if (owner != NULL) {
     // owning_thread_from_monitor_owner() may also return NULL here
-    return Threads::owning_thread_from_monitor_owner(owner, doLock);
+    return Threads::owning_thread_from_monitor_owner(t_list, owner);
   }
 
   // Unlocked case, header in place
diff --git a/src/hotspot/share/runtime/synchronizer.hpp b/src/hotspot/share/runtime/synchronizer.hpp
index ba3ac60a267..415c222ab6f 100644
--- a/src/hotspot/share/runtime/synchronizer.hpp
+++ b/src/hotspot/share/runtime/synchronizer.hpp
@@ -32,6 +32,7 @@
 #include "runtime/perfData.hpp"
 
 class ObjectMonitor;
+class ThreadsList;
 
 struct DeflateMonitorCounters {
   int nInuse;          // currently associated with objects
@@ -125,7 +126,7 @@ class ObjectSynchronizer : AllStatic {
   static bool current_thread_holds_lock(JavaThread* thread, Handle h_obj);
   static LockOwnership query_lock_ownership(JavaThread * self, Handle h_obj);
 
-  static JavaThread* get_lock_owner(Handle h_obj, bool doLock);
+  static JavaThread* get_lock_owner(ThreadsList * t_list, Handle h_obj);
 
   // JNI detach support
   static void release_monitors_owned_by_thread(TRAPS);
diff --git a/src/hotspot/share/runtime/thread.cpp b/src/hotspot/share/runtime/thread.cpp
index 48c757fbd83..d4191b0d89f 100644
--- a/src/hotspot/share/runtime/thread.cpp
+++ b/src/hotspot/share/runtime/thread.cpp
@@ -71,12 +71,12 @@
 #include "runtime/java.hpp"
 #include "runtime/javaCalls.hpp"
 #include "runtime/jniPeriodicChecker.hpp"
-#include "runtime/timerTrace.hpp"
 #include "runtime/memprofiler.hpp"
 #include "runtime/mutexLocker.hpp"
 #include "runtime/objectMonitor.hpp"
 #include "runtime/orderAccess.inline.hpp"
 #include "runtime/osThread.hpp"
+#include "runtime/prefetch.inline.hpp"
 #include "runtime/safepoint.hpp"
 #include "runtime/safepointMechanism.inline.hpp"
 #include "runtime/sharedRuntime.hpp"
@@ -86,6 +86,9 @@
 #include "runtime/task.hpp"
 #include "runtime/thread.inline.hpp"
 #include "runtime/threadCritical.hpp"
+#include "runtime/threadSMR.inline.hpp"
+#include "runtime/timer.hpp"
+#include "runtime/timerTrace.hpp"
 #include "runtime/vframe.hpp"
 #include "runtime/vframeArray.hpp"
 #include "runtime/vframe_hp.hpp"
@@ -104,6 +107,7 @@
 #include "utilities/events.hpp"
 #include "utilities/macros.hpp"
 #include "utilities/preserveException.hpp"
+#include "utilities/resourceHash.hpp"
 #include "utilities/vmError.hpp"
 #if INCLUDE_ALL_GCS
 #include "gc/cms/concurrentMarkSweepThread.hpp"
@@ -195,13 +199,19 @@ void* Thread::allocate(size_t size, bool throw_excpt, MEMFLAGS flags) {
 
 void Thread::operator delete(void* p) {
   if (UseBiasedLocking) {
-    void* real_malloc_addr = ((Thread*) p)->_real_malloc_address;
-    FreeHeap(real_malloc_addr);
+    FreeHeap(((Thread*) p)->_real_malloc_address);
   } else {
     FreeHeap(p);
   }
 }
 
+void JavaThread::smr_delete() {
+  if (_on_thread_list) {
+    Threads::smr_delete(this);
+  } else {
+    delete this;
+  }
+}
 
 // Base class for all threads: VMThread, WatcherThread, ConcurrentMarkSweepThread,
 // JavaThread
@@ -227,6 +237,9 @@ Thread::Thread() {
 
   // This initial value ==> never claimed.
   _oops_do_parity = 0;
+  _threads_hazard_ptr = NULL;
+  _nested_threads_hazard_ptr = NULL;
+  _nested_threads_hazard_ptr_cnt = 0;
 
   // the handle mark links itself to last_handle_mark
   new HandleMark(this);
@@ -398,9 +411,15 @@ void Thread::run() {
 }
 
 #ifdef ASSERT
-// Private method to check for dangling thread pointer
-void check_for_dangling_thread_pointer(Thread *thread) {
-  assert(!thread->is_Java_thread() || Thread::current() == thread || Threads_lock->owned_by_self(),
+// A JavaThread is considered "dangling" if it is not the current
+// thread, has been added the Threads list, the system is not at a
+// safepoint and the Thread is not "protected".
+//
+void Thread::check_for_dangling_thread_pointer(Thread *thread) {
+  assert(!thread->is_Java_thread() || Thread::current() == thread ||
+         !((JavaThread *) thread)->on_thread_list() ||
+         SafepointSynchronize::is_at_safepoint() ||
+         Threads::is_a_protected_JavaThread_with_lock((JavaThread *) thread),
          "possibility of dangling Thread pointer");
 }
 #endif
@@ -732,6 +751,37 @@ bool JavaThread::wait_for_ext_suspend_completion(int retries, int delay,
   return false;
 }
 
+// Called from API entry points which perform stack walking. If the
+// associated JavaThread is the current thread, then wait_for_suspend
+// is not used. Otherwise, it determines if we should wait for the
+// "other" thread to complete external suspension. (NOTE: in future
+// releases the suspension mechanism should be reimplemented so this
+// is not necessary.)
+//
+bool
+JavaThread::is_thread_fully_suspended(bool wait_for_suspend, uint32_t *bits) {
+  if (this != JavaThread::current()) {
+    // "other" threads require special handling.
+    if (wait_for_suspend) {
+      // We are allowed to wait for the external suspend to complete
+      // so give the other thread a chance to get suspended.
+      if (!wait_for_ext_suspend_completion(SuspendRetryCount,
+                                           SuspendRetryDelay, bits)) {
+        // Didn't make it so let the caller know.
+        return false;
+      }
+    }
+    // We aren't allowed to wait for the external suspend to complete
+    // so if the other thread isn't externally suspended we need to
+    // let the caller know.
+    else if (!is_ext_suspend_completed_with_lock(bits)) {
+      return false;
+    }
+  }
+
+  return true;
+}
+
 #ifndef PRODUCT
 void JavaThread::record_jump(address target, address instr, const char* file,
                              int line) {
@@ -810,9 +860,33 @@ void Thread::print_on(outputStream* st) const {
     ext().print_on(st);
     osthread()->print_on(st);
   }
+  if (_threads_hazard_ptr != NULL) {
+    st->print("_threads_hazard_ptr=" INTPTR_FORMAT, p2i(_threads_hazard_ptr));
+  }
+  if (_nested_threads_hazard_ptr != NULL) {
+    print_nested_threads_hazard_ptrs_on(st);
+  }
+  st->print(" ");
   debug_only(if (WizardMode) print_owned_locks_on(st);)
 }
 
+void Thread::print_nested_threads_hazard_ptrs_on(outputStream* st) const {
+  assert(_nested_threads_hazard_ptr != NULL, "must be set to print");
+
+  if (EnableThreadSMRStatistics) {
+    st->print(", _nested_threads_hazard_ptr_cnt=%u", _nested_threads_hazard_ptr_cnt);
+  }
+  st->print(", _nested_threads_hazard_ptrs=");
+  for (NestedThreadsList* node = _nested_threads_hazard_ptr; node != NULL;
+       node = node->next()) {
+    if (node != _nested_threads_hazard_ptr) {
+      // First node does not need a comma-space separator.
+      st->print(", ");
+    }
+    st->print(INTPTR_FORMAT, p2i(node->t_list()));
+  }
+}
+
 // Thread::print_on_error() is called by fatal error handler. Don't use
 // any lock or allocate memory.
 void Thread::print_on_error(outputStream* st, char* buf, int buflen) const {
@@ -834,6 +908,13 @@ void Thread::print_on_error(outputStream* st, char* buf, int buflen) const {
   if (osthread()) {
     st->print(" [id=%d]", osthread()->thread_id());
   }
+
+  if (_threads_hazard_ptr != NULL) {
+    st->print(" _threads_hazard_ptr=" INTPTR_FORMAT, p2i(_threads_hazard_ptr));
+  }
+  if (_nested_threads_hazard_ptr != NULL) {
+    print_nested_threads_hazard_ptrs_on(st);
+  }
 }
 
 void Thread::print_value_on(outputStream* st) const {
@@ -871,8 +952,8 @@ bool Thread::owns_locks_but_compiled_lock() const {
 
 #ifndef PRODUCT
 
-// The flag: potential_vm_operation notifies if this particular safepoint state could potential
-// invoke the vm-thread (i.e., and oop allocation). In that case, we also have to make sure that
+// The flag: potential_vm_operation notifies if this particular safepoint state could potentially
+// invoke the vm-thread (e.g., an oop allocation). In that case, we also have to make sure that
 // no threads which allow_vm_block's are held
 void Thread::check_for_valid_safepoint_state(bool potential_vm_operation) {
   // Check if current thread is allowed to block at a safepoint
@@ -1399,10 +1480,11 @@ bool jvmci_counters_include(JavaThread* thread) {
 void JavaThread::collect_counters(typeArrayOop array) {
   if (JVMCICounterSize > 0) {
     MutexLocker tl(Threads_lock);
+    JavaThreadIteratorWithHandle jtiwh;
     for (int i = 0; i < array->length(); i++) {
       array->long_at_put(i, _jvmci_old_thread_counters[i]);
     }
-    for (JavaThread* tp = Threads::first(); tp != NULL; tp = tp->next()) {
+    for (; JavaThread *tp = jtiwh.next(); ) {
       if (jvmci_counters_include(tp)) {
         for (int i = 0; i < array->length(); i++) {
           array->long_at_put(i, array->long_at(i) + tp->_jvmci_counters[i]);
@@ -1435,6 +1517,7 @@ void JavaThread::initialize() {
   clear_must_deopt_id();
   set_monitor_chunks(NULL);
   set_next(NULL);
+  _on_thread_list = false;
   set_thread_state(_thread_new);
   _terminated = _not_terminated;
   _privileged_stack_top = NULL;
@@ -1715,12 +1798,12 @@ void JavaThread::thread_main_inner() {
   DTRACE_THREAD_PROBE(stop, this);
 
   this->exit(false);
-  delete this;
+  this->smr_delete();
 }
 
 
 static void ensure_join(JavaThread* thread) {
-  // We do not need to grap the Threads_lock, since we are operating on ourself.
+  // We do not need to grab the Threads_lock, since we are operating on ourself.
   Handle threadObj(thread, thread->threadObj());
   assert(threadObj.not_null(), "java thread object must exist");
   ObjectLocker lock(threadObj, thread);
@@ -1742,6 +1825,15 @@ static void ensure_join(JavaThread* thread) {
 void JavaThread::exit(bool destroy_vm, ExitType exit_type) {
   assert(this == JavaThread::current(), "thread consistency check");
 
+  elapsedTimer _timer_exit_phase1;
+  elapsedTimer _timer_exit_phase2;
+  elapsedTimer _timer_exit_phase3;
+  elapsedTimer _timer_exit_phase4;
+
+  if (log_is_enabled(Debug, os, thread, timer)) {
+    _timer_exit_phase1.start();
+  }
+
   HandleMark hm(this);
   Handle uncaught_exception(this, this->pending_exception());
   this->clear_pending_exception();
@@ -1841,12 +1933,20 @@ void JavaThread::exit(bool destroy_vm, ExitType exit_type) {
     // before_exit() has already posted JVMTI THREAD_END events
   }
 
+  if (log_is_enabled(Debug, os, thread, timer)) {
+    _timer_exit_phase1.stop();
+    _timer_exit_phase2.start();
+  }
   // Notify waiters on thread object. This has to be done after exit() is called
   // on the thread (if the thread is the last thread in a daemon ThreadGroup the
   // group should have the destroyed bit set before waiters are notified).
   ensure_join(this);
   assert(!this->has_pending_exception(), "ensure_join should have cleared");
 
+  if (log_is_enabled(Debug, os, thread, timer)) {
+    _timer_exit_phase2.stop();
+    _timer_exit_phase3.start();
+  }
   // 6282335 JNI DetachCurrentThread spec states that all Java monitors
   // held by this thread must be released. The spec does not distinguish
   // between JNI-acquired and regular Java monitors. We can only see
@@ -1914,12 +2014,26 @@ void JavaThread::exit(bool destroy_vm, ExitType exit_type) {
     exit_type == JavaThread::normal_exit ? "exiting" : "detaching",
     os::current_thread_id());
 
+  if (log_is_enabled(Debug, os, thread, timer)) {
+    _timer_exit_phase3.stop();
+    _timer_exit_phase4.start();
+  }
   // Remove from list of active threads list, and notify VM thread if we are the last non-daemon thread
   Threads::remove(this);
 
-  // If someone set a handshake on us just as we entered exit path, we simple cancel it.
-  if (ThreadLocalHandshakes) {
-    cancel_handshake();
+  if (log_is_enabled(Debug, os, thread, timer)) {
+    _timer_exit_phase4.stop();
+    ResourceMark rm(this);
+    log_debug(os, thread, timer)("name='%s'"
+                                 ", exit-phase1=" JLONG_FORMAT
+                                 ", exit-phase2=" JLONG_FORMAT
+                                 ", exit-phase3=" JLONG_FORMAT
+                                 ", exit-phase4=" JLONG_FORMAT,
+                                 get_thread_name(),
+                                 _timer_exit_phase1.milliseconds(),
+                                 _timer_exit_phase2.milliseconds(),
+                                 _timer_exit_phase3.milliseconds(),
+                                 _timer_exit_phase4.milliseconds());
   }
 }
 
@@ -1980,7 +2094,7 @@ void JavaThread::cleanup_failed_attach_current_thread() {
 #endif // INCLUDE_ALL_GCS
 
   Threads::remove(this);
-  delete this;
+  this->smr_delete();
 }
 
 
@@ -2235,10 +2349,9 @@ void JavaThread::send_thread_stop(oop java_throwable)  {
 //   + Target thread will not enter any new monitors
 //
 void JavaThread::java_suspend() {
-  { MutexLocker mu(Threads_lock);
-    if (!Threads::includes(this) || is_exiting() || this->threadObj() == NULL) {
-      return;
-    }
+  ThreadsListHandle tlh;
+  if (!tlh.includes(this) || threadObj() == NULL || is_exiting()) {
+    return;
   }
 
   { MutexLockerEx ml(SR_lock(), Mutex::_no_safepoint_check_flag);
@@ -2327,14 +2440,8 @@ int JavaThread::java_suspend_self() {
 // verify the JavaThread has not yet been published in the Threads::list, and
 // hence doesn't need protection from concurrent access at this stage
 void JavaThread::verify_not_published() {
-  if (!Threads_lock->owned_by_self()) {
-    MutexLockerEx ml(Threads_lock,  Mutex::_no_safepoint_check_flag);
-    assert(!Threads::includes(this),
-           "java thread shouldn't have been published yet!");
-  } else {
-    assert(!Threads::includes(this),
-           "java thread shouldn't have been published yet!");
-  }
+  ThreadsListHandle tlh;
+  assert(!tlh.includes(this), "JavaThread shouldn't have been published yet!");
 }
 #endif
 
@@ -2451,7 +2558,8 @@ void JavaThread::java_resume() {
 
   // Sanity check: thread is gone, has started exiting or the thread
   // was not externally suspended.
-  if (!Threads::includes(this) || is_exiting() || !is_external_suspend()) {
+  ThreadsListHandle tlh;
+  if (!tlh.includes(this) || is_exiting() || !is_external_suspend()) {
     return;
   }
 
@@ -2925,6 +3033,13 @@ void JavaThread::print_on_error(outputStream* st, char *buf, int buflen) const {
   st->print(", stack(" PTR_FORMAT "," PTR_FORMAT ")",
             p2i(stack_end()), p2i(stack_base()));
   st->print("]");
+
+  if (_threads_hazard_ptr != NULL) {
+    st->print(" _threads_hazard_ptr=" INTPTR_FORMAT, p2i(_threads_hazard_ptr));
+  }
+  if (_nested_threads_hazard_ptr != NULL) {
+    print_nested_threads_hazard_ptrs_on(st);
+  }
   return;
 }
 
@@ -3318,23 +3433,140 @@ void CodeCacheSweeperThread::nmethods_do(CodeBlobClosure* cf) {
 // ======= Threads ========
 
 // The Threads class links together all active threads, and provides
-// operations over all threads.  It is protected by its own Mutex
-// lock, which is also used in other contexts to protect thread
-// operations from having the thread being operated on from exiting
-// and going away unexpectedly (e.g., safepoint synchronization)
+// operations over all threads. It is protected by the Threads_lock,
+// which is also used in other global contexts like safepointing.
+// ThreadsListHandles are used to safely perform operations on one
+// or more threads without the risk of the thread exiting during the
+// operation.
+//
+// Note: The Threads_lock is currently more widely used than we
+// would like. We are actively migrating Threads_lock uses to other
+// mechanisms in order to reduce Threads_lock contention.
+
+JavaThread*           Threads::_thread_list = NULL;
+int                   Threads::_number_of_threads = 0;
+int                   Threads::_number_of_non_daemon_threads = 0;
+int                   Threads::_return_code = 0;
+int                   Threads::_thread_claim_parity = 0;
+size_t                JavaThread::_stack_size_at_create = 0;
+// Safe Memory Reclamation (SMR) support:
+Monitor*              Threads::_smr_delete_lock =
+                          new Monitor(Monitor::special, "smr_delete_lock",
+                                      false /* allow_vm_block */,
+                                      Monitor::_safepoint_check_never);
+// The '_cnt', '_max' and '_times" fields are enabled via
+// -XX:+EnableThreadSMRStatistics:
+
+// # of parallel threads in _smr_delete_lock->wait().
+// Impl note: Hard to imagine > 64K waiting threads so this could be 16-bit,
+// but there is no nice 16-bit _FORMAT support.
+uint                  Threads::_smr_delete_lock_wait_cnt = 0;
+
+// Max # of parallel threads in _smr_delete_lock->wait().
+// Impl note: See _smr_delete_lock_wait_cnt note.
+uint                  Threads::_smr_delete_lock_wait_max = 0;
+
+// Flag to indicate when an _smr_delete_lock->notify() is needed.
+// Impl note: See _smr_delete_lock_wait_cnt note.
+volatile uint         Threads::_smr_delete_notify = 0;
+
+// # of threads deleted over VM lifetime.
+// Impl note: Atomically incremented over VM lifetime so use unsigned for more
+// range. Unsigned 64-bit would be more future proof, but 64-bit atomic inc
+// isn't available everywhere (or is it?).
+volatile uint         Threads::_smr_deleted_thread_cnt = 0;
+
+// Max time in millis to delete a thread.
+// Impl note: 16-bit might be too small on an overloaded machine. Use
+// unsigned since this is a time value. Set via Atomic::cmpxchg() in a
+// loop for correctness.
+volatile uint         Threads::_smr_deleted_thread_time_max = 0;
+
+// Cumulative time in millis to delete threads.
+// Impl note: Atomically added to over VM lifetime so use unsigned for more
+// range. Unsigned 64-bit would be more future proof, but 64-bit atomic inc
+// isn't available everywhere (or is it?).
+volatile uint         Threads::_smr_deleted_thread_times = 0;
+
+ThreadsList* volatile Threads::_smr_java_thread_list = new ThreadsList(0);
+
+// # of ThreadsLists allocated over VM lifetime.
+// Impl note: We allocate a new ThreadsList for every thread create and
+// every thread delete so we need a bigger type than the
+// _smr_deleted_thread_cnt field.
+uint64_t              Threads::_smr_java_thread_list_alloc_cnt = 1;
+
+// # of ThreadsLists freed over VM lifetime.
+// Impl note: See _smr_java_thread_list_alloc_cnt note.
+uint64_t              Threads::_smr_java_thread_list_free_cnt = 0;
+
+// Max size ThreadsList allocated.
+// Impl note: Max # of threads alive at one time should fit in unsigned 32-bit.
+uint                  Threads::_smr_java_thread_list_max = 0;
+
+// Max # of nested ThreadsLists for a thread.
+// Impl note: Hard to imagine > 64K nested ThreadsLists so this could be
+// 16-bit, but there is no nice 16-bit _FORMAT support.
+uint                  Threads::_smr_nested_thread_list_max = 0;
+
+// # of ThreadsListHandles deleted over VM lifetime.
+// Impl note: Atomically incremented over VM lifetime so use unsigned for
+// more range. There will be fewer ThreadsListHandles than threads so
+// unsigned 32-bit should be fine.
+volatile uint         Threads::_smr_tlh_cnt = 0;
+
+// Max time in millis to delete a ThreadsListHandle.
+// Impl note: 16-bit might be too small on an overloaded machine. Use
+// unsigned since this is a time value. Set via Atomic::cmpxchg() in a
+// loop for correctness.
+volatile uint         Threads::_smr_tlh_time_max = 0;
+
+// Cumulative time in millis to delete ThreadsListHandles.
+// Impl note: Atomically added to over VM lifetime so use unsigned for more
+// range. Unsigned 64-bit would be more future proof, but 64-bit atomic inc
+// isn't available everywhere (or is it?).
+volatile uint         Threads::_smr_tlh_times = 0;
+
+ThreadsList*          Threads::_smr_to_delete_list = NULL;
+
+// # of parallel ThreadsLists on the to-delete list.
+// Impl note: Hard to imagine > 64K ThreadsLists needing to be deleted so
+// this could be 16-bit, but there is no nice 16-bit _FORMAT support.
+uint                  Threads::_smr_to_delete_list_cnt = 0;
+
+// Max # of parallel ThreadsLists on the to-delete list.
+// Impl note: See _smr_to_delete_list_cnt note.
+uint                  Threads::_smr_to_delete_list_max = 0;
 
-JavaThread* Threads::_thread_list = NULL;
-int         Threads::_number_of_threads = 0;
-int         Threads::_number_of_non_daemon_threads = 0;
-int         Threads::_return_code = 0;
-int         Threads::_thread_claim_parity = 0;
-size_t      JavaThread::_stack_size_at_create = 0;
 #ifdef ASSERT
-bool        Threads::_vm_complete = false;
+bool                  Threads::_vm_complete = false;
 #endif
 
+static inline void *prefetch_and_load_ptr(void **addr, intx prefetch_interval) {
+  Prefetch::read((void*)addr, prefetch_interval);
+  return *addr;
+}
+
+// Possibly the ugliest for loop the world has seen. C++ does not allow
+// multiple types in the declaration section of the for loop. In this case
+// we are only dealing with pointers and hence can cast them. It looks ugly
+// but macros are ugly and therefore it's fine to make things absurdly ugly.
+#define DO_JAVA_THREADS(LIST, X)                                                                                          \
+    for (JavaThread *MACRO_scan_interval = (JavaThread*)(uintptr_t)PrefetchScanIntervalInBytes,                           \
+             *MACRO_list = (JavaThread*)(LIST),                                                                           \
+             **MACRO_end = ((JavaThread**)((ThreadsList*)MACRO_list)->threads()) + ((ThreadsList*)MACRO_list)->length(),  \
+             **MACRO_current_p = (JavaThread**)((ThreadsList*)MACRO_list)->threads(),                                     \
+             *X = (JavaThread*)prefetch_and_load_ptr((void**)MACRO_current_p, (intx)MACRO_scan_interval);                 \
+         MACRO_current_p != MACRO_end;                                                                                    \
+         MACRO_current_p++,                                                                                               \
+             X = (JavaThread*)prefetch_and_load_ptr((void**)MACRO_current_p, (intx)MACRO_scan_interval))
+
+inline ThreadsList* Threads::get_smr_java_thread_list() {
+  return (ThreadsList*)OrderAccess::load_acquire(&_smr_java_thread_list);
+}
+
 // All JavaThreads
-#define ALL_JAVA_THREADS(X) for (JavaThread* X = _thread_list; X; X = X->next())
+#define ALL_JAVA_THREADS(X) DO_JAVA_THREADS(get_smr_java_thread_list(), X)
 
 // All JavaThreads + all non-JavaThreads (i.e., every thread in the system)
 void Threads::threads_do(ThreadClosure* tc) {
@@ -3435,6 +3667,240 @@ static void call_initPhase3(TRAPS) {
                                          vmSymbols::void_method_signature(), CHECK);
 }
 
+// Safe Memory Reclamation (SMR) support:
+//
+
+// Acquire a stable ThreadsList.
+//
+ThreadsList *Threads::acquire_stable_list(Thread *self, bool is_ThreadsListSetter) {
+  assert(self != NULL, "sanity check");
+  // acquire_stable_list_nested_path() will grab the Threads_lock
+  // so let's make sure the ThreadsListHandle is in a safe place.
+  // ThreadsListSetter cannot make this check on this code path.
+  debug_only(if (!is_ThreadsListSetter && StrictSafepointChecks) self->check_for_valid_safepoint_state(/* potential_vm_operation */ false);)
+
+  if (self->get_threads_hazard_ptr() == NULL) {
+    // The typical case is first.
+    return acquire_stable_list_fast_path(self);
+  }
+
+  // The nested case is rare.
+  return acquire_stable_list_nested_path(self);
+}
+
+// Fast path (and lock free) way to acquire a stable ThreadsList.
+//
+ThreadsList *Threads::acquire_stable_list_fast_path(Thread *self) {
+  assert(self != NULL, "sanity check");
+  assert(self->get_threads_hazard_ptr() == NULL, "sanity check");
+  assert(self->get_nested_threads_hazard_ptr() == NULL,
+         "cannot have a nested hazard ptr with a NULL regular hazard ptr");
+
+  ThreadsList* threads;
+
+  // Stable recording of a hazard ptr for SMR. This code does not use
+  // locks so its use of the _smr_java_thread_list & _threads_hazard_ptr
+  // fields is racy relative to code that uses those fields with locks.
+  // OrderAccess and Atomic functions are used to deal with those races.
+  //
+  while (true) {
+    threads = get_smr_java_thread_list();
+
+    // Publish a tagged hazard ptr to denote that the hazard ptr is not
+    // yet verified as being stable. Due to the fence after the hazard
+    // ptr write, it will be sequentially consistent w.r.t. the
+    // sequentially consistent writes of the ThreadsList, even on
+    // non-multiple copy atomic machines where stores can be observed
+    // in different order from different observer threads.
+    ThreadsList* unverified_threads = Thread::tag_hazard_ptr(threads);
+    self->set_threads_hazard_ptr(unverified_threads);
+
+    // If _smr_java_thread_list has changed, we have lost a race with
+    // Threads::add() or Threads::remove() and have to try again.
+    if (get_smr_java_thread_list() != threads) {
+      continue;
+    }
+
+    // We try to remove the tag which will verify the hazard ptr as
+    // being stable. This exchange can race with a scanning thread
+    // which might invalidate the tagged hazard ptr to keep it from
+    // being followed to access JavaThread ptrs. If we lose the race,
+    // we simply retry. If we win the race, then the stable hazard
+    // ptr is officially published.
+    if (self->cmpxchg_threads_hazard_ptr(threads, unverified_threads) == unverified_threads) {
+      break;
+    }
+  }
+
+  // A stable hazard ptr has been published letting other threads know
+  // that the ThreadsList and the JavaThreads reachable from this list
+  // are protected and hence they should not be deleted until everyone
+  // agrees it is safe to do so.
+
+  return threads;
+}
+
+// Acquire a nested stable ThreadsList; this is rare so it uses
+// Threads_lock.
+//
+ThreadsList *Threads::acquire_stable_list_nested_path(Thread *self) {
+  assert(self != NULL, "sanity check");
+  assert(self->get_threads_hazard_ptr() != NULL,
+         "cannot have a NULL regular hazard ptr when acquiring a nested hazard ptr");
+
+  // The thread already has a hazard ptr (ThreadsList ref) so we need
+  // to create a nested ThreadsListHandle with the current ThreadsList
+  // since it might be different than our current hazard ptr. The need
+  // for a nested ThreadsListHandle is rare so we do this while holding
+  // the Threads_lock so we don't race with the scanning code; the code
+  // is so much simpler this way.
+
+  NestedThreadsList* node;
+  {
+    // Only grab the Threads_lock if we don't already own it.
+    MutexLockerEx ml(Threads_lock->owned_by_self() ? NULL : Threads_lock);
+    node = new NestedThreadsList(get_smr_java_thread_list());
+    // We insert at the front of the list to match up with the delete
+    // in release_stable_list().
+    node->set_next(self->get_nested_threads_hazard_ptr());
+    self->set_nested_threads_hazard_ptr(node);
+    if (EnableThreadSMRStatistics) {
+      self->inc_nested_threads_hazard_ptr_cnt();
+      if (self->nested_threads_hazard_ptr_cnt() > _smr_nested_thread_list_max) {
+        _smr_nested_thread_list_max = self->nested_threads_hazard_ptr_cnt();
+      }
+    }
+  }
+  log_debug(thread, smr)("tid=" UINTX_FORMAT ": Threads::acquire_stable_list: add NestedThreadsList node containing ThreadsList=" INTPTR_FORMAT, os::current_thread_id(), p2i(node->t_list()));
+
+  return node->t_list();
+}
+
+inline void Threads::add_smr_deleted_thread_times(uint add_value) {
+  Atomic::add(add_value, &_smr_deleted_thread_times);
+}
+
+inline void Threads::inc_smr_deleted_thread_cnt() {
+  Atomic::inc(&_smr_deleted_thread_cnt);
+}
+
+// Release a stable ThreadsList.
+//
+void Threads::release_stable_list(Thread *self) {
+  assert(self != NULL, "sanity check");
+  // release_stable_list_nested_path() will grab the Threads_lock
+  // so let's make sure the ThreadsListHandle is in a safe place.
+  debug_only(if (StrictSafepointChecks) self->check_for_valid_safepoint_state(/* potential_vm_operation */ false);)
+
+  if (self->get_nested_threads_hazard_ptr() == NULL) {
+    // The typical case is first.
+    release_stable_list_fast_path(self);
+    return;
+  }
+
+  // The nested case is rare.
+  release_stable_list_nested_path(self);
+}
+
+// Fast path way to release a stable ThreadsList. The release portion
+// is lock-free, but the wake up portion is not.
+//
+void Threads::release_stable_list_fast_path(Thread *self) {
+  assert(self != NULL, "sanity check");
+  assert(self->get_threads_hazard_ptr() != NULL, "sanity check");
+  assert(self->get_nested_threads_hazard_ptr() == NULL,
+         "cannot have a nested hazard ptr when releasing a regular hazard ptr");
+
+  // After releasing the hazard ptr, other threads may go ahead and
+  // free up some memory temporarily used by a ThreadsList snapshot.
+  self->set_threads_hazard_ptr(NULL);
+
+  // We use double-check locking to reduce traffic on the system
+  // wide smr_delete_lock.
+  if (Threads::smr_delete_notify()) {
+    // An exiting thread might be waiting in smr_delete(); we need to
+    // check with smr_delete_lock to be sure.
+    release_stable_list_wake_up((char *) "regular hazard ptr");
+  }
+}
+
+// Release a nested stable ThreadsList; this is rare so it uses
+// Threads_lock.
+//
+void Threads::release_stable_list_nested_path(Thread *self) {
+  assert(self != NULL, "sanity check");
+  assert(self->get_nested_threads_hazard_ptr() != NULL, "sanity check");
+  assert(self->get_threads_hazard_ptr() != NULL,
+         "must have a regular hazard ptr to have nested hazard ptrs");
+
+  // We have a nested ThreadsListHandle so we have to release it first.
+  // The need for a nested ThreadsListHandle is rare so we do this while
+  // holding the Threads_lock so we don't race with the scanning code;
+  // the code is so much simpler this way.
+
+  NestedThreadsList *node;
+  {
+    // Only grab the Threads_lock if we don't already own it.
+    MutexLockerEx ml(Threads_lock->owned_by_self() ? NULL : Threads_lock);
+    // We remove from the front of the list to match up with the insert
+    // in acquire_stable_list().
+    node = self->get_nested_threads_hazard_ptr();
+    self->set_nested_threads_hazard_ptr(node->next());
+    if (EnableThreadSMRStatistics) {
+      self->dec_nested_threads_hazard_ptr_cnt();
+    }
+  }
+
+  // An exiting thread might be waiting in smr_delete(); we need to
+  // check with smr_delete_lock to be sure.
+  release_stable_list_wake_up((char *) "nested hazard ptr");
+
+  log_debug(thread, smr)("tid=" UINTX_FORMAT ": Threads::release_stable_list: delete NestedThreadsList node containing ThreadsList=" INTPTR_FORMAT, os::current_thread_id(), p2i(node->t_list()));
+
+  delete node;
+}
+
+// Wake up portion of the release stable ThreadsList protocol;
+// uses the smr_delete_lock().
+//
+void Threads::release_stable_list_wake_up(char *log_str) {
+  assert(log_str != NULL, "sanity check");
+
+  // Note: smr_delete_lock is held in smr_delete() for the entire
+  // hazard ptr search so that we do not lose this notify() if
+  // the exiting thread has to wait. That code path also holds
+  // Threads_lock (which was grabbed before smr_delete_lock) so that
+  // threads_do() can be called. This means the system can't start a
+  // safepoint which means this thread can't take too long to get to
+  // a safepoint because of being blocked on smr_delete_lock.
+  //
+  MonitorLockerEx ml(Threads::smr_delete_lock(), Monitor::_no_safepoint_check_flag);
+  if (Threads::smr_delete_notify()) {
+    // Notify any exiting JavaThreads that are waiting in smr_delete()
+    // that we've released a ThreadsList.
+    ml.notify_all();
+    log_debug(thread, smr)("tid=" UINTX_FORMAT ": Threads::release_stable_list notified %s", os::current_thread_id(), log_str);
+  }
+}
+
+inline void Threads::update_smr_deleted_thread_time_max(uint new_value) {
+  while (true) {
+    uint cur_value = _smr_deleted_thread_time_max;
+    if (new_value <= cur_value) {
+      // No need to update max value so we're done.
+      break;
+    }
+    if (Atomic::cmpxchg(new_value, &_smr_deleted_thread_time_max, cur_value) == cur_value) {
+      // Updated max value so we're done. Otherwise try it all again.
+      break;
+    }
+  }
+}
+
+inline ThreadsList* Threads::xchg_smr_java_thread_list(ThreadsList* new_list) {
+  return (ThreadsList*)Atomic::xchg(new_list, &_smr_java_thread_list);
+}
+
 void Threads::initialize_java_lang_classes(JavaThread* main_thread, TRAPS) {
   TraceTime timer("Initialize java.lang classes", TRACETIME_LOG(Info, startuptime));
 
@@ -3616,7 +4082,7 @@ jint Threads::create_vm(JavaVMInitArgs* args, bool* canTryAgain) {
   if (!main_thread->set_as_starting_thread()) {
     vm_shutdown_during_initialization(
                                       "Failed necessary internal allocation. Out of swap space");
-    delete main_thread;
+    main_thread->smr_delete();
     *canTryAgain = false; // don't let caller call JNI_CreateJavaVM again
     return JNI_ENOMEM;
   }
@@ -3631,7 +4097,7 @@ jint Threads::create_vm(JavaVMInitArgs* args, bool* canTryAgain) {
   // Initialize global modules
   jint status = init_globals();
   if (status != JNI_OK) {
-    delete main_thread;
+    main_thread->smr_delete();
     *canTryAgain = false; // don't let caller call JNI_CreateJavaVM again
     return status;
   }
@@ -4037,23 +4503,6 @@ void Threads::create_vm_init_libraries() {
   }
 }
 
-JavaThread* Threads::find_java_thread_from_java_tid(jlong java_tid) {
-  assert(Threads_lock->owned_by_self(), "Must hold Threads_lock");
-
-  JavaThread* java_thread = NULL;
-  // Sequential search for now.  Need to do better optimization later.
-  for (JavaThread* thread = Threads::first(); thread != NULL; thread = thread->next()) {
-    oop tobj = thread->threadObj();
-    if (!thread->is_exiting() &&
-        tobj != NULL &&
-        java_tid == java_lang_Thread::thread_id(tobj)) {
-      java_thread = thread;
-      break;
-    }
-  }
-  return java_thread;
-}
-
 
 // Last thread running calls java.lang.Shutdown.shutdown()
 void JavaThread::invoke_shutdown_hooks() {
@@ -4179,6 +4628,11 @@ bool Threads::destroy_vm() {
 
   notify_vm_shutdown();
 
+  // We are after VM_Exit::set_vm_exited() so we can't call
+  // thread->smr_delete() or we will block on the Threads_lock.
+  // Deleting the shutdown thread here is safe because another
+  // JavaThread cannot have an active ThreadsListHandle for
+  // this JavaThread.
   delete thread;
 
 #if INCLUDE_JVMCI
@@ -4212,6 +4666,501 @@ jboolean Threads::is_supported_jni_version(jint version) {
   return JNI_FALSE;
 }
 
+// Hash table of pointers found by a scan. Used for collecting hazard
+// pointers (ThreadsList references). Also used for collecting JavaThreads
+// that are indirectly referenced by hazard ptrs. An instance of this
+// class only contains one type of pointer.
+//
+class ThreadScanHashtable : public CHeapObj<mtThread> {
+ private:
+  static bool ptr_equals(void * const& s1, void * const& s2) {
+    return s1 == s2;
+  }
+
+  static unsigned int ptr_hash(void * const& s1) {
+    // 2654435761 = 2^32 * Phi (golden ratio)
+    return (unsigned int)(((uint32_t)(uintptr_t)s1) * 2654435761u);
+  }
+
+  int _table_size;
+  // ResourceHashtable SIZE is specified at compile time so our
+  // dynamic _table_size is unused for now; 1031 is the first prime
+  // after 1024.
+  typedef ResourceHashtable<void *, int, &ThreadScanHashtable::ptr_hash,
+                            &ThreadScanHashtable::ptr_equals, 1031,
+                            ResourceObj::C_HEAP, mtThread> PtrTable;
+  PtrTable * _ptrs;
+
+ public:
+  // ResourceHashtable is passed to various functions and populated in
+  // different places so we allocate it using C_HEAP to make it immune
+  // from any ResourceMarks that happen to be in the code paths.
+  ThreadScanHashtable(int table_size) : _table_size(table_size), _ptrs(new (ResourceObj::C_HEAP, mtThread) PtrTable()) {}
+
+  ~ThreadScanHashtable() { delete _ptrs; }
+
+  bool has_entry(void *pointer) {
+    int *val_ptr = _ptrs->get(pointer);
+    return val_ptr != NULL && *val_ptr == 1;
+  }
+
+  void add_entry(void *pointer) {
+    _ptrs->put(pointer, 1);
+  }
+};
+
+// Closure to gather JavaThreads indirectly referenced by hazard ptrs
+// (ThreadsList references) into a hash table. This closure handles part 2
+// of the dance - adding all the JavaThreads referenced by the hazard
+// pointer (ThreadsList reference) to the hash table.
+//
+class AddThreadHazardPointerThreadClosure : public ThreadClosure {
+ private:
+  ThreadScanHashtable *_table;
+
+ public:
+  AddThreadHazardPointerThreadClosure(ThreadScanHashtable *table) : _table(table) {}
+
+  virtual void do_thread(Thread *thread) {
+    if (!_table->has_entry((void*)thread)) {
+      // The same JavaThread might be on more than one ThreadsList or
+      // more than one thread might be using the same ThreadsList. In
+      // either case, we only need a single entry for a JavaThread.
+      _table->add_entry((void*)thread);
+    }
+  }
+};
+
+// Closure to gather JavaThreads indirectly referenced by hazard ptrs
+// (ThreadsList references) into a hash table. This closure handles part 1
+// of the dance - hazard ptr chain walking and dispatch to another
+// closure.
+//
+class ScanHazardPtrGatherProtectedThreadsClosure : public ThreadClosure {
+ private:
+  ThreadScanHashtable *_table;
+ public:
+  ScanHazardPtrGatherProtectedThreadsClosure(ThreadScanHashtable *table) : _table(table) {}
+
+  virtual void do_thread(Thread *thread) {
+    assert_locked_or_safepoint(Threads_lock);
+
+    if (thread == NULL) return;
+
+    // This code races with Threads::acquire_stable_list() which is
+    // lock-free so we have to handle some special situations.
+    //
+    ThreadsList *current_list = NULL;
+    while (true) {
+      current_list = thread->get_threads_hazard_ptr();
+      // No hazard ptr so nothing more to do.
+      if (current_list == NULL) {
+        assert(thread->get_nested_threads_hazard_ptr() == NULL,
+               "cannot have a nested hazard ptr with a NULL regular hazard ptr");
+        return;
+      }
+
+      // If the hazard ptr is verified as stable (since it is not tagged),
+      // then it is safe to use.
+      if (!Thread::is_hazard_ptr_tagged(current_list)) break;
+
+      // The hazard ptr is tagged as not yet verified as being stable
+      // so we are racing with acquire_stable_list(). This exchange
+      // attempts to invalidate the hazard ptr. If we win the race,
+      // then we can ignore this unstable hazard ptr and the other
+      // thread will retry the attempt to publish a stable hazard ptr.
+      // If we lose the race, then we retry our attempt to look at the
+      // hazard ptr.
+      if (thread->cmpxchg_threads_hazard_ptr(NULL, current_list) == current_list) return;
+    }
+
+    // The current JavaThread has a hazard ptr (ThreadsList reference)
+    // which might be _smr_java_thread_list or it might be an older
+    // ThreadsList that has been removed but not freed. In either case,
+    // the hazard ptr is protecting all the JavaThreads on that
+    // ThreadsList.
+    AddThreadHazardPointerThreadClosure add_cl(_table);
+    current_list->threads_do(&add_cl);
+
+    // Any NestedThreadsLists are also protecting JavaThreads so
+    // gather those also; the ThreadsLists may be different.
+    for (NestedThreadsList* node = thread->get_nested_threads_hazard_ptr();
+         node != NULL; node = node->next()) {
+      node->t_list()->threads_do(&add_cl);
+    }
+  }
+};
+
+// Closure to print JavaThreads that have a hazard ptr (ThreadsList
+// reference) that contains an indirect reference to a specific JavaThread.
+//
+class ScanHazardPtrPrintMatchingThreadsClosure : public ThreadClosure {
+ private:
+  JavaThread *_thread;
+ public:
+  ScanHazardPtrPrintMatchingThreadsClosure(JavaThread *thread) : _thread(thread) {}
+
+  virtual void do_thread(Thread *thread) {
+    assert_locked_or_safepoint(Threads_lock);
+
+    if (thread == NULL) return;
+    ThreadsList *current_list = thread->get_threads_hazard_ptr();
+    if (current_list == NULL) {
+      assert(thread->get_nested_threads_hazard_ptr() == NULL,
+             "cannot have a nested hazard ptr with a NULL regular hazard ptr");
+      return;
+    }
+    // If the hazard ptr is unverified, then ignore it.
+    if (Thread::is_hazard_ptr_tagged(current_list)) return;
+
+    // The current JavaThread has a hazard ptr (ThreadsList reference)
+    // which might be _smr_java_thread_list or it might be an older
+    // ThreadsList that has been removed but not freed. In either case,
+    // the hazard ptr is protecting all the JavaThreads on that
+    // ThreadsList, but we only care about matching a specific JavaThread.
+    DO_JAVA_THREADS(current_list, p) {
+      if (p == _thread) {
+        log_debug(thread, smr)("tid=" UINTX_FORMAT ": Threads::smr_delete: thread1=" INTPTR_FORMAT " has a hazard pointer for thread2=" INTPTR_FORMAT, os::current_thread_id(), p2i(thread), p2i(_thread));
+        break;
+      }
+    }
+
+    // Any NestedThreadsLists are also protecting JavaThreads so
+    // check those also; the ThreadsLists may be different.
+    for (NestedThreadsList* node = thread->get_nested_threads_hazard_ptr();
+         node != NULL; node = node->next()) {
+      DO_JAVA_THREADS(node->t_list(), p) {
+        if (p == _thread) {
+          log_debug(thread, smr)("tid=" UINTX_FORMAT ": Threads::smr_delete: thread1=" INTPTR_FORMAT " has a nested hazard pointer for thread2=" INTPTR_FORMAT, os::current_thread_id(), p2i(thread), p2i(_thread));
+          return;
+        }
+      }
+    }
+  }
+};
+
+// Return true if the specified JavaThread is protected by a hazard
+// pointer (ThreadsList reference). Otherwise, returns false.
+//
+bool Threads::is_a_protected_JavaThread(JavaThread *thread) {
+  assert_locked_or_safepoint(Threads_lock);
+
+  // Hash table size should be first power of two higher than twice
+  // the length of the Threads list.
+  int hash_table_size = MIN2(_number_of_threads, 32) << 1;
+  hash_table_size--;
+  hash_table_size |= hash_table_size >> 1;
+  hash_table_size |= hash_table_size >> 2;
+  hash_table_size |= hash_table_size >> 4;
+  hash_table_size |= hash_table_size >> 8;
+  hash_table_size |= hash_table_size >> 16;
+  hash_table_size++;
+
+  // Gather a hash table of the JavaThreads indirectly referenced by
+  // hazard ptrs.
+  ThreadScanHashtable *scan_table = new ThreadScanHashtable(hash_table_size);
+  ScanHazardPtrGatherProtectedThreadsClosure scan_cl(scan_table);
+  Threads::threads_do(&scan_cl);
+
+  bool thread_is_protected = false;
+  if (scan_table->has_entry((void*)thread)) {
+    thread_is_protected = true;
+  }
+  delete scan_table;
+  return thread_is_protected;
+}
+
+// Safely delete a JavaThread when it is no longer in use by a
+// ThreadsListHandle.
+//
+void Threads::smr_delete(JavaThread *thread) {
+  assert(!Threads_lock->owned_by_self(), "sanity");
+
+  bool has_logged_once = false;
+  elapsedTimer timer;
+  if (EnableThreadSMRStatistics) {
+    timer.start();
+  }
+
+  while (true) {
+    {
+      // No safepoint check because this JavaThread is not on the
+      // Threads list.
+      MutexLockerEx ml(Threads_lock, Mutex::_no_safepoint_check_flag);
+      // Cannot use a MonitorLockerEx helper here because we have
+      // to drop the Threads_lock first if we wait.
+      Threads::smr_delete_lock()->lock_without_safepoint_check();
+      // Set the smr_delete_notify flag after we grab smr_delete_lock
+      // and before we scan hazard ptrs because we're doing
+      // double-check locking in release_stable_list().
+      Threads::set_smr_delete_notify();
+
+      if (!is_a_protected_JavaThread(thread)) {
+        // This is the common case.
+        Threads::clear_smr_delete_notify();
+        Threads::smr_delete_lock()->unlock();
+        break;
+      }
+      if (!has_logged_once) {
+        has_logged_once = true;
+        log_debug(thread, smr)("tid=" UINTX_FORMAT ": Threads::smr_delete: thread=" INTPTR_FORMAT " is not deleted.", os::current_thread_id(), p2i(thread));
+        if (log_is_enabled(Debug, os, thread)) {
+          ScanHazardPtrPrintMatchingThreadsClosure scan_cl(thread);
+          Threads::threads_do(&scan_cl);
+        }
+      }
+    } // We have to drop the Threads_lock to wait or delete the thread
+
+    if (EnableThreadSMRStatistics) {
+      _smr_delete_lock_wait_cnt++;
+      if (_smr_delete_lock_wait_cnt > _smr_delete_lock_wait_max) {
+        _smr_delete_lock_wait_max = _smr_delete_lock_wait_cnt;
+      }
+    }
+    // Wait for a release_stable_list() call before we check again. No
+    // safepoint check, no timeout, and not as suspend equivalent flag
+    // because this JavaThread is not on the Threads list.
+    Threads::smr_delete_lock()->wait(Mutex::_no_safepoint_check_flag, 0,
+                                     !Mutex::_as_suspend_equivalent_flag);
+    if (EnableThreadSMRStatistics) {
+      _smr_delete_lock_wait_cnt--;
+    }
+
+    Threads::clear_smr_delete_notify();
+    Threads::smr_delete_lock()->unlock();
+    // Retry the whole scenario.
+  }
+
+  if (ThreadLocalHandshakes) {
+    // The thread is about to be deleted so cancel any handshake.
+    thread->cancel_handshake();
+  }
+
+  delete thread;
+  if (EnableThreadSMRStatistics) {
+    timer.stop();
+    uint millis = (uint)timer.milliseconds();
+    Threads::inc_smr_deleted_thread_cnt();
+    Threads::add_smr_deleted_thread_times(millis);
+    Threads::update_smr_deleted_thread_time_max(millis);
+  }
+
+  log_debug(thread, smr)("tid=" UINTX_FORMAT ": Threads::smr_delete: thread=" INTPTR_FORMAT " is deleted.", os::current_thread_id(), p2i(thread));
+}
+
+bool Threads::smr_delete_notify() {
+  // Use load_acquire() in order to see any updates to _smr_delete_notify
+  // earlier than when smr_delete_lock is grabbed.
+  return (OrderAccess::load_acquire(&_smr_delete_notify) != 0);
+}
+
+// set_smr_delete_notify() and clear_smr_delete_notify() are called
+// under the protection of the smr_delete_lock, but we also use an
+// Atomic operation to ensure the memory update is seen earlier than
+// when the smr_delete_lock is dropped.
+//
+void Threads::set_smr_delete_notify() {
+  Atomic::inc(&_smr_delete_notify);
+}
+
+void Threads::clear_smr_delete_notify() {
+  Atomic::dec(&_smr_delete_notify);
+}
+
+// Closure to gather hazard ptrs (ThreadsList references) into a hash table.
+//
+class ScanHazardPtrGatherThreadsListClosure : public ThreadClosure {
+ private:
+  ThreadScanHashtable *_table;
+ public:
+  ScanHazardPtrGatherThreadsListClosure(ThreadScanHashtable *table) : _table(table) {}
+
+  virtual void do_thread(Thread* thread) {
+    assert_locked_or_safepoint(Threads_lock);
+
+    if (thread == NULL) return;
+    ThreadsList *threads = thread->get_threads_hazard_ptr();
+    if (threads == NULL) {
+      assert(thread->get_nested_threads_hazard_ptr() == NULL,
+             "cannot have a nested hazard ptr with a NULL regular hazard ptr");
+      return;
+    }
+    // In this closure we always ignore the tag that might mark this
+    // hazard ptr as not yet verified. If we happen to catch an
+    // unverified hazard ptr that is subsequently discarded (not
+    // published), then the only side effect is that we might keep a
+    // to-be-deleted ThreadsList alive a little longer.
+    threads = Thread::untag_hazard_ptr(threads);
+    if (!_table->has_entry((void*)threads)) {
+      _table->add_entry((void*)threads);
+    }
+
+    // Any NestedThreadsLists are also protecting JavaThreads so
+    // gather those also; the ThreadsLists may be different.
+    for (NestedThreadsList* node = thread->get_nested_threads_hazard_ptr();
+         node != NULL; node = node->next()) {
+      threads = node->t_list();
+      if (!_table->has_entry((void*)threads)) {
+        _table->add_entry((void*)threads);
+      }
+    }
+  }
+};
+
+// Safely free a ThreadsList after a Threads::add() or Threads::remove().
+// The specified ThreadsList may not get deleted during this call if it
+// is still in-use (referenced by a hazard ptr). Other ThreadsLists
+// in the chain may get deleted by this call if they are no longer in-use.
+void Threads::smr_free_list(ThreadsList* threads) {
+  assert_locked_or_safepoint(Threads_lock);
+
+  threads->set_next_list(_smr_to_delete_list);
+  _smr_to_delete_list = threads;
+  if (EnableThreadSMRStatistics) {
+    _smr_to_delete_list_cnt++;
+    if (_smr_to_delete_list_cnt > _smr_to_delete_list_max) {
+      _smr_to_delete_list_max = _smr_to_delete_list_cnt;
+    }
+  }
+
+  // Hash table size should be first power of two higher than twice the length of the ThreadsList
+  int hash_table_size = MIN2(_number_of_threads, 32) << 1;
+  hash_table_size--;
+  hash_table_size |= hash_table_size >> 1;
+  hash_table_size |= hash_table_size >> 2;
+  hash_table_size |= hash_table_size >> 4;
+  hash_table_size |= hash_table_size >> 8;
+  hash_table_size |= hash_table_size >> 16;
+  hash_table_size++;
+
+  // Gather a hash table of the current hazard ptrs:
+  ThreadScanHashtable *scan_table = new ThreadScanHashtable(hash_table_size);
+  ScanHazardPtrGatherThreadsListClosure scan_cl(scan_table);
+  Threads::threads_do(&scan_cl);
+
+  // Walk through the linked list of pending freeable ThreadsLists
+  // and free the ones that are not referenced from hazard ptrs.
+  ThreadsList* current = _smr_to_delete_list;
+  ThreadsList* prev = NULL;
+  ThreadsList* next = NULL;
+  bool threads_is_freed = false;
+  while (current != NULL) {
+    next = current->next_list();
+    if (!scan_table->has_entry((void*)current)) {
+      // This ThreadsList is not referenced by a hazard ptr.
+      if (prev != NULL) {
+        prev->set_next_list(next);
+      }
+      if (_smr_to_delete_list == current) {
+        _smr_to_delete_list = next;
+      }
+
+      log_debug(thread, smr)("tid=" UINTX_FORMAT ": Threads::smr_free_list: threads=" INTPTR_FORMAT " is freed.", os::current_thread_id(), p2i(current));
+      if (current == threads) threads_is_freed = true;
+      delete current;
+      if (EnableThreadSMRStatistics) {
+        _smr_java_thread_list_free_cnt++;
+        _smr_to_delete_list_cnt--;
+      }
+    } else {
+      prev = current;
+    }
+    current = next;
+  }
+
+  if (!threads_is_freed) {
+    // Only report "is not freed" on the original call to
+    // smr_free_list() for this ThreadsList.
+    log_debug(thread, smr)("tid=" UINTX_FORMAT ": Threads::smr_free_list: threads=" INTPTR_FORMAT " is not freed.", os::current_thread_id(), p2i(threads));
+  }
+
+  delete scan_table;
+}
+
+// Remove a JavaThread from a ThreadsList. The returned ThreadsList is a
+// new copy of the specified ThreadsList with the specified JavaThread
+// removed.
+ThreadsList *ThreadsList::remove_thread(ThreadsList* list, JavaThread* java_thread) {
+  assert(list->_length > 0, "sanity");
+
+  uint i = 0;
+  DO_JAVA_THREADS(list, current) {
+    if (current == java_thread) {
+      break;
+    }
+    i++;
+  }
+  assert(i < list->_length, "did not find JavaThread on the list");
+  const uint index = i;
+  const uint new_length = list->_length - 1;
+  const uint head_length = index;
+  const uint tail_length = (new_length >= index) ? (new_length - index) : 0;
+  ThreadsList *const new_list = new ThreadsList(new_length);
+
+  if (head_length > 0) {
+    Copy::disjoint_words((HeapWord*)list->_threads, (HeapWord*)new_list->_threads, head_length);
+  }
+  if (tail_length > 0) {
+    Copy::disjoint_words((HeapWord*)list->_threads + index + 1, (HeapWord*)new_list->_threads + index, tail_length);
+  }
+
+  return new_list;
+}
+
+// Add a JavaThread to a ThreadsList. The returned ThreadsList is a
+// new copy of the specified ThreadsList with the specified JavaThread
+// appended to the end.
+ThreadsList *ThreadsList::add_thread(ThreadsList *list, JavaThread *java_thread) {
+  const uint index = list->_length;
+  const uint new_length = index + 1;
+  const uint head_length = index;
+  ThreadsList *const new_list = new ThreadsList(new_length);
+
+  if (head_length > 0) {
+    Copy::disjoint_words((HeapWord*)list->_threads, (HeapWord*)new_list->_threads, head_length);
+  }
+  *(JavaThread**)(new_list->_threads + index) = java_thread;
+
+  return new_list;
+}
+
+int ThreadsList::find_index_of_JavaThread(JavaThread *target) {
+  if (target == NULL) {
+    return -1;
+  }
+  for (uint i = 0; i < length(); i++) {
+    if (target == thread_at(i)) {
+      return (int)i;
+    }
+  }
+  return -1;
+}
+
+JavaThread* ThreadsList::find_JavaThread_from_java_tid(jlong java_tid) const {
+  DO_JAVA_THREADS(this, thread) {
+    oop tobj = thread->threadObj();
+    // Ignore the thread if it hasn't run yet, has exited
+    // or is starting to exit.
+    if (tobj != NULL && !thread->is_exiting() &&
+        java_tid == java_lang_Thread::thread_id(tobj)) {
+      // found a match
+      return thread;
+    }
+  }
+  return NULL;
+}
+
+bool ThreadsList::includes(const JavaThread * const p) const {
+  if (p == NULL) {
+    return false;
+  }
+  DO_JAVA_THREADS(this, q) {
+    if (q == p) {
+      return true;
+    }
+  }
+  return false;
+}
 
 void Threads::add(JavaThread* p, bool force_daemon) {
   // The threads lock must be owned at this point
@@ -4222,6 +5171,11 @@ void Threads::add(JavaThread* p, bool force_daemon) {
   p->initialize_queues();
   p->set_next(_thread_list);
   _thread_list = p;
+
+  // Once a JavaThread is added to the Threads list, smr_delete() has
+  // to be used to delete it. Otherwise we can just delete it directly.
+  p->set_on_thread_list();
+
   _number_of_threads++;
   oop threadObj = p->threadObj();
   bool daemon = true;
@@ -4234,6 +5188,20 @@ void Threads::add(JavaThread* p, bool force_daemon) {
 
   ThreadService::add_thread(p, daemon);
 
+  // Maintain fast thread list
+  ThreadsList *new_list = ThreadsList::add_thread(get_smr_java_thread_list(), p);
+  if (EnableThreadSMRStatistics) {
+    _smr_java_thread_list_alloc_cnt++;
+    if (new_list->length() > _smr_java_thread_list_max) {
+      _smr_java_thread_list_max = new_list->length();
+    }
+  }
+  // Initial _smr_java_thread_list will not generate a "Threads::add" mesg.
+  log_debug(thread, smr)("tid=" UINTX_FORMAT ": Threads::add: new ThreadsList=" INTPTR_FORMAT, os::current_thread_id(), p2i(new_list));
+
+  ThreadsList *old_list = xchg_smr_java_thread_list(new_list);
+  smr_free_list(old_list);
+
   // Possible GC point.
   Events::log(p, "Thread added: " INTPTR_FORMAT, p2i(p));
 }
@@ -4247,7 +5215,20 @@ void Threads::remove(JavaThread* p) {
   // that we do not remove thread without safepoint code notice
   { MutexLocker ml(Threads_lock);
 
-    assert(includes(p), "p must be present");
+    assert(get_smr_java_thread_list()->includes(p), "p must be present");
+
+    // Maintain fast thread list
+    ThreadsList *new_list = ThreadsList::remove_thread(get_smr_java_thread_list(), p);
+    if (EnableThreadSMRStatistics) {
+      _smr_java_thread_list_alloc_cnt++;
+      // This list is smaller so no need to check for a "longest" update.
+    }
+
+    // Final _smr_java_thread_list will not generate a "Threads::remove" mesg.
+    log_debug(thread, smr)("tid=" UINTX_FORMAT ": Threads::remove: new ThreadsList=" INTPTR_FORMAT, os::current_thread_id(), p2i(new_list));
+
+    ThreadsList *old_list = xchg_smr_java_thread_list(new_list);
+    smr_free_list(old_list);
 
     JavaThread* current = _thread_list;
     JavaThread* prev    = NULL;
@@ -4262,6 +5243,7 @@ void Threads::remove(JavaThread* p) {
     } else {
       _thread_list = p->next();
     }
+
     _number_of_threads--;
     oop threadObj = p->threadObj();
     bool daemon = true;
@@ -4288,17 +5270,6 @@ void Threads::remove(JavaThread* p) {
   Events::log(p, "Thread exited: " INTPTR_FORMAT, p2i(p));
 }
 
-// Threads_lock must be held when this is called (or must be called during a safepoint)
-bool Threads::includes(JavaThread* p) {
-  assert(Threads_lock->is_locked(), "sanity check");
-  ALL_JAVA_THREADS(q) {
-    if (q == p) {
-      return true;
-    }
-  }
-  return false;
-}
-
 // Operations on the Threads list for GC.  These are not explicitly locked,
 // but the garbage collector must provide a safe context for them to run.
 // In particular, these things should never be called when the Threads_lock
@@ -4411,47 +5382,36 @@ void Threads::deoptimized_wrt_marked_nmethods() {
 
 
 // Get count Java threads that are waiting to enter the specified monitor.
-GrowableArray<JavaThread*>* Threads::get_pending_threads(int count,
-                                                         address monitor,
-                                                         bool doLock) {
-  assert(doLock || SafepointSynchronize::is_at_safepoint(),
-         "must grab Threads_lock or be at safepoint");
+GrowableArray<JavaThread*>* Threads::get_pending_threads(ThreadsList * t_list,
+                                                         int count,
+                                                         address monitor) {
   GrowableArray<JavaThread*>* result = new GrowableArray<JavaThread*>(count);
 
   int i = 0;
-  {
-    MutexLockerEx ml(doLock ? Threads_lock : NULL);
-    ALL_JAVA_THREADS(p) {
-      if (!p->can_call_java()) continue;
+  DO_JAVA_THREADS(t_list, p) {
+    if (!p->can_call_java()) continue;
 
-      address pending = (address)p->current_pending_monitor();
-      if (pending == monitor) {             // found a match
-        if (i < count) result->append(p);   // save the first count matches
-        i++;
-      }
+    address pending = (address)p->current_pending_monitor();
+    if (pending == monitor) {             // found a match
+      if (i < count) result->append(p);   // save the first count matches
+      i++;
     }
   }
+
   return result;
 }
 
 
-JavaThread *Threads::owning_thread_from_monitor_owner(address owner,
-                                                      bool doLock) {
-  assert(doLock ||
-         Threads_lock->owned_by_self() ||
-         SafepointSynchronize::is_at_safepoint(),
-         "must grab Threads_lock or be at safepoint");
-
+JavaThread *Threads::owning_thread_from_monitor_owner(ThreadsList * t_list,
+                                                      address owner) {
   // NULL owner means not locked so we can skip the search
   if (owner == NULL) return NULL;
 
-  {
-    MutexLockerEx ml(doLock ? Threads_lock : NULL);
-    ALL_JAVA_THREADS(p) {
-      // first, see if owner is the address of a Java thread
-      if (owner == (address)p) return p;
-    }
+  DO_JAVA_THREADS(t_list, p) {
+    // first, see if owner is the address of a Java thread
+    if (owner == (address)p) return p;
   }
+
   // Cannot assert on lack of success here since this function may be
   // used by code that is trying to report useful problem information
   // like deadlock detection.
@@ -4462,15 +5422,13 @@ JavaThread *Threads::owning_thread_from_monitor_owner(address owner,
   // Lock Word in the owning Java thread's stack.
   //
   JavaThread* the_owner = NULL;
-  {
-    MutexLockerEx ml(doLock ? Threads_lock : NULL);
-    ALL_JAVA_THREADS(q) {
-      if (q->is_lock_owned(owner)) {
-        the_owner = q;
-        break;
-      }
+  DO_JAVA_THREADS(t_list, q) {
+    if (q->is_lock_owned(owner)) {
+      the_owner = q;
+      break;
     }
   }
+
   // cannot assert on lack of success here; see above comment
   return the_owner;
 }
@@ -4495,6 +5453,9 @@ void Threads::print_on(outputStream* st, bool print_stacks,
   }
 #endif // INCLUDE_SERVICES
 
+  print_smr_info_on(st);
+  st->cr();
+
   ALL_JAVA_THREADS(p) {
     ResourceMark rm;
     p->print_on(st);
@@ -4521,9 +5482,105 @@ void Threads::print_on(outputStream* st, bool print_stacks,
     wt->print_on(st);
     st->cr();
   }
+
   st->flush();
 }
 
+// Log Threads class SMR info.
+void Threads::log_smr_statistics() {
+  LogTarget(Info, thread, smr) log;
+  if (log.is_enabled()) {
+    LogStream out(log);
+    print_smr_info_on(&out);
+  }
+}
+
+// Print Threads class SMR info.
+void Threads::print_smr_info_on(outputStream* st) {
+  // Only grab the Threads_lock if we don't already own it
+  // and if we are not reporting an error.
+  MutexLockerEx ml((Threads_lock->owned_by_self() || VMError::is_error_reported()) ? NULL : Threads_lock);
+
+  st->print_cr("Threads class SMR info:");
+  st->print_cr("_smr_java_thread_list=" INTPTR_FORMAT ", length=%u, "
+               "elements={", p2i(_smr_java_thread_list),
+               _smr_java_thread_list->length());
+  print_smr_info_elements_on(st, _smr_java_thread_list);
+  st->print_cr("}");
+  if (_smr_to_delete_list != NULL) {
+    st->print_cr("_smr_to_delete_list=" INTPTR_FORMAT ", length=%u, "
+                 "elements={", p2i(_smr_to_delete_list),
+                 _smr_to_delete_list->length());
+    print_smr_info_elements_on(st, _smr_to_delete_list);
+    st->print_cr("}");
+    for (ThreadsList *t_list = _smr_to_delete_list->next_list();
+         t_list != NULL; t_list = t_list->next_list()) {
+      st->print("next-> " INTPTR_FORMAT ", length=%u, "
+                "elements={", p2i(t_list), t_list->length());
+      print_smr_info_elements_on(st, t_list);
+      st->print_cr("}");
+    }
+  }
+  if (!EnableThreadSMRStatistics) {
+    return;
+  }
+  st->print_cr("_smr_java_thread_list_alloc_cnt=" UINT64_FORMAT ","
+               "_smr_java_thread_list_free_cnt=" UINT64_FORMAT ","
+               "_smr_java_thread_list_max=%u, "
+               "_smr_nested_thread_list_max=%u",
+               _smr_java_thread_list_alloc_cnt,
+               _smr_java_thread_list_free_cnt,
+               _smr_java_thread_list_max,
+               _smr_nested_thread_list_max);
+  if (_smr_tlh_cnt > 0) {
+    st->print_cr("_smr_tlh_cnt=%u"
+                 ", _smr_tlh_times=%u"
+                 ", avg_smr_tlh_time=%0.2f"
+                 ", _smr_tlh_time_max=%u",
+                 _smr_tlh_cnt, _smr_tlh_times,
+                 ((double) _smr_tlh_times / _smr_tlh_cnt),
+                 _smr_tlh_time_max);
+  }
+  if (_smr_deleted_thread_cnt > 0) {
+    st->print_cr("_smr_deleted_thread_cnt=%u"
+                 ", _smr_deleted_thread_times=%u"
+                 ", avg_smr_deleted_thread_time=%0.2f"
+                 ", _smr_deleted_thread_time_max=%u",
+                 _smr_deleted_thread_cnt, _smr_deleted_thread_times,
+                 ((double) _smr_deleted_thread_times / _smr_deleted_thread_cnt),
+                 _smr_deleted_thread_time_max);
+  }
+  st->print_cr("_smr_delete_lock_wait_cnt=%u, _smr_delete_lock_wait_max=%u",
+               _smr_delete_lock_wait_cnt, _smr_delete_lock_wait_max);
+  st->print_cr("_smr_to_delete_list_cnt=%u, _smr_to_delete_list_max=%u",
+               _smr_to_delete_list_cnt, _smr_to_delete_list_max);
+}
+
+// Print ThreadsList elements (4 per line).
+void Threads::print_smr_info_elements_on(outputStream* st,
+                                         ThreadsList* t_list) {
+  uint cnt = 0;
+  JavaThreadIterator jti(t_list);
+  for (JavaThread *jt = jti.first(); jt != NULL; jt = jti.next()) {
+    st->print(INTPTR_FORMAT, p2i(jt));
+    if (cnt < t_list->length() - 1) {
+      // Separate with comma or comma-space except for the last one.
+      if (((cnt + 1) % 4) == 0) {
+        // Four INTPTR_FORMAT fit on an 80 column line so end the
+        // current line with just a comma.
+        st->print_cr(",");
+      } else {
+        // Not the last one on the current line so use comma-space:
+        st->print(", ");
+      }
+    } else {
+      // Last one so just end the current line.
+      st->cr();
+    }
+    cnt++;
+  }
+}
+
 void Threads::print_on_error(Thread* this_thread, outputStream* st, Thread* current, char* buf,
                              int buflen, bool* found_current) {
   if (this_thread != NULL) {
@@ -4560,6 +5617,9 @@ class PrintOnErrorClosure : public ThreadClosure {
 // memory (even in resource area), it might deadlock the error handler.
 void Threads::print_on_error(outputStream* st, Thread* current, char* buf,
                              int buflen) {
+  print_smr_info_on(st);
+  st->cr();
+
   bool found_current = false;
   st->print_cr("Java Threads: ( => current thread )");
   ALL_JAVA_THREADS(thread) {
@@ -4581,6 +5641,7 @@ void Threads::print_on_error(outputStream* st, Thread* current, char* buf,
     st->cr();
   }
   st->cr();
+
   st->print_cr("Threads with active compile tasks:");
   print_threads_compiling(st, buf, buflen);
 }
diff --git a/src/hotspot/share/runtime/thread.hpp b/src/hotspot/share/runtime/thread.hpp
index 65fa5aae1c5..db1d465c751 100644
--- a/src/hotspot/share/runtime/thread.hpp
+++ b/src/hotspot/share/runtime/thread.hpp
@@ -57,6 +57,8 @@
 #endif
 
 class ThreadSafepointState;
+class ThreadsList;
+class NestedThreadsList;
 
 class JvmtiThreadState;
 class JvmtiGetLoadedClassesClosure;
@@ -101,6 +103,7 @@ class WorkerThread;
 //   - WatcherThread
 
 class Thread: public ThreadShadow {
+  friend class Threads;
   friend class VMStructs;
   friend class JVMCIVMStructs;
  private:
@@ -118,6 +121,47 @@ class Thread: public ThreadShadow {
  protected:
   // Support for forcing alignment of thread objects for biased locking
   void*       _real_malloc_address;
+  // JavaThread lifecycle support:
+  friend class ScanHazardPtrGatherProtectedThreadsClosure;
+  friend class ScanHazardPtrGatherThreadsListClosure;
+  friend class ScanHazardPtrPrintMatchingThreadsClosure;
+  friend class ThreadsListHandle;
+  friend class ThreadsListSetter;
+  ThreadsList* volatile _threads_hazard_ptr;
+  ThreadsList*          cmpxchg_threads_hazard_ptr(ThreadsList* exchange_value, ThreadsList* compare_value);
+  ThreadsList*          get_threads_hazard_ptr();
+  void                  set_threads_hazard_ptr(ThreadsList* new_list);
+  static bool           is_hazard_ptr_tagged(ThreadsList* list) {
+    return (intptr_t(list) & intptr_t(1)) == intptr_t(1);
+  }
+  static ThreadsList*   tag_hazard_ptr(ThreadsList* list) {
+    return (ThreadsList*)(intptr_t(list) | intptr_t(1));
+  }
+  static ThreadsList*   untag_hazard_ptr(ThreadsList* list) {
+    return (ThreadsList*)(intptr_t(list) & ~intptr_t(1));
+  }
+  NestedThreadsList* _nested_threads_hazard_ptr;
+  NestedThreadsList* get_nested_threads_hazard_ptr() {
+    return _nested_threads_hazard_ptr;
+  }
+  void set_nested_threads_hazard_ptr(NestedThreadsList* value) {
+    assert(Threads_lock->owned_by_self(),
+           "must own Threads_lock for _nested_threads_hazard_ptr to be valid.");
+    _nested_threads_hazard_ptr = value;
+  }
+  // This field is enabled via -XX:+EnableThreadSMRStatistics:
+  uint _nested_threads_hazard_ptr_cnt;
+  void dec_nested_threads_hazard_ptr_cnt() {
+    assert(_nested_threads_hazard_ptr_cnt != 0, "mismatched {dec,inc}_nested_threads_hazard_ptr_cnt()");
+    _nested_threads_hazard_ptr_cnt--;
+  }
+  void inc_nested_threads_hazard_ptr_cnt() {
+    _nested_threads_hazard_ptr_cnt++;
+  }
+  uint nested_threads_hazard_ptr_cnt() {
+    return _nested_threads_hazard_ptr_cnt;
+  }
+
  public:
   void* operator new(size_t size) throw() { return allocate(size, true); }
   void* operator new(size_t size, const std::nothrow_t& nothrow_constant) throw() {
@@ -359,6 +403,9 @@ class Thread: public ThreadShadow {
   static inline Thread* current_or_null_safe();
 
   // Common thread operations
+#ifdef ASSERT
+  static void check_for_dangling_thread_pointer(Thread *thread);
+#endif
   static void set_priority(Thread* thread, ThreadPriority priority);
   static ThreadPriority get_priority(const Thread* const thread);
   static void start(Thread* thread);
@@ -576,6 +623,7 @@ protected:
 
   // Printing
   virtual void print_on(outputStream* st) const;
+  virtual void print_nested_threads_hazard_ptrs_on(outputStream* st) const;
   void print() const { print_on(tty); }
   virtual void print_on_error(outputStream* st, char* buf, int buflen) const;
   void print_value_on(outputStream* st) const;
@@ -798,6 +846,7 @@ class JavaThread: public Thread {
   friend class WhiteBox;
  private:
   JavaThread*    _next;                          // The next thread in the Threads list
+  bool           _on_thread_list;                // Is set when this JavaThread is added to the Threads list
   oop            _threadObj;                     // The Java level thread object
 
 #ifdef ASSERT
@@ -1125,15 +1174,23 @@ class JavaThread: public Thread {
   void set_safepoint_state(ThreadSafepointState *state) { _safepoint_state = state; }
   bool is_at_poll_safepoint()                    { return _safepoint_state->is_at_poll_safepoint(); }
 
+  // JavaThread termination and lifecycle support:
+  void smr_delete();
+  bool on_thread_list() const { return _on_thread_list; }
+  void set_on_thread_list() { _on_thread_list = true; }
+
   // thread has called JavaThread::exit() or is terminated
-  bool is_exiting()                              { return _terminated == _thread_exiting || is_terminated(); }
+  bool is_exiting() const;
   // thread is terminated (no longer on the threads list); we compare
   // against the two non-terminated values so that a freed JavaThread
   // will also be considered terminated.
-  bool is_terminated()                           { return _terminated != _not_terminated && _terminated != _thread_exiting; }
-  void set_terminated(TerminatedTypes t)         { _terminated = t; }
+  bool check_is_terminated(TerminatedTypes l_terminated) const {
+    return l_terminated != _not_terminated && l_terminated != _thread_exiting;
+  }
+  bool is_terminated() const;
+  void set_terminated(TerminatedTypes t);
   // special for Threads::remove() which is static:
-  void set_terminated_value()                    { _terminated = _thread_terminated; }
+  void set_terminated_value();
   void block_if_vm_exited();
 
   bool doing_unsafe_access()                     { return _doing_unsafe_access; }
@@ -1220,6 +1277,9 @@ class JavaThread: public Thread {
   // via the appropriate -XX options.
   bool wait_for_ext_suspend_completion(int count, int delay, uint32_t *bits);
 
+  // test for suspend - most (all?) of these should go away
+  bool is_thread_fully_suspended(bool wait_for_suspend, uint32_t *bits);
+
   inline void set_external_suspend();
   inline void clear_external_suspend();
 
@@ -2066,28 +2126,84 @@ inline CompilerThread* CompilerThread::current() {
 class Threads: AllStatic {
   friend class VMStructs;
  private:
-  static JavaThread* _thread_list;
-  static int         _number_of_threads;
-  static int         _number_of_non_daemon_threads;
-  static int         _return_code;
-  static int         _thread_claim_parity;
+  // Safe Memory Reclamation (SMR) support:
+  // The coordination between Threads::release_stable_list() and
+  // Threads::smr_delete() uses the smr_delete_lock in order to
+  // reduce the traffic on the Threads_lock.
+  static Monitor*              _smr_delete_lock;
+  // The '_cnt', '_max' and '_times" fields are enabled via
+  // -XX:+EnableThreadSMRStatistics (see thread.cpp for a
+  // description about each field):
+  static uint                  _smr_delete_lock_wait_cnt;
+  static uint                  _smr_delete_lock_wait_max;
+  // The smr_delete_notify flag is used for proper double-check
+  // locking in order to reduce the traffic on the smr_delete_lock.
+  static volatile uint         _smr_delete_notify;
+  static volatile uint         _smr_deleted_thread_cnt;
+  static volatile uint         _smr_deleted_thread_time_max;
+  static volatile uint         _smr_deleted_thread_times;
+  static ThreadsList* volatile _smr_java_thread_list;
+  static uint64_t              _smr_java_thread_list_alloc_cnt;
+  static uint64_t              _smr_java_thread_list_free_cnt;
+  static uint                  _smr_java_thread_list_max;
+  static uint                  _smr_nested_thread_list_max;
+  static volatile uint         _smr_tlh_cnt;
+  static volatile uint         _smr_tlh_time_max;
+  static volatile uint         _smr_tlh_times;
+  static ThreadsList*          _smr_to_delete_list;
+  static uint                  _smr_to_delete_list_cnt;
+  static uint                  _smr_to_delete_list_max;
+
+  static JavaThread*           _thread_list;
+  static int                   _number_of_threads;
+  static int                   _number_of_non_daemon_threads;
+  static int                   _return_code;
+  static int                   _thread_claim_parity;
 #ifdef ASSERT
-  static bool        _vm_complete;
+  static bool                  _vm_complete;
 #endif
 
   static void initialize_java_lang_classes(JavaThread* main_thread, TRAPS);
   static void initialize_jsr292_core_classes(TRAPS);
+
+  static ThreadsList *acquire_stable_list_fast_path(Thread *self);
+  static ThreadsList *acquire_stable_list_nested_path(Thread *self);
+  static void add_smr_deleted_thread_times(uint add_value);
+  static void clear_smr_delete_notify();
+  static ThreadsList* get_smr_java_thread_list();
+  static void inc_smr_deleted_thread_cnt();
+  static void release_stable_list_fast_path(Thread *self);
+  static void release_stable_list_nested_path(Thread *self);
+  static void release_stable_list_wake_up(char *log_str);
+  static void set_smr_delete_notify();
+  static Monitor* smr_delete_lock() { return _smr_delete_lock; }
+  static bool smr_delete_notify();
+  static void smr_free_list(ThreadsList* threads);
+  static void update_smr_deleted_thread_time_max(uint new_value);
+  static ThreadsList* xchg_smr_java_thread_list(ThreadsList* new_list);
+
  public:
   // Thread management
   // force_daemon is a concession to JNI, where we may need to add a
   // thread to the thread list before allocating its thread object
   static void add(JavaThread* p, bool force_daemon = false);
   static void remove(JavaThread* p);
-  static bool includes(JavaThread* p);
-  static JavaThread* first()                     { return _thread_list; }
   static void threads_do(ThreadClosure* tc);
   static void possibly_parallel_threads_do(bool is_par, ThreadClosure* tc);
 
+  // SMR support:
+  static ThreadsList *acquire_stable_list(Thread *self, bool is_ThreadsListSetter);
+  static void release_stable_list(Thread *self);
+  static bool is_a_protected_JavaThread(JavaThread *thread);
+  static bool is_a_protected_JavaThread_with_lock(JavaThread *thread) {
+    MutexLockerEx ml(Threads_lock->owned_by_self() ? NULL : Threads_lock);
+    return is_a_protected_JavaThread(thread);
+  }
+  static void smr_delete(JavaThread *thread);
+  static void inc_smr_tlh_cnt();
+  static void update_smr_tlh_time_max(uint new_value);
+  static void add_smr_tlh_times(uint add_value);
+
   // Initializes the vm and creates the vm thread
   static jint create_vm(JavaVMInitArgs* args, bool* canTryAgain);
   static void convert_vm_init_libraries_to_agents();
@@ -2148,7 +2264,10 @@ class Threads: AllStatic {
 
   // Verification
   static void verify();
+  static void log_smr_statistics();
   static void print_on(outputStream* st, bool print_stacks, bool internal_format, bool print_concurrent_locks);
+  static void print_smr_info_on(outputStream* st);
+  static void print_smr_info_elements_on(outputStream* st, ThreadsList* t_list);
   static void print(bool print_stacks, bool internal_format) {
     // this function is only used by debug.cpp
     print_on(tty, print_stacks, internal_format, false /* no concurrent lock printed */);
@@ -2158,17 +2277,13 @@ class Threads: AllStatic {
                              int buflen, bool* found_current);
   static void print_threads_compiling(outputStream* st, char* buf, int buflen);
 
-  // Get Java threads that are waiting to enter a monitor. If doLock
-  // is true, then Threads_lock is grabbed as needed. Otherwise, the
-  // VM needs to be at a safepoint.
-  static GrowableArray<JavaThread*>* get_pending_threads(int count,
-                                                         address monitor, bool doLock);
+  // Get Java threads that are waiting to enter a monitor.
+  static GrowableArray<JavaThread*>* get_pending_threads(ThreadsList * t_list,
+                                                         int count, address monitor);
 
-  // Get owning Java thread from the monitor's owner field. If doLock
-  // is true, then Threads_lock is grabbed as needed. Otherwise, the
-  // VM needs to be at a safepoint.
-  static JavaThread *owning_thread_from_monitor_owner(address owner,
-                                                      bool doLock);
+  // Get owning Java thread from the monitor's owner field.
+  static JavaThread *owning_thread_from_monitor_owner(ThreadsList * t_list,
+                                                      address owner);
 
   // Number of threads on the active threads list
   static int number_of_threads()                 { return _number_of_threads; }
@@ -2177,9 +2292,6 @@ class Threads: AllStatic {
 
   // Deoptimizes all frames tied to marked nmethods
   static void deoptimized_wrt_marked_nmethods();
-
-  static JavaThread* find_java_thread_from_java_tid(jlong java_tid);
-
 };
 
 
diff --git a/src/hotspot/share/runtime/thread.inline.hpp b/src/hotspot/share/runtime/thread.inline.hpp
index 5a664953b9a..3821d9317bf 100644
--- a/src/hotspot/share/runtime/thread.inline.hpp
+++ b/src/hotspot/share/runtime/thread.inline.hpp
@@ -25,13 +25,10 @@
 #ifndef SHARE_VM_RUNTIME_THREAD_INLINE_HPP
 #define SHARE_VM_RUNTIME_THREAD_INLINE_HPP
 
-#define SHARE_VM_RUNTIME_THREAD_INLINE_HPP_SCOPE
-
 #include "runtime/atomic.hpp"
 #include "runtime/os.inline.hpp"
 #include "runtime/thread.hpp"
-
-#undef SHARE_VM_RUNTIME_THREAD_INLINE_HPP_SCOPE
+#include "runtime/threadSMR.hpp"
 
 inline void Thread::set_suspend_flag(SuspendFlags f) {
   assert(sizeof(jint) == sizeof(_suspend_flags), "size mismatch");
@@ -89,6 +86,18 @@ inline jlong Thread::cooked_allocated_bytes() {
   return allocated_bytes;
 }
 
+inline ThreadsList* Thread::cmpxchg_threads_hazard_ptr(ThreadsList* exchange_value, ThreadsList* compare_value) {
+  return (ThreadsList*)Atomic::cmpxchg(exchange_value, &_threads_hazard_ptr, compare_value);
+}
+
+inline ThreadsList* Thread::get_threads_hazard_ptr() {
+  return (ThreadsList*)OrderAccess::load_acquire(&_threads_hazard_ptr);
+}
+
+inline void Thread::set_threads_hazard_ptr(ThreadsList* new_list) {
+  OrderAccess::release_store_fence(&_threads_hazard_ptr, new_list);
+}
+
 inline void JavaThread::set_ext_suspended() {
   set_suspend_flag (_ext_suspended);
 }
@@ -176,4 +185,53 @@ inline volatile void* JavaThread::get_polling_page() {
   return OrderAccess::load_acquire(polling_page_addr());
 }
 
+inline bool JavaThread::is_exiting() const {
+  // Use load-acquire so that setting of _terminated by
+  // JavaThread::exit() is seen more quickly.
+  TerminatedTypes l_terminated = (TerminatedTypes)
+      OrderAccess::load_acquire((volatile jint *) &_terminated);
+  return l_terminated == _thread_exiting || check_is_terminated(l_terminated);
+}
+
+inline bool JavaThread::is_terminated() const {
+  // Use load-acquire so that setting of _terminated by
+  // JavaThread::exit() is seen more quickly.
+  TerminatedTypes l_terminated = (TerminatedTypes)
+      OrderAccess::load_acquire((volatile jint *) &_terminated);
+  return check_is_terminated(l_terminated);
+}
+
+inline void JavaThread::set_terminated(TerminatedTypes t) {
+  // use release-store so the setting of _terminated is seen more quickly
+  OrderAccess::release_store((volatile jint *) &_terminated, (jint) t);
+}
+
+// special for Threads::remove() which is static:
+inline void JavaThread::set_terminated_value() {
+  // use release-store so the setting of _terminated is seen more quickly
+  OrderAccess::release_store((volatile jint *) &_terminated, (jint) _thread_terminated);
+}
+
+inline void Threads::add_smr_tlh_times(uint add_value) {
+  Atomic::add(add_value, &_smr_tlh_times);
+}
+
+inline void Threads::inc_smr_tlh_cnt() {
+  Atomic::inc(&_smr_tlh_cnt);
+}
+
+inline void Threads::update_smr_tlh_time_max(uint new_value) {
+  while (true) {
+    uint cur_value = _smr_tlh_time_max;
+    if (new_value <= cur_value) {
+      // No need to update max value so we're done.
+      break;
+    }
+    if (Atomic::cmpxchg(new_value, &_smr_tlh_time_max, cur_value) == cur_value) {
+      // Updated max value so we're done. Otherwise try it all again.
+      break;
+    }
+  }
+}
+
 #endif // SHARE_VM_RUNTIME_THREAD_INLINE_HPP
diff --git a/src/hotspot/share/runtime/threadSMR.cpp b/src/hotspot/share/runtime/threadSMR.cpp
new file mode 100644
index 00000000000..82ecc2590eb
--- /dev/null
+++ b/src/hotspot/share/runtime/threadSMR.cpp
@@ -0,0 +1,121 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+#include "precompiled.hpp"
+#include "memory/allocation.inline.hpp"
+#include "runtime/thread.inline.hpp"
+#include "runtime/threadSMR.hpp"
+#include "services/threadService.hpp"
+
+// 'entries + 1' so we always have at least one entry.
+ThreadsList::ThreadsList(int entries) : _length(entries), _threads(NEW_C_HEAP_ARRAY(JavaThread*, entries + 1, mtThread)), _next_list(NULL) {
+  *(JavaThread**)(_threads + entries) = NULL;  // Make sure the extra entry is NULL.
+}
+
+ThreadsList::~ThreadsList() {
+  FREE_C_HEAP_ARRAY(JavaThread*, _threads);
+}
+
+ThreadsListSetter::~ThreadsListSetter() {
+  if (_target_needs_release) {
+    // The hazard ptr in the target needs to be released.
+    Threads::release_stable_list(_target);
+  }
+}
+
+void ThreadsListSetter::set() {
+  assert(_target->get_threads_hazard_ptr() == NULL, "hazard ptr should not already be set");
+  (void) Threads::acquire_stable_list(_target, /* is_ThreadsListSetter */ true);
+  _target_needs_release = true;
+}
+
+ThreadsListHandle::ThreadsListHandle(Thread *self) : _list(Threads::acquire_stable_list(self, /* is_ThreadsListSetter */ false)), _self(self) {
+  assert(self == Thread::current(), "sanity check");
+  if (EnableThreadSMRStatistics) {
+    _timer.start();
+  }
+}
+
+ThreadsListHandle::~ThreadsListHandle() {
+  Threads::release_stable_list(_self);
+  if (EnableThreadSMRStatistics) {
+    _timer.stop();
+    uint millis = (uint)_timer.milliseconds();
+    Threads::inc_smr_tlh_cnt();
+    Threads::add_smr_tlh_times(millis);
+    Threads::update_smr_tlh_time_max(millis);
+  }
+}
+
+// Convert an internal thread reference to a JavaThread found on the
+// associated ThreadsList. This ThreadsListHandle "protects" the
+// returned JavaThread *.
+//
+// If thread_oop_p is not NULL, then the caller wants to use the oop
+// after this call so the oop is returned. On success, *jt_pp is set
+// to the converted JavaThread * and true is returned. On error,
+// returns false.
+//
+bool ThreadsListHandle::cv_internal_thread_to_JavaThread(jobject jthread,
+                                                         JavaThread ** jt_pp,
+                                                         oop * thread_oop_p) {
+  assert(this->list() != NULL, "must have a ThreadsList");
+  assert(jt_pp != NULL, "must have a return JavaThread pointer");
+  // thread_oop_p is optional so no assert()
+
+  // The JVM_* interfaces don't allow a NULL thread parameter; JVM/TI
+  // allows a NULL thread parameter to signify "current thread" which
+  // allows us to avoid calling cv_external_thread_to_JavaThread().
+  // The JVM_* interfaces have no such leeway.
+
+  oop thread_oop = JNIHandles::resolve_non_null(jthread);
+  // Looks like an oop at this point.
+  if (thread_oop_p != NULL) {
+    // Return the oop to the caller; the caller may still want
+    // the oop even if this function returns false.
+    *thread_oop_p = thread_oop;
+  }
+
+  JavaThread *java_thread = java_lang_Thread::thread(thread_oop);
+  if (java_thread == NULL) {
+    // The java.lang.Thread does not contain a JavaThread * so it has
+    // not yet run or it has died.
+    return false;
+  }
+  // Looks like a live JavaThread at this point.
+
+  if (java_thread != JavaThread::current()) {
+    // jthread is not for the current JavaThread so have to verify
+    // the JavaThread * against the ThreadsList.
+    if (EnableThreadSMRExtraValidityChecks && !includes(java_thread)) {
+      // Not on the JavaThreads list so it is not alive.
+      return false;
+    }
+  }
+
+  // Return a live JavaThread that is "protected" by the
+  // ThreadsListHandle in the caller.
+  *jt_pp = java_thread;
+  return true;
+}
diff --git a/src/hotspot/share/runtime/threadSMR.hpp b/src/hotspot/share/runtime/threadSMR.hpp
new file mode 100644
index 00000000000..1e177b7f435
--- /dev/null
+++ b/src/hotspot/share/runtime/threadSMR.hpp
@@ -0,0 +1,257 @@
+/*
+ * Copyright (c) 2017, 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_RUNTIME_THREADSMR_HPP
+#define SHARE_VM_RUNTIME_THREADSMR_HPP
+
+#include "memory/allocation.hpp"
+#include "runtime/timer.hpp"
+
+// Thread Safe Memory Reclamation (Thread-SMR) support.
+//
+// ThreadsListHandles are used to safely perform operations on one or more
+// threads without the risk of the thread or threads exiting during the
+// operation. It is no longer necessary to hold the Threads_lock to safely
+// perform an operation on a target thread.
+//
+// There are several different ways to refer to java.lang.Thread objects
+// so we have a few ways to get a protected JavaThread *:
+//
+// JNI jobject example:
+//   jobject jthread = ...;
+//   :
+//   ThreadsListHandle tlh;
+//   JavaThread* jt = NULL;
+//   bool is_alive = tlh.cv_internal_thread_to_JavaThread(jthread, &jt, NULL);
+//   if (is_alive) {
+//     :  // do stuff with 'jt'...
+//   }
+//
+// JVM/TI jthread example:
+//   jthread thread = ...;
+//   :
+//   JavaThread* jt = NULL;
+//   ThreadsListHandle tlh;
+//   jvmtiError err = JvmtiExport::cv_external_thread_to_JavaThread(tlh.list(), thread, &jt, NULL);
+//   if (err != JVMTI_ERROR_NONE) {
+//     return err;
+//   }
+//   :  // do stuff with 'jt'...
+//
+// JVM/TI oop example (this one should be very rare):
+//   oop thread_obj = ...;
+//   :
+//   JavaThread *jt = NULL;
+//   ThreadsListHandle tlh;
+//   jvmtiError err = JvmtiExport::cv_oop_to_JavaThread(tlh.list(), thread_obj, &jt);
+//   if (err != JVMTI_ERROR_NONE) {
+//     return err;
+//   }
+//   :  // do stuff with 'jt'...
+//
+// A JavaThread * that is included in the ThreadsList that is held by
+// a ThreadsListHandle is protected as long as the ThreadsListHandle
+// remains in scope. The target JavaThread * may have logically exited,
+// but that target JavaThread * will not be deleted until it is no
+// longer protected by a ThreadsListHandle.
+
+
+// A fast list of JavaThreads.
+//
+class ThreadsList : public CHeapObj<mtThread> {
+  friend class ScanHazardPtrGatherProtectedThreadsClosure;
+  friend class Threads;
+
+  const uint _length;
+  ThreadsList* _next_list;
+  JavaThread *const *const _threads;
+
+  template <class T>
+  void threads_do_dispatch(T *cl, JavaThread *const thread) const;
+
+  ThreadsList *next_list() const        { return _next_list; }
+  void set_next_list(ThreadsList *list) { _next_list = list; }
+
+public:
+  ThreadsList(int entries);
+  ~ThreadsList();
+
+  template <class T>
+  void threads_do(T *cl) const;
+
+  uint length() const                       { return _length; }
+
+  JavaThread *const thread_at(uint i) const { return _threads[i]; }
+
+  JavaThread *const *threads() const        { return _threads; }
+
+  // Returns -1 if target is not found.
+  int find_index_of_JavaThread(JavaThread* target);
+  JavaThread* find_JavaThread_from_java_tid(jlong java_tid) const;
+  bool includes(const JavaThread * const p) const;
+
+  static ThreadsList* add_thread(ThreadsList* list, JavaThread* java_thread);
+  static ThreadsList* remove_thread(ThreadsList* list, JavaThread* java_thread);
+};
+
+// Linked list of ThreadsLists to support nested ThreadsListHandles.
+class NestedThreadsList : public CHeapObj<mtThread> {
+  ThreadsList*const _t_list;
+  NestedThreadsList* _next;
+
+public:
+  NestedThreadsList(ThreadsList* t_list) : _t_list(t_list) {
+    assert(Threads_lock->owned_by_self(),
+           "must own Threads_lock for saved t_list to be valid.");
+  }
+
+  ThreadsList* t_list() { return _t_list; }
+  NestedThreadsList* next() { return _next; }
+  void set_next(NestedThreadsList* value) { _next = value; }
+};
+
+// A helper to optionally set the hazard ptr in ourself. This helper can
+// be used by ourself or by another thread. If the hazard ptr is set(),
+// then the destructor will release it.
+//
+class ThreadsListSetter : public StackObj {
+private:
+  bool _target_needs_release;  // needs release only when set()
+  Thread * _target;
+
+public:
+  ThreadsListSetter() : _target_needs_release(false), _target(Thread::current()) {
+  }
+  ~ThreadsListSetter();
+  ThreadsList* list();
+  void set();
+  bool target_needs_release() { return _target_needs_release; }
+};
+
+// This stack allocated ThreadsListHandle keeps all JavaThreads in the
+// ThreadsList from being deleted until it is safe.
+//
+class ThreadsListHandle : public StackObj {
+  ThreadsList * _list;
+  Thread *const _self;
+  elapsedTimer _timer;  // Enabled via -XX:+EnableThreadSMRStatistics.
+
+public:
+  ThreadsListHandle(Thread *self = Thread::current());
+  ~ThreadsListHandle();
+
+  ThreadsList *list() const {
+    return _list;
+  }
+
+  template <class T>
+  void threads_do(T *cl) const {
+    return _list->threads_do(cl);
+  }
+
+  bool cv_internal_thread_to_JavaThread(jobject jthread, JavaThread ** jt_pp, oop * thread_oop_p);
+
+  bool includes(JavaThread* p) {
+    return _list->includes(p);
+  }
+
+  uint length() const {
+    return _list->length();
+  }
+};
+
+// This stack allocated JavaThreadIterator is used to walk the
+// specified ThreadsList using the following style:
+//
+//   JavaThreadIterator jti(t_list);
+//   for (JavaThread *jt = jti.first(); jt != NULL; jt = jti.next()) {
+//     ...
+//   }
+//
+class JavaThreadIterator : public StackObj {
+  ThreadsList * _list;
+  uint _index;
+
+public:
+  JavaThreadIterator(ThreadsList *list) : _list(list), _index(0) {
+    assert(list != NULL, "ThreadsList must not be NULL.");
+  }
+
+  JavaThread *first() {
+    _index = 0;
+    return _list->thread_at(_index);
+  }
+
+  uint length() const {
+    return _list->length();
+  }
+
+  ThreadsList *list() const {
+    return _list;
+  }
+
+  JavaThread *next() {
+    if (++_index >= length()) {
+      return NULL;
+    }
+    return _list->thread_at(_index);
+  }
+};
+
+// This stack allocated ThreadsListHandle and JavaThreadIterator combo
+// is used to walk the ThreadsList in the included ThreadsListHandle
+// using the following style:
+//
+//   for (JavaThreadIteratorWithHandle jtiwh; JavaThread *jt = jtiwh.next(); ) {
+//     ...
+//   }
+//
+class JavaThreadIteratorWithHandle : public StackObj {
+  ThreadsListHandle _tlh;
+  uint _index;
+
+public:
+  JavaThreadIteratorWithHandle() : _index(0) {}
+
+  uint length() const {
+    return _tlh.length();
+  }
+
+  ThreadsList *list() const {
+    return _tlh.list();
+  }
+
+  JavaThread *next() {
+    if (_index >= length()) {
+      return NULL;
+    }
+    return _tlh.list()->thread_at(_index++);
+  }
+
+  void rewind() {
+    _index = 0;
+  }
+};
+
+#endif // SHARE_VM_RUNTIME_THREADSMR_HPP
diff --git a/src/hotspot/share/runtime/threadSMR.inline.hpp b/src/hotspot/share/runtime/threadSMR.inline.hpp
new file mode 100644
index 00000000000..0203fc6e55f
--- /dev/null
+++ b/src/hotspot/share/runtime/threadSMR.inline.hpp
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2017, 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_RUNTIME_THREADSMR_INLINE_HPP
+#define SHARE_VM_RUNTIME_THREADSMR_INLINE_HPP
+
+#include "runtime/atomic.hpp"
+#include "runtime/prefetch.inline.hpp"
+#include "runtime/thread.inline.hpp"
+#include "runtime/threadSMR.hpp"
+
+// Devirtualize known thread closure types.
+template <class T>
+inline void ThreadsList::threads_do_dispatch(T *cl, JavaThread *const thread) const {
+  cl->T::do_thread(thread);
+}
+
+template <>
+inline void ThreadsList::threads_do_dispatch<ThreadClosure>(ThreadClosure *cl, JavaThread *const thread) const {
+  cl->do_thread(thread);
+}
+
+template <class T>
+inline void ThreadsList::threads_do(T *cl) const {
+  const intx scan_interval = PrefetchScanIntervalInBytes;
+  JavaThread *const *const end = _threads + _length;
+  for (JavaThread *const *current_p = _threads; current_p != end; current_p++) {
+    Prefetch::read((void*)current_p, scan_interval);
+    JavaThread *const current = *current_p;
+    threads_do_dispatch(cl, current);
+  }
+}
+
+inline ThreadsList* ThreadsListSetter::list() {
+  ThreadsList *ret = _target->get_threads_hazard_ptr();
+  assert(ret != NULL, "hazard ptr should be set");
+  assert(!Thread::is_hazard_ptr_tagged(ret), "hazard ptr should be validated");
+  return ret;
+}
+
+#endif // SHARE_VM_RUNTIME_THREADSMR_INLINE_HPP
diff --git a/src/hotspot/share/runtime/vmStructs.cpp b/src/hotspot/share/runtime/vmStructs.cpp
index 0466f7ae7db..9fb22233abb 100644
--- a/src/hotspot/share/runtime/vmStructs.cpp
+++ b/src/hotspot/share/runtime/vmStructs.cpp
@@ -830,7 +830,7 @@ typedef PaddedEnd<ObjectMonitor>              PaddedObjectMonitor;
   nonstatic_field(nmethod,                     _osr_link,                                     nmethod*)                              \
   nonstatic_field(nmethod,                     _scavenge_root_link,                           nmethod*)                              \
   nonstatic_field(nmethod,                     _scavenge_root_state,                          jbyte)                                 \
-  nonstatic_field(nmethod,                     _state,                                        volatile char)                         \
+  nonstatic_field(nmethod,                     _state,                                        volatile signed char)                         \
   nonstatic_field(nmethod,                     _exception_offset,                             int)                                   \
   nonstatic_field(nmethod,                     _orig_pc_offset,                               int)                                   \
   nonstatic_field(nmethod,                     _stub_offset,                                  int)                                   \
@@ -1350,8 +1350,8 @@ typedef PaddedEnd<ObjectMonitor>              PaddedObjectMonitor;
   declare_integer_type(int)                                               \
   declare_integer_type(long)                                              \
   declare_integer_type(char)                                              \
+  declare_integer_type(volatile signed char)                              \
   declare_unsigned_integer_type(unsigned char)                            \
-  declare_unsigned_integer_type(volatile char)                            \
   declare_unsigned_integer_type(u_char)                                   \
   declare_unsigned_integer_type(unsigned int)                             \
   declare_unsigned_integer_type(uint)                                     \
@@ -1534,6 +1534,7 @@ typedef PaddedEnd<ObjectMonitor>              PaddedObjectMonitor;
   declare_toplevel_type(PerfDataPrologue*)                                \
   declare_toplevel_type(PerfDataEntry)                                    \
   declare_toplevel_type(PerfMemory)                                       \
+  declare_type(PerfData, CHeapObj<mtInternal>)                            \
                                                                           \
   /*********************************/                                     \
   /* SymbolTable, SystemDictionary */                                     \
@@ -1958,6 +1959,7 @@ typedef PaddedEnd<ObjectMonitor>              PaddedObjectMonitor;
   declare_c2_type(NegFNode, NegNode)                                      \
   declare_c2_type(NegDNode, NegNode)                                      \
   declare_c2_type(AtanDNode, Node)                                        \
+  declare_c2_type(SqrtFNode, Node)                                        \
   declare_c2_type(SqrtDNode, Node)                                        \
   declare_c2_type(ReverseBytesINode, Node)                                \
   declare_c2_type(ReverseBytesLNode, Node)                                \
@@ -2480,6 +2482,12 @@ typedef PaddedEnd<ObjectMonitor>              PaddedObjectMonitor;
   declare_constant(InstanceKlass::inner_class_access_flags_offset)        \
   declare_constant(InstanceKlass::inner_class_next_offset)                \
                                                                           \
+  /*****************************************************/                 \
+  /* InstanceKlass EnclosingMethodAttributeOffset enum */                 \
+  /*****************************************************/                 \
+                                                                          \
+  declare_constant(InstanceKlass::enclosing_method_attribute_size)        \
+                                                                          \
   /*********************************/                                     \
   /* InstanceKlass ClassState enum */                                     \
   /*********************************/                                     \
@@ -2635,6 +2643,46 @@ typedef PaddedEnd<ObjectMonitor>              PaddedObjectMonitor;
   declare_constant(Deoptimization::_reason_shift)                         \
   declare_constant(Deoptimization::_debug_id_shift)                       \
                                                                           \
+  /******************************************/                            \
+  /* BasicType enum (globalDefinitions.hpp) */                            \
+  /******************************************/                            \
+                                                                          \
+  declare_constant(T_BOOLEAN)                                             \
+  declare_constant(T_CHAR)                                                \
+  declare_constant(T_FLOAT)                                               \
+  declare_constant(T_DOUBLE)                                              \
+  declare_constant(T_BYTE)                                                \
+  declare_constant(T_SHORT)                                               \
+  declare_constant(T_INT)                                                 \
+  declare_constant(T_LONG)                                                \
+  declare_constant(T_OBJECT)                                              \
+  declare_constant(T_ARRAY)                                               \
+  declare_constant(T_VOID)                                                \
+  declare_constant(T_ADDRESS)                                             \
+  declare_constant(T_NARROWOOP)                                           \
+  declare_constant(T_METADATA)                                            \
+  declare_constant(T_NARROWKLASS)                                         \
+  declare_constant(T_CONFLICT)                                            \
+  declare_constant(T_ILLEGAL)                                             \
+                                                                          \
+  /**********************************************/                        \
+  /* BasicTypeSize enum (globalDefinitions.hpp) */                        \
+  /**********************************************/                        \
+                                                                          \
+  declare_constant(T_BOOLEAN_size)                                        \
+  declare_constant(T_CHAR_size)                                           \
+  declare_constant(T_FLOAT_size)                                          \
+  declare_constant(T_DOUBLE_size)                                         \
+  declare_constant(T_BYTE_size)                                           \
+  declare_constant(T_SHORT_size)                                          \
+  declare_constant(T_INT_size)                                            \
+  declare_constant(T_LONG_size)                                           \
+  declare_constant(T_OBJECT_size)                                         \
+  declare_constant(T_ARRAY_size)                                          \
+  declare_constant(T_NARROWOOP_size)                                      \
+  declare_constant(T_NARROWKLASS_size)                                    \
+  declare_constant(T_VOID_size)                                           \
+                                                                          \
   /*********************/                                                 \
   /* Matcher (C2 only) */                                                 \
   /*********************/                                                 \
@@ -2733,6 +2781,21 @@ typedef PaddedEnd<ObjectMonitor>              PaddedObjectMonitor;
   declare_c2_preprocessor_constant("SAVED_ON_ENTRY_REG_COUNT", SAVED_ON_ENTRY_REG_COUNT) \
   declare_c2_preprocessor_constant("C_SAVED_ON_ENTRY_REG_COUNT", C_SAVED_ON_ENTRY_REG_COUNT) \
                                                                           \
+  /************/                                                          \
+  /* PerfData */                                                          \
+  /************/                                                          \
+                                                                          \
+  /***********************/                                               \
+  /* PerfData Units enum */                                               \
+  /***********************/                                               \
+                                                                          \
+  declare_constant(PerfData::U_None)                                      \
+  declare_constant(PerfData::U_Bytes)                                     \
+  declare_constant(PerfData::U_Ticks)                                     \
+  declare_constant(PerfData::U_Events)                                    \
+  declare_constant(PerfData::U_String)                                    \
+  declare_constant(PerfData::U_Hertz)                                     \
+                                                                          \
   /****************/                                                      \
   /* JVMCI */                                                             \
   /****************/                                                      \
diff --git a/src/hotspot/share/runtime/vm_operations.cpp b/src/hotspot/share/runtime/vm_operations.cpp
index e9ad8af4314..9e5608f6f06 100644
--- a/src/hotspot/share/runtime/vm_operations.cpp
+++ b/src/hotspot/share/runtime/vm_operations.cpp
@@ -38,6 +38,7 @@
 #include "runtime/interfaceSupport.hpp"
 #include "runtime/sweeper.hpp"
 #include "runtime/thread.inline.hpp"
+#include "runtime/threadSMR.inline.hpp"
 #include "runtime/vm_operations.hpp"
 #include "services/threadService.hpp"
 #include "trace/tracing.hpp"
@@ -96,11 +97,12 @@ void VM_Operation::print_on_error(outputStream* st) const {
 
 void VM_ThreadStop::doit() {
   assert(SafepointSynchronize::is_at_safepoint(), "must be at a safepoint");
+  ThreadsListHandle tlh;
   JavaThread* target = java_lang_Thread::thread(target_thread());
   // Note that this now allows multiple ThreadDeath exceptions to be
   // thrown at a thread.
-  if (target != NULL) {
-    // the thread has run and is not already in the process of exiting
+  if (target != NULL && (!EnableThreadSMRExtraValidityChecks || tlh.includes(target))) {
+    // The target thread has run and has not exited yet.
     target->send_thread_stop(throwable());
   }
 }
@@ -146,9 +148,10 @@ void VM_DeoptimizeFrame::doit() {
 
 void VM_DeoptimizeAll::doit() {
   DeoptimizationMarker dm;
+  JavaThreadIteratorWithHandle jtiwh;
   // deoptimize all java threads in the system
   if (DeoptimizeALot) {
-    for (JavaThread* thread = Threads::first(); thread != NULL; thread = thread->next()) {
+    for (; JavaThread *thread = jtiwh.next(); ) {
       if (thread->has_last_Java_frame()) {
         thread->deoptimize();
       }
@@ -159,7 +162,7 @@ void VM_DeoptimizeAll::doit() {
     int tnum = os::random() & 0x3;
     int fnum =  os::random() & 0x3;
     int tcount = 0;
-    for (JavaThread* thread = Threads::first(); thread != NULL; thread = thread->next()) {
+    for (; JavaThread *thread = jtiwh.next(); ) {
       if (thread->has_last_Java_frame()) {
         if (tcount++ == tnum)  {
         tcount = 0;
@@ -259,12 +262,19 @@ bool VM_FindDeadlocks::doit_prologue() {
 }
 
 void VM_FindDeadlocks::doit() {
-  _deadlocks = ThreadService::find_deadlocks_at_safepoint(_concurrent_locks);
+  // Update the hazard ptr in the originating thread to the current
+  // list of threads. This VM operation needs the current list of
+  // threads for proper deadlock detection and those are the
+  // JavaThreads we need to be protected when we return info to the
+  // originating thread.
+  _setter.set();
+
+  _deadlocks = ThreadService::find_deadlocks_at_safepoint(_setter.list(), _concurrent_locks);
   if (_out != NULL) {
     int num_deadlocks = 0;
     for (DeadlockCycle* cycle = _deadlocks; cycle != NULL; cycle = cycle->next()) {
       num_deadlocks++;
-      cycle->print_on(_out);
+      cycle->print_on_with(_setter.list(), _out);
     }
 
     if (num_deadlocks == 1) {
@@ -331,6 +341,12 @@ void VM_ThreadDump::doit_epilogue() {
 void VM_ThreadDump::doit() {
   ResourceMark rm;
 
+  // Set the hazard ptr in the originating thread to protect the
+  // current list of threads. This VM operation needs the current list
+  // of threads for a proper dump and those are the JavaThreads we need
+  // to be protected when we return info to the originating thread.
+  _result->set_t_list();
+
   ConcurrentLocksDump concurrent_locks(true);
   if (_with_locked_synchronizers) {
     concurrent_locks.dump_at_safepoint();
@@ -338,7 +354,9 @@ void VM_ThreadDump::doit() {
 
   if (_num_threads == 0) {
     // Snapshot all live threads
-    for (JavaThread* jt = Threads::first(); jt != NULL; jt = jt->next()) {
+
+    for (uint i = 0; i < _result->t_list()->length(); i++) {
+      JavaThread* jt = _result->t_list()->thread_at(i);
       if (jt->is_exiting() ||
           jt->is_hidden_from_external_view())  {
         // skip terminating threads and hidden threads
@@ -354,6 +372,7 @@ void VM_ThreadDump::doit() {
   } else {
     // Snapshot threads in the given _threads array
     // A dummy snapshot is created if a thread doesn't exist
+
     for (int i = 0; i < _num_threads; i++) {
       instanceHandle th = _threads->at(i);
       if (th() == NULL) {
@@ -366,6 +385,12 @@ void VM_ThreadDump::doit() {
       // Dump thread stack only if the thread is alive and not exiting
       // and not VM internal thread.
       JavaThread* jt = java_lang_Thread::thread(th());
+      if (jt != NULL && !_result->t_list()->includes(jt)) {
+        // _threads[i] doesn't refer to a valid JavaThread; this check
+        // is primarily for JVM_DumpThreads() which doesn't have a good
+        // way to validate the _threads array.
+        jt = NULL;
+      }
       if (jt == NULL || /* thread not alive */
           jt->is_exiting() ||
           jt->is_hidden_from_external_view())  {
@@ -384,7 +409,7 @@ void VM_ThreadDump::doit() {
 }
 
 ThreadSnapshot* VM_ThreadDump::snapshot_thread(JavaThread* java_thread, ThreadConcurrentLocks* tcl) {
-  ThreadSnapshot* snapshot = new ThreadSnapshot(java_thread);
+  ThreadSnapshot* snapshot = new ThreadSnapshot(_result->t_list(), java_thread);
   snapshot->dump_stack_at_safepoint(_max_depth, _with_locked_monitors);
   snapshot->set_concurrent_locks(tcl);
   return snapshot;
@@ -403,11 +428,12 @@ int VM_Exit::set_vm_exited() {
 
   _shutdown_thread = thr_cur;
   _vm_exited = true;                                // global flag
-  for(JavaThread *thr = Threads::first(); thr != NULL; thr = thr->next())
+  for (JavaThreadIteratorWithHandle jtiwh; JavaThread *thr = jtiwh.next(); ) {
     if (thr!=thr_cur && thr->thread_state() == _thread_in_native) {
       ++num_active;
       thr->set_terminated(JavaThread::_vm_exited);  // per-thread flag
     }
+  }
 
   return num_active;
 }
@@ -435,11 +461,13 @@ int VM_Exit::wait_for_threads_in_native_to_block() {
   int max_wait = max_wait_compiler_thread;
 
   int attempts = 0;
+  JavaThreadIteratorWithHandle jtiwh;
   while (true) {
     int num_active = 0;
     int num_active_compiler_thread = 0;
 
-    for(JavaThread *thr = Threads::first(); thr != NULL; thr = thr->next()) {
+    jtiwh.rewind();
+    for (; JavaThread *thr = jtiwh.next(); ) {
       if (thr!=thr_cur && thr->thread_state() == _thread_in_native) {
         num_active++;
         if (thr->is_Compiler_thread()) {
diff --git a/src/hotspot/share/runtime/vm_operations.hpp b/src/hotspot/share/runtime/vm_operations.hpp
index 4bc2fdd7794..3311ee8be25 100644
--- a/src/hotspot/share/runtime/vm_operations.hpp
+++ b/src/hotspot/share/runtime/vm_operations.hpp
@@ -392,12 +392,14 @@ class VM_PrintMetadata : public VM_Operation {
 class DeadlockCycle;
 class VM_FindDeadlocks: public VM_Operation {
  private:
-  bool           _concurrent_locks;
-  DeadlockCycle* _deadlocks;
-  outputStream*  _out;
+  bool              _concurrent_locks;
+  DeadlockCycle*    _deadlocks;
+  outputStream*     _out;
+  ThreadsListSetter _setter;  // Helper to set hazard ptr in the originating thread
+                              // which protects the JavaThreads in _deadlocks.
 
  public:
-  VM_FindDeadlocks(bool concurrent_locks) :  _concurrent_locks(concurrent_locks), _out(NULL), _deadlocks(NULL) {};
+  VM_FindDeadlocks(bool concurrent_locks) :  _concurrent_locks(concurrent_locks), _out(NULL), _deadlocks(NULL), _setter() {};
   VM_FindDeadlocks(outputStream* st) : _concurrent_locks(true), _out(st), _deadlocks(NULL) {};
   ~VM_FindDeadlocks();
 
diff --git a/src/hotspot/share/services/diagnosticArgument.cpp b/src/hotspot/share/services/diagnosticArgument.cpp
index 691bd797dd1..4456260f29b 100644
--- a/src/hotspot/share/services/diagnosticArgument.cpp
+++ b/src/hotspot/share/services/diagnosticArgument.cpp
@@ -29,6 +29,29 @@
 #include "runtime/thread.hpp"
 #include "services/diagnosticArgument.hpp"
 
+StringArrayArgument::StringArrayArgument() {
+  _array = new(ResourceObj::C_HEAP, mtInternal)GrowableArray<char *>(32, true);
+  assert(_array != NULL, "Sanity check");
+}
+
+StringArrayArgument::~StringArrayArgument() {
+  for (int i=0; i<_array->length(); i++) {
+    if(_array->at(i) != NULL) { // Safety check
+      FREE_C_HEAP_ARRAY(char, _array->at(i));
+    }
+  }
+  delete _array;
+}
+
+void StringArrayArgument::add(const char* str, size_t len) {
+  if (str != NULL) {
+    char* ptr = NEW_C_HEAP_ARRAY(char, len+1, mtInternal);
+    strncpy(ptr, str, len);
+    ptr[len] = 0;
+    _array->append(ptr);
+  }
+}
+
 void GenDCmdArgument::read_value(const char* str, size_t len, TRAPS) {
   /* NOTE:Some argument types doesn't require a value,
    * for instance boolean arguments: "enableFeatureX". is
diff --git a/src/hotspot/share/services/diagnosticArgument.hpp b/src/hotspot/share/services/diagnosticArgument.hpp
index d276d3b6294..654650ccd68 100644
--- a/src/hotspot/share/services/diagnosticArgument.hpp
+++ b/src/hotspot/share/services/diagnosticArgument.hpp
@@ -35,29 +35,14 @@ class StringArrayArgument : public CHeapObj<mtInternal> {
 private:
   GrowableArray<char*>* _array;
 public:
-  StringArrayArgument() {
-    _array = new(ResourceObj::C_HEAP, mtInternal)GrowableArray<char *>(32, true);
-    assert(_array != NULL, "Sanity check");
-  }
-  void add(const char* str, size_t len) {
-    if (str != NULL) {
-      char* ptr = NEW_C_HEAP_ARRAY(char, len+1, mtInternal);
-      strncpy(ptr, str, len);
-      ptr[len] = 0;
-      _array->append(ptr);
-    }
-  }
+  StringArrayArgument();
+  ~StringArrayArgument();
+
+  void add(const char* str, size_t len);
+
   GrowableArray<char*>* array() {
     return _array;
   }
-  ~StringArrayArgument() {
-    for (int i=0; i<_array->length(); i++) {
-      if(_array->at(i) != NULL) { // Safety check
-        FREE_C_HEAP_ARRAY(char, _array->at(i));
-      }
-    }
-    delete _array;
-  }
 };
 
 class NanoTimeArgument {
diff --git a/src/hotspot/share/services/heapDumper.cpp b/src/hotspot/share/services/heapDumper.cpp
index e931b2ec334..71dbe24f0b5 100644
--- a/src/hotspot/share/services/heapDumper.cpp
+++ b/src/hotspot/share/services/heapDumper.cpp
@@ -39,6 +39,8 @@
 #include "runtime/jniHandles.hpp"
 #include "runtime/os.hpp"
 #include "runtime/reflectionUtils.hpp"
+#include "runtime/thread.inline.hpp"
+#include "runtime/threadSMR.hpp"
 #include "runtime/vframe.hpp"
 #include "runtime/vmThread.hpp"
 #include "runtime/vm_operations.hpp"
@@ -1895,7 +1897,7 @@ void VM_HeapDumper::dump_stack_traces() {
 
   _stack_traces = NEW_C_HEAP_ARRAY(ThreadStackTrace*, Threads::number_of_threads(), mtInternal);
   int frame_serial_num = 0;
-  for (JavaThread* thread = Threads::first(); thread != NULL ; thread = thread->next()) {
+  for (JavaThreadIteratorWithHandle jtiwh; JavaThread *thread = jtiwh.next(); ) {
     oop threadObj = thread->threadObj();
     if (threadObj != NULL && !thread->is_exiting() && !thread->is_hidden_from_external_view()) {
       // dump thread stack trace
diff --git a/src/hotspot/share/services/jmm.h b/src/hotspot/share/services/jmm.h
deleted file mode 100644
index df232f6feec..00000000000
--- a/src/hotspot/share/services/jmm.h
+++ /dev/null
@@ -1,345 +0,0 @@
-/*
- * Copyright (c) 2003, 2017, 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.  Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * 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 _JAVA_JMM_H_
-#define _JAVA_JMM_H_
-
-/*
- * This is a private interface used by JDK for JVM monitoring
- * and management.
- *
- * Bump the version number when either of the following happens:
- *
- * 1. There is a change in functions in JmmInterface.
- *
- * 2. There is a change in the contract between VM and Java classes.
- */
-
-#include "jni.h"
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-enum {
-  JMM_VERSION_1   = 0x20010000,
-  JMM_VERSION_1_0 = 0x20010000,
-  JMM_VERSION_1_1 = 0x20010100, // JDK 6
-  JMM_VERSION_1_2 = 0x20010200, // JDK 7
-  JMM_VERSION_1_2_1 = 0x20010201, // JDK 7 GA
-  JMM_VERSION_1_2_2 = 0x20010202,
-  JMM_VERSION_2  = 0x20020000,  // JDK 10
-  JMM_VERSION     = 0x20020000
-};
-
-typedef struct {
-  unsigned int isLowMemoryDetectionSupported : 1;
-  unsigned int isCompilationTimeMonitoringSupported : 1;
-  unsigned int isThreadContentionMonitoringSupported : 1;
-  unsigned int isCurrentThreadCpuTimeSupported : 1;
-  unsigned int isOtherThreadCpuTimeSupported : 1;
-  unsigned int isObjectMonitorUsageSupported : 1;
-  unsigned int isSynchronizerUsageSupported : 1;
-  unsigned int isThreadAllocatedMemorySupported : 1;
-  unsigned int isRemoteDiagnosticCommandsSupported : 1;
-  unsigned int : 22;
-} jmmOptionalSupport;
-
-typedef enum {
-  JMM_CLASS_LOADED_COUNT             = 1,    /* Total number of loaded classes */
-  JMM_CLASS_UNLOADED_COUNT           = 2,    /* Total number of unloaded classes */
-  JMM_THREAD_TOTAL_COUNT             = 3,    /* Total number of threads that have been started */
-  JMM_THREAD_LIVE_COUNT              = 4,    /* Current number of live threads */
-  JMM_THREAD_PEAK_COUNT              = 5,    /* Peak number of live threads */
-  JMM_THREAD_DAEMON_COUNT            = 6,    /* Current number of daemon threads */
-  JMM_JVM_INIT_DONE_TIME_MS          = 7,    /* Time when the JVM finished initialization */
-  JMM_COMPILE_TOTAL_TIME_MS          = 8,    /* Total accumulated time spent in compilation */
-  JMM_GC_TIME_MS                     = 9,    /* Total accumulated time spent in collection */
-  JMM_GC_COUNT                       = 10,   /* Total number of collections */
-  JMM_JVM_UPTIME_MS                  = 11,   /* The JVM uptime in milliseconds */
-
-  JMM_INTERNAL_ATTRIBUTE_INDEX       = 100,
-  JMM_CLASS_LOADED_BYTES             = 101,  /* Number of bytes loaded instance classes */
-  JMM_CLASS_UNLOADED_BYTES           = 102,  /* Number of bytes unloaded instance classes */
-  JMM_TOTAL_CLASSLOAD_TIME_MS        = 103,  /* Accumulated VM class loader time (TraceClassLoadingTime) */
-  JMM_VM_GLOBAL_COUNT                = 104,  /* Number of VM internal flags */
-  JMM_SAFEPOINT_COUNT                = 105,  /* Total number of safepoints */
-  JMM_TOTAL_SAFEPOINTSYNC_TIME_MS    = 106,  /* Accumulated time spent getting to safepoints */
-  JMM_TOTAL_STOPPED_TIME_MS          = 107,  /* Accumulated time spent at safepoints */
-  JMM_TOTAL_APP_TIME_MS              = 108,  /* Accumulated time spent in Java application */
-  JMM_VM_THREAD_COUNT                = 109,  /* Current number of VM internal threads */
-  JMM_CLASS_INIT_TOTAL_COUNT         = 110,  /* Number of classes for which initializers were run */
-  JMM_CLASS_INIT_TOTAL_TIME_MS       = 111,  /* Accumulated time spent in class initializers */
-  JMM_METHOD_DATA_SIZE_BYTES         = 112,  /* Size of method data in memory */
-  JMM_CLASS_VERIFY_TOTAL_TIME_MS     = 113,  /* Accumulated time spent in class verifier */
-  JMM_SHARED_CLASS_LOADED_COUNT      = 114,  /* Number of shared classes loaded */
-  JMM_SHARED_CLASS_UNLOADED_COUNT    = 115,  /* Number of shared classes unloaded */
-  JMM_SHARED_CLASS_LOADED_BYTES      = 116,  /* Number of bytes loaded shared classes */
-  JMM_SHARED_CLASS_UNLOADED_BYTES    = 117,  /* Number of bytes unloaded shared classes */
-
-  JMM_OS_ATTRIBUTE_INDEX             = 200,
-  JMM_OS_PROCESS_ID                  = 201,  /* Process id of the JVM */
-  JMM_OS_MEM_TOTAL_PHYSICAL_BYTES    = 202,  /* Physical memory size */
-
-  JMM_GC_EXT_ATTRIBUTE_INFO_SIZE     = 401   /* the size of the GC specific attributes for a given GC memory manager */
-} jmmLongAttribute;
-
-typedef enum {
-  JMM_VERBOSE_GC                     = 21,
-  JMM_VERBOSE_CLASS                  = 22,
-  JMM_THREAD_CONTENTION_MONITORING   = 23,
-  JMM_THREAD_CPU_TIME                = 24,
-  JMM_THREAD_ALLOCATED_MEMORY        = 25
-} jmmBoolAttribute;
-
-
-enum {
-  JMM_THREAD_STATE_FLAG_SUSPENDED = 0x00100000,
-  JMM_THREAD_STATE_FLAG_NATIVE    = 0x00400000
-};
-
-#define JMM_THREAD_STATE_FLAG_MASK  0xFFF00000
-
-typedef enum {
-  JMM_STAT_PEAK_THREAD_COUNT         = 801,
-  JMM_STAT_THREAD_CONTENTION_COUNT   = 802,
-  JMM_STAT_THREAD_CONTENTION_TIME    = 803,
-  JMM_STAT_THREAD_CONTENTION_STAT    = 804,
-  JMM_STAT_PEAK_POOL_USAGE           = 805,
-  JMM_STAT_GC_STAT                   = 806
-} jmmStatisticType;
-
-typedef enum {
-  JMM_USAGE_THRESHOLD_HIGH            = 901,
-  JMM_USAGE_THRESHOLD_LOW             = 902,
-  JMM_COLLECTION_USAGE_THRESHOLD_HIGH = 903,
-  JMM_COLLECTION_USAGE_THRESHOLD_LOW  = 904
-} jmmThresholdType;
-
-/* Should match what is allowed in globals.hpp */
-typedef enum {
-  JMM_VMGLOBAL_TYPE_UNKNOWN  = 0,
-  JMM_VMGLOBAL_TYPE_JBOOLEAN = 1,
-  JMM_VMGLOBAL_TYPE_JSTRING  = 2,
-  JMM_VMGLOBAL_TYPE_JLONG    = 3,
-  JMM_VMGLOBAL_TYPE_JDOUBLE  = 4
-} jmmVMGlobalType;
-
-typedef enum {
-  JMM_VMGLOBAL_ORIGIN_DEFAULT      = 1,   /* Default value */
-  JMM_VMGLOBAL_ORIGIN_COMMAND_LINE = 2,   /* Set at command line (or JNI invocation) */
-  JMM_VMGLOBAL_ORIGIN_MANAGEMENT   = 3,   /* Set via management interface */
-  JMM_VMGLOBAL_ORIGIN_ENVIRON_VAR  = 4,   /* Set via environment variables */
-  JMM_VMGLOBAL_ORIGIN_CONFIG_FILE  = 5,   /* Set via config file (such as .hotspotrc) */
-  JMM_VMGLOBAL_ORIGIN_ERGONOMIC    = 6,   /* Set via ergonomic */
-  JMM_VMGLOBAL_ORIGIN_ATTACH_ON_DEMAND = 7,   /* Set via attach */
-  JMM_VMGLOBAL_ORIGIN_OTHER        = 99   /* Set via some other mechanism */
-} jmmVMGlobalOrigin;
-
-typedef struct {
-  jstring           name;
-  jvalue            value;
-  jmmVMGlobalType   type;           /* Data type */
-  jmmVMGlobalOrigin origin;         /* Default or non-default value */
-  unsigned int      writeable : 1;  /* dynamically writeable */
-  unsigned int      external  : 1;  /* external supported interface */
-  unsigned int      reserved  : 30;
-  void *reserved1;
-  void *reserved2;
-} jmmVMGlobal;
-
-typedef struct {
-  const char*  name;
-  char         type;
-  const char*  description;
-} jmmExtAttributeInfo;
-
-/* Caller has to set the following fields before calling GetLastGCStat
- *   o usage_before_gc               - array of MemoryUsage objects
- *   o usage_after_gc                - array of MemoryUsage objects
- *   o gc_ext_attribute_values_size - size of gc_ext_atttribute_values array
- *   o gc_ext_attribtue_values      - array of jvalues
- */
-typedef struct {
-  jlong        gc_index;                       /* Index of the collections */
-  jlong        start_time;                     /* Start time of the GC */
-  jlong        end_time;                       /* End time of the GC */
-  jobjectArray usage_before_gc;                /* Memory usage array before GC */
-  jobjectArray usage_after_gc;                 /* Memory usage array after GC */
-  jint         gc_ext_attribute_values_size;   /* set by the caller of GetGCStat */
-  jvalue*      gc_ext_attribute_values;        /* Array of jvalue for GC extension attributes */
-  jint         num_gc_ext_attributes;          /* number of GC extension attribute values s are filled */
-                                               /* -1 indicates gc_ext_attribute_values is not big enough */
-} jmmGCStat;
-
-typedef struct {
-  const char* name;                /* Name of the diagnostic command */
-  const char* description;         /* Short description */
-  const char* impact;              /* Impact on the JVM */
-  const char* permission_class;    /* Class name of the required permission if any */
-  const char* permission_name;     /* Permission name of the required permission if any */
-  const char* permission_action;   /* Action name of the required permission if any*/
-  int         num_arguments;       /* Number of supported options or arguments */
-  jboolean    enabled;             /* True if the diagnostic command can be invoked, false otherwise */
-} dcmdInfo;
-
-typedef struct {
-  const char* name;                /* Option/Argument name*/
-  const char* description;         /* Short description */
-  const char* type;                /* Type: STRING, BOOLEAN, etc. */
-  const char* default_string;      /* Default value in a parsable string */
-  jboolean    mandatory;           /* True if the option/argument is mandatory */
-  jboolean    option;              /* True if it is an option, false if it is an argument */
-                                   /* (see diagnosticFramework.hpp for option/argument definitions) */
-  jboolean    multiple;            /* True if the option can be specified several time */
-  int         position;            /* Expected position for this argument (this field is */
-                                   /* meaningless for options) */
-} dcmdArgInfo;
-
-typedef struct jmmInterface_1_ {
-  void*        reserved1;
-  void*        reserved2;
-
-  jint         (JNICALL *GetVersion)             (JNIEnv *env);
-
-  jint         (JNICALL *GetOptionalSupport)     (JNIEnv *env,
-                                                  jmmOptionalSupport* support_ptr);
-
-  jint         (JNICALL *GetThreadInfo)          (JNIEnv *env,
-                                                  jlongArray ids,
-                                                  jint maxDepth,
-                                                  jobjectArray infoArray);
-
-  jobjectArray (JNICALL *GetMemoryPools)         (JNIEnv* env, jobject mgr);
-
-  jobjectArray (JNICALL *GetMemoryManagers)      (JNIEnv* env, jobject pool);
-
-  jobject      (JNICALL *GetMemoryPoolUsage)     (JNIEnv* env, jobject pool);
-  jobject      (JNICALL *GetPeakMemoryPoolUsage) (JNIEnv* env, jobject pool);
-
-  void         (JNICALL *GetThreadAllocatedMemory)
-                                                 (JNIEnv *env,
-                                                  jlongArray ids,
-                                                  jlongArray sizeArray);
-
-  jobject      (JNICALL *GetMemoryUsage)         (JNIEnv* env, jboolean heap);
-
-  jlong        (JNICALL *GetLongAttribute)       (JNIEnv *env, jobject obj, jmmLongAttribute att);
-  jboolean     (JNICALL *GetBoolAttribute)       (JNIEnv *env, jmmBoolAttribute att);
-  jboolean     (JNICALL *SetBoolAttribute)       (JNIEnv *env, jmmBoolAttribute att, jboolean flag);
-
-  jint         (JNICALL *GetLongAttributes)      (JNIEnv *env,
-                                                  jobject obj,
-                                                  jmmLongAttribute* atts,
-                                                  jint count,
-                                                  jlong* result);
-
-  jobjectArray (JNICALL *FindCircularBlockedThreads) (JNIEnv *env);
-
-  // Not used in JDK 6 or JDK 7
-  jlong        (JNICALL *GetThreadCpuTime)       (JNIEnv *env, jlong thread_id);
-
-  jobjectArray (JNICALL *GetVMGlobalNames)       (JNIEnv *env);
-  jint         (JNICALL *GetVMGlobals)           (JNIEnv *env,
-                                                  jobjectArray names,
-                                                  jmmVMGlobal *globals,
-                                                  jint count);
-
-  jint         (JNICALL *GetInternalThreadTimes) (JNIEnv *env,
-                                                  jobjectArray names,
-                                                  jlongArray times);
-
-  jboolean     (JNICALL *ResetStatistic)         (JNIEnv *env,
-                                                  jvalue obj,
-                                                  jmmStatisticType type);
-
-  void         (JNICALL *SetPoolSensor)          (JNIEnv *env,
-                                                  jobject pool,
-                                                  jmmThresholdType type,
-                                                  jobject sensor);
-
-  jlong        (JNICALL *SetPoolThreshold)       (JNIEnv *env,
-                                                  jobject pool,
-                                                  jmmThresholdType type,
-                                                  jlong threshold);
-  jobject      (JNICALL *GetPoolCollectionUsage) (JNIEnv* env, jobject pool);
-
-  jint         (JNICALL *GetGCExtAttributeInfo)  (JNIEnv *env,
-                                                  jobject mgr,
-                                                  jmmExtAttributeInfo *ext_info,
-                                                  jint count);
-  void         (JNICALL *GetLastGCStat)          (JNIEnv *env,
-                                                  jobject mgr,
-                                                  jmmGCStat *gc_stat);
-
-  jlong        (JNICALL *GetThreadCpuTimeWithKind)
-                                                 (JNIEnv *env,
-                                                  jlong thread_id,
-                                                  jboolean user_sys_cpu_time);
-  void         (JNICALL *GetThreadCpuTimesWithKind)
-                                                 (JNIEnv *env,
-                                                  jlongArray ids,
-                                                  jlongArray timeArray,
-                                                  jboolean user_sys_cpu_time);
-
-  jint         (JNICALL *DumpHeap0)              (JNIEnv *env,
-                                                  jstring outputfile,
-                                                  jboolean live);
-  jobjectArray (JNICALL *FindDeadlocks)          (JNIEnv *env,
-                                                  jboolean object_monitors_only);
-  void         (JNICALL *SetVMGlobal)            (JNIEnv *env,
-                                                  jstring flag_name,
-                                                  jvalue  new_value);
-  void*        reserved6;
-  jobjectArray (JNICALL *DumpThreads)            (JNIEnv *env,
-                                                  jlongArray ids,
-                                                  jboolean lockedMonitors,
-                                                  jboolean lockedSynchronizers,
-                                                  jint maxDepth);
-  void         (JNICALL *SetGCNotificationEnabled) (JNIEnv *env,
-                                                    jobject mgr,
-                                                    jboolean enabled);
-  jobjectArray (JNICALL *GetDiagnosticCommands)  (JNIEnv *env);
-  void         (JNICALL *GetDiagnosticCommandInfo)
-                                                 (JNIEnv *env,
-                                                  jobjectArray cmds,
-                                                  dcmdInfo *infoArray);
-  void         (JNICALL *GetDiagnosticCommandArgumentsInfo)
-                                                 (JNIEnv *env,
-                                                  jstring commandName,
-                                                  dcmdArgInfo *infoArray);
-  jstring      (JNICALL *ExecuteDiagnosticCommand)
-                                                 (JNIEnv *env,
-                                                  jstring command);
-  void         (JNICALL *SetDiagnosticFrameworkNotificationEnabled)
-                                                 (JNIEnv *env,
-                                                  jboolean enabled);
-} JmmInterface;
-
-#ifdef __cplusplus
-} /* extern "C" */
-#endif /* __cplusplus */
-
-#endif /* !_JAVA_JMM_H_ */
diff --git a/src/hotspot/share/services/management.cpp b/src/hotspot/share/services/management.cpp
index bc50816d83d..8273fec1b2e 100644
--- a/src/hotspot/share/services/management.cpp
+++ b/src/hotspot/share/services/management.cpp
@@ -23,6 +23,7 @@
  */
 
 #include "precompiled.hpp"
+#include "jmm.h"
 #include "classfile/systemDictionary.hpp"
 #include "compiler/compileBroker.hpp"
 #include "memory/iterator.hpp"
@@ -41,12 +42,12 @@
 #include "runtime/os.hpp"
 #include "runtime/serviceThread.hpp"
 #include "runtime/thread.inline.hpp"
+#include "runtime/threadSMR.hpp"
 #include "services/classLoadingService.hpp"
 #include "services/diagnosticCommand.hpp"
 #include "services/diagnosticFramework.hpp"
 #include "services/writeableFlags.hpp"
 #include "services/heapDumper.hpp"
-#include "services/jmm.h"
 #include "services/lowMemoryDetector.hpp"
 #include "services/gcNotifier.hpp"
 #include "services/nmtDCmd.hpp"
@@ -1025,11 +1026,15 @@ static void do_thread_dump(ThreadDumpResult* dump_result,
   // First get an array of threadObj handles.
   // A JavaThread may terminate before we get the stack trace.
   GrowableArray<instanceHandle>* thread_handle_array = new GrowableArray<instanceHandle>(num_threads);
+
   {
-    MutexLockerEx ml(Threads_lock);
+    // Need this ThreadsListHandle for converting Java thread IDs into
+    // threadObj handles; dump_result->set_t_list() is called in the
+    // VM op below so we can't use it yet.
+    ThreadsListHandle tlh;
     for (int i = 0; i < num_threads; i++) {
       jlong tid = ids_ah->long_at(i);
-      JavaThread* jt = Threads::find_java_thread_from_java_tid(tid);
+      JavaThread* jt = tlh.list()->find_JavaThread_from_java_tid(tid);
       oop thread_obj = (jt != NULL ? jt->threadObj() : (oop)NULL);
       instanceHandle threadObj_h(THREAD, (instanceOop) thread_obj);
       thread_handle_array->append(threadObj_h);
@@ -1101,22 +1106,21 @@ JVM_ENTRY(jint, jmm_GetThreadInfo(JNIEnv *env, jlongArray ids, jint maxDepth, jo
   ThreadDumpResult dump_result(num_threads);
 
   if (maxDepth == 0) {
-    // no stack trace dumped - do not need to stop the world
-    {
-      MutexLockerEx ml(Threads_lock);
-      for (int i = 0; i < num_threads; i++) {
-        jlong tid = ids_ah->long_at(i);
-        JavaThread* jt = Threads::find_java_thread_from_java_tid(tid);
-        ThreadSnapshot* ts;
-        if (jt == NULL) {
-          // if the thread does not exist or now it is terminated,
-          // create dummy snapshot
-          ts = new ThreadSnapshot();
-        } else {
-          ts = new ThreadSnapshot(jt);
-        }
-        dump_result.add_thread_snapshot(ts);
+    // No stack trace to dump so we do not need to stop the world.
+    // Since we never do the VM op here we must set the threads list.
+    dump_result.set_t_list();
+    for (int i = 0; i < num_threads; i++) {
+      jlong tid = ids_ah->long_at(i);
+      JavaThread* jt = dump_result.t_list()->find_JavaThread_from_java_tid(tid);
+      ThreadSnapshot* ts;
+      if (jt == NULL) {
+        // if the thread does not exist or now it is terminated,
+        // create dummy snapshot
+        ts = new ThreadSnapshot();
+      } else {
+        ts = new ThreadSnapshot(dump_result.t_list(), jt);
       }
+      dump_result.add_thread_snapshot(ts);
     }
   } else {
     // obtain thread dump with the specific list of threads with stack trace
@@ -1131,6 +1135,7 @@ JVM_ENTRY(jint, jmm_GetThreadInfo(JNIEnv *env, jlongArray ids, jint maxDepth, jo
 
   int num_snapshots = dump_result.num_snapshots();
   assert(num_snapshots == num_threads, "Must match the number of thread snapshots");
+  assert(num_snapshots == 0 || dump_result.t_list_has_been_set(), "ThreadsList must have been set if we have a snapshot");
   int index = 0;
   for (ThreadSnapshot* ts = dump_result.snapshots(); ts != NULL; index++, ts = ts->next()) {
     // For each thread, create an java/lang/management/ThreadInfo object
@@ -1196,6 +1201,7 @@ JVM_ENTRY(jobjectArray, jmm_DumpThreads(JNIEnv *env, jlongArray thread_ids, jboo
   }
 
   int num_snapshots = dump_result.num_snapshots();
+  assert(num_snapshots == 0 || dump_result.t_list_has_been_set(), "ThreadsList must have been set if we have a snapshot");
 
   // create the result ThreadInfo[] object
   InstanceKlass* ik = Management::java_lang_management_ThreadInfo_klass(CHECK_NULL);
@@ -1319,10 +1325,10 @@ JVM_ENTRY(jboolean, jmm_ResetStatistic(JNIEnv *env, jvalue obj, jmmStatisticType
       }
 
       // Look for the JavaThread of this given tid
-      MutexLockerEx ml(Threads_lock);
+      JavaThreadIteratorWithHandle jtiwh;
       if (tid == 0) {
         // reset contention statistics for all threads if tid == 0
-        for (JavaThread* java_thread = Threads::first(); java_thread != NULL; java_thread = java_thread->next()) {
+        for (; JavaThread *java_thread = jtiwh.next(); ) {
           if (type == JMM_STAT_THREAD_CONTENTION_COUNT) {
             ThreadService::reset_contention_count_stat(java_thread);
           } else {
@@ -1331,7 +1337,7 @@ JVM_ENTRY(jboolean, jmm_ResetStatistic(JNIEnv *env, jvalue obj, jmmStatisticType
         }
       } else {
         // reset contention statistics for a given thread
-        JavaThread* java_thread = Threads::find_java_thread_from_java_tid(tid);
+        JavaThread* java_thread = jtiwh.list()->find_JavaThread_from_java_tid(tid);
         if (java_thread == NULL) {
           return false;
         }
@@ -1399,8 +1405,8 @@ JVM_ENTRY(jlong, jmm_GetThreadCpuTime(JNIEnv *env, jlong thread_id))
     // current thread
     return os::current_thread_cpu_time();
   } else {
-    MutexLockerEx ml(Threads_lock);
-    java_thread = Threads::find_java_thread_from_java_tid(thread_id);
+    ThreadsListHandle tlh;
+    java_thread = tlh.list()->find_JavaThread_from_java_tid(thread_id);
     if (java_thread != NULL) {
       return os::thread_cpu_time((Thread*) java_thread);
     }
@@ -1649,6 +1655,7 @@ ThreadTimesClosure::ThreadTimesClosure(objArrayHandle names,
 // Called with Threads_lock held
 //
 void ThreadTimesClosure::do_thread(Thread* thread) {
+  assert(Threads_lock->owned_by_self(), "Must hold Threads_lock");
   assert(thread != NULL, "thread was NULL");
 
   // exclude externally visible JavaThreads
@@ -2109,9 +2116,9 @@ JVM_ENTRY(void, jmm_GetThreadAllocatedMemory(JNIEnv *env, jlongArray ids,
               "the given array of thread IDs");
   }
 
-  MutexLockerEx ml(Threads_lock);
+  ThreadsListHandle tlh;
   for (int i = 0; i < num_threads; i++) {
-    JavaThread* java_thread = Threads::find_java_thread_from_java_tid(ids_ah->long_at(i));
+    JavaThread* java_thread = tlh.list()->find_JavaThread_from_java_tid(ids_ah->long_at(i));
     if (java_thread != NULL) {
       sizeArray_h->long_at_put(i, java_thread->cooked_allocated_bytes());
     }
@@ -2138,8 +2145,8 @@ JVM_ENTRY(jlong, jmm_GetThreadCpuTimeWithKind(JNIEnv *env, jlong thread_id, jboo
     // current thread
     return os::current_thread_cpu_time(user_sys_cpu_time != 0);
   } else {
-    MutexLockerEx ml(Threads_lock);
-    java_thread = Threads::find_java_thread_from_java_tid(thread_id);
+    ThreadsListHandle tlh;
+    java_thread = tlh.list()->find_JavaThread_from_java_tid(thread_id);
     if (java_thread != NULL) {
       return os::thread_cpu_time((Thread*) java_thread, user_sys_cpu_time != 0);
     }
@@ -2180,9 +2187,9 @@ JVM_ENTRY(void, jmm_GetThreadCpuTimesWithKind(JNIEnv *env, jlongArray ids,
               "the given array of thread IDs");
   }
 
-  MutexLockerEx ml(Threads_lock);
+  ThreadsListHandle tlh;
   for (int i = 0; i < num_threads; i++) {
-    JavaThread* java_thread = Threads::find_java_thread_from_java_tid(ids_ah->long_at(i));
+    JavaThread* java_thread = tlh.list()->find_JavaThread_from_java_tid(ids_ah->long_at(i));
     if (java_thread != NULL) {
       timeArray_h->long_at_put(i, os::thread_cpu_time((Thread*)java_thread,
                                                       user_sys_cpu_time != 0));
diff --git a/src/hotspot/share/services/management.hpp b/src/hotspot/share/services/management.hpp
index f1168166281..4f475060d67 100644
--- a/src/hotspot/share/services/management.hpp
+++ b/src/hotspot/share/services/management.hpp
@@ -25,10 +25,10 @@
 #ifndef SHARE_VM_SERVICES_MANAGEMENT_HPP
 #define SHARE_VM_SERVICES_MANAGEMENT_HPP
 
+#include "jmm.h"
 #include "memory/allocation.hpp"
 #include "runtime/handles.hpp"
 #include "runtime/timer.hpp"
-#include "services/jmm.h"
 
 class OopClosure;
 class ThreadSnapshot;
diff --git a/src/hotspot/share/services/memoryManager.cpp b/src/hotspot/share/services/memoryManager.cpp
index 8c6fc6d6f1a..5bc16bfd837 100644
--- a/src/hotspot/share/services/memoryManager.cpp
+++ b/src/hotspot/share/services/memoryManager.cpp
@@ -37,7 +37,7 @@
 #include "services/gcNotifier.hpp"
 #include "utilities/dtrace.hpp"
 
-MemoryManager::MemoryManager() {
+MemoryManager::MemoryManager(const char* name) : _name(name) {
   _num_pools = 0;
   (void)const_cast<instanceOop&>(_memory_mgr_obj = instanceOop(NULL));
 }
@@ -52,43 +52,11 @@ void MemoryManager::add_pool(MemoryPool* pool) {
 }
 
 MemoryManager* MemoryManager::get_code_cache_memory_manager() {
-  return (MemoryManager*) new CodeCacheMemoryManager();
+  return new MemoryManager("CodeCacheManager");
 }
 
 MemoryManager* MemoryManager::get_metaspace_memory_manager() {
-  return (MemoryManager*) new MetaspaceMemoryManager();
-}
-
-GCMemoryManager* MemoryManager::get_copy_memory_manager() {
-  return (GCMemoryManager*) new CopyMemoryManager();
-}
-
-GCMemoryManager* MemoryManager::get_msc_memory_manager() {
-  return (GCMemoryManager*) new MSCMemoryManager();
-}
-
-GCMemoryManager* MemoryManager::get_parnew_memory_manager() {
-  return (GCMemoryManager*) new ParNewMemoryManager();
-}
-
-GCMemoryManager* MemoryManager::get_cms_memory_manager() {
-  return (GCMemoryManager*) new CMSMemoryManager();
-}
-
-GCMemoryManager* MemoryManager::get_psScavenge_memory_manager() {
-  return (GCMemoryManager*) new PSScavengeMemoryManager();
-}
-
-GCMemoryManager* MemoryManager::get_psMarkSweep_memory_manager() {
-  return (GCMemoryManager*) new PSMarkSweepMemoryManager();
-}
-
-GCMemoryManager* MemoryManager::get_g1YoungGen_memory_manager() {
-  return (GCMemoryManager*) new G1YoungGenMemoryManager();
-}
-
-GCMemoryManager* MemoryManager::get_g1OldGen_memory_manager() {
-  return (GCMemoryManager*) new G1OldGenMemoryManager();
+  return new MemoryManager("Metaspace Manager");
 }
 
 instanceOop MemoryManager::get_memory_manager_instance(TRAPS) {
@@ -203,7 +171,8 @@ void GCStatInfo::clear() {
 }
 
 
-GCMemoryManager::GCMemoryManager() : MemoryManager() {
+GCMemoryManager::GCMemoryManager(const char* name, const char* gc_end_message) :
+  MemoryManager(name), _gc_end_message(gc_end_message) {
   _num_collections = 0;
   _last_gc_stat = NULL;
   _last_gc_lock = new Mutex(Mutex::leaf, "_last_gc_lock", true,
@@ -308,9 +277,7 @@ void GCMemoryManager::gc_end(bool recordPostGCUsage,
     }
 
     if (is_notification_enabled()) {
-      bool isMajorGC = this == MemoryService::get_major_gc_manager();
-      GCNotifier::pushNotification(this, isMajorGC ? "end of major GC" : "end of minor GC",
-                                   GCCause::to_string(cause));
+      GCNotifier::pushNotification(this, _gc_end_message, GCCause::to_string(cause));
     }
   }
 }
diff --git a/src/hotspot/share/services/memoryManager.hpp b/src/hotspot/share/services/memoryManager.hpp
index f2a7d4c3420..7d8ef854813 100644
--- a/src/hotspot/share/services/memoryManager.hpp
+++ b/src/hotspot/share/services/memoryManager.hpp
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2017, 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
@@ -25,7 +25,10 @@
 #ifndef SHARE_VM_SERVICES_MEMORYMANAGER_HPP
 #define SHARE_VM_SERVICES_MEMORYMANAGER_HPP
 
+#include "gc/shared/gcCause.hpp"
 #include "memory/allocation.hpp"
+#include "oops/oopsHierarchy.hpp"
+#include "runtime/handles.hpp"
 #include "runtime/timer.hpp"
 #include "services/memoryUsage.hpp"
 
@@ -49,11 +52,13 @@ private:
   MemoryPool* _pools[max_num_pools];
   int         _num_pools;
 
+  const char* _name;
+
 protected:
   volatile instanceOop _memory_mgr_obj;
 
 public:
-  MemoryManager();
+  MemoryManager(const char* name);
 
   int num_memory_pools() const           { return _num_pools; }
   MemoryPool* get_memory_pool(int index) {
@@ -67,7 +72,8 @@ public:
 
   virtual instanceOop get_memory_manager_instance(TRAPS);
   virtual bool is_gc_memory_manager()    { return false; }
-  virtual const char* name() = 0;
+
+  const char* name() const { return _name; }
 
   // GC support
   void oops_do(OopClosure* f);
@@ -75,29 +81,6 @@ public:
   // Static factory methods to get a memory manager of a specific type
   static MemoryManager*   get_code_cache_memory_manager();
   static MemoryManager*   get_metaspace_memory_manager();
-  static GCMemoryManager* get_copy_memory_manager();
-  static GCMemoryManager* get_msc_memory_manager();
-  static GCMemoryManager* get_parnew_memory_manager();
-  static GCMemoryManager* get_cms_memory_manager();
-  static GCMemoryManager* get_psScavenge_memory_manager();
-  static GCMemoryManager* get_psMarkSweep_memory_manager();
-  static GCMemoryManager* get_g1YoungGen_memory_manager();
-  static GCMemoryManager* get_g1OldGen_memory_manager();
-};
-
-class CodeCacheMemoryManager : public MemoryManager {
-private:
-public:
-  CodeCacheMemoryManager() : MemoryManager() {}
-
-  const char* name() { return "CodeCacheManager"; }
-};
-
-class MetaspaceMemoryManager : public MemoryManager {
-public:
-  MetaspaceMemoryManager() : MemoryManager() {}
-
-  const char* name() { return "Metaspace Manager"; }
 };
 
 class GCStatInfo : public ResourceObj {
@@ -159,8 +142,9 @@ private:
   GCStatInfo*  _current_gc_stat;
   int          _num_gc_threads;
   volatile bool _notification_enabled;
+  const char* _gc_end_message;
 public:
-  GCMemoryManager();
+  GCMemoryManager(const char* name, const char* gc_end_message);
   ~GCMemoryManager();
 
   void   initialize_gc_stat_info();
@@ -186,71 +170,4 @@ public:
   bool is_notification_enabled() { return _notification_enabled; }
 };
 
-// These subclasses of GCMemoryManager are defined to include
-// GC-specific information.
-// TODO: Add GC-specific information
-class CopyMemoryManager : public GCMemoryManager {
-private:
-public:
-  CopyMemoryManager() : GCMemoryManager() {}
-
-  const char* name() { return "Copy"; }
-};
-
-class MSCMemoryManager : public GCMemoryManager {
-private:
-public:
-  MSCMemoryManager() : GCMemoryManager() {}
-
-  const char* name() { return "MarkSweepCompact"; }
-};
-
-class ParNewMemoryManager : public GCMemoryManager {
-private:
-public:
-  ParNewMemoryManager() : GCMemoryManager() {}
-
-  const char* name() { return "ParNew"; }
-};
-
-class CMSMemoryManager : public GCMemoryManager {
-private:
-public:
-  CMSMemoryManager() : GCMemoryManager() {}
-
-  const char* name() { return "ConcurrentMarkSweep";}
-};
-
-class PSScavengeMemoryManager : public GCMemoryManager {
-private:
-public:
-  PSScavengeMemoryManager() : GCMemoryManager() {}
-
-  const char* name() { return "PS Scavenge"; }
-};
-
-class PSMarkSweepMemoryManager : public GCMemoryManager {
-private:
-public:
-  PSMarkSweepMemoryManager() : GCMemoryManager() {}
-
-  const char* name() { return "PS MarkSweep"; }
-};
-
-class G1YoungGenMemoryManager : public GCMemoryManager {
-private:
-public:
-  G1YoungGenMemoryManager() : GCMemoryManager() {}
-
-  const char* name() { return "G1 Young Generation"; }
-};
-
-class G1OldGenMemoryManager : public GCMemoryManager {
-private:
-public:
-  G1OldGenMemoryManager() : GCMemoryManager() {}
-
-  const char* name() { return "G1 Old Generation"; }
-};
-
 #endif // SHARE_VM_SERVICES_MEMORYMANAGER_HPP
diff --git a/src/hotspot/share/services/memoryPool.cpp b/src/hotspot/share/services/memoryPool.cpp
index cec78ae67a4..8248b472225 100644
--- a/src/hotspot/share/services/memoryPool.cpp
+++ b/src/hotspot/share/services/memoryPool.cpp
@@ -25,8 +25,6 @@
 #include "precompiled.hpp"
 #include "classfile/systemDictionary.hpp"
 #include "classfile/vmSymbols.hpp"
-#include "gc/serial/defNewGeneration.hpp"
-#include "gc/shared/space.hpp"
 #include "memory/metaspace.hpp"
 #include "oops/oop.inline.hpp"
 #include "runtime/handles.inline.hpp"
@@ -38,9 +36,6 @@
 #include "services/memoryPool.hpp"
 #include "utilities/globalDefinitions.hpp"
 #include "utilities/macros.hpp"
-#if INCLUDE_ALL_GCS
-#include "gc/cms/compactibleFreeListSpace.hpp"
-#endif
 
 MemoryPool::MemoryPool(const char* name,
                        PoolType type,
@@ -182,95 +177,6 @@ void MemoryPool::oops_do(OopClosure* f) {
   }
 }
 
-ContiguousSpacePool::ContiguousSpacePool(ContiguousSpace* space,
-                                         const char* name,
-                                         PoolType type,
-                                         size_t max_size,
-                                         bool support_usage_threshold) :
-  CollectedMemoryPool(name, type, space->capacity(), max_size,
-                      support_usage_threshold), _space(space) {
-}
-
-size_t ContiguousSpacePool::used_in_bytes() {
-  return space()->used();
-}
-
-MemoryUsage ContiguousSpacePool::get_memory_usage() {
-  size_t maxSize   = (available_for_allocation() ? max_size() : 0);
-  size_t used      = used_in_bytes();
-  size_t committed = _space->capacity();
-
-  return MemoryUsage(initial_size(), used, committed, maxSize);
-}
-
-SurvivorContiguousSpacePool::SurvivorContiguousSpacePool(DefNewGeneration* young_gen,
-                                                         const char* name,
-                                                         PoolType type,
-                                                         size_t max_size,
-                                                         bool support_usage_threshold) :
-  CollectedMemoryPool(name, type, young_gen->from()->capacity(), max_size,
-                      support_usage_threshold), _young_gen(young_gen) {
-}
-
-size_t SurvivorContiguousSpacePool::used_in_bytes() {
-  return _young_gen->from()->used();
-}
-
-size_t SurvivorContiguousSpacePool::committed_in_bytes() {
-  return _young_gen->from()->capacity();
-}
-
-MemoryUsage SurvivorContiguousSpacePool::get_memory_usage() {
-  size_t maxSize = (available_for_allocation() ? max_size() : 0);
-  size_t used    = used_in_bytes();
-  size_t committed = committed_in_bytes();
-
-  return MemoryUsage(initial_size(), used, committed, maxSize);
-}
-
-#if INCLUDE_ALL_GCS
-CompactibleFreeListSpacePool::CompactibleFreeListSpacePool(CompactibleFreeListSpace* space,
-                                                           const char* name,
-                                                           PoolType type,
-                                                           size_t max_size,
-                                                           bool support_usage_threshold) :
-  CollectedMemoryPool(name, type, space->capacity(), max_size,
-                      support_usage_threshold), _space(space) {
-}
-
-size_t CompactibleFreeListSpacePool::used_in_bytes() {
-  return _space->used();
-}
-
-MemoryUsage CompactibleFreeListSpacePool::get_memory_usage() {
-  size_t maxSize   = (available_for_allocation() ? max_size() : 0);
-  size_t used      = used_in_bytes();
-  size_t committed = _space->capacity();
-
-  return MemoryUsage(initial_size(), used, committed, maxSize);
-}
-#endif // INCLUDE_ALL_GCS
-
-GenerationPool::GenerationPool(Generation* gen,
-                               const char* name,
-                               PoolType type,
-                               bool support_usage_threshold) :
-  CollectedMemoryPool(name, type, gen->capacity(), gen->max_capacity(),
-                      support_usage_threshold), _gen(gen) {
-}
-
-size_t GenerationPool::used_in_bytes() {
-  return _gen->used();
-}
-
-MemoryUsage GenerationPool::get_memory_usage() {
-  size_t used      = used_in_bytes();
-  size_t committed = _gen->capacity();
-  size_t maxSize   = (available_for_allocation() ? max_size() : 0);
-
-  return MemoryUsage(initial_size(), used, committed, maxSize);
-}
-
 CodeHeapPool::CodeHeapPool(CodeHeap* codeHeap, const char* name, bool support_usage_threshold) :
   MemoryPool(name, NonHeap, codeHeap->capacity(), codeHeap->max_capacity(),
              support_usage_threshold, false), _codeHeap(codeHeap) {
diff --git a/src/hotspot/share/services/memoryPool.hpp b/src/hotspot/share/services/memoryPool.hpp
index 0144f61786c..b1c21580b43 100644
--- a/src/hotspot/share/services/memoryPool.hpp
+++ b/src/hotspot/share/services/memoryPool.hpp
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2003, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2017, 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
@@ -37,12 +37,8 @@
 // both heap and non-heap memory.
 
 // Forward declaration
-class CompactibleFreeListSpace;
-class ContiguousSpace;
 class MemoryManager;
 class SensorInfo;
-class Generation;
-class DefNewGeneration;
 class ThresholdSupport;
 
 class MemoryPool : public CHeapObj<mtInternal> {
@@ -144,67 +140,11 @@ class MemoryPool : public CHeapObj<mtInternal> {
 
 class CollectedMemoryPool : public MemoryPool {
 public:
-  CollectedMemoryPool(const char* name, PoolType type, size_t init_size, size_t max_size, bool support_usage_threshold) :
-    MemoryPool(name, type, init_size, max_size, support_usage_threshold, true) {};
+  CollectedMemoryPool(const char* name, size_t init_size, size_t max_size, bool support_usage_threshold) :
+    MemoryPool(name, MemoryPool::Heap, init_size, max_size, support_usage_threshold, true) {};
   bool is_collected_pool()            { return true; }
 };
 
-class ContiguousSpacePool : public CollectedMemoryPool {
-private:
-  ContiguousSpace* _space;
-
-public:
-  ContiguousSpacePool(ContiguousSpace* space, const char* name, PoolType type, size_t max_size, bool support_usage_threshold);
-
-  ContiguousSpace* space()              { return _space; }
-  MemoryUsage get_memory_usage();
-  size_t used_in_bytes();
-};
-
-class SurvivorContiguousSpacePool : public CollectedMemoryPool {
-private:
-  DefNewGeneration* _young_gen;
-
-public:
-  SurvivorContiguousSpacePool(DefNewGeneration* young_gen,
-                              const char* name,
-                              PoolType type,
-                              size_t max_size,
-                              bool support_usage_threshold);
-
-  MemoryUsage get_memory_usage();
-
-  size_t used_in_bytes();
-  size_t committed_in_bytes();
-};
-
-#if INCLUDE_ALL_GCS
-class CompactibleFreeListSpacePool : public CollectedMemoryPool {
-private:
-  CompactibleFreeListSpace* _space;
-public:
-  CompactibleFreeListSpacePool(CompactibleFreeListSpace* space,
-                               const char* name,
-                               PoolType type,
-                               size_t max_size,
-                               bool support_usage_threshold);
-
-  MemoryUsage get_memory_usage();
-  size_t used_in_bytes();
-};
-#endif // INCLUDE_ALL_GCS
-
-
-class GenerationPool : public CollectedMemoryPool {
-private:
-  Generation* _gen;
-public:
-  GenerationPool(Generation* gen, const char* name, PoolType type, bool support_usage_threshold);
-
-  MemoryUsage get_memory_usage();
-  size_t used_in_bytes();
-};
-
 class CodeHeapPool: public MemoryPool {
 private:
   CodeHeap* _codeHeap;
diff --git a/src/hotspot/share/services/memoryService.cpp b/src/hotspot/share/services/memoryService.cpp
index d579cd91738..e96e2a0d151 100644
--- a/src/hotspot/share/services/memoryService.cpp
+++ b/src/hotspot/share/services/memoryService.cpp
@@ -25,13 +25,7 @@
 #include "precompiled.hpp"
 #include "classfile/systemDictionary.hpp"
 #include "classfile/vmSymbols.hpp"
-#include "gc/parallel/mutableSpace.hpp"
-#include "gc/serial/defNewGeneration.hpp"
-#include "gc/serial/tenuredGeneration.hpp"
-#include "gc/shared/collectorPolicy.hpp"
-#include "gc/shared/genCollectedHeap.hpp"
-#include "gc/shared/generation.hpp"
-#include "gc/shared/generationSpec.hpp"
+#include "gc/shared/collectedHeap.hpp"
 #include "logging/logConfiguration.hpp"
 #include "memory/heap.hpp"
 #include "memory/memRegion.hpp"
@@ -46,24 +40,12 @@
 #include "services/memoryService.hpp"
 #include "utilities/growableArray.hpp"
 #include "utilities/macros.hpp"
-#if INCLUDE_ALL_GCS
-#include "gc/cms/concurrentMarkSweepGeneration.hpp"
-#include "gc/cms/parNewGeneration.hpp"
-#include "gc/g1/g1CollectedHeap.inline.hpp"
-#include "gc/parallel/parallelScavengeHeap.hpp"
-#include "gc/parallel/psOldGen.hpp"
-#include "gc/parallel/psYoungGen.hpp"
-#include "services/g1MemoryPool.hpp"
-#include "services/psMemoryPool.hpp"
-#endif // INCLUDE_ALL_GCS
 
 GrowableArray<MemoryPool*>* MemoryService::_pools_list =
   new (ResourceObj::C_HEAP, mtInternal) GrowableArray<MemoryPool*>(init_pools_list_size, true);
 GrowableArray<MemoryManager*>* MemoryService::_managers_list =
   new (ResourceObj::C_HEAP, mtInternal) GrowableArray<MemoryManager*>(init_managers_list_size, true);
 
-GCMemoryManager* MemoryService::_minor_gc_manager      = NULL;
-GCMemoryManager* MemoryService::_major_gc_manager      = NULL;
 MemoryManager*   MemoryService::_code_cache_manager    = NULL;
 GrowableArray<MemoryPool*>* MemoryService::_code_heap_pools =
     new (ResourceObj::C_HEAP, mtInternal) GrowableArray<MemoryPool*>(init_code_heap_pools_size, true);
@@ -84,311 +66,28 @@ void GcThreadCountClosure::do_thread(Thread* thread) {
 }
 
 void MemoryService::set_universe_heap(CollectedHeap* heap) {
-  CollectedHeap::Name kind = heap->kind();
-  switch (kind) {
-    case CollectedHeap::SerialHeap :
-    case CollectedHeap::CMSHeap : {
-      add_gen_collected_heap_info(GenCollectedHeap::heap());
-      break;
-    }
-#if INCLUDE_ALL_GCS
-    case CollectedHeap::ParallelScavengeHeap : {
-      add_parallel_scavenge_heap_info(ParallelScavengeHeap::heap());
-      break;
-    }
-    case CollectedHeap::G1CollectedHeap : {
-      add_g1_heap_info(G1CollectedHeap::heap());
-      break;
-    }
-#endif // INCLUDE_ALL_GCS
-    default: {
-      guarantee(false, "Unrecognized kind of heap");
-    }
-  }
+  ResourceMark rm; // For internal allocations in GrowableArray.
+
+  GrowableArray<MemoryPool*> gc_mem_pools = heap->memory_pools();
+  _pools_list->appendAll(&gc_mem_pools);
 
   // set the GC thread count
   GcThreadCountClosure gctcc;
   heap->gc_threads_do(&gctcc);
   int count = gctcc.count();
-  if (count > 0) {
-    _minor_gc_manager->set_num_gc_threads(count);
-    _major_gc_manager->set_num_gc_threads(count);
-  }
 
-  // All memory pools and memory managers are initialized.
-  //
-  _minor_gc_manager->initialize_gc_stat_info();
-  _major_gc_manager->initialize_gc_stat_info();
-}
+  GrowableArray<GCMemoryManager*> gc_memory_managers = heap->memory_managers();
+  for (int i = 0; i < gc_memory_managers.length(); i++) {
+    GCMemoryManager* gc_manager = gc_memory_managers.at(i);
 
-// Add memory pools for GenCollectedHeap
-// This function currently only supports two generations collected heap.
-// The collector for GenCollectedHeap will have two memory managers.
-void MemoryService::add_gen_collected_heap_info(GenCollectedHeap* heap) {
-  CollectorPolicy* policy = heap->collector_policy();
-
-  assert(policy->is_generation_policy(), "Only support two generations");
-  GenCollectorPolicy* gen_policy = policy->as_generation_policy();
-  if (gen_policy != NULL) {
-    Generation::Name kind = gen_policy->young_gen_spec()->name();
-    switch (kind) {
-      case Generation::DefNew:
-        _minor_gc_manager = MemoryManager::get_copy_memory_manager();
-        break;
-#if INCLUDE_ALL_GCS
-      case Generation::ParNew:
-        _minor_gc_manager = MemoryManager::get_parnew_memory_manager();
-        break;
-#endif // INCLUDE_ALL_GCS
-      default:
-        guarantee(false, "Unrecognized generation spec");
-        break;
-    }
-    if (policy->is_mark_sweep_policy()) {
-      _major_gc_manager = MemoryManager::get_msc_memory_manager();
-#if INCLUDE_ALL_GCS
-    } else if (policy->is_concurrent_mark_sweep_policy()) {
-      _major_gc_manager = MemoryManager::get_cms_memory_manager();
-#endif // INCLUDE_ALL_GCS
-    } else {
-      guarantee(false, "Unknown two-gen policy");
-    }
-  } else {
-    guarantee(false, "Non two-gen policy");
-  }
-  _managers_list->append(_minor_gc_manager);
-  _managers_list->append(_major_gc_manager);
-
-  add_generation_memory_pool(heap->young_gen(), _major_gc_manager, _minor_gc_manager);
-  add_generation_memory_pool(heap->old_gen(), _major_gc_manager);
-}
-
-#if INCLUDE_ALL_GCS
-// Add memory pools for ParallelScavengeHeap
-// This function currently only supports two generations collected heap.
-// The collector for ParallelScavengeHeap will have two memory managers.
-void MemoryService::add_parallel_scavenge_heap_info(ParallelScavengeHeap* heap) {
-  // Two managers to keep statistics about _minor_gc_manager and _major_gc_manager GC.
-  _minor_gc_manager = MemoryManager::get_psScavenge_memory_manager();
-  _major_gc_manager = MemoryManager::get_psMarkSweep_memory_manager();
-  _managers_list->append(_minor_gc_manager);
-  _managers_list->append(_major_gc_manager);
-
-  add_psYoung_memory_pool(heap->young_gen(), _major_gc_manager, _minor_gc_manager);
-  add_psOld_memory_pool(heap->old_gen(), _major_gc_manager);
-}
-
-void MemoryService::add_g1_heap_info(G1CollectedHeap* g1h) {
-  assert(UseG1GC, "sanity");
-
-  _minor_gc_manager = MemoryManager::get_g1YoungGen_memory_manager();
-  _major_gc_manager = MemoryManager::get_g1OldGen_memory_manager();
-  _managers_list->append(_minor_gc_manager);
-  _managers_list->append(_major_gc_manager);
-
-  add_g1YoungGen_memory_pool(g1h, _major_gc_manager, _minor_gc_manager);
-  add_g1OldGen_memory_pool(g1h, _major_gc_manager);
-}
-#endif // INCLUDE_ALL_GCS
-
-MemoryPool* MemoryService::add_gen(Generation* gen,
-                                   const char* name,
-                                   bool is_heap,
-                                   bool support_usage_threshold) {
-
-  MemoryPool::PoolType type = (is_heap ? MemoryPool::Heap : MemoryPool::NonHeap);
-  GenerationPool* pool = new GenerationPool(gen, name, type, support_usage_threshold);
-  _pools_list->append(pool);
-  return (MemoryPool*) pool;
-}
-
-MemoryPool* MemoryService::add_space(ContiguousSpace* space,
-                                     const char* name,
-                                     bool is_heap,
-                                     size_t max_size,
-                                     bool support_usage_threshold) {
-  MemoryPool::PoolType type = (is_heap ? MemoryPool::Heap : MemoryPool::NonHeap);
-  ContiguousSpacePool* pool = new ContiguousSpacePool(space, name, type, max_size, support_usage_threshold);
-
-  _pools_list->append(pool);
-  return (MemoryPool*) pool;
-}
-
-MemoryPool* MemoryService::add_survivor_spaces(DefNewGeneration* young_gen,
-                                               const char* name,
-                                               bool is_heap,
-                                               size_t max_size,
-                                               bool support_usage_threshold) {
-  MemoryPool::PoolType type = (is_heap ? MemoryPool::Heap : MemoryPool::NonHeap);
-  SurvivorContiguousSpacePool* pool = new SurvivorContiguousSpacePool(young_gen, name, type, max_size, support_usage_threshold);
-
-  _pools_list->append(pool);
-  return (MemoryPool*) pool;
-}
-
-#if INCLUDE_ALL_GCS
-MemoryPool* MemoryService::add_cms_space(CompactibleFreeListSpace* space,
-                                         const char* name,
-                                         bool is_heap,
-                                         size_t max_size,
-                                         bool support_usage_threshold) {
-  MemoryPool::PoolType type = (is_heap ? MemoryPool::Heap : MemoryPool::NonHeap);
-  CompactibleFreeListSpacePool* pool = new CompactibleFreeListSpacePool(space, name, type, max_size, support_usage_threshold);
-  _pools_list->append(pool);
-  return (MemoryPool*) pool;
-}
-#endif // INCLUDE_ALL_GCS
-
-// Add memory pool(s) for one generation
-void MemoryService::add_generation_memory_pool(Generation* gen,
-                                               MemoryManager* major_mgr,
-                                               MemoryManager* minor_mgr) {
-  guarantee(gen != NULL, "No generation for memory pool");
-  Generation::Name kind = gen->kind();
-  int index = _pools_list->length();
-
-  switch (kind) {
-    case Generation::DefNew: {
-      assert(major_mgr != NULL && minor_mgr != NULL, "Should have two managers");
-      DefNewGeneration* young_gen = (DefNewGeneration*) gen;
-      // Add a memory pool for each space and young gen doesn't
-      // support low memory detection as it is expected to get filled up.
-      MemoryPool* eden = add_space(young_gen->eden(),
-                                   "Eden Space",
-                                   true, /* is_heap */
-                                   young_gen->max_eden_size(),
-                                   false /* support_usage_threshold */);
-      MemoryPool* survivor = add_survivor_spaces(young_gen,
-                                                 "Survivor Space",
-                                                 true, /* is_heap */
-                                                 young_gen->max_survivor_size(),
-                                                 false /* support_usage_threshold */);
-      break;
-    }
-
-#if INCLUDE_ALL_GCS
-    case Generation::ParNew:
-    {
-      assert(major_mgr != NULL && minor_mgr != NULL, "Should have two managers");
-      // Add a memory pool for each space and young gen doesn't
-      // support low memory detection as it is expected to get filled up.
-      ParNewGeneration* parnew_gen = (ParNewGeneration*) gen;
-      MemoryPool* eden = add_space(parnew_gen->eden(),
-                                   "Par Eden Space",
-                                   true /* is_heap */,
-                                   parnew_gen->max_eden_size(),
-                                   false /* support_usage_threshold */);
-      MemoryPool* survivor = add_survivor_spaces(parnew_gen,
-                                                 "Par Survivor Space",
-                                                 true, /* is_heap */
-                                                 parnew_gen->max_survivor_size(),
-                                                 false /* support_usage_threshold */);
-
-      break;
-    }
-#endif // INCLUDE_ALL_GCS
-
-    case Generation::MarkSweepCompact: {
-      assert(major_mgr != NULL && minor_mgr == NULL, "Should have only one manager");
-      add_gen(gen,
-              "Tenured Gen",
-              true, /* is_heap */
-              true  /* support_usage_threshold */);
-      break;
-    }
-
-#if INCLUDE_ALL_GCS
-    case Generation::ConcurrentMarkSweep:
-    {
-      assert(major_mgr != NULL && minor_mgr == NULL, "Should have only one manager");
-      ConcurrentMarkSweepGeneration* cms = (ConcurrentMarkSweepGeneration*) gen;
-      MemoryPool* pool = add_cms_space(cms->cmsSpace(),
-                                       "CMS Old Gen",
-                                       true, /* is_heap */
-                                       cms->reserved().byte_size(),
-                                       true  /* support_usage_threshold */);
-      break;
-    }
-#endif // INCLUDE_ALL_GCS
-
-    default:
-      assert(false, "should not reach here");
-      // no memory pool added for others
-      break;
-  }
-
-  assert(major_mgr != NULL, "Should have at least one manager");
-  // Link managers and the memory pools together
-  for (int i = index; i < _pools_list->length(); i++) {
-    MemoryPool* pool = _pools_list->at(i);
-    major_mgr->add_pool(pool);
-    if (minor_mgr != NULL) {
-      minor_mgr->add_pool(pool);
+    if (count > 0) {
+      gc_manager->set_num_gc_threads(count);
     }
+    gc_manager->initialize_gc_stat_info();
+    _managers_list->append(gc_manager);
   }
 }
 
-
-#if INCLUDE_ALL_GCS
-void MemoryService::add_psYoung_memory_pool(PSYoungGen* young_gen, MemoryManager* major_mgr, MemoryManager* minor_mgr) {
-  assert(major_mgr != NULL && minor_mgr != NULL, "Should have two managers");
-
-  // Add a memory pool for each space and young gen doesn't
-  // support low memory detection as it is expected to get filled up.
-  EdenMutableSpacePool* eden = new EdenMutableSpacePool(young_gen,
-                                                        young_gen->eden_space(),
-                                                        "PS Eden Space",
-                                                        MemoryPool::Heap,
-                                                        false /* support_usage_threshold */);
-
-  SurvivorMutableSpacePool* survivor = new SurvivorMutableSpacePool(young_gen,
-                                                                    "PS Survivor Space",
-                                                                    MemoryPool::Heap,
-                                                                    false /* support_usage_threshold */);
-
-  major_mgr->add_pool(eden);
-  major_mgr->add_pool(survivor);
-  minor_mgr->add_pool(eden);
-  minor_mgr->add_pool(survivor);
-  _pools_list->append(eden);
-  _pools_list->append(survivor);
-}
-
-void MemoryService::add_psOld_memory_pool(PSOldGen* old_gen, MemoryManager* mgr) {
-  PSGenerationPool* old_gen_pool = new PSGenerationPool(old_gen,
-                                                       "PS Old Gen",
-                                                       MemoryPool::Heap,
-                                                       true /* support_usage_threshold */);
-  mgr->add_pool(old_gen_pool);
-  _pools_list->append(old_gen_pool);
-}
-
-void MemoryService::add_g1YoungGen_memory_pool(G1CollectedHeap* g1h,
-                                               MemoryManager* major_mgr,
-                                               MemoryManager* minor_mgr) {
-  assert(major_mgr != NULL && minor_mgr != NULL, "should have two managers");
-
-  G1EdenPool* eden = new G1EdenPool(g1h);
-  G1SurvivorPool* survivor = new G1SurvivorPool(g1h);
-
-  major_mgr->add_pool(eden);
-  major_mgr->add_pool(survivor);
-  minor_mgr->add_pool(eden);
-  minor_mgr->add_pool(survivor);
-  _pools_list->append(eden);
-  _pools_list->append(survivor);
-}
-
-void MemoryService::add_g1OldGen_memory_pool(G1CollectedHeap* g1h,
-                                             MemoryManager* mgr) {
-  assert(mgr != NULL, "should have one manager");
-
-  G1OldGenPool* old_gen = new G1OldGenPool(g1h);
-  mgr->add_pool(old_gen);
-  _pools_list->append(old_gen);
-}
-#endif // INCLUDE_ALL_GCS
-
 void MemoryService::add_code_heap_memory_pool(CodeHeap* heap, const char* name) {
   // Create new memory pool for this heap
   MemoryPool* code_heap_pool = new CodeHeapPool(heap, name, true /* support_usage_threshold */);
@@ -463,18 +162,11 @@ void MemoryService::track_memory_pool_usage(MemoryPool* pool) {
   }
 }
 
-void MemoryService::gc_begin(bool fullGC, bool recordGCBeginTime,
+void MemoryService::gc_begin(GCMemoryManager* manager, bool recordGCBeginTime,
                              bool recordAccumulatedGCTime,
                              bool recordPreGCUsage, bool recordPeakUsage) {
 
-  GCMemoryManager* mgr;
-  if (fullGC) {
-    mgr = _major_gc_manager;
-  } else {
-    mgr = _minor_gc_manager;
-  }
-  assert(mgr->is_gc_memory_manager(), "Sanity check");
-  mgr->gc_begin(recordGCBeginTime, recordPreGCUsage, recordAccumulatedGCTime);
+  manager->gc_begin(recordGCBeginTime, recordPreGCUsage, recordAccumulatedGCTime);
 
   // Track the peak memory usage when GC begins
   if (recordPeakUsage) {
@@ -485,22 +177,13 @@ void MemoryService::gc_begin(bool fullGC, bool recordGCBeginTime,
   }
 }
 
-void MemoryService::gc_end(bool fullGC, bool recordPostGCUsage,
+void MemoryService::gc_end(GCMemoryManager* manager, bool recordPostGCUsage,
                            bool recordAccumulatedGCTime,
                            bool recordGCEndTime, bool countCollection,
                            GCCause::Cause cause) {
-
-  GCMemoryManager* mgr;
-  if (fullGC) {
-    mgr = (GCMemoryManager*) _major_gc_manager;
-  } else {
-    mgr = (GCMemoryManager*) _minor_gc_manager;
-  }
-  assert(mgr->is_gc_memory_manager(), "Sanity check");
-
   // register the GC end statistics and memory usage
-  mgr->gc_end(recordPostGCUsage, recordAccumulatedGCTime, recordGCEndTime,
-              countCollection, cause);
+  manager->gc_end(recordPostGCUsage, recordAccumulatedGCTime, recordGCEndTime,
+                  countCollection, cause);
 }
 
 void MemoryService::oops_do(OopClosure* f) {
@@ -551,36 +234,7 @@ Handle MemoryService::create_MemoryUsage_obj(MemoryUsage usage, TRAPS) {
   return obj;
 }
 
-// GC manager type depends on the type of Generation. Depending on the space
-// availability and vm options the gc uses major gc manager or minor gc
-// manager or both. The type of gc manager depends on the generation kind.
-// For DefNew and ParNew generation doing scavenge gc uses minor gc manager (so
-// _fullGC is set to false ) and for other generation kinds doing
-// mark-sweep-compact uses major gc manager (so _fullGC is set to true).
-TraceMemoryManagerStats::TraceMemoryManagerStats(Generation::Name kind, GCCause::Cause cause) {
-  switch (kind) {
-    case Generation::DefNew:
-#if INCLUDE_ALL_GCS
-    case Generation::ParNew:
-#endif // INCLUDE_ALL_GCS
-      _fullGC = false;
-      break;
-    case Generation::MarkSweepCompact:
-#if INCLUDE_ALL_GCS
-    case Generation::ConcurrentMarkSweep:
-#endif // INCLUDE_ALL_GCS
-      _fullGC = true;
-      break;
-    default:
-      _fullGC = false;
-      assert(false, "Unrecognized gc generation kind.");
-  }
-  // this has to be called in a stop the world pause and represent
-  // an entire gc pause, start to finish:
-  initialize(_fullGC, cause, true, true, true, true, true, true, true);
-}
-
-TraceMemoryManagerStats::TraceMemoryManagerStats(bool fullGC,
+TraceMemoryManagerStats::TraceMemoryManagerStats(GCMemoryManager* gc_memory_manager,
                                                  GCCause::Cause cause,
                                                  bool recordGCBeginTime,
                                                  bool recordPreGCUsage,
@@ -589,14 +243,14 @@ TraceMemoryManagerStats::TraceMemoryManagerStats(bool fullGC,
                                                  bool recordAccumulatedGCTime,
                                                  bool recordGCEndTime,
                                                  bool countCollection) {
-  initialize(fullGC, cause, recordGCBeginTime, recordPreGCUsage, recordPeakUsage,
+  initialize(gc_memory_manager, cause, recordGCBeginTime, recordPreGCUsage, recordPeakUsage,
              recordPostGCUsage, recordAccumulatedGCTime, recordGCEndTime,
              countCollection);
 }
 
 // for a subclass to create then initialize an instance before invoking
 // the MemoryService
-void TraceMemoryManagerStats::initialize(bool fullGC,
+void TraceMemoryManagerStats::initialize(GCMemoryManager* gc_memory_manager,
                                          GCCause::Cause cause,
                                          bool recordGCBeginTime,
                                          bool recordPreGCUsage,
@@ -605,7 +259,7 @@ void TraceMemoryManagerStats::initialize(bool fullGC,
                                          bool recordAccumulatedGCTime,
                                          bool recordGCEndTime,
                                          bool countCollection) {
-  _fullGC = fullGC;
+  _gc_memory_manager = gc_memory_manager;
   _recordGCBeginTime = recordGCBeginTime;
   _recordPreGCUsage = recordPreGCUsage;
   _recordPeakUsage = recordPeakUsage;
@@ -615,11 +269,11 @@ void TraceMemoryManagerStats::initialize(bool fullGC,
   _countCollection = countCollection;
   _cause = cause;
 
-  MemoryService::gc_begin(_fullGC, _recordGCBeginTime, _recordAccumulatedGCTime,
+  MemoryService::gc_begin(_gc_memory_manager, _recordGCBeginTime, _recordAccumulatedGCTime,
                           _recordPreGCUsage, _recordPeakUsage);
 }
 
 TraceMemoryManagerStats::~TraceMemoryManagerStats() {
-  MemoryService::gc_end(_fullGC, _recordPostGCUsage, _recordAccumulatedGCTime,
+  MemoryService::gc_end(_gc_memory_manager, _recordPostGCUsage, _recordAccumulatedGCTime,
                         _recordGCEndTime, _countCollection, _cause);
 }
diff --git a/src/hotspot/share/services/memoryService.hpp b/src/hotspot/share/services/memoryService.hpp
index 86a6a95fa90..4ebc0813f23 100644
--- a/src/hotspot/share/services/memoryService.hpp
+++ b/src/hotspot/share/services/memoryService.hpp
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2003, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2017, 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
@@ -26,7 +26,6 @@
 #define SHARE_VM_SERVICES_MEMORYSERVICE_HPP
 
 #include "gc/shared/gcCause.hpp"
-#include "gc/shared/generation.hpp"
 #include "logging/log.hpp"
 #include "memory/allocation.hpp"
 #include "runtime/handles.hpp"
@@ -37,16 +36,7 @@ class MemoryPool;
 class MemoryManager;
 class GCMemoryManager;
 class CollectedHeap;
-class Generation;
-class DefNewGeneration;
-class PSYoungGen;
-class PSOldGen;
 class CodeHeap;
-class ContiguousSpace;
-class CompactibleFreeListSpace;
-class GenCollectedHeap;
-class ParallelScavengeHeap;
-class G1CollectedHeap;
 
 // VM Monitoring and Management Support
 
@@ -61,10 +51,6 @@ private:
   static GrowableArray<MemoryPool*>*    _pools_list;
   static GrowableArray<MemoryManager*>* _managers_list;
 
-  // memory managers for minor and major GC statistics
-  static GCMemoryManager*               _major_gc_manager;
-  static GCMemoryManager*               _minor_gc_manager;
-
   // memory manager and code heap pools for the CodeCache
   static MemoryManager*                 _code_cache_manager;
   static GrowableArray<MemoryPool*>*    _code_heap_pools;
@@ -72,51 +58,6 @@ private:
   static MemoryPool*                    _metaspace_pool;
   static MemoryPool*                    _compressed_class_pool;
 
-  static void add_generation_memory_pool(Generation* gen,
-                                         MemoryManager* major_mgr,
-                                         MemoryManager* minor_mgr);
-  static void add_generation_memory_pool(Generation* gen,
-                                         MemoryManager* major_mgr) {
-    add_generation_memory_pool(gen, major_mgr, NULL);
-  }
-
-
-  static void add_psYoung_memory_pool(PSYoungGen* young_gen,
-                                      MemoryManager* major_mgr,
-                                      MemoryManager* minor_mgr);
-  static void add_psOld_memory_pool(PSOldGen* old_gen,
-                                    MemoryManager* mgr);
-
-  static void add_g1YoungGen_memory_pool(G1CollectedHeap* g1h,
-                                         MemoryManager* major_mgr,
-                                         MemoryManager* minor_mgr);
-  static void add_g1OldGen_memory_pool(G1CollectedHeap* g1h,
-                                       MemoryManager* mgr);
-
-  static MemoryPool* add_space(ContiguousSpace* space,
-                               const char* name,
-                               bool is_heap,
-                               size_t max_size,
-                               bool support_usage_threshold);
-  static MemoryPool* add_survivor_spaces(DefNewGeneration* young_gen,
-                                         const char* name,
-                                         bool is_heap,
-                                         size_t max_size,
-                                         bool support_usage_threshold);
-  static MemoryPool* add_gen(Generation* gen,
-                             const char* name,
-                             bool is_heap,
-                             bool support_usage_threshold);
-  static MemoryPool* add_cms_space(CompactibleFreeListSpace* space,
-                                   const char* name,
-                                   bool is_heap,
-                                   size_t max_size,
-                                   bool support_usage_threshold);
-
-  static void add_gen_collected_heap_info(GenCollectedHeap* heap);
-  static void add_parallel_scavenge_heap_info(ParallelScavengeHeap* heap);
-  static void add_g1_heap_info(G1CollectedHeap* g1h);
-
 public:
   static void set_universe_heap(CollectedHeap* heap);
   static void add_code_heap_memory_pool(CodeHeap* heap, const char* name);
@@ -155,10 +96,10 @@ public:
   }
   static void track_memory_pool_usage(MemoryPool* pool);
 
-  static void gc_begin(bool fullGC, bool recordGCBeginTime,
+  static void gc_begin(GCMemoryManager* manager, bool recordGCBeginTime,
                        bool recordAccumulatedGCTime,
                        bool recordPreGCUsage, bool recordPeakUsage);
-  static void gc_end(bool fullGC, bool recordPostGCUsage,
+  static void gc_end(GCMemoryManager* manager, bool recordPostGCUsage,
                      bool recordAccumulatedGCTime,
                      bool recordGCEndTime, bool countCollection,
                      GCCause::Cause cause);
@@ -170,19 +111,11 @@ public:
 
   // Create an instance of java/lang/management/MemoryUsage
   static Handle create_MemoryUsage_obj(MemoryUsage usage, TRAPS);
-
-  static const GCMemoryManager* get_minor_gc_manager() {
-      return _minor_gc_manager;
-  }
-
-  static const GCMemoryManager* get_major_gc_manager() {
-      return _major_gc_manager;
-  }
 };
 
 class TraceMemoryManagerStats : public StackObj {
 private:
-  bool         _fullGC;
+  GCMemoryManager* _gc_memory_manager;
   bool         _recordGCBeginTime;
   bool         _recordPreGCUsage;
   bool         _recordPeakUsage;
@@ -193,7 +126,7 @@ private:
   GCCause::Cause _cause;
 public:
   TraceMemoryManagerStats() {}
-  TraceMemoryManagerStats(bool fullGC,
+  TraceMemoryManagerStats(GCMemoryManager* gc_memory_manager,
                           GCCause::Cause cause,
                           bool recordGCBeginTime = true,
                           bool recordPreGCUsage = true,
@@ -203,7 +136,7 @@ public:
                           bool recordGCEndTime = true,
                           bool countCollection = true);
 
-  void initialize(bool fullGC,
+  void initialize(GCMemoryManager* gc_memory_manager,
                   GCCause::Cause cause,
                   bool recordGCBeginTime,
                   bool recordPreGCUsage,
@@ -213,7 +146,6 @@ public:
                   bool recordGCEndTime,
                   bool countCollection);
 
-  TraceMemoryManagerStats(Generation::Name kind, GCCause::Cause cause);
   ~TraceMemoryManagerStats();
 };
 
diff --git a/src/hotspot/share/services/threadService.cpp b/src/hotspot/share/services/threadService.cpp
index da25120bb37..9a1a4ae393d 100644
--- a/src/hotspot/share/services/threadService.cpp
+++ b/src/hotspot/share/services/threadService.cpp
@@ -34,9 +34,9 @@
 #include "runtime/atomic.hpp"
 #include "runtime/handles.inline.hpp"
 #include "runtime/init.hpp"
-#include "runtime/thread.hpp"
-#include "runtime/vframe.hpp"
 #include "runtime/thread.inline.hpp"
+#include "runtime/threadSMR.inline.hpp"
+#include "runtime/vframe.hpp"
 #include "runtime/vmThread.hpp"
 #include "runtime/vm_operations.hpp"
 #include "services/threadService.hpp"
@@ -148,7 +148,7 @@ void ThreadService::current_thread_exiting(JavaThread* jt) {
 // FIXME: JVMTI should call this function
 Handle ThreadService::get_current_contended_monitor(JavaThread* thread) {
   assert(thread != NULL, "should be non-NULL");
-  assert(Threads_lock->owned_by_self(), "must grab Threads_lock or be at safepoint");
+  debug_only(Thread::check_for_dangling_thread_pointer(thread);)
 
   ObjectMonitor *wait_obj = thread->current_waiting_monitor();
 
@@ -266,6 +266,7 @@ Handle ThreadService::dump_stack_traces(GrowableArray<instanceHandle>* threads,
 
   int num_snapshots = dump_result.num_snapshots();
   assert(num_snapshots == num_threads, "Must have num_threads thread snapshots");
+  assert(num_snapshots == 0 || dump_result.t_list_has_been_set(), "ThreadsList must have been set if we have a snapshot");
   int i = 0;
   for (ThreadSnapshot* ts = dump_result.snapshots(); ts != NULL; i++, ts = ts->next()) {
     ThreadStackTrace* stacktrace = ts->get_stack_trace();
@@ -297,7 +298,9 @@ void ThreadService::reset_contention_time_stat(JavaThread* thread) {
 }
 
 // Find deadlocks involving object monitors and concurrent locks if concurrent_locks is true
-DeadlockCycle* ThreadService::find_deadlocks_at_safepoint(bool concurrent_locks) {
+DeadlockCycle* ThreadService::find_deadlocks_at_safepoint(ThreadsList * t_list, bool concurrent_locks) {
+  assert(SafepointSynchronize::is_at_safepoint(), "must be at safepoint");
+
   // This code was modified from the original Threads::find_deadlocks code.
   int globalDfn = 0, thisDfn;
   ObjectMonitor* waitingToLockMonitor = NULL;
@@ -306,15 +309,16 @@ DeadlockCycle* ThreadService::find_deadlocks_at_safepoint(bool concurrent_locks)
   JavaThread *currentThread, *previousThread;
   int num_deadlocks = 0;
 
-  for (JavaThread* p = Threads::first(); p != NULL; p = p->next()) {
-    // Initialize the depth-first-number
-    p->set_depth_first_number(-1);
+  // Initialize the depth-first-number for each JavaThread.
+  JavaThreadIterator jti(t_list);
+  for (JavaThread* jt = jti.first(); jt != NULL; jt = jti.next()) {
+    jt->set_depth_first_number(-1);
   }
 
   DeadlockCycle* deadlocks = NULL;
   DeadlockCycle* last = NULL;
   DeadlockCycle* cycle = new DeadlockCycle();
-  for (JavaThread* jt = Threads::first(); jt != NULL; jt = jt->next()) {
+  for (JavaThread* jt = jti.first(); jt != NULL; jt = jti.next()) {
     if (jt->depth_first_number() >= 0) {
       // this thread was already visited
       continue;
@@ -339,9 +343,8 @@ DeadlockCycle* ThreadService::find_deadlocks_at_safepoint(bool concurrent_locks)
       if (waitingToLockMonitor != NULL) {
         address currentOwner = (address)waitingToLockMonitor->owner();
         if (currentOwner != NULL) {
-          currentThread = Threads::owning_thread_from_monitor_owner(
-                            currentOwner,
-                            false /* no locking needed */);
+          currentThread = Threads::owning_thread_from_monitor_owner(t_list,
+                                                                    currentOwner);
           if (currentThread == NULL) {
             // This function is called at a safepoint so the JavaThread
             // that owns waitingToLockMonitor should be findable, but
@@ -366,6 +369,8 @@ DeadlockCycle* ThreadService::find_deadlocks_at_safepoint(bool concurrent_locks)
         if (concurrent_locks) {
           if (waitingToLockBlocker->is_a(SystemDictionary::abstract_ownable_synchronizer_klass())) {
             oop threadObj = java_util_concurrent_locks_AbstractOwnableSynchronizer::get_owner_threadObj(waitingToLockBlocker);
+            // This JavaThread (if there is one) is protected by the
+            // ThreadsListSetter in VM_FindDeadlocks::doit().
             currentThread = threadObj != NULL ? java_lang_Thread::thread(threadObj) : NULL;
           } else {
             currentThread = NULL;
@@ -414,7 +419,7 @@ DeadlockCycle* ThreadService::find_deadlocks_at_safepoint(bool concurrent_locks)
   return deadlocks;
 }
 
-ThreadDumpResult::ThreadDumpResult() : _num_threads(0), _num_snapshots(0), _snapshots(NULL), _next(NULL), _last(NULL) {
+ThreadDumpResult::ThreadDumpResult() : _num_threads(0), _num_snapshots(0), _snapshots(NULL), _next(NULL), _last(NULL), _setter() {
 
   // Create a new ThreadDumpResult object and append to the list.
   // If GC happens before this function returns, Method*
@@ -422,7 +427,7 @@ ThreadDumpResult::ThreadDumpResult() : _num_threads(0), _num_snapshots(0), _snap
   ThreadService::add_thread_dump(this);
 }
 
-ThreadDumpResult::ThreadDumpResult(int num_threads) : _num_threads(num_threads), _num_snapshots(0), _snapshots(NULL), _next(NULL), _last(NULL) {
+ThreadDumpResult::ThreadDumpResult(int num_threads) : _num_threads(num_threads), _num_snapshots(0), _snapshots(NULL), _next(NULL), _last(NULL), _setter() {
   // Create a new ThreadDumpResult object and append to the list.
   // If GC happens before this function returns, oops
   // will be visited.
@@ -467,6 +472,10 @@ void ThreadDumpResult::metadata_do(void f(Metadata*)) {
   }
 }
 
+ThreadsList* ThreadDumpResult::t_list() {
+  return _setter.list();
+}
+
 StackFrameInfo::StackFrameInfo(javaVFrame* jvf, bool with_lock_info) {
   _method = jvf->method();
   _bci = jvf->bci();
@@ -683,6 +692,8 @@ void ConcurrentLocksDump::build_map(GrowableArray<oop>* aos_objects) {
     oop o = aos_objects->at(i);
     oop owner_thread_obj = java_util_concurrent_locks_AbstractOwnableSynchronizer::get_owner_threadObj(o);
     if (owner_thread_obj != NULL) {
+      // See comments in ThreadConcurrentLocks to see how this
+      // JavaThread* is protected.
       JavaThread* thread = java_lang_Thread::thread(owner_thread_obj);
       assert(o->is_instance(), "Must be an instanceOop");
       add_lock(thread, (instanceOop) o);
@@ -764,7 +775,7 @@ ThreadStatistics::ThreadStatistics() {
   memset((void*) _perf_recursion_counts, 0, sizeof(_perf_recursion_counts));
 }
 
-ThreadSnapshot::ThreadSnapshot(JavaThread* thread) {
+ThreadSnapshot::ThreadSnapshot(ThreadsList * t_list, JavaThread* thread) {
   _thread = thread;
   _threadObj = thread->threadObj();
   _stack_trace = NULL;
@@ -796,7 +807,7 @@ ThreadSnapshot::ThreadSnapshot(JavaThread* thread) {
       _thread_status = java_lang_Thread::RUNNABLE;
     } else {
       _blocker_object = obj();
-      JavaThread* owner = ObjectSynchronizer::get_lock_owner(obj, false);
+      JavaThread* owner = ObjectSynchronizer::get_lock_owner(t_list, obj);
       if ((owner == NULL && _thread_status == java_lang_Thread::BLOCKED_ON_MONITOR_ENTER)
           || (owner != NULL && owner->is_attaching_via_jni())) {
         // ownership information of the monitor is not available
@@ -865,7 +876,7 @@ DeadlockCycle::~DeadlockCycle() {
   delete _threads;
 }
 
-void DeadlockCycle::print_on(outputStream* st) const {
+void DeadlockCycle::print_on_with(ThreadsList * t_list, outputStream* st) const {
   st->cr();
   st->print_cr("Found one Java-level deadlock:");
   st->print("=============================");
@@ -895,9 +906,8 @@ void DeadlockCycle::print_on(outputStream* st) const {
         // No Java object associated - a JVMTI raw monitor
         owner_desc = " (JVMTI raw monitor),\n  which is held by";
       }
-      currentThread = Threads::owning_thread_from_monitor_owner(
-                        (address)waitingToLockMonitor->owner(),
-                        false /* no locking needed */);
+      currentThread = Threads::owning_thread_from_monitor_owner(t_list,
+                                                                (address)waitingToLockMonitor->owner());
       if (currentThread == NULL) {
         // The deadlock was detected at a safepoint so the JavaThread
         // that owns waitingToLockMonitor should be findable, but
@@ -915,6 +925,7 @@ void DeadlockCycle::print_on(outputStream* st) const {
              "Must be an AbstractOwnableSynchronizer");
       oop ownerObj = java_util_concurrent_locks_AbstractOwnableSynchronizer::get_owner_threadObj(waitingToLockBlocker);
       currentThread = java_lang_Thread::thread(ownerObj);
+      assert(currentThread != NULL, "AbstractOwnableSynchronizer owning thread is unexpectedly NULL");
     }
     st->print("%s \"%s\"", owner_desc, currentThread->get_thread_name());
   }
@@ -943,9 +954,7 @@ ThreadsListEnumerator::ThreadsListEnumerator(Thread* cur_thread,
   int init_size = ThreadService::get_live_thread_count();
   _threads_array = new GrowableArray<instanceHandle>(init_size);
 
-  MutexLockerEx ml(Threads_lock);
-
-  for (JavaThread* jt = Threads::first(); jt != NULL; jt = jt->next()) {
+  for (JavaThreadIteratorWithHandle jtiwh; JavaThread *jt = jtiwh.next(); ) {
     // skips JavaThreads in the process of exiting
     // and also skips VM internal JavaThreads
     // Threads in _thread_new or _thread_new_trans state are included.
diff --git a/src/hotspot/share/services/threadService.hpp b/src/hotspot/share/services/threadService.hpp
index 46bc012f73e..950482e462d 100644
--- a/src/hotspot/share/services/threadService.hpp
+++ b/src/hotspot/share/services/threadService.hpp
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2017, 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,6 +32,7 @@
 #include "runtime/objectMonitor.hpp"
 #include "runtime/objectMonitor.inline.hpp"
 #include "runtime/perfData.hpp"
+#include "runtime/thread.hpp"
 #include "services/management.hpp"
 #include "services/serviceUtil.hpp"
 
@@ -109,7 +110,7 @@ public:
   static void   reset_contention_count_stat(JavaThread* thread);
   static void   reset_contention_time_stat(JavaThread* thread);
 
-  static DeadlockCycle*       find_deadlocks_at_safepoint(bool object_monitors_only);
+  static DeadlockCycle*       find_deadlocks_at_safepoint(ThreadsList * t_list, bool object_monitors_only);
 
   // GC support
   static void   oops_do(OopClosure* f);
@@ -189,6 +190,8 @@ public:
 // Thread snapshot to represent the thread state and statistics
 class ThreadSnapshot : public CHeapObj<mtInternal> {
 private:
+  // This JavaThread* is protected by being stored in objects that are
+  // protected by a ThreadsListSetter (ThreadDumpResult).
   JavaThread* _thread;
   oop         _threadObj;
   java_lang_Thread::ThreadStatus _thread_status;
@@ -213,7 +216,7 @@ public:
   // Dummy snapshot
   ThreadSnapshot() : _thread(NULL), _threadObj(NULL), _stack_trace(NULL), _concurrent_locks(NULL), _next(NULL),
                      _blocker_object(NULL), _blocker_object_owner(NULL) {};
-  ThreadSnapshot(JavaThread* thread);
+  ThreadSnapshot(ThreadsList * t_list, JavaThread* thread);
   ~ThreadSnapshot();
 
   java_lang_Thread::ThreadStatus thread_status() { return _thread_status; }
@@ -310,6 +313,12 @@ class ThreadConcurrentLocks : public CHeapObj<mtInternal> {
 private:
   GrowableArray<instanceOop>* _owned_locks;
   ThreadConcurrentLocks*      _next;
+  // This JavaThread* is protected in one of two different ways
+  // depending on the usage of the ThreadConcurrentLocks object:
+  // 1) by being stored in objects that are only allocated and used at a
+  // safepoint (ConcurrentLocksDump), or 2) by being stored in objects
+  // that are protected by a ThreadsListSetter (ThreadSnapshot inside
+  // ThreadDumpResult).
   JavaThread*                 _thread;
  public:
   ThreadConcurrentLocks(JavaThread* thread);
@@ -333,8 +342,12 @@ class ConcurrentLocksDump : public StackObj {
   void add_lock(JavaThread* thread, instanceOop o);
 
  public:
-  ConcurrentLocksDump(bool retain_map_on_free) : _map(NULL), _last(NULL), _retain_map_on_free(retain_map_on_free) {};
-  ConcurrentLocksDump() : _map(NULL), _last(NULL), _retain_map_on_free(false) {};
+  ConcurrentLocksDump(bool retain_map_on_free) : _map(NULL), _last(NULL), _retain_map_on_free(retain_map_on_free) {
+    assert(SafepointSynchronize::is_at_safepoint(), "Must be constructed at a safepoint.");
+  };
+  ConcurrentLocksDump() : _map(NULL), _last(NULL), _retain_map_on_free(false) {
+    assert(SafepointSynchronize::is_at_safepoint(), "Must be constructed at a safepoint.");
+  };
   ~ConcurrentLocksDump();
 
   void                        dump_at_safepoint();
@@ -349,6 +362,9 @@ class ThreadDumpResult : public StackObj {
   ThreadSnapshot*      _snapshots;
   ThreadSnapshot*      _last;
   ThreadDumpResult*    _next;
+  ThreadsListSetter    _setter;  // Helper to set hazard ptr in the originating thread
+                                 // which protects the JavaThreads in _snapshots.
+
  public:
   ThreadDumpResult();
   ThreadDumpResult(int num_threads);
@@ -360,6 +376,9 @@ class ThreadDumpResult : public StackObj {
   int                  num_threads()                    { return _num_threads; }
   int                  num_snapshots()                  { return _num_snapshots; }
   ThreadSnapshot*      snapshots()                      { return _snapshots; }
+  void                 set_t_list()                     { _setter.set(); }
+  ThreadsList*         t_list();
+  bool                 t_list_has_been_set()            { return _setter.target_needs_release(); }
   void                 oops_do(OopClosure* f);
   void                 metadata_do(void f(Metadata*));
 };
@@ -381,7 +400,7 @@ class DeadlockCycle : public CHeapObj<mtInternal> {
   bool           is_deadlock()              { return _is_deadlock; }
   int            num_threads()              { return _threads->length(); }
   GrowableArray<JavaThread*>* threads()     { return _threads; }
-  void           print_on(outputStream* st) const;
+  void           print_on_with(ThreadsList * t_list, outputStream* st) const;
 };
 
 // Utility class to get list of java threads.
diff --git a/src/hotspot/share/utilities/decoder.cpp b/src/hotspot/share/utilities/decoder.cpp
index 61a274c68fb..38ba9decb6e 100644
--- a/src/hotspot/share/utilities/decoder.cpp
+++ b/src/hotspot/share/utilities/decoder.cpp
@@ -24,6 +24,7 @@
 
 #include "precompiled.hpp"
 #include "jvm.h"
+#include "memory/allocation.inline.hpp"
 #include "runtime/os.hpp"
 #include "utilities/decoder.hpp"
 #include "utilities/vmError.hpp"
diff --git a/src/hotspot/share/utilities/decoder_elf.cpp b/src/hotspot/share/utilities/decoder_elf.cpp
index cd438dc1c49..c8639c2b972 100644
--- a/src/hotspot/share/utilities/decoder_elf.cpp
+++ b/src/hotspot/share/utilities/decoder_elf.cpp
@@ -26,6 +26,7 @@
 
 #if !defined(_WINDOWS) && !defined(__APPLE__)
 #include "decoder_elf.hpp"
+#include "memory/allocation.inline.hpp"
 
 ElfDecoder::~ElfDecoder() {
   if (_opened_elf_files != NULL) {
diff --git a/src/hotspot/share/utilities/globalDefinitions.hpp b/src/hotspot/share/utilities/globalDefinitions.hpp
index 33f9962da15..e98ec391144 100644
--- a/src/hotspot/share/utilities/globalDefinitions.hpp
+++ b/src/hotspot/share/utilities/globalDefinitions.hpp
@@ -951,7 +951,6 @@ const int      badResourceValue = 0xAB;                     // value used to zap
 const int      freeBlockPad     = 0xBA;                     // value used to pad freed blocks.
 const int      uninitBlockPad   = 0xF1;                     // value used to zap newly malloc'd blocks.
 const juint    uninitMetaWordVal= 0xf7f7f7f7;               // value used to zap newly allocated metachunk
-const intptr_t badJNIHandleVal  = (intptr_t) UCONST64(0xFEFEFEFEFEFEFEFE); // value used to zap jni handle area
 const juint    badHeapWordVal   = 0xBAADBABE;               // value used to zap heap after GC
 const juint    badMetaWordVal   = 0xBAADFADE;               // value used to zap metadata heap after GC
 const int      badCodeHeapNewVal= 0xCC;                     // value used to zap Code heap at allocation
@@ -963,7 +962,6 @@ const int      badCodeHeapFreeVal = 0xDD;                   // value used to zap
 #define       badAddress        ((address)::badAddressVal)
 #define       badOop            (cast_to_oop(::badOopVal))
 #define       badHeapWord       (::badHeapWordVal)
-#define       badJNIHandle      (cast_to_oop(::badJNIHandleVal))
 
 // Default TaskQueue size is 16K (32-bit) or 128K (64-bit)
 #define TASKQUEUE_SIZE (NOT_LP64(1<<14) LP64_ONLY(1<<17))
diff --git a/src/hotspot/share/utilities/growableArray.cpp b/src/hotspot/share/utilities/growableArray.cpp
index d6dc256a2c7..076529897c6 100644
--- a/src/hotspot/share/utilities/growableArray.cpp
+++ b/src/hotspot/share/utilities/growableArray.cpp
@@ -23,6 +23,7 @@
  */
 
 #include "precompiled.hpp"
+#include "memory/allocation.inline.hpp"
 #include "memory/resourceArea.hpp"
 #include "runtime/thread.inline.hpp"
 #include "utilities/growableArray.hpp"
@@ -56,3 +57,7 @@ void* GenericGrowableArray::raw_allocate(int elementSize) {
     return _arena->Amalloc(byte_size);
   }
 }
+
+void GenericGrowableArray::free_C_heap(void* elements) {
+  FreeHeap(elements);
+}
diff --git a/src/hotspot/share/utilities/growableArray.hpp b/src/hotspot/share/utilities/growableArray.hpp
index f65f45cede7..4c5a4914db7 100644
--- a/src/hotspot/share/utilities/growableArray.hpp
+++ b/src/hotspot/share/utilities/growableArray.hpp
@@ -26,9 +26,9 @@
 #define SHARE_VM_UTILITIES_GROWABLEARRAY_HPP
 
 #include "memory/allocation.hpp"
-#include "memory/allocation.inline.hpp"
 #include "utilities/debug.hpp"
 #include "utilities/globalDefinitions.hpp"
+#include "utilities/ostream.hpp"
 
 // A growable array.
 
@@ -144,6 +144,8 @@ class GenericGrowableArray : public ResourceObj {
     assert(on_stack(), "fast ResourceObj path only");
     return (void*)resource_allocate_bytes(thread, elementSize * _max);
   }
+
+  void free_C_heap(void* elements);
 };
 
 template<class E> class GrowableArrayIterator;
@@ -451,7 +453,7 @@ template<class E> void GrowableArray<E>::grow(int j) {
     for (     ; i < _max; i++) ::new ((void*)&newData[i]) E();
     for (i = 0; i < old_max; i++) _data[i].~E();
     if (on_C_heap() && _data != NULL) {
-      FreeHeap(_data);
+      free_C_heap(_data);
     }
     _data = newData;
 }
@@ -475,7 +477,7 @@ template<class E> void GrowableArray<E>::clear_and_deallocate() {
     clear();
     if (_data != NULL) {
       for (int i = 0; i < _max; i++) _data[i].~E();
-      FreeHeap(_data);
+      free_C_heap(_data);
       _data = NULL;
     }
 }
diff --git a/src/hotspot/share/utilities/stack.hpp b/src/hotspot/share/utilities/stack.hpp
index 7c866897691..b606bce533a 100644
--- a/src/hotspot/share/utilities/stack.hpp
+++ b/src/hotspot/share/utilities/stack.hpp
@@ -26,7 +26,6 @@
 #define SHARE_VM_UTILITIES_STACK_HPP
 
 #include "memory/allocation.hpp"
-#include "memory/allocation.inline.hpp"
 
 // Class Stack (below) grows and shrinks by linking together "segments" which
 // are allocated on demand.  Segments are arrays of the element type (E) plus an
diff --git a/src/hotspot/share/utilities/vmError.cpp b/src/hotspot/share/utilities/vmError.cpp
index 580fc86e89e..8d127605e13 100644
--- a/src/hotspot/share/utilities/vmError.cpp
+++ b/src/hotspot/share/utilities/vmError.cpp
@@ -36,6 +36,7 @@
 #include "runtime/init.hpp"
 #include "runtime/os.hpp"
 #include "runtime/thread.inline.hpp"
+#include "runtime/threadSMR.hpp"
 #include "runtime/vmThread.hpp"
 #include "runtime/vm_operations.hpp"
 #include "runtime/vm_version.hpp"
@@ -1655,7 +1656,12 @@ void VMError::controlled_crash(int how) {
   char * const dataPtr = NULL;  // bad data pointer
   const void (*funcPtr)(void) = (const void(*)()) 0xF;  // bad function pointer
 
-  // Keep this in sync with test/runtime/ErrorHandling/ErrorHandler.java
+  // Keep this in sync with test/hotspot/jtreg/runtime/ErrorHandling/ErrorHandler.java
+  // which tests cases 1 thru 13.
+  // Case 14 is tested by test/hotspot/jtreg/runtime/ErrorHandling/SafeFetchInErrorHandlingTest.java.
+  // Case 15 is tested by test/hotspot/jtreg/runtime/ErrorHandling/SecondaryErrorTest.java.
+  // Case 16 is tested by test/hotspot/jtreg/runtime/ErrorHandling/ThreadsListHandleInErrorHandlingTest.java.
+  // Case 17 is tested by test/hotspot/jtreg/runtime/ErrorHandling/NestedThreadsListHandleInErrorHandlingTest.java.
   switch (how) {
     case  1: vmassert(str == NULL, "expected null");
     case  2: vmassert(num == 1023 && *str == 'X',
@@ -1683,6 +1689,17 @@ void VMError::controlled_crash(int how) {
     case 13: (*funcPtr)(); break;
     case 14: crash_with_segfault(); break;
     case 15: crash_with_sigfpe(); break;
+    case 16: {
+      ThreadsListHandle tlh;
+      fatal("Force crash with an active ThreadsListHandle.");
+    }
+    case 17: {
+      ThreadsListHandle tlh;
+      {
+        ThreadsListHandle tlh2;
+        fatal("Force crash with a nested ThreadsListHandle.");
+      }
+    }
 
     default: tty->print_cr("ERROR: %d: unexpected test_num value.", how);
   }
diff --git a/src/java.base/share/classes/com/sun/crypto/provider/SunJCE.java b/src/java.base/share/classes/com/sun/crypto/provider/SunJCE.java
index e44a8476dc2..3e681a57285 100644
--- a/src/java.base/share/classes/com/sun/crypto/provider/SunJCE.java
+++ b/src/java.base/share/classes/com/sun/crypto/provider/SunJCE.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2017, 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
@@ -769,6 +769,8 @@ public final class SunJCE extends Provider {
                         "com.sun.crypto.provider.TlsMasterSecretGenerator");
                     put("Alg.Alias.KeyGenerator.SunTls12MasterSecret",
                         "SunTlsMasterSecret");
+                    put("Alg.Alias.KeyGenerator.SunTlsExtendedMasterSecret",
+                        "SunTlsMasterSecret");
 
                     put("KeyGenerator.SunTlsKeyMaterial",
                         "com.sun.crypto.provider.TlsKeyMaterialGenerator");
diff --git a/src/java.base/share/classes/com/sun/crypto/provider/TlsMasterSecretGenerator.java b/src/java.base/share/classes/com/sun/crypto/provider/TlsMasterSecretGenerator.java
index c772396e990..fda0828f6d4 100644
--- a/src/java.base/share/classes/com/sun/crypto/provider/TlsMasterSecretGenerator.java
+++ b/src/java.base/share/classes/com/sun/crypto/provider/TlsMasterSecretGenerator.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2005, 2011, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2005, 2017, 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
@@ -102,21 +102,32 @@ public final class TlsMasterSecretGenerator extends KeyGeneratorSpi {
 
         try {
             byte[] master;
-            byte[] clientRandom = spec.getClientRandom();
-            byte[] serverRandom = spec.getServerRandom();
-
             if (protocolVersion >= 0x0301) {
-                byte[] seed = concat(clientRandom, serverRandom);
+                byte[] label;
+                byte[] seed;
+                byte[] extendedMasterSecretSessionHash =
+                        spec.getExtendedMasterSecretSessionHash();
+                if (extendedMasterSecretSessionHash.length != 0) {
+                    label = LABEL_EXTENDED_MASTER_SECRET;
+                    seed = extendedMasterSecretSessionHash;
+                } else {
+                    byte[] clientRandom = spec.getClientRandom();
+                    byte[] serverRandom = spec.getServerRandom();
+                    label = LABEL_MASTER_SECRET;
+                    seed = concat(clientRandom, serverRandom);
+                }
                 master = ((protocolVersion >= 0x0303) ?
-                    doTLS12PRF(premaster, LABEL_MASTER_SECRET, seed, 48,
-                        spec.getPRFHashAlg(), spec.getPRFHashLength(),
-                        spec.getPRFBlockSize()) :
-                    doTLS10PRF(premaster, LABEL_MASTER_SECRET, seed, 48));
+                        doTLS12PRF(premaster, label, seed, 48,
+                                spec.getPRFHashAlg(), spec.getPRFHashLength(),
+                                spec.getPRFBlockSize()) :
+                        doTLS10PRF(premaster, label, seed, 48));
             } else {
                 master = new byte[48];
                 MessageDigest md5 = MessageDigest.getInstance("MD5");
                 MessageDigest sha = MessageDigest.getInstance("SHA");
 
+                byte[] clientRandom = spec.getClientRandom();
+                byte[] serverRandom = spec.getServerRandom();
                 byte[] tmp = new byte[20];
                 for (int i = 0; i < 3; i++) {
                     sha.update(SSL3_CONST[i]);
@@ -175,5 +186,5 @@ public final class TlsMasterSecretGenerator extends KeyGeneratorSpi {
         }
 
     }
-
 }
+
diff --git a/src/java.base/share/classes/com/sun/crypto/provider/TlsPrfGenerator.java b/src/java.base/share/classes/com/sun/crypto/provider/TlsPrfGenerator.java
index 90cde7c6bfb..2f945361c3f 100644
--- a/src/java.base/share/classes/com/sun/crypto/provider/TlsPrfGenerator.java
+++ b/src/java.base/share/classes/com/sun/crypto/provider/TlsPrfGenerator.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2005, 2017, 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
@@ -55,6 +55,11 @@ abstract class TlsPrfGenerator extends KeyGeneratorSpi {
     static final byte[] LABEL_MASTER_SECRET = // "master secret"
         { 109, 97, 115, 116, 101, 114, 32, 115, 101, 99, 114, 101, 116 };
 
+    static final byte[] LABEL_EXTENDED_MASTER_SECRET =
+                                            // "extended master secret"
+        { 101, 120, 116, 101, 110, 100, 101, 100, 32, 109, 97, 115, 116,
+          101, 114, 32, 115, 101, 99, 114, 101, 116 };
+
     static final byte[] LABEL_KEY_EXPANSION = // "key expansion"
         { 107, 101, 121, 32, 101, 120, 112, 97, 110, 115, 105, 111, 110 };
 
diff --git a/src/java.base/share/classes/com/sun/java/util/jar/pack/Constants.java b/src/java.base/share/classes/com/sun/java/util/jar/pack/Constants.java
index 50dc8020b25..5eb79adb6ae 100644
--- a/src/java.base/share/classes/com/sun/java/util/jar/pack/Constants.java
+++ b/src/java.base/share/classes/com/sun/java/util/jar/pack/Constants.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2001, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2001, 2017, 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
@@ -47,6 +47,7 @@ class Constants {
         1.7 to 1.7.X 51,0
         1.8 to 1.8.X 52,0
         1.9 to 1.9.X 53,0
+        1.10 to 1.10.X 54,0
     */
 
     public static final Package.Version JAVA_MIN_CLASS_VERSION =
@@ -67,6 +68,9 @@ class Constants {
     public static final Package.Version JAVA9_MAX_CLASS_VERSION =
             Package.Version.of(53, 00);
 
+    public static final Package.Version JAVA10_MAX_CLASS_VERSION =
+            Package.Version.of(54, 00);
+
     public static final int JAVA_PACKAGE_MAGIC = 0xCAFED00D;
 
     public static final Package.Version JAVA5_PACKAGE_VERSION =
@@ -83,7 +87,7 @@ class Constants {
 
     // upper limit, should point to the latest class version
     public static final Package.Version JAVA_MAX_CLASS_VERSION =
-            JAVA9_MAX_CLASS_VERSION;
+            JAVA10_MAX_CLASS_VERSION;
 
     // upper limit should point to the latest package version, for version info!.
     public static final Package.Version MAX_PACKAGE_VERSION =
diff --git a/src/java.base/share/classes/java/io/FileInputStream.java b/src/java.base/share/classes/java/io/FileInputStream.java
index 5dfe71907fe..72a12ff76de 100644
--- a/src/java.base/share/classes/java/io/FileInputStream.java
+++ b/src/java.base/share/classes/java/io/FileInputStream.java
@@ -25,6 +25,7 @@
 
 package java.io;
 
+import java.lang.reflect.Method;
 import java.nio.channels.FileChannel;
 import sun.nio.ch.FileChannelImpl;
 
@@ -37,6 +38,22 @@ import sun.nio.ch.FileChannelImpl;
  * <p><code>FileInputStream</code> is meant for reading streams of raw bytes
  * such as image data. For reading streams of characters, consider using
  * <code>FileReader</code>.
+ *
+ * @apiNote
+ * To release resources used by this stream {@link #close} should be called
+ * directly or by try-with-resources. Subclasses are responsible for the cleanup
+ * of resources acquired by the subclass.
+ * Subclasses that override {@link #finalize} in order to perform cleanup
+ * should be modified to use alternative cleanup mechanisms such as
+ * {@link java.lang.ref.Cleaner} and remove the overriding {@code finalize} method.
+ *
+ * @implSpec
+ * If this FileInputStream has been subclassed and the {@link #close}
+ * method has been overridden, the {@link #close} method will be
+ * called when the FileInputStream is unreachable.
+ * Otherwise, it is implementation specific how the resource cleanup described in
+ * {@link #close} is performed.
+
  *
  * @author  Arthur van Hoff
  * @see     java.io.File
@@ -63,6 +80,8 @@ class FileInputStream extends InputStream
 
     private volatile boolean closed;
 
+    private final AltFinalizer altFinalizer;
+
     /**
      * Creates a <code>FileInputStream</code> by
      * opening a connection to an actual file,
@@ -137,6 +156,10 @@ class FileInputStream extends InputStream
         fd.attach(this);
         path = name;
         open(name);
+        altFinalizer = AltFinalizer.get(this);
+        if (altFinalizer == null) {
+            fd.registerCleanup();         // open set the fd, register the cleanup
+        }
     }
 
     /**
@@ -173,6 +196,7 @@ class FileInputStream extends InputStream
         }
         fd = fdObj;
         path = null;
+        altFinalizer = null;
 
         /*
          * FileDescriptor is being shared by streams.
@@ -316,6 +340,14 @@ class FileInputStream extends InputStream
      * <p> If this stream has an associated channel then the channel is closed
      * as well.
      *
+     * @apiNote
+     * Overriding {@link #close} to perform cleanup actions is reliable
+     * only when called directly or when called by try-with-resources.
+     * Do not depend on finalization to invoke {@code close};
+     * finalization is not reliable and is deprecated.
+     * If cleanup of native resources is needed, other mechanisms such as
+     * {@linkplain java.lang.ref.Cleaner} should be used.
+     *
      * @exception  IOException  if an I/O error occurs.
      *
      * @revised 1.4
@@ -404,16 +436,27 @@ class FileInputStream extends InputStream
 
     private static native void initIDs();
 
-
     static {
         initIDs();
     }
 
     /**
-     * Ensures that the <code>close</code> method of this file input stream is
+     * Ensures that the {@link #close} method of this file input stream is
      * called when there are no more references to it.
+     * The {@link #finalize} method does not call {@link #close} directly.
      *
-     * @deprecated The {@code finalize} method has been deprecated.
+     * @apiNote
+     * To release resources used by this stream {@link #close} should be called
+     * directly or by try-with-resources.
+     *
+     * @implSpec
+     * If this FileInputStream has been subclassed and the {@link #close}
+     * method has been overridden, the {@link #close} method will be
+     * called when the FileInputStream is unreachable.
+     * Otherwise, it is implementation specific how the resource cleanup described in
+     * {@link #close} is performed.
+     *
+     * @deprecated The {@code finalize} method has been deprecated and will be removed.
      *     Subclasses that override {@code finalize} in order to perform cleanup
      *     should be modified to use alternative cleanup mechanisms and
      *     to remove the overriding {@code finalize} method.
@@ -425,15 +468,57 @@ class FileInputStream extends InputStream
      * @exception  IOException  if an I/O error occurs.
      * @see        java.io.FileInputStream#close()
      */
-    @Deprecated(since="9")
+    @Deprecated(since="9", forRemoval = true)
     protected void finalize() throws IOException {
-        if ((fd != null) &&  (fd != FileDescriptor.in)) {
-            /* if fd is shared, the references in FileDescriptor
-             * will ensure that finalizer is only called when
-             * safe to do so. All references using the fd have
-             * become unreachable. We can call close()
-             */
-            close();
+    }
+
+    /**
+     * Class to call {@code FileInputStream.close} when finalized.
+     * If finalization of the stream is needed, an instance is created
+     * in its constructor(s).  When the set of instances
+     * related to the stream is unreachable, the AltFinalizer performs
+     * the needed call to the stream's {@code close} method.
+     */
+    static class AltFinalizer {
+        private final FileInputStream fis;
+
+        /*
+         * Returns a finalizer object if the FIS needs a finalizer; otherwise null.
+         * If the FIS has a close method; it needs an AltFinalizer.
+         */
+        static AltFinalizer get(FileInputStream fis) {
+            Class<?> clazz = fis.getClass();
+            while (clazz != FileInputStream.class) {
+                try {
+                    clazz.getDeclaredMethod("close");
+                    return new AltFinalizer(fis);
+                } catch (NoSuchMethodException nsme) {
+                    // ignore
+                }
+                clazz = clazz.getSuperclass();
+            }
+            return null;
+        }
+
+        private AltFinalizer(FileInputStream fis) {
+            this.fis = fis;
+        }
+
+        @Override
+        @SuppressWarnings("deprecation")
+        protected final void finalize() {
+            try {
+                if ((fis.fd != null) && (fis.fd != FileDescriptor.in)) {
+                    /* if fd is shared, the references in FileDescriptor
+                     * will ensure that finalizer is only called when
+                     * safe to do so. All references using the fd have
+                     * become unreachable. We can call close()
+                     */
+                    fis.close();
+                }
+            } catch (IOException ioe) {
+                // ignore
+            }
         }
     }
 }
diff --git a/src/java.base/share/classes/java/io/FileOutputStream.java b/src/java.base/share/classes/java/io/FileOutputStream.java
index f9c1baa3518..cb9c8cda0f3 100644
--- a/src/java.base/share/classes/java/io/FileOutputStream.java
+++ b/src/java.base/share/classes/java/io/FileOutputStream.java
@@ -25,6 +25,7 @@
 
 package java.io;
 
+import java.lang.reflect.Method;
 import java.nio.channels.FileChannel;
 import jdk.internal.misc.SharedSecrets;
 import jdk.internal.misc.JavaIOFileDescriptorAccess;
@@ -44,6 +45,21 @@ import sun.nio.ch.FileChannelImpl;
  * such as image data. For writing streams of characters, consider using
  * <code>FileWriter</code>.
  *
+ * @apiNote
+ * To release resources used by this stream {@link #close} should be called
+ * directly or by try-with-resources. Subclasses are responsible for the cleanup
+ * of resources acquired by the subclass.
+ * Subclasses that override {@link #finalize} in order to perform cleanup
+ * should be modified to use alternative cleanup mechanisms such as
+ * {@link java.lang.ref.Cleaner} and remove the overriding {@code finalize} method.
+ *
+ * @implSpec
+ * If this FileOutputStream has been subclassed and the {@link #close}
+ * method has been overridden, the {@link #close} method will be
+ * called when the FileInputStream is unreachable.
+ * Otherwise, it is implementation specific how the resource cleanup described in
+ * {@link #close} is performed.
+ *
  * @author  Arthur van Hoff
  * @see     java.io.File
  * @see     java.io.FileDescriptor
@@ -80,6 +96,8 @@ class FileOutputStream extends OutputStream
 
     private volatile boolean closed;
 
+    private final AltFinalizer altFinalizer;
+
     /**
      * Creates a file output stream to write to the file with the
      * specified name. A new <code>FileDescriptor</code> object is
@@ -218,6 +236,10 @@ class FileOutputStream extends OutputStream
         this.path = name;
 
         open(name, append);
+        altFinalizer = AltFinalizer.get(this);
+        if (altFinalizer == null) {
+            fd.registerCleanup();         // open set the fd, register the cleanup
+        }
     }
 
     /**
@@ -253,6 +275,7 @@ class FileOutputStream extends OutputStream
         }
         this.fd = fdObj;
         this.path = null;
+        this.altFinalizer = null;
 
         fd.attach(this);
     }
@@ -340,6 +363,14 @@ class FileOutputStream extends OutputStream
      * <p> If this stream has an associated channel then the channel is closed
      * as well.
      *
+     * @apiNote
+     * Overriding {@link #close} to perform cleanup actions is reliable
+     * only when called directly or when called by try-with-resources.
+     * Do not depend on finalization to invoke {@code close};
+     * finalization is not reliable and is deprecated.
+     * If cleanup of native resources is needed, other mechanisms such as
+     * {@linkplain java.lang.ref.Cleaner} should be used.
+     *
      * @exception  IOException  if an I/O error occurs.
      *
      * @revised 1.4
@@ -429,34 +460,35 @@ class FileOutputStream extends OutputStream
 
     /**
      * Cleans up the connection to the file, and ensures that the
-     * <code>close</code> method of this file output stream is
+     * {@link #close} method of this file output stream is
      * called when there are no more references to this stream.
+     * The {@link #finalize} method does not call {@link #close} directly.
+     *
+     * @apiNote
+     * To release resources used by this stream {@link #close} should be called
+     * directly or by try-with-resources.
+     *
+     * @implSpec
+     * If this FileOutputStream has been subclassed and the {@link #close}
+     * method has been overridden, the {@link #close} method will be
+     * called when the FileOutputStream is unreachable.
+     * Otherwise, it is implementation specific how the resource cleanup described in
+     * {@link #close} is performed.
+     *
+     * @deprecated The {@code finalize} method has been deprecated and will be removed.
+     *     Subclasses that override {@code finalize} in order to perform cleanup
+     *     should be modified to use alternative cleanup mechanisms and
+     *     to remove the overriding {@code finalize} method.
+     *     When overriding the {@code finalize} method, its implementation must explicitly
+     *     ensure that {@code super.finalize()} is invoked as described in {@link Object#finalize}.
+     *     See the specification for {@link Object#finalize()} for further
+     *     information about migration options.
      *
-     * @deprecated The {@code finalize} method has been deprecated.
-     * Subclasses that override {@code finalize} in order to perform cleanup
-     * should be modified to use alternative cleanup mechanisms and
-     * to remove the overriding {@code finalize} method.
-     * When overriding the {@code finalize} method, its implementation must explicitly
-     * ensure that {@code super.finalize()} is invoked as described in {@link Object#finalize}.
-     * See the specification for {@link Object#finalize()} for further
-     * information about migration options.
      * @exception  IOException  if an I/O error occurs.
      * @see        java.io.FileInputStream#close()
      */
-    @Deprecated(since="9")
+    @Deprecated(since="9", forRemoval = true)
     protected void finalize() throws IOException {
-        if (fd != null) {
-            if (fd == FileDescriptor.out || fd == FileDescriptor.err) {
-                flush();
-            } else {
-                /* if fd is shared, the references in FileDescriptor
-                 * will ensure that finalizer is only called when
-                 * safe to do so. All references using the fd have
-                 * become unreachable. We can call close()
-                 */
-                close();
-            }
-        }
     }
 
     private static native void initIDs();
@@ -465,4 +497,59 @@ class FileOutputStream extends OutputStream
         initIDs();
     }
 
+    /**
+     * Class to call {@code FileOutputStream.close} when finalized.
+     * If finalization of the stream is needed, an instance is created
+     * in its constructor(s).  When the set of instances
+     * related to the stream is unreachable, the AltFinalizer performs
+     * the needed call to the stream's {@code close} method.
+     */
+    static class AltFinalizer {
+        private final FileOutputStream fos;
+
+        /*
+         * Returns a finalizer object if the FOS needs a finalizer; otherwise null.
+         * If the FOS has a close method; it needs an AltFinalizer.
+         */
+        static AltFinalizer get(FileOutputStream fos) {
+            Class<?> clazz = fos.getClass();
+            while (clazz != FileOutputStream.class) {
+                try {
+                    clazz.getDeclaredMethod("close");
+                    return new AltFinalizer(fos);
+                } catch (NoSuchMethodException nsme) {
+                    // ignore
+                }
+                clazz = clazz.getSuperclass();
+            }
+            return null;
+        }
+
+        private AltFinalizer(FileOutputStream fos) {
+            this.fos = fos;
+        }
+
+        @Override
+        @SuppressWarnings("deprecation")
+        protected final void finalize() {
+            try {
+                if (fos.fd != null) {
+                    if (fos.fd == FileDescriptor.out || fos.fd == FileDescriptor.err) {
+                        // Subclass may override flush; otherwise it is no-op
+                        fos.flush();
+                    } else {
+                        /* if fd is shared, the references in FileDescriptor
+                         * will ensure that finalizer is only called when
+                         * safe to do so. All references using the fd have
+                         * become unreachable. We can call close()
+                         */
+                        fos.close();
+                    }
+                }
+            } catch (IOException ioe) {
+                // ignore
+            }
+        }
+    }
+
 }
diff --git a/src/java.base/share/classes/java/io/RandomAccessFile.java b/src/java.base/share/classes/java/io/RandomAccessFile.java
index 5c946d1d715..4b6e66ff140 100644
--- a/src/java.base/share/classes/java/io/RandomAccessFile.java
+++ b/src/java.base/share/classes/java/io/RandomAccessFile.java
@@ -257,6 +257,7 @@ public class RandomAccessFile implements DataOutput, DataInput, Closeable {
         fd.attach(this);
         path = name;
         open(name, imode);
+        fd.registerCleanup();       // open sets the fd, register the cleanup
     }
 
     /**
diff --git a/src/java.base/share/classes/java/io/Reader.java b/src/java.base/share/classes/java/io/Reader.java
index 13e90d76ea6..a1fada4952e 100644
--- a/src/java.base/share/classes/java/io/Reader.java
+++ b/src/java.base/share/classes/java/io/Reader.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1996, 2012, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1996, 2017, 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
@@ -26,6 +26,8 @@
 package java.io;
 
 
+import java.util.Objects;
+
 /**
  * Abstract class for reading character streams.  The only methods that a
  * subclass must implement are read(char[], int, int) and close().  Most
@@ -50,6 +52,8 @@ package java.io;
 
 public abstract class Reader implements Readable, Closeable {
 
+    private static final int TRANSFER_BUFFER_SIZE = 8192;
+
     /**
      * The object used to synchronize operations on this stream.  For
      * efficiency, a character-stream object may use an object other than
@@ -262,4 +266,41 @@ public abstract class Reader implements Readable, Closeable {
      */
      public abstract void close() throws IOException;
 
+    /**
+     * Reads all characters from this reader and writes the characters to the
+     * given writer in the order that they are read. On return, this reader
+     * will be at end of the stream. This method does not close either reader
+     * or writer.
+     * <p>
+     * This method may block indefinitely reading from the reader, or
+     * writing to the writer. The behavior for the case where the reader
+     * and/or writer is <i>asynchronously closed</i>, or the thread
+     * interrupted during the transfer, is highly reader and writer
+     * specific, and therefore not specified.
+     * <p>
+     * If an I/O error occurs reading from the reader or writing to the
+     * writer, then it may do so after some characters have been read or
+     * written. Consequently the reader may not be at end of the stream and
+     * one, or both, streams may be in an inconsistent state. It is strongly
+     * recommended that both streams be promptly closed if an I/O error occurs.
+     *
+     * @param  out the writer, non-null
+     * @return the number of characters transferred
+     * @throws IOException if an I/O error occurs when reading or writing
+     * @throws NullPointerException if {@code out} is {@code null}
+     *
+     * @since 10
+     */
+    public long transferTo(Writer out) throws IOException {
+        Objects.requireNonNull(out, "out");
+        long transferred = 0;
+        char[] buffer = new char[TRANSFER_BUFFER_SIZE];
+        int nRead;
+        while ((nRead = read(buffer, 0, TRANSFER_BUFFER_SIZE)) >= 0) {
+            out.write(buffer, 0, nRead);
+            transferred += nRead;
+        }
+        return transferred;
+    }
+
 }
diff --git a/src/java.base/share/classes/java/lang/ClassLoader.java b/src/java.base/share/classes/java/lang/ClassLoader.java
index 74c46417631..c18a9ad9774 100644
--- a/src/java.base/share/classes/java/lang/ClassLoader.java
+++ b/src/java.base/share/classes/java/lang/ClassLoader.java
@@ -30,6 +30,7 @@ import java.io.IOException;
 import java.io.UncheckedIOException;
 import java.io.File;
 import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
 import java.net.URL;
 import java.security.AccessController;
 import java.security.AccessControlContext;
@@ -37,6 +38,7 @@ import java.security.CodeSource;
 import java.security.PrivilegedAction;
 import java.security.ProtectionDomain;
 import java.security.cert.Certificate;
+import java.util.ArrayDeque;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.Deque;
@@ -44,7 +46,6 @@ import java.util.Enumeration;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Hashtable;
-import java.util.LinkedList;
 import java.util.Map;
 import java.util.NoSuchElementException;
 import java.util.Objects;
@@ -1867,7 +1868,7 @@ public abstract class ClassLoader {
      * to be the system class loader. During construction, the class loader
      * should take great care to avoid calling {@code getSystemClassLoader()}.
      * If circular initialization of the system class loader is detected then
-     * an unspecified error or exception is thrown.
+     * an {@code IllegalStateException} is thrown.
      *
      * @implNote The system property to override the system class loader is not
      * examined until the VM is almost fully initialized. Code that executes
@@ -1918,8 +1919,8 @@ public abstract class ClassLoader {
                 // the system class loader is the built-in app class loader during startup
                 return getBuiltinAppClassLoader();
             case 3:
-                String msg = "getSystemClassLoader should only be called after VM booted";
-                throw new InternalError(msg);
+                String msg = "getSystemClassLoader cannot be called during the system class loader instantiation";
+                throw new IllegalStateException(msg);
             case 4:
                 // system fully initialized
                 assert VM.isBooted() && scl != null;
@@ -1969,7 +1970,17 @@ public abstract class ClassLoader {
                                            .getDeclaredConstructor(ClassLoader.class);
                 scl = (ClassLoader) ctor.newInstance(builtinLoader);
             } catch (Exception e) {
-                throw new Error(e);
+                Throwable cause = e;
+                if (e instanceof InvocationTargetException) {
+                    cause = e.getCause();
+                    if (cause instanceof Error) {
+                        throw (Error) cause;
+                    }
+                }
+                if (cause instanceof RuntimeException) {
+                    throw (RuntimeException) cause;
+                }
+                throw new Error(cause.getMessage(), cause);
             }
         } else {
             scl = builtinLoader;
@@ -2485,7 +2496,7 @@ public abstract class ClassLoader {
         }
 
         // native libraries being loaded
-        static Deque<NativeLibrary> nativeLibraryContext = new LinkedList<>();
+        static Deque<NativeLibrary> nativeLibraryContext = new ArrayDeque<>(8);
 
         /*
          * The run() method will be invoked when this class loader becomes
diff --git a/src/java.base/share/classes/java/lang/ModuleLayer.java b/src/java.base/share/classes/java/lang/ModuleLayer.java
index d9b1b3a3714..3dc809632c7 100644
--- a/src/java.base/share/classes/java/lang/ModuleLayer.java
+++ b/src/java.base/share/classes/java/lang/ModuleLayer.java
@@ -322,8 +322,8 @@ public final class ModuleLayer {
      * @return The newly created layer
      *
      * @throws IllegalArgumentException
-     *         If the parent of the given configuration is not the configuration
-     *         for this layer
+     *         If the given configuration has more than one parent or the parent
+     *         of the configuration is not the configuration for this layer
      * @throws LayerInstantiationException
      *         If the layer cannot be created for any of the reasons specified
      *         by the static {@code defineModulesWithOneLoader} method
@@ -364,8 +364,8 @@ public final class ModuleLayer {
      * @return The newly created layer
      *
      * @throws IllegalArgumentException
-     *         If the parent of the given configuration is not the configuration
-     *         for this layer
+     *         If the given configuration has more than one parent or the parent
+     *         of the configuration is not the configuration for this layer
      * @throws LayerInstantiationException
      *         If the layer cannot be created for any of the reasons specified
      *         by the static {@code defineModulesWithManyLoaders} method
@@ -403,8 +403,8 @@ public final class ModuleLayer {
      * @return The newly created layer
      *
      * @throws IllegalArgumentException
-     *         If the parent of the given configuration is not the configuration
-     *         for this layer
+     *         If the given configuration has more than one parent or the parent
+     *         of the configuration is not the configuration for this layer
      * @throws LayerInstantiationException
      *         If the layer cannot be created for any of the reasons specified
      *         by the static {@code defineModules} method
@@ -473,8 +473,8 @@ public final class ModuleLayer {
      * @return A controller that controls the newly created layer
      *
      * @throws IllegalArgumentException
-     *         If the parent configurations do not match the configuration of
-     *         the parent layers, including order
+     *         If the parent(s) of the given configuration do not match the
+     *         configuration of the parent layers, including order
      * @throws LayerInstantiationException
      *         If all modules cannot be defined to the same class loader for any
      *         of the reasons listed above
@@ -546,8 +546,8 @@ public final class ModuleLayer {
      * @return A controller that controls the newly created layer
      *
      * @throws IllegalArgumentException
-     *         If the parent configurations do not match the configuration of
-     *         the parent layers, including order
+     *         If the parent(s) of the given configuration do not match the
+     *         configuration of the parent layers, including order
      * @throws LayerInstantiationException
      *         If the layer cannot be created because the configuration contains
      *         a module named "{@code java.base}" or a module contains a package
@@ -637,8 +637,8 @@ public final class ModuleLayer {
      * @return A controller that controls the newly created layer
      *
      * @throws IllegalArgumentException
-     *         If the parent configurations do not match the configuration of
-     *         the parent layers, including order
+     *         If the parent(s) of the given configuration do not match the
+     *         configuration of the parent layers, including order
      * @throws LayerInstantiationException
      *         If creating the layer fails for any of the reasons listed above
      * @throws SecurityException
@@ -845,9 +845,8 @@ public final class ModuleLayer {
 
         return layers()
                 .skip(1)  // skip this layer
-                .map(l -> l.nameToModule)
-                .filter(map -> map.containsKey(name))
-                .map(map -> map.get(name))
+                .map(l -> l.nameToModule.get(name))
+                .filter(Objects::nonNull)
                 .findAny();
     }
 
diff --git a/src/java.base/share/classes/java/lang/Runtime.java b/src/java.base/share/classes/java/lang/Runtime.java
index dc36b769a07..b3f5c08b659 100644
--- a/src/java.base/share/classes/java/lang/Runtime.java
+++ b/src/java.base/share/classes/java/lang/Runtime.java
@@ -876,62 +876,6 @@ public class Runtime {
         ClassLoader.loadLibrary(fromClass, libname, false);
     }
 
-    /**
-     * Creates a localized version of an input stream. This method takes
-     * an {@code InputStream} and returns an {@code InputStream}
-     * equivalent to the argument in all respects except that it is
-     * localized: as characters in the local character set are read from
-     * the stream, they are automatically converted from the local
-     * character set to Unicode.
-     * <p>
-     * If the argument is already a localized stream, it may be returned
-     * as the result.
-     *
-     * @param      in InputStream to localize
-     * @return     a localized input stream
-     * @see        java.io.InputStream
-     * @see        java.io.BufferedReader#BufferedReader(java.io.Reader)
-     * @see        java.io.InputStreamReader#InputStreamReader(java.io.InputStream)
-     * @deprecated As of JDK&nbsp;1.1, the preferred way to translate a byte
-     * stream in the local encoding into a character stream in Unicode is via
-     * the {@code InputStreamReader} and {@code BufferedReader}
-     * classes.
-     * This method is subject to removal in a future version of Java SE.
-     */
-    @Deprecated(since="1.1", forRemoval=true)
-    public InputStream getLocalizedInputStream(InputStream in) {
-        return in;
-    }
-
-    /**
-     * Creates a localized version of an output stream. This method
-     * takes an {@code OutputStream} and returns an
-     * {@code OutputStream} equivalent to the argument in all respects
-     * except that it is localized: as Unicode characters are written to
-     * the stream, they are automatically converted to the local
-     * character set.
-     * <p>
-     * If the argument is already a localized stream, it may be returned
-     * as the result.
-     *
-     * @deprecated As of JDK&nbsp;1.1, the preferred way to translate a
-     * Unicode character stream into a byte stream in the local encoding is via
-     * the {@code OutputStreamWriter}, {@code BufferedWriter}, and
-     * {@code PrintWriter} classes.
-     * This method is subject to removal in a future version of Java SE.
-     *
-     * @param      out OutputStream to localize
-     * @return     a localized output stream
-     * @see        java.io.OutputStream
-     * @see        java.io.BufferedWriter#BufferedWriter(java.io.Writer)
-     * @see        java.io.OutputStreamWriter#OutputStreamWriter(java.io.OutputStream)
-     * @see        java.io.PrintWriter#PrintWriter(java.io.OutputStream)
-     */
-    @Deprecated(since="1.1", forRemoval=true)
-    public OutputStream getLocalizedOutputStream(OutputStream out) {
-        return out;
-    }
-
     /**
      * Returns the version of the Java Runtime Environment as a {@link Version}.
      *
diff --git a/src/java.base/share/classes/java/lang/String.java b/src/java.base/share/classes/java/lang/String.java
index db55267ee83..e3da498617c 100644
--- a/src/java.base/share/classes/java/lang/String.java
+++ b/src/java.base/share/classes/java/lang/String.java
@@ -645,19 +645,6 @@ public final class String
         this(builder, null);
     }
 
-   /*
-    * Package private constructor which shares value array for speed.
-    * this constructor is always expected to be called with share==true.
-    * a separate constructor is needed because we already have a public
-    * String(char[]) constructor that makes a copy of the given char[].
-    */
-    // TBD: this is kept for package internal use (Thread/System),
-    // should be removed if they all have a byte[] version
-    String(char[] val, boolean share) {
-        // assert share : "unshared not supported";
-        this(val, 0, val.length, null);
-    }
-
     /**
      * Returns the length of this string.
      * The length is equal to the number of <a href="Character.html#unicode">Unicode
diff --git a/src/java.base/share/classes/java/lang/System.java b/src/java.base/share/classes/java/lang/System.java
index 0232642b42c..6271f87a6f0 100644
--- a/src/java.base/share/classes/java/lang/System.java
+++ b/src/java.base/share/classes/java/lang/System.java
@@ -2109,9 +2109,6 @@ public final class System {
             public void registerShutdownHook(int slot, boolean registerShutdownInProgress, Runnable hook) {
                 Shutdown.add(slot, registerShutdownInProgress, hook);
             }
-            public String newStringUnsafe(char[] chars) {
-                return new String(chars, true);
-            }
             public Thread newThreadWithAcc(Runnable target, AccessControlContext acc) {
                 return new Thread(target, acc);
             }
diff --git a/src/java.base/share/classes/java/lang/module/Configuration.java b/src/java.base/share/classes/java/lang/module/Configuration.java
index 042a1362dd9..988d5c7a40a 100644
--- a/src/java.base/share/classes/java/lang/module/Configuration.java
+++ b/src/java.base/share/classes/java/lang/module/Configuration.java
@@ -543,9 +543,8 @@ public final class Configuration {
         if (!parents.isEmpty()) {
             return configurations()
                     .skip(1)  // skip this configuration
-                    .map(cf -> cf.nameToModule)
-                    .filter(map -> map.containsKey(name))
-                    .map(map -> map.get(name))
+                    .map(cf -> cf.nameToModule.get(name))
+                    .filter(Objects::nonNull)
                     .findFirst();
         }
 
diff --git a/src/java.base/share/classes/java/lang/module/package-info.java b/src/java.base/share/classes/java/lang/module/package-info.java
index 74021cd0bef..41278ffda0f 100644
--- a/src/java.base/share/classes/java/lang/module/package-info.java
+++ b/src/java.base/share/classes/java/lang/module/package-info.java
@@ -34,7 +34,7 @@
  * will cause a {@code NullPointerException}, unless otherwise specified. </p>
  *
  *
- * <h1><a id="resolution">Resolution</a></h1>
+ * <h1><a id="resolution">{@index "Module Resolution"}</a></h1>
  *
  * <p> Resolution is the process of computing how modules depend on each other.
  * The process occurs at compile time and run time. </p>
diff --git a/src/java.base/share/classes/java/net/SocketInputStream.java b/src/java.base/share/classes/java/net/SocketInputStream.java
index 04ee3378eaa..bb3481b7164 100644
--- a/src/java.base/share/classes/java/net/SocketInputStream.java
+++ b/src/java.base/share/classes/java/net/SocketInputStream.java
@@ -283,7 +283,7 @@ class SocketInputStream extends FileInputStream
     /**
      * Overrides finalize, the fd is closed by the Socket.
      */
-    @SuppressWarnings("deprecation")
+    @SuppressWarnings({"deprecation", "removal"})
     protected void finalize() {}
 
     /**
diff --git a/src/java.base/share/classes/java/net/SocketOutputStream.java b/src/java.base/share/classes/java/net/SocketOutputStream.java
index 0ba877bf56e..ddf3dcf9bb5 100644
--- a/src/java.base/share/classes/java/net/SocketOutputStream.java
+++ b/src/java.base/share/classes/java/net/SocketOutputStream.java
@@ -175,7 +175,7 @@ class SocketOutputStream extends FileOutputStream
     /**
      * Overrides finalize, the fd is closed by the Socket.
      */
-    @SuppressWarnings("deprecation")
+    @SuppressWarnings({"deprecation", "removal"})
     protected void finalize() {}
 
     /**
diff --git a/src/java.base/share/classes/java/security/IdentityScope.java b/src/java.base/share/classes/java/security/IdentityScope.java
index 49c7c180322..2bd517cd422 100644
--- a/src/java.base/share/classes/java/security/IdentityScope.java
+++ b/src/java.base/share/classes/java/security/IdentityScope.java
@@ -90,8 +90,8 @@ class IdentityScope extends Identity {
             try {
                 Class.forName(classname);
             } catch (ClassNotFoundException e) {
-                //Security.error("unable to establish a system scope from " +
-                //             classname);
+                System.err.println("unable to establish a system scope from " +
+                             classname);
                 e.printStackTrace();
             }
         }
diff --git a/src/java.base/share/classes/java/security/ProtectionDomain.java b/src/java.base/share/classes/java/security/ProtectionDomain.java
index 365096f619a..7830ee4d8f9 100644
--- a/src/java.base/share/classes/java/security/ProtectionDomain.java
+++ b/src/java.base/share/classes/java/security/ProtectionDomain.java
@@ -25,15 +25,13 @@
 
 package java.security;
 
-import java.lang.ref.Reference;
-import java.lang.ref.ReferenceQueue;
-import java.lang.ref.SoftReference;
-import java.lang.ref.WeakReference;
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.Enumeration;
 import java.util.List;
+import java.util.Map;
 import java.util.Objects;
-import java.util.concurrent.ConcurrentHashMap;
+import java.util.WeakHashMap;
 import jdk.internal.misc.JavaSecurityAccess;
 import jdk.internal.misc.JavaSecurityProtectionDomainAccess;
 import static jdk.internal.misc.JavaSecurityProtectionDomainAccess.ProtectionDomainCache;
@@ -115,23 +113,10 @@ public class ProtectionDomain {
     }
 
     static {
-        // setup SharedSecrets to allow access to doIntersectionPrivilege
-        // methods and ProtectionDomain cache
+        // Set up JavaSecurityAccess in SharedSecrets
         SharedSecrets.setJavaSecurityAccess(new JavaSecurityAccessImpl());
-        SharedSecrets.setJavaSecurityProtectionDomainAccess(
-            new JavaSecurityProtectionDomainAccess() {
-                @Override
-                public ProtectionDomainCache getProtectionDomainCache() {
-                    return new PDCache();
-                }
-            });
     }
 
-    /**
-     * Used for storing ProtectionDomains as keys in a Map.
-     */
-    static final class Key {}
-
     /* CodeSource */
     private CodeSource codesource ;
 
@@ -571,117 +556,27 @@ public class ProtectionDomain {
     }
 
     /**
-     * A cache of ProtectionDomains and their Permissions.
-     *
-     * This class stores ProtectionDomains as weak keys in a ConcurrentHashMap
-     * with additional support for checking and removing weak keys that are no
-     * longer in use. There can be cases where the permission collection may
-     * have a chain of strong references back to the ProtectionDomain, which
-     * ordinarily would prevent the entry from being removed from the map. To
-     * address that, we wrap the permission collection in a SoftReference so
-     * that it can be reclaimed by the garbage collector due to memory demand.
+     * Used for storing ProtectionDomains as keys in a Map.
      */
-    private static class PDCache implements ProtectionDomainCache {
-        private final ConcurrentHashMap<WeakProtectionDomainKey,
-                                        SoftReference<PermissionCollection>>
-                                        pdMap = new ConcurrentHashMap<>();
-        private final ReferenceQueue<Key> queue = new ReferenceQueue<>();
+    final class Key {}
 
-        @Override
-        public void put(ProtectionDomain pd, PermissionCollection pc) {
-            processQueue(queue, pdMap);
-            WeakProtectionDomainKey weakPd =
-                new WeakProtectionDomainKey(pd, queue);
-            pdMap.put(weakPd, new SoftReference<>(pc));
-        }
-
-        @Override
-        public PermissionCollection get(ProtectionDomain pd) {
-            processQueue(queue, pdMap);
-            WeakProtectionDomainKey weakPd = new WeakProtectionDomainKey(pd);
-            SoftReference<PermissionCollection> sr = pdMap.get(weakPd);
-            return (sr == null) ? null : sr.get();
-        }
-
-        /**
-         * Removes weak keys from the map that have been enqueued
-         * on the reference queue and are no longer in use.
-         */
-        private static void processQueue(ReferenceQueue<Key> queue,
-                                         ConcurrentHashMap<? extends
-                                         WeakReference<Key>, ?> pdMap) {
-            Reference<? extends Key> ref;
-            while ((ref = queue.poll()) != null) {
-                pdMap.remove(ref);
-            }
-        }
-    }
-
-    /**
-     * A weak key for a ProtectionDomain.
-     */
-    private static class WeakProtectionDomainKey extends WeakReference<Key> {
-        /**
-         * Saved value of the referent's identity hash code, to maintain
-         * a consistent hash code after the referent has been cleared
-         */
-        private final int hash;
-
-        /**
-         * A key representing a null ProtectionDomain.
-         */
-        private static final Key NULL_KEY = new Key();
-
-        /**
-         * Create a new WeakProtectionDomain with the specified domain and
-         * registered with a queue.
-         */
-        WeakProtectionDomainKey(ProtectionDomain pd, ReferenceQueue<Key> rq) {
-            this((pd == null ? NULL_KEY : pd.key), rq);
-        }
-
-        WeakProtectionDomainKey(ProtectionDomain pd) {
-            this(pd == null ? NULL_KEY : pd.key);
-        }
-
-        private WeakProtectionDomainKey(Key key, ReferenceQueue<Key> rq) {
-            super(key, rq);
-            hash = key.hashCode();
-        }
-
-        private WeakProtectionDomainKey(Key key) {
-            super(key);
-            hash = key.hashCode();
-        }
-
-        /**
-         * Returns the identity hash code of the original referent.
-         */
-        @Override
-        public int hashCode() {
-            return hash;
-        }
-
-        /**
-         * Returns true if the given object is an identical
-         * WeakProtectionDomainKey instance, or, if this object's referent
-         * has not been cleared and the given object is another
-         * WeakProtectionDomainKey instance with an identical non-null
-         * referent as this one.
-         */
-        @Override
-        public boolean equals(Object obj) {
-            if (obj == this) {
-                return true;
-            }
-
-            if (obj instanceof WeakProtectionDomainKey) {
-                Object referent = get();
-                return (referent != null) &&
-                       (referent == ((WeakProtectionDomainKey)obj).get());
-            } else {
-                return false;
-            }
-        }
+    static {
+        SharedSecrets.setJavaSecurityProtectionDomainAccess(
+            new JavaSecurityProtectionDomainAccess() {
+                public ProtectionDomainCache getProtectionDomainCache() {
+                    return new ProtectionDomainCache() {
+                        private final Map<Key, PermissionCollection> map =
+                            Collections.synchronizedMap
+                                (new WeakHashMap<Key, PermissionCollection>());
+                        public void put(ProtectionDomain pd,
+                            PermissionCollection pc) {
+                            map.put((pd == null ? null : pd.key), pc);
+                        }
+                        public PermissionCollection get(ProtectionDomain pd) {
+                            return pd == null ? map.get(null) : map.get(pd.key);
+                        }
+                    };
+                }
+            });
     }
 }
diff --git a/src/java.base/share/classes/java/security/Signature.java b/src/java.base/share/classes/java/security/Signature.java
index 3f0a9d96771..83a154ed216 100644
--- a/src/java.base/share/classes/java/security/Signature.java
+++ b/src/java.base/share/classes/java/security/Signature.java
@@ -1074,7 +1074,7 @@ public abstract class Signature extends SignatureSpi {
                             debug.println("Further warnings of this type will "
                                 + "be suppressed");
                         }
-                        new Exception("Call trace").printStackTrace();
+                        new Exception("Debug call trace").printStackTrace();
                     }
                 }
                 Exception lastException = null;
diff --git a/src/java.base/share/classes/java/text/MessageFormat.java b/src/java.base/share/classes/java/text/MessageFormat.java
index 5dd78c87432..4560abd822b 100644
--- a/src/java.base/share/classes/java/text/MessageFormat.java
+++ b/src/java.base/share/classes/java/text/MessageFormat.java
@@ -701,6 +701,10 @@ public class MessageFormat extends Format {
      *            larger than the number of format elements in the pattern string
      */
     public void setFormat(int formatElementIndex, Format newFormat) {
+
+        if (formatElementIndex > maxOffset) {
+            throw new ArrayIndexOutOfBoundsException(formatElementIndex);
+        }
         formats[formatElementIndex] = newFormat;
     }
 
diff --git a/src/java.base/share/classes/java/util/Collection.java b/src/java.base/share/classes/java/util/Collection.java
index e66bd49aa78..d016f8c374b 100644
--- a/src/java.base/share/classes/java/util/Collection.java
+++ b/src/java.base/share/classes/java/util/Collection.java
@@ -54,19 +54,15 @@ import java.util.stream.StreamSupport;
  * constructors) but all of the general-purpose {@code Collection}
  * implementations in the Java platform libraries comply.
  *
- * <p>The "destructive" methods contained in this interface, that is, the
- * methods that modify the collection on which they operate, are specified to
- * throw {@code UnsupportedOperationException} if this collection does not
- * support the operation.  If this is the case, these methods may, but are not
- * required to, throw an {@code UnsupportedOperationException} if the
- * invocation would have no effect on the collection.  For example, invoking
- * the {@link #addAll(Collection)} method on an unmodifiable collection may,
- * but is not required to, throw the exception if the collection to be added
- * is empty.
+ * <p>Certain methods are specified to be
+ * <i>optional</i>. If a collection implementation doesn't implement a
+ * particular operation, it should define the corresponding method to throw
+ * {@code UnsupportedOperationException}. Such methods are marked "optional
+ * operation" in method specifications of the collections interfaces.
  *
- * <p><a id="optional-restrictions">
- * Some collection implementations have restrictions on the elements that
- * they may contain.</a>  For example, some implementations prohibit null elements,
+ * <p><a id="optional-restrictions"></a>Some collection implementations
+ * have restrictions on the elements that they may contain.
+ * For example, some implementations prohibit null elements,
  * and some have restrictions on the types of their elements.  Attempting to
  * add an ineligible element throws an unchecked exception, typically
  * {@code NullPointerException} or {@code ClassCastException}.  Attempting
@@ -111,6 +107,86 @@ import java.util.stream.StreamSupport;
  * methods. Implementations may optionally handle the self-referential scenario,
  * however most current implementations do not do so.
  *
+ * <h2><a id="view">View Collections</a></h2>
+ *
+ * <p>Most collections manage storage for elements they contain. By contrast, <i>view
+ * collections</i> themselves do not store elements, but instead they rely on a
+ * backing collection to store the actual elements. Operations that are not handled
+ * by the view collection itself are delegated to the backing collection. Examples of
+ * view collections include the wrapper collections returned by methods such as
+ * {@link Collections#checkedCollection Collections.checkedCollection},
+ * {@link Collections#synchronizedCollection Collections.synchronizedCollection}, and
+ * {@link Collections#unmodifiableCollection Collections.unmodifiableCollection}.
+ * Other examples of view collections include collections that provide a
+ * different representation of the same elements, for example, as
+ * provided by {@link List#subList List.subList},
+ * {@link NavigableSet#subSet NavigableSet.subSet}, or
+ * {@link Map#entrySet Map.entrySet}.
+ * Any changes made to the backing collection are visible in the view collection.
+ * Correspondingly, any changes made to the view collection &mdash; if changes
+ * are permitted &mdash; are written through to the backing collection.
+ * Although they technically aren't collections, instances of
+ * {@link Iterator} and {@link ListIterator} can also allow modifications
+ * to be written through to the backing collection, and in some cases,
+ * modifications to the backing collection will be visible to the Iterator
+ * during iteration.
+ *
+ * <h2><a id="unmodifiable">Unmodifiable Collections</a></h2>
+ *
+ * <p>Certain methods of this interface are considered "destructive" and are called
+ * "mutator" methods in that they modify the group of objects contained within
+ * the collection on which they operate. They can be specified to throw
+ * {@code UnsupportedOperationException} if this collection implementation
+ * does not support the operation. Such methods should (but are not required
+ * to) throw an {@code UnsupportedOperationException} if the invocation would
+ * have no effect on the collection. For example, consider a collection that
+ * does not support the {@link #add add} operation. What will happen if the
+ * {@link #addAll addAll} method is invoked on this collection, with an empty
+ * collection as the argument? The addition of zero elements has no effect,
+ * so it is permissible for this collection simply to do nothing and not to throw
+ * an exception. However, it is recommended that such cases throw an exception
+ * unconditionally, as throwing only in certain cases can lead to
+ * programming errors.
+ *
+ * <p>An <i>unmodifiable collection</i> is a collection, all of whose
+ * mutator methods (as defined above) are specified to throw
+ * {@code UnsupportedOperationException}. Such a collection thus cannot be
+ * modified by calling any methods on it. For a collection to be properly
+ * unmodifiable, any view collections derived from it must also be unmodifiable.
+ * For example, if a List is unmodifiable, the List returned by
+ * {@link List#subList List.subList} is also unmodifiable.
+ *
+ * <p>An unmodifiable collection is not necessarily immutable. If the
+ * contained elements are mutable, the entire collection is clearly
+ * mutable, even though it might be unmodifiable. For example, consider
+ * two unmodifiable lists containing mutable elements. The result of calling
+ * {@code list1.equals(list2)} might differ from one call to the next if
+ * the elements had been mutated, even though both lists are unmodifiable.
+ * However, if an unmodifiable collection contains all immutable elements,
+ * it can be considered effectively immutable.
+ *
+ * <h2><a id="unmodview">Unmodifiable View Collections</a></h2>
+ *
+ * <p>An <i>unmodifiable view collection</i> is a collection that is unmodifiable
+ * and that is also a view onto a backing collection. Its mutator methods throw
+ * {@code UnsupportedOperationException}, as described above, while
+ * reading and querying methods are delegated to the backing collection.
+ * The effect is to provide read-only access to the backing collection.
+ * This is useful for a component to provide users with read access to
+ * an internal collection, while preventing them from modifying such
+ * collections unexpectedly. Examples of unmodifiable view collections
+ * are those returned by the
+ * {@link Collections#unmodifiableCollection Collections.unmodifiableCollection},
+ * {@link Collections#unmodifiableList Collections.unmodifiableList}, and
+ * related methods.
+ *
+ * <p>Note that changes to the backing collection might still be possible,
+ * and if they occur, they are visible through the unmodifiable view. Thus,
+ * an unmodifiable view collection is not necessarily immutable. However,
+ * if the backing collection of an unmodifiable view is effectively immutable,
+ * or if the only reference to the backing collection is through an
+ * unmodifiable view, the view can be considered effectively immutable.
+ *
  * <p>This interface is a member of the
  * <a href="{@docRoot}/java/util/package-summary.html#CollectionsFramework">
  * Java Collections Framework</a>.
diff --git a/src/java.base/share/classes/java/util/Collections.java b/src/java.base/share/classes/java/util/Collections.java
index 8aa9c53c455..4904ef56c18 100644
--- a/src/java.base/share/classes/java/util/Collections.java
+++ b/src/java.base/share/classes/java/util/Collections.java
@@ -989,9 +989,8 @@ public class Collections {
     // Unmodifiable Wrappers
 
     /**
-     * Returns an unmodifiable view of the specified collection.  This method
-     * allows modules to provide users with "read-only" access to internal
-     * collections.  Query operations on the returned collection "read through"
+     * Returns an <a href="Collection.html#unmodview">unmodifiable view</a> of the
+     * specified collection. Query operations on the returned collection "read through"
      * to the specified collection, and attempts to modify the returned
      * collection, whether direct or via its iterator, result in an
      * {@code UnsupportedOperationException}.<p>
@@ -1102,9 +1101,8 @@ public class Collections {
     }
 
     /**
-     * Returns an unmodifiable view of the specified set.  This method allows
-     * modules to provide users with "read-only" access to internal sets.
-     * Query operations on the returned set "read through" to the specified
+     * Returns an <a href="Collection.html#unmodview">unmodifiable view</a> of the
+     * specified set. Query operations on the returned set "read through" to the specified
      * set, and attempts to modify the returned set, whether direct or via its
      * iterator, result in an {@code UnsupportedOperationException}.<p>
      *
@@ -1132,9 +1130,8 @@ public class Collections {
     }
 
     /**
-     * Returns an unmodifiable view of the specified sorted set.  This method
-     * allows modules to provide users with "read-only" access to internal
-     * sorted sets.  Query operations on the returned sorted set "read
+     * Returns an <a href="Collection.html#unmodview">unmodifiable view</a> of the
+     * specified sorted set. Query operations on the returned sorted set "read
      * through" to the specified sorted set.  Attempts to modify the returned
      * sorted set, whether direct, via its iterator, or via its
      * {@code subSet}, {@code headSet}, or {@code tailSet} views, result in
@@ -1180,9 +1177,8 @@ public class Collections {
     }
 
     /**
-     * Returns an unmodifiable view of the specified navigable set.  This method
-     * allows modules to provide users with "read-only" access to internal
-     * navigable sets.  Query operations on the returned navigable set "read
+     * Returns an <a href="Collection.html#unmodview">unmodifiable view</a> of the
+     * specified navigable set. Query operations on the returned navigable set "read
      * through" to the specified navigable set.  Attempts to modify the returned
      * navigable set, whether direct, via its iterator, or via its
      * {@code subSet}, {@code headSet}, or {@code tailSet} views, result in
@@ -1269,9 +1265,8 @@ public class Collections {
     }
 
     /**
-     * Returns an unmodifiable view of the specified list.  This method allows
-     * modules to provide users with "read-only" access to internal
-     * lists.  Query operations on the returned list "read through" to the
+     * Returns an <a href="Collection.html#unmodview">unmodifiable view</a> of the
+     * specified list. Query operations on the returned list "read through" to the
      * specified list, and attempts to modify the returned list, whether
      * direct or via its iterator, result in an
      * {@code UnsupportedOperationException}.<p>
@@ -1415,9 +1410,8 @@ public class Collections {
     }
 
     /**
-     * Returns an unmodifiable view of the specified map.  This method
-     * allows modules to provide users with "read-only" access to internal
-     * maps.  Query operations on the returned map "read through"
+     * Returns an <a href="Collection.html#unmodview">unmodifiable view</a> of the
+     * specified map. Query operations on the returned map "read through"
      * to the specified map, and attempts to modify the returned
      * map, whether direct or via its collection views, result in an
      * {@code UnsupportedOperationException}.<p>
@@ -1765,9 +1759,8 @@ public class Collections {
     }
 
     /**
-     * Returns an unmodifiable view of the specified sorted map.  This method
-     * allows modules to provide users with "read-only" access to internal
-     * sorted maps.  Query operations on the returned sorted map "read through"
+     * Returns an <a href="Collection.html#unmodview">unmodifiable view</a> of the
+     * specified sorted map. Query operations on the returned sorted map "read through"
      * to the specified sorted map.  Attempts to modify the returned
      * sorted map, whether direct, via its collection views, or via its
      * {@code subMap}, {@code headMap}, or {@code tailMap} views, result in
@@ -1809,9 +1802,8 @@ public class Collections {
     }
 
     /**
-     * Returns an unmodifiable view of the specified navigable map.  This method
-     * allows modules to provide users with "read-only" access to internal
-     * navigable maps.  Query operations on the returned navigable map "read
+     * Returns an <a href="Collection.html#unmodview">unmodifiable view</a> of the
+     * specified navigable map. Query operations on the returned navigable map "read
      * through" to the specified navigable map.  Attempts to modify the returned
      * navigable map, whether direct, via its collection views, or via its
      * {@code subMap}, {@code headMap}, or {@code tailMap} views, result in
diff --git a/src/java.base/share/classes/java/util/EnumSet.java b/src/java.base/share/classes/java/util/EnumSet.java
index 0adeab410b0..88c3cb9056a 100644
--- a/src/java.base/share/classes/java/util/EnumSet.java
+++ b/src/java.base/share/classes/java/util/EnumSet.java
@@ -75,7 +75,6 @@ import jdk.internal.misc.SharedSecrets;
  * @author Josh Bloch
  * @since 1.5
  * @see EnumMap
- * @serial exclude
  */
 @SuppressWarnings("serial") // No serialVersionUID due to usage of
                             // serial proxy pattern
@@ -85,12 +84,12 @@ public abstract class EnumSet<E extends Enum<E>> extends AbstractSet<E>
     /**
      * The class of all the elements of this set.
      */
-    final Class<E> elementType;
+    final transient Class<E> elementType;
 
     /**
-     * All of the values comprising T.  (Cached for performance.)
+     * All of the values comprising E.  (Cached for performance.)
      */
-    final Enum<?>[] universe;
+    final transient Enum<?>[] universe;
 
     EnumSet(Class<E>elementType, Enum<?>[] universe) {
         this.elementType = elementType;
@@ -416,7 +415,7 @@ public abstract class EnumSet<E extends Enum<E>> extends AbstractSet<E>
      *
      * @serial include
      */
-    private static class SerializationProxy <E extends Enum<E>>
+    private static class SerializationProxy<E extends Enum<E>>
         implements java.io.Serializable
     {
 
@@ -441,10 +440,18 @@ public abstract class EnumSet<E extends Enum<E>> extends AbstractSet<E>
             elements = set.toArray(ZERO_LENGTH_ENUM_ARRAY);
         }
 
-        // instead of cast to E, we should perhaps use elementType.cast()
-        // to avoid injection of forged stream, but it will slow the implementation
+        /**
+         * Returns an {@code EnumSet} object with initial state
+         * held by this proxy.
+         *
+         * @return a {@code EnumSet} object with initial state
+         * held by this proxy
+         */
         @SuppressWarnings("unchecked")
         private Object readResolve() {
+            // instead of cast to E, we should perhaps use elementType.cast()
+            // to avoid injection of forged stream, but it will slow the
+            // implementation
             EnumSet<E> result = EnumSet.noneOf(elementType);
             for (Enum<?> e : elements)
                 result.add((E)e);
@@ -454,13 +461,24 @@ public abstract class EnumSet<E extends Enum<E>> extends AbstractSet<E>
         private static final long serialVersionUID = 362491234563181265L;
     }
 
+    /**
+     * Returns a
+     * <a href="../../serialized-form.html#java.util.EnumSet.SerializationProxy">
+     * SerializationProxy</a>
+     * representing the state of this instance.
+     *
+     * @return a {@link SerializationProxy}
+     * representing the state of this instance
+     */
     Object writeReplace() {
         return new SerializationProxy<>(this);
     }
 
-    // readObject method for the serialization proxy pattern
-    // See Effective Java, Second Ed., Item 78.
-    private void readObject(java.io.ObjectInputStream stream)
+    /**
+     * @param s the stream
+     * @throws java.io.InvalidObjectException always
+     */
+    private void readObject(java.io.ObjectInputStream s)
         throws java.io.InvalidObjectException {
         throw new java.io.InvalidObjectException("Proxy required");
     }
diff --git a/src/java.base/share/classes/java/util/List.java b/src/java.base/share/classes/java/util/List.java
index 8ba368bf6f6..5672508afb1 100644
--- a/src/java.base/share/classes/java/util/List.java
+++ b/src/java.base/share/classes/java/util/List.java
@@ -87,15 +87,16 @@ import java.util.function.UnaryOperator;
  * Such exceptions are marked as "optional" in the specification for this
  * interface.
  *
- * <h2><a id="immutable">Immutable List Static Factory Methods</a></h2>
- * <p>The {@link List#of(Object...) List.of()} static factory methods
- * provide a convenient way to create immutable lists. The {@code List}
+ * <h2><a id="unmodifiable">Unmodifiable Lists</a></h2>
+ * <p>The {@link List#of(Object...) List.of} and
+ * {@link List#copyOf List.copyOf} static factory methods
+ * provide a convenient way to create unmodifiable lists. The {@code List}
  * instances created by these methods have the following characteristics:
  *
  * <ul>
- * <li>They are <em>structurally immutable</em>. Elements cannot be added, removed,
- * or replaced. Calling any mutator method will always cause
- * {@code UnsupportedOperationException} to be thrown.
+ * <li>They are <a href="Collection.html#unmodifiable"><i>unmodifiable</i></a>. Elements cannot
+ * be added, removed, or replaced. Calling any mutator method on the List
+ * will always cause {@code UnsupportedOperationException} to be thrown.
  * However, if the contained elements are themselves mutable,
  * this may cause the List's contents to appear to change.
  * <li>They disallow {@code null} elements. Attempts to create them with
@@ -777,9 +778,9 @@ public interface List<E> extends Collection<E> {
     }
 
     /**
-     * Returns an immutable list containing zero elements.
+     * Returns an unmodifiable list containing zero elements.
      *
-     * See <a href="#immutable">Immutable List Static Factory Methods</a> for details.
+     * See <a href="#unmodifiable">Unmodifiable Lists</a> for details.
      *
      * @param <E> the {@code List}'s element type
      * @return an empty {@code List}
@@ -791,9 +792,9 @@ public interface List<E> extends Collection<E> {
     }
 
     /**
-     * Returns an immutable list containing one element.
+     * Returns an unmodifiable list containing one element.
      *
-     * See <a href="#immutable">Immutable List Static Factory Methods</a> for details.
+     * See <a href="#unmodifiable">Unmodifiable Lists</a> for details.
      *
      * @param <E> the {@code List}'s element type
      * @param e1 the single element
@@ -807,9 +808,9 @@ public interface List<E> extends Collection<E> {
     }
 
     /**
-     * Returns an immutable list containing two elements.
+     * Returns an unmodifiable list containing two elements.
      *
-     * See <a href="#immutable">Immutable List Static Factory Methods</a> for details.
+     * See <a href="#unmodifiable">Unmodifiable Lists</a> for details.
      *
      * @param <E> the {@code List}'s element type
      * @param e1 the first element
@@ -824,9 +825,9 @@ public interface List<E> extends Collection<E> {
     }
 
     /**
-     * Returns an immutable list containing three elements.
+     * Returns an unmodifiable list containing three elements.
      *
-     * See <a href="#immutable">Immutable List Static Factory Methods</a> for details.
+     * See <a href="#unmodifiable">Unmodifiable Lists</a> for details.
      *
      * @param <E> the {@code List}'s element type
      * @param e1 the first element
@@ -842,9 +843,9 @@ public interface List<E> extends Collection<E> {
     }
 
     /**
-     * Returns an immutable list containing four elements.
+     * Returns an unmodifiable list containing four elements.
      *
-     * See <a href="#immutable">Immutable List Static Factory Methods</a> for details.
+     * See <a href="#unmodifiable">Unmodifiable Lists</a> for details.
      *
      * @param <E> the {@code List}'s element type
      * @param e1 the first element
@@ -861,9 +862,9 @@ public interface List<E> extends Collection<E> {
     }
 
     /**
-     * Returns an immutable list containing five elements.
+     * Returns an unmodifiable list containing five elements.
      *
-     * See <a href="#immutable">Immutable List Static Factory Methods</a> for details.
+     * See <a href="#unmodifiable">Unmodifiable Lists</a> for details.
      *
      * @param <E> the {@code List}'s element type
      * @param e1 the first element
@@ -881,9 +882,9 @@ public interface List<E> extends Collection<E> {
     }
 
     /**
-     * Returns an immutable list containing six elements.
+     * Returns an unmodifiable list containing six elements.
      *
-     * See <a href="#immutable">Immutable List Static Factory Methods</a> for details.
+     * See <a href="#unmodifiable">Unmodifiable Lists</a> for details.
      *
      * @param <E> the {@code List}'s element type
      * @param e1 the first element
@@ -903,9 +904,9 @@ public interface List<E> extends Collection<E> {
     }
 
     /**
-     * Returns an immutable list containing seven elements.
+     * Returns an unmodifiable list containing seven elements.
      *
-     * See <a href="#immutable">Immutable List Static Factory Methods</a> for details.
+     * See <a href="#unmodifiable">Unmodifiable Lists</a> for details.
      *
      * @param <E> the {@code List}'s element type
      * @param e1 the first element
@@ -926,9 +927,9 @@ public interface List<E> extends Collection<E> {
     }
 
     /**
-     * Returns an immutable list containing eight elements.
+     * Returns an unmodifiable list containing eight elements.
      *
-     * See <a href="#immutable">Immutable List Static Factory Methods</a> for details.
+     * See <a href="#unmodifiable">Unmodifiable Lists</a> for details.
      *
      * @param <E> the {@code List}'s element type
      * @param e1 the first element
@@ -950,9 +951,9 @@ public interface List<E> extends Collection<E> {
     }
 
     /**
-     * Returns an immutable list containing nine elements.
+     * Returns an unmodifiable list containing nine elements.
      *
-     * See <a href="#immutable">Immutable List Static Factory Methods</a> for details.
+     * See <a href="#unmodifiable">Unmodifiable Lists</a> for details.
      *
      * @param <E> the {@code List}'s element type
      * @param e1 the first element
@@ -975,9 +976,9 @@ public interface List<E> extends Collection<E> {
     }
 
     /**
-     * Returns an immutable list containing ten elements.
+     * Returns an unmodifiable list containing ten elements.
      *
-     * See <a href="#immutable">Immutable List Static Factory Methods</a> for details.
+     * See <a href="#unmodifiable">Unmodifiable Lists</a> for details.
      *
      * @param <E> the {@code List}'s element type
      * @param e1 the first element
@@ -1001,8 +1002,8 @@ public interface List<E> extends Collection<E> {
     }
 
     /**
-     * Returns an immutable list containing an arbitrary number of elements.
-     * See <a href="#immutable">Immutable List Static Factory Methods</a> for details.
+     * Returns an unmodifiable list containing an arbitrary number of elements.
+     * See <a href="#unmodifiable">Unmodifiable Lists</a> for details.
      *
      * @apiNote
      * This method also accepts a single array as an argument. The element type of
@@ -1039,4 +1040,29 @@ public interface List<E> extends Collection<E> {
                 return new ImmutableCollections.ListN<>(elements);
         }
     }
+
+    /**
+     * Returns an <a href="#unmodifiable">unmodifiable List</a> containing the elements of
+     * the given Collection, in its iteration order. The given Collection must not be null,
+     * and it must not contain any null elements. If the given Collection is subsequently
+     * modified, the returned List will not reflect such modifications.
+     *
+     * @implNote
+     * If the given Collection is an <a href="#unmodifiable">unmodifiable List</a>,
+     * calling copyOf will generally not create a copy.
+     *
+     * @param <E> the {@code List}'s element type
+     * @param coll a {@code Collection} from which elements are drawn, must be non-null
+     * @return a {@code List} containing the elements of the given {@code Collection}
+     * @throws NullPointerException if coll is null, or if it contains any nulls
+     * @since 10
+     */
+    @SuppressWarnings("unchecked")
+    static <E> List<E> copyOf(Collection<? extends E> coll) {
+        if (coll instanceof ImmutableCollections.AbstractImmutableList) {
+            return (List<E>)coll;
+        } else {
+            return (List<E>)List.of(coll.toArray());
+        }
+    }
 }
diff --git a/src/java.base/share/classes/java/util/Map.java b/src/java.base/share/classes/java/util/Map.java
index e3bee23b7ea..efe330e80ad 100644
--- a/src/java.base/share/classes/java/util/Map.java
+++ b/src/java.base/share/classes/java/util/Map.java
@@ -110,17 +110,18 @@ import java.io.Serializable;
  * Implementations may optionally handle the self-referential scenario, however
  * most current implementations do not do so.
  *
- * <h2><a id="immutable">Immutable Map Static Factory Methods</a></h2>
- * <p>The {@link Map#of() Map.of()} and
- * {@link Map#ofEntries(Map.Entry...) Map.ofEntries()}
- * static factory methods provide a convenient way to create immutable maps.
+ * <h2><a id="unmodifiable">Unmodifiable Maps</a></h2>
+ * <p>The {@link Map#of() Map.of},
+ * {@link Map#ofEntries(Map.Entry...) Map.ofEntries}, and
+ * {@link Map#copyOf Map.copyOf}
+ * static factory methods provide a convenient way to create unmodifiable maps.
  * The {@code Map}
  * instances created by these methods have the following characteristics:
  *
  * <ul>
- * <li>They are <em>structurally immutable</em>. Keys and values cannot be added,
- * removed, or updated. Calling any mutator method will always cause
- * {@code UnsupportedOperationException} to be thrown.
+ * <li>They are <a href="Collection.html#unmodifiable"><i>unmodifiable</i></a>. Keys and values
+ * cannot be added, removed, or updated. Calling any mutator method on the Map
+ * will always cause {@code UnsupportedOperationException} to be thrown.
  * However, if the contained keys or values are themselves mutable, this may cause the
  * Map to behave inconsistently or its contents to appear to change.
  * <li>They disallow {@code null} keys and values. Attempts to create them with
@@ -1276,8 +1277,8 @@ public interface Map<K, V> {
     }
 
     /**
-     * Returns an immutable map containing zero mappings.
-     * See <a href="#immutable">Immutable Map Static Factory Methods</a> for details.
+     * Returns an unmodifiable map containing zero mappings.
+     * See <a href="#unmodifiable">Unmodifiable Maps</a> for details.
      *
      * @param <K> the {@code Map}'s key type
      * @param <V> the {@code Map}'s value type
@@ -1290,8 +1291,8 @@ public interface Map<K, V> {
     }
 
     /**
-     * Returns an immutable map containing a single mapping.
-     * See <a href="#immutable">Immutable Map Static Factory Methods</a> for details.
+     * Returns an unmodifiable map containing a single mapping.
+     * See <a href="#unmodifiable">Unmodifiable Maps</a> for details.
      *
      * @param <K> the {@code Map}'s key type
      * @param <V> the {@code Map}'s value type
@@ -1307,8 +1308,8 @@ public interface Map<K, V> {
     }
 
     /**
-     * Returns an immutable map containing two mappings.
-     * See <a href="#immutable">Immutable Map Static Factory Methods</a> for details.
+     * Returns an unmodifiable map containing two mappings.
+     * See <a href="#unmodifiable">Unmodifiable Maps</a> for details.
      *
      * @param <K> the {@code Map}'s key type
      * @param <V> the {@code Map}'s value type
@@ -1327,8 +1328,8 @@ public interface Map<K, V> {
     }
 
     /**
-     * Returns an immutable map containing three mappings.
-     * See <a href="#immutable">Immutable Map Static Factory Methods</a> for details.
+     * Returns an unmodifiable map containing three mappings.
+     * See <a href="#unmodifiable">Unmodifiable Maps</a> for details.
      *
      * @param <K> the {@code Map}'s key type
      * @param <V> the {@code Map}'s value type
@@ -1349,8 +1350,8 @@ public interface Map<K, V> {
     }
 
     /**
-     * Returns an immutable map containing four mappings.
-     * See <a href="#immutable">Immutable Map Static Factory Methods</a> for details.
+     * Returns an unmodifiable map containing four mappings.
+     * See <a href="#unmodifiable">Unmodifiable Maps</a> for details.
      *
      * @param <K> the {@code Map}'s key type
      * @param <V> the {@code Map}'s value type
@@ -1373,8 +1374,8 @@ public interface Map<K, V> {
     }
 
     /**
-     * Returns an immutable map containing five mappings.
-     * See <a href="#immutable">Immutable Map Static Factory Methods</a> for details.
+     * Returns an unmodifiable map containing five mappings.
+     * See <a href="#unmodifiable">Unmodifiable Maps</a> for details.
      *
      * @param <K> the {@code Map}'s key type
      * @param <V> the {@code Map}'s value type
@@ -1399,8 +1400,8 @@ public interface Map<K, V> {
     }
 
     /**
-     * Returns an immutable map containing six mappings.
-     * See <a href="#immutable">Immutable Map Static Factory Methods</a> for details.
+     * Returns an unmodifiable map containing six mappings.
+     * See <a href="#unmodifiable">Unmodifiable Maps</a> for details.
      *
      * @param <K> the {@code Map}'s key type
      * @param <V> the {@code Map}'s value type
@@ -1429,8 +1430,8 @@ public interface Map<K, V> {
     }
 
     /**
-     * Returns an immutable map containing seven mappings.
-     * See <a href="#immutable">Immutable Map Static Factory Methods</a> for details.
+     * Returns an unmodifiable map containing seven mappings.
+     * See <a href="#unmodifiable">Unmodifiable Maps</a> for details.
      *
      * @param <K> the {@code Map}'s key type
      * @param <V> the {@code Map}'s value type
@@ -1461,8 +1462,8 @@ public interface Map<K, V> {
     }
 
     /**
-     * Returns an immutable map containing eight mappings.
-     * See <a href="#immutable">Immutable Map Static Factory Methods</a> for details.
+     * Returns an unmodifiable map containing eight mappings.
+     * See <a href="#unmodifiable">Unmodifiable Maps</a> for details.
      *
      * @param <K> the {@code Map}'s key type
      * @param <V> the {@code Map}'s value type
@@ -1495,8 +1496,8 @@ public interface Map<K, V> {
     }
 
     /**
-     * Returns an immutable map containing nine mappings.
-     * See <a href="#immutable">Immutable Map Static Factory Methods</a> for details.
+     * Returns an unmodifiable map containing nine mappings.
+     * See <a href="#unmodifiable">Unmodifiable Maps</a> for details.
      *
      * @param <K> the {@code Map}'s key type
      * @param <V> the {@code Map}'s value type
@@ -1531,8 +1532,8 @@ public interface Map<K, V> {
     }
 
     /**
-     * Returns an immutable map containing ten mappings.
-     * See <a href="#immutable">Immutable Map Static Factory Methods</a> for details.
+     * Returns an unmodifiable map containing ten mappings.
+     * See <a href="#unmodifiable">Unmodifiable Maps</a> for details.
      *
      * @param <K> the {@code Map}'s key type
      * @param <V> the {@code Map}'s value type
@@ -1569,9 +1570,9 @@ public interface Map<K, V> {
     }
 
     /**
-     * Returns an immutable map containing keys and values extracted from the given entries.
+     * Returns an unmodifiable map containing keys and values extracted from the given entries.
      * The entries themselves are not stored in the map.
-     * See <a href="#immutable">Immutable Map Static Factory Methods</a> for details.
+     * See <a href="#unmodifiable">Unmodifiable Maps</a> for details.
      *
      * @apiNote
      * It is convenient to create the map entries using the {@link Map#entry Map.entry()} method.
@@ -1602,15 +1603,17 @@ public interface Map<K, V> {
     @SafeVarargs
     @SuppressWarnings("varargs")
     static <K, V> Map<K, V> ofEntries(Entry<? extends K, ? extends V>... entries) {
-        if (entries.length == 0) { // implicit null check of entries
+        if (entries.length == 0) { // implicit null check of entries array
             return ImmutableCollections.Map0.instance();
         } else if (entries.length == 1) {
+            // implicit null check of the array slot
             return new ImmutableCollections.Map1<>(entries[0].getKey(),
                                                    entries[0].getValue());
         } else {
             Object[] kva = new Object[entries.length << 1];
             int a = 0;
             for (Entry<? extends K, ? extends V> entry : entries) {
+                // implicit null checks of each array slot
                 kva[a++] = entry.getKey();
                 kva[a++] = entry.getValue();
             }
@@ -1619,7 +1622,7 @@ public interface Map<K, V> {
     }
 
     /**
-     * Returns an immutable {@link Entry} containing the given key and value.
+     * Returns an unmodifiable {@link Entry} containing the given key and value.
      * These entries are suitable for populating {@code Map} instances using the
      * {@link Map#ofEntries Map.ofEntries()} method.
      * The {@code Entry} instances created by this method have the following characteristics:
@@ -1627,7 +1630,7 @@ public interface Map<K, V> {
      * <ul>
      * <li>They disallow {@code null} keys and values. Attempts to create them using a {@code null}
      * key or value result in {@code NullPointerException}.
-     * <li>They are immutable. Calls to {@link Entry#setValue Entry.setValue()}
+     * <li>They are unmodifiable. Calls to {@link Entry#setValue Entry.setValue()}
      * on a returned {@code Entry} result in {@code UnsupportedOperationException}.
      * <li>They are not serializable.
      * <li>They are <a href="../lang/doc-files/ValueBased.html">value-based</a>.
@@ -1655,4 +1658,30 @@ public interface Map<K, V> {
         // KeyValueHolder checks for nulls
         return new KeyValueHolder<>(k, v);
     }
+
+    /**
+     * Returns an <a href="#unmodifiable">unmodifiable Map</a> containing the entries
+     * of the given Map. The given Map must not be null, and it must not contain any
+     * null keys or values. If the given Map is subsequently modified, the returned
+     * Map will not reflect such modifications.
+     *
+     * @implNote
+     * If the given Map is an <a href="#unmodifiable">unmodifiable Map</a>,
+     * calling copyOf will generally not create a copy.
+     *
+     * @param <K> the {@code Map}'s key type
+     * @param <V> the {@code Map}'s value type
+     * @param map a {@code Map} from which entries are drawn, must be non-null
+     * @return a {@code Map} containing the entries of the given {@code Map}
+     * @throws NullPointerException if map is null, or if it contains any null keys or values
+     * @since 10
+     */
+    @SuppressWarnings({"rawtypes","unchecked"})
+    static <K, V> Map<K, V> copyOf(Map<? extends K, ? extends V> map) {
+        if (map instanceof ImmutableCollections.AbstractImmutableMap) {
+            return (Map<K,V>)map;
+        } else {
+            return (Map<K,V>)Map.ofEntries(map.entrySet().toArray(new Entry[0]));
+        }
+    }
 }
diff --git a/src/java.base/share/classes/java/util/Set.java b/src/java.base/share/classes/java/util/Set.java
index 32966e993e3..b662896dd7f 100644
--- a/src/java.base/share/classes/java/util/Set.java
+++ b/src/java.base/share/classes/java/util/Set.java
@@ -63,15 +63,16 @@ package java.util;
  * Such exceptions are marked as "optional" in the specification for this
  * interface.
  *
- * <h2><a id="immutable">Immutable Set Static Factory Methods</a></h2>
- * <p>The {@link Set#of(Object...) Set.of()} static factory methods
- * provide a convenient way to create immutable sets. The {@code Set}
+ * <h2><a id="unmodifiable">Unmodifiable Sets</a></h2>
+ * <p>The {@link Set#of(Object...) Set.of} and
+ * {@link Set#copyOf Set.copyOf} static factory methods
+ * provide a convenient way to create unmodifiable sets. The {@code Set}
  * instances created by these methods have the following characteristics:
  *
  * <ul>
- * <li>They are <em>structurally immutable</em>. Elements cannot be added or
- * removed. Calling any mutator method will always cause
- * {@code UnsupportedOperationException} to be thrown.
+ * <li>They are <a href="Collection.html#unmodifiable"><i>unmodifiable</i></a>. Elements cannot
+ * be added or removed. Calling any mutator method on the Set
+ * will always cause {@code UnsupportedOperationException} to be thrown.
  * However, if the contained elements are themselves mutable, this may cause the
  * Set to behave inconsistently or its contents to appear to change.
  * <li>They disallow {@code null} elements. Attempts to create them with
@@ -439,8 +440,8 @@ public interface Set<E> extends Collection<E> {
     }
 
     /**
-     * Returns an immutable set containing zero elements.
-     * See <a href="#immutable">Immutable Set Static Factory Methods</a> for details.
+     * Returns an unmodifiable set containing zero elements.
+     * See <a href="#unmodifiable">Unmodifiable Sets</a> for details.
      *
      * @param <E> the {@code Set}'s element type
      * @return an empty {@code Set}
@@ -452,8 +453,8 @@ public interface Set<E> extends Collection<E> {
     }
 
     /**
-     * Returns an immutable set containing one element.
-     * See <a href="#immutable">Immutable Set Static Factory Methods</a> for details.
+     * Returns an unmodifiable set containing one element.
+     * See <a href="#unmodifiable">Unmodifiable Sets</a> for details.
      *
      * @param <E> the {@code Set}'s element type
      * @param e1 the single element
@@ -467,8 +468,8 @@ public interface Set<E> extends Collection<E> {
     }
 
     /**
-     * Returns an immutable set containing two elements.
-     * See <a href="#immutable">Immutable Set Static Factory Methods</a> for details.
+     * Returns an unmodifiable set containing two elements.
+     * See <a href="#unmodifiable">Unmodifiable Sets</a> for details.
      *
      * @param <E> the {@code Set}'s element type
      * @param e1 the first element
@@ -484,8 +485,8 @@ public interface Set<E> extends Collection<E> {
     }
 
     /**
-     * Returns an immutable set containing three elements.
-     * See <a href="#immutable">Immutable Set Static Factory Methods</a> for details.
+     * Returns an unmodifiable set containing three elements.
+     * See <a href="#unmodifiable">Unmodifiable Sets</a> for details.
      *
      * @param <E> the {@code Set}'s element type
      * @param e1 the first element
@@ -502,8 +503,8 @@ public interface Set<E> extends Collection<E> {
     }
 
     /**
-     * Returns an immutable set containing four elements.
-     * See <a href="#immutable">Immutable Set Static Factory Methods</a> for details.
+     * Returns an unmodifiable set containing four elements.
+     * See <a href="#unmodifiable">Unmodifiable Sets</a> for details.
      *
      * @param <E> the {@code Set}'s element type
      * @param e1 the first element
@@ -521,8 +522,8 @@ public interface Set<E> extends Collection<E> {
     }
 
     /**
-     * Returns an immutable set containing five elements.
-     * See <a href="#immutable">Immutable Set Static Factory Methods</a> for details.
+     * Returns an unmodifiable set containing five elements.
+     * See <a href="#unmodifiable">Unmodifiable Sets</a> for details.
      *
      * @param <E> the {@code Set}'s element type
      * @param e1 the first element
@@ -541,8 +542,8 @@ public interface Set<E> extends Collection<E> {
     }
 
     /**
-     * Returns an immutable set containing six elements.
-     * See <a href="#immutable">Immutable Set Static Factory Methods</a> for details.
+     * Returns an unmodifiable set containing six elements.
+     * See <a href="#unmodifiable">Unmodifiable Sets</a> for details.
      *
      * @param <E> the {@code Set}'s element type
      * @param e1 the first element
@@ -563,8 +564,8 @@ public interface Set<E> extends Collection<E> {
     }
 
     /**
-     * Returns an immutable set containing seven elements.
-     * See <a href="#immutable">Immutable Set Static Factory Methods</a> for details.
+     * Returns an unmodifiable set containing seven elements.
+     * See <a href="#unmodifiable">Unmodifiable Sets</a> for details.
      *
      * @param <E> the {@code Set}'s element type
      * @param e1 the first element
@@ -586,8 +587,8 @@ public interface Set<E> extends Collection<E> {
     }
 
     /**
-     * Returns an immutable set containing eight elements.
-     * See <a href="#immutable">Immutable Set Static Factory Methods</a> for details.
+     * Returns an unmodifiable set containing eight elements.
+     * See <a href="#unmodifiable">Unmodifiable Sets</a> for details.
      *
      * @param <E> the {@code Set}'s element type
      * @param e1 the first element
@@ -610,8 +611,8 @@ public interface Set<E> extends Collection<E> {
     }
 
     /**
-     * Returns an immutable set containing nine elements.
-     * See <a href="#immutable">Immutable Set Static Factory Methods</a> for details.
+     * Returns an unmodifiable set containing nine elements.
+     * See <a href="#unmodifiable">Unmodifiable Sets</a> for details.
      *
      * @param <E> the {@code Set}'s element type
      * @param e1 the first element
@@ -635,8 +636,8 @@ public interface Set<E> extends Collection<E> {
     }
 
     /**
-     * Returns an immutable set containing ten elements.
-     * See <a href="#immutable">Immutable Set Static Factory Methods</a> for details.
+     * Returns an unmodifiable set containing ten elements.
+     * See <a href="#unmodifiable">Unmodifiable Sets</a> for details.
      *
      * @param <E> the {@code Set}'s element type
      * @param e1 the first element
@@ -661,8 +662,8 @@ public interface Set<E> extends Collection<E> {
     }
 
     /**
-     * Returns an immutable set containing an arbitrary number of elements.
-     * See <a href="#immutable">Immutable Set Static Factory Methods</a> for details.
+     * Returns an unmodifiable set containing an arbitrary number of elements.
+     * See <a href="#unmodifiable">Unmodifiable Sets</a> for details.
      *
      * @apiNote
      * This method also accepts a single array as an argument. The element type of
@@ -700,4 +701,30 @@ public interface Set<E> extends Collection<E> {
                 return new ImmutableCollections.SetN<>(elements);
         }
     }
+
+    /**
+     * Returns an <a href="#unmodifiable">unmodifiable Set</a> containing the elements
+     * of the given Collection. The given Collection must not be null, and it must not
+     * contain any null elements. If the given Collection contains duplicate elements,
+     * an arbitrary element of the duplicates is preserved. If the given Collection is
+     * subsequently modified, the returned Set will not reflect such modifications.
+     *
+     * @implNote
+     * If the given Collection is an <a href="#unmodifiable">unmodifiable Set</a>,
+     * calling copyOf will generally not create a copy.
+     *
+     * @param <E> the {@code Set}'s element type
+     * @param coll a {@code Collection} from which elements are drawn, must be non-null
+     * @return a {@code Set} containing the elements of the given {@code Collection}
+     * @throws NullPointerException if coll is null, or if it contains any nulls
+     * @since 10
+     */
+    @SuppressWarnings("unchecked")
+    static <E> Set<E> copyOf(Collection<? extends E> coll) {
+        if (coll instanceof ImmutableCollections.AbstractImmutableSet) {
+            return (Set<E>)coll;
+        } else {
+            return (Set<E>)Set.of(new HashSet<>(coll).toArray());
+        }
+    }
 }
diff --git a/src/java.base/share/classes/java/util/StringJoiner.java b/src/java.base/share/classes/java/util/StringJoiner.java
index 3dca2df4d46..c72d5a213bd 100644
--- a/src/java.base/share/classes/java/util/StringJoiner.java
+++ b/src/java.base/share/classes/java/util/StringJoiner.java
@@ -24,9 +24,6 @@
  */
 package java.util;
 
-import jdk.internal.misc.JavaLangAccess;
-import jdk.internal.misc.SharedSecrets;
-
 /**
  * {@code StringJoiner} is used to construct a sequence of characters separated
  * by a delimiter and optionally starting with a supplied prefix
@@ -86,8 +83,6 @@ public final class StringJoiner {
      */
     private String emptyValue;
 
-    private static final JavaLangAccess jla = SharedSecrets.getJavaLangAccess();
-
     /**
      * Constructs a {@code StringJoiner} with no characters in it, with no
      * {@code prefix} or {@code suffix}, and a copy of the supplied
@@ -189,7 +184,7 @@ public final class StringJoiner {
             }
         }
         k += getChars(suffix, chars, k);
-        return jla.newStringUnsafe(chars);
+        return new String(chars);
     }
 
     /**
@@ -252,7 +247,7 @@ public final class StringJoiner {
                 elts[i] = null;
             } while (++i < size);
             size = 1;
-            elts[0] = jla.newStringUnsafe(chars);
+            elts[0] = new String(chars);
         }
     }
 
diff --git a/src/java.base/share/classes/java/util/concurrent/CountedCompleter.java b/src/java.base/share/classes/java/util/concurrent/CountedCompleter.java
index 9d273a56fde..9321ccfbfb0 100644
--- a/src/java.base/share/classes/java/util/concurrent/CountedCompleter.java
+++ b/src/java.base/share/classes/java/util/concurrent/CountedCompleter.java
@@ -735,7 +735,7 @@ public abstract class CountedCompleter<T> extends ForkJoinTask<T> {
         CountedCompleter<?> a = this, s = a;
         while (a.onExceptionalCompletion(ex, s) &&
                (a = (s = a).completer) != null && a.status >= 0 &&
-               a.recordExceptionalCompletion(ex) == EXCEPTIONAL)
+               isExceptionalStatus(a.recordExceptionalCompletion(ex)))
             ;
     }
 
diff --git a/src/java.base/share/classes/java/util/concurrent/ForkJoinTask.java b/src/java.base/share/classes/java/util/concurrent/ForkJoinTask.java
index 193cb9e81cb..e4e3aa8f934 100644
--- a/src/java.base/share/classes/java/util/concurrent/ForkJoinTask.java
+++ b/src/java.base/share/classes/java/util/concurrent/ForkJoinTask.java
@@ -219,52 +219,59 @@ public abstract class ForkJoinTask<V> implements Future<V>, Serializable {
      * methods in a way that flows well in javadocs.
      */
 
-    /*
+    /**
      * The status field holds run control status bits packed into a
-     * single int to minimize footprint and to ensure atomicity (via
-     * CAS).  Status is initially zero, and takes on nonnegative
-     * values until completed, upon which status (anded with
-     * DONE_MASK) holds value NORMAL, CANCELLED, or EXCEPTIONAL. Tasks
-     * undergoing blocking waits by other threads have the SIGNAL bit
-     * set.  Completion of a stolen task with SIGNAL set awakens any
-     * waiters via notifyAll. Even though suboptimal for some
-     * purposes, we use basic builtin wait/notify to take advantage of
-     * "monitor inflation" in JVMs that we would otherwise need to
-     * emulate to avoid adding further per-task bookkeeping overhead.
-     * We want these monitors to be "fat", i.e., not use biasing or
-     * thin-lock techniques, so use some odd coding idioms that tend
-     * to avoid them, mainly by arranging that every synchronized
-     * block performs a wait, notifyAll or both.
+     * single int to ensure atomicity.  Status is initially zero, and
+     * takes on nonnegative values until completed, upon which it
+     * holds (sign bit) DONE, possibly with ABNORMAL (cancelled or
+     * exceptional) and THROWN (in which case an exception has been
+     * stored). Tasks with dependent blocked waiting joiners have the
+     * SIGNAL bit set.  Completion of a task with SIGNAL set awakens
+     * any waiters via notifyAll. (Waiters also help signal others
+     * upon completion.)
      *
      * These control bits occupy only (some of) the upper half (16
      * bits) of status field. The lower bits are used for user-defined
      * tags.
      */
-
-    /** The run status of this task */
     volatile int status; // accessed directly by pool and workers
-    static final int DONE_MASK   = 0xf0000000;  // mask out non-completion bits
-    static final int NORMAL      = 0xf0000000;  // must be negative
-    static final int CANCELLED   = 0xc0000000;  // must be < NORMAL
-    static final int EXCEPTIONAL = 0x80000000;  // must be < CANCELLED
-    static final int SIGNAL      = 0x00010000;  // must be >= 1 << 16
-    static final int SMASK       = 0x0000ffff;  // short bits for tags
+
+    private static final int DONE     = 1 << 31; // must be negative
+    private static final int ABNORMAL = 1 << 18; // set atomically with DONE
+    private static final int THROWN   = 1 << 17; // set atomically with ABNORMAL
+    private static final int SIGNAL   = 1 << 16; // true if joiner waiting
+    private static final int SMASK    = 0xffff;  // short bits for tags
+
+    static boolean isExceptionalStatus(int s) {  // needed by subclasses
+        return (s & THROWN) != 0;
+    }
 
     /**
-     * Marks completion and wakes up threads waiting to join this
-     * task.
+     * Sets DONE status and wakes up threads waiting to join this task.
      *
-     * @param completion one of NORMAL, CANCELLED, EXCEPTIONAL
-     * @return completion status on exit
+     * @return status on exit
      */
-    private int setCompletion(int completion) {
-        for (int s;;) {
+    private int setDone() {
+        int s;
+        if (((s = (int)STATUS.getAndBitwiseOr(this, DONE)) & SIGNAL) != 0)
+            synchronized (this) { notifyAll(); }
+        return s | DONE;
+    }
+
+    /**
+     * Marks cancelled or exceptional completion unless already done.
+     *
+     * @param completion must be DONE | ABNORMAL, ORed with THROWN if exceptional
+     * @return status on exit
+     */
+    private int abnormalCompletion(int completion) {
+        for (int s, ns;;) {
             if ((s = status) < 0)
                 return s;
-            if (STATUS.compareAndSet(this, s, s | completion)) {
-                if ((s >>> 16) != 0)
+            else if (STATUS.weakCompareAndSet(this, s, ns = s | completion)) {
+                if ((s & SIGNAL) != 0)
                     synchronized (this) { notifyAll(); }
-                return completion;
+                return ns;
             }
         }
     }
@@ -282,10 +289,11 @@ public abstract class ForkJoinTask<V> implements Future<V>, Serializable {
             try {
                 completed = exec();
             } catch (Throwable rex) {
-                return setExceptionalCompletion(rex);
+                completed = false;
+                s = setExceptionalCompletion(rex);
             }
             if (completed)
-                s = setCompletion(NORMAL);
+                s = setDone();
         }
         return s;
     }
@@ -297,9 +305,7 @@ public abstract class ForkJoinTask<V> implements Future<V>, Serializable {
      * @param timeout using Object.wait conventions.
      */
     final void internalWait(long timeout) {
-        int s;
-        if ((s = status) >= 0 && // force completer to issue notify
-            STATUS.compareAndSet(this, s, s | SIGNAL)) {
+        if ((int)STATUS.getAndBitwiseOr(this, SIGNAL) >= 0) {
             synchronized (this) {
                 if (status >= 0)
                     try { wait(timeout); } catch (InterruptedException ie) { }
@@ -314,27 +320,24 @@ public abstract class ForkJoinTask<V> implements Future<V>, Serializable {
      * @return status upon completion
      */
     private int externalAwaitDone() {
-        int s = ((this instanceof CountedCompleter) ? // try helping
-                 ForkJoinPool.common.externalHelpComplete(
-                     (CountedCompleter<?>)this, 0) :
-                 ForkJoinPool.common.tryExternalUnpush(this) ? doExec() : 0);
-        if (s >= 0 && (s = status) >= 0) {
+        int s = tryExternalHelp();
+        if (s >= 0 && (s = (int)STATUS.getAndBitwiseOr(this, SIGNAL)) >= 0) {
             boolean interrupted = false;
-            do {
-                if (STATUS.compareAndSet(this, s, s | SIGNAL)) {
-                    synchronized (this) {
-                        if (status >= 0) {
-                            try {
-                                wait(0L);
-                            } catch (InterruptedException ie) {
-                                interrupted = true;
-                            }
+            synchronized (this) {
+                for (;;) {
+                    if ((s = status) >= 0) {
+                        try {
+                            wait(0L);
+                        } catch (InterruptedException ie) {
+                            interrupted = true;
                         }
-                        else
-                            notifyAll();
+                    }
+                    else {
+                        notifyAll();
+                        break;
                     }
                 }
-            } while ((s = status) >= 0);
+            }
             if (interrupted)
                 Thread.currentThread().interrupt();
         }
@@ -345,29 +348,39 @@ public abstract class ForkJoinTask<V> implements Future<V>, Serializable {
      * Blocks a non-worker-thread until completion or interruption.
      */
     private int externalInterruptibleAwaitDone() throws InterruptedException {
-        int s;
-        if (Thread.interrupted())
-            throw new InterruptedException();
-        if ((s = status) >= 0 &&
-            (s = ((this instanceof CountedCompleter) ?
-                  ForkJoinPool.common.externalHelpComplete(
-                      (CountedCompleter<?>)this, 0) :
-                  ForkJoinPool.common.tryExternalUnpush(this) ? doExec() :
-                  0)) >= 0) {
-            while ((s = status) >= 0) {
-                if (STATUS.compareAndSet(this, s, s | SIGNAL)) {
-                    synchronized (this) {
-                        if (status >= 0)
-                            wait(0L);
-                        else
-                            notifyAll();
+        int s = tryExternalHelp();
+        if (s >= 0 && (s = (int)STATUS.getAndBitwiseOr(this, SIGNAL)) >= 0) {
+            synchronized (this) {
+                for (;;) {
+                    if ((s = status) >= 0)
+                        wait(0L);
+                    else {
+                        notifyAll();
+                        break;
                     }
                 }
             }
         }
+        else if (Thread.interrupted())
+            throw new InterruptedException();
         return s;
     }
 
+    /**
+     * Tries to help with tasks allowed for external callers.
+     *
+     * @return current status
+     */
+    private int tryExternalHelp() {
+        int s;
+        return ((s = status) < 0 ? s:
+                (this instanceof CountedCompleter) ?
+                ForkJoinPool.common.externalHelpComplete(
+                    (CountedCompleter<?>)this, 0) :
+                ForkJoinPool.common.tryExternalUnpush(this) ?
+                doExec() : 0);
+    }
+
     /**
      * Implementation for join, get, quietlyJoin. Directly handles
      * only cases of already-completed, external wait, and
@@ -475,7 +488,7 @@ public abstract class ForkJoinTask<V> implements Future<V>, Serializable {
             } finally {
                 lock.unlock();
             }
-            s = setCompletion(EXCEPTIONAL);
+            s = abnormalCompletion(DONE | ABNORMAL | THROWN);
         }
         return s;
     }
@@ -487,7 +500,7 @@ public abstract class ForkJoinTask<V> implements Future<V>, Serializable {
      */
     private int setExceptionalCompletion(Throwable ex) {
         int s = recordExceptionalCompletion(ex);
-        if ((s & DONE_MASK) == EXCEPTIONAL)
+        if ((s & THROWN) != 0)
             internalPropagateException(ex);
         return s;
     }
@@ -662,10 +675,8 @@ public abstract class ForkJoinTask<V> implements Future<V>, Serializable {
      * Throws exception, if any, associated with the given status.
      */
     private void reportException(int s) {
-        if (s == CANCELLED)
-            throw new CancellationException();
-        if (s == EXCEPTIONAL)
-            rethrow(getThrowableException());
+        rethrow((s & THROWN) != 0 ? getThrowableException() :
+                new CancellationException());
     }
 
     // public methods
@@ -707,7 +718,7 @@ public abstract class ForkJoinTask<V> implements Future<V>, Serializable {
      */
     public final V join() {
         int s;
-        if ((s = doJoin() & DONE_MASK) != NORMAL)
+        if (((s = doJoin()) & ABNORMAL) != 0)
             reportException(s);
         return getRawResult();
     }
@@ -722,7 +733,7 @@ public abstract class ForkJoinTask<V> implements Future<V>, Serializable {
      */
     public final V invoke() {
         int s;
-        if ((s = doInvoke() & DONE_MASK) != NORMAL)
+        if (((s = doInvoke()) & ABNORMAL) != 0)
             reportException(s);
         return getRawResult();
     }
@@ -747,9 +758,9 @@ public abstract class ForkJoinTask<V> implements Future<V>, Serializable {
     public static void invokeAll(ForkJoinTask<?> t1, ForkJoinTask<?> t2) {
         int s1, s2;
         t2.fork();
-        if ((s1 = t1.doInvoke() & DONE_MASK) != NORMAL)
+        if (((s1 = t1.doInvoke()) & ABNORMAL) != 0)
             t1.reportException(s1);
-        if ((s2 = t2.doJoin() & DONE_MASK) != NORMAL)
+        if (((s2 = t2.doJoin()) & ABNORMAL) != 0)
             t2.reportException(s2);
     }
 
@@ -779,7 +790,7 @@ public abstract class ForkJoinTask<V> implements Future<V>, Serializable {
             }
             else if (i != 0)
                 t.fork();
-            else if (t.doInvoke() < NORMAL && ex == null)
+            else if ((t.doInvoke() & ABNORMAL) != 0 && ex == null)
                 ex = t.getException();
         }
         for (int i = 1; i <= last; ++i) {
@@ -787,7 +798,7 @@ public abstract class ForkJoinTask<V> implements Future<V>, Serializable {
             if (t != null) {
                 if (ex != null)
                     t.cancel(false);
-                else if (t.doJoin() < NORMAL)
+                else if ((t.doJoin() & ABNORMAL) != 0)
                     ex = t.getException();
             }
         }
@@ -831,7 +842,7 @@ public abstract class ForkJoinTask<V> implements Future<V>, Serializable {
             }
             else if (i != 0)
                 t.fork();
-            else if (t.doInvoke() < NORMAL && ex == null)
+            else if ((t.doInvoke() & ABNORMAL) != 0 && ex == null)
                 ex = t.getException();
         }
         for (int i = 1; i <= last; ++i) {
@@ -839,7 +850,7 @@ public abstract class ForkJoinTask<V> implements Future<V>, Serializable {
             if (t != null) {
                 if (ex != null)
                     t.cancel(false);
-                else if (t.doJoin() < NORMAL)
+                else if ((t.doJoin() & ABNORMAL) != 0)
                     ex = t.getException();
             }
         }
@@ -876,7 +887,8 @@ public abstract class ForkJoinTask<V> implements Future<V>, Serializable {
      * @return {@code true} if this task is now cancelled
      */
     public boolean cancel(boolean mayInterruptIfRunning) {
-        return (setCompletion(CANCELLED) & DONE_MASK) == CANCELLED;
+        int s = abnormalCompletion(DONE | ABNORMAL);
+        return (s & (ABNORMAL | THROWN)) == ABNORMAL;
     }
 
     public final boolean isDone() {
@@ -884,7 +896,7 @@ public abstract class ForkJoinTask<V> implements Future<V>, Serializable {
     }
 
     public final boolean isCancelled() {
-        return (status & DONE_MASK) == CANCELLED;
+        return (status & (ABNORMAL | THROWN)) == ABNORMAL;
     }
 
     /**
@@ -893,7 +905,7 @@ public abstract class ForkJoinTask<V> implements Future<V>, Serializable {
      * @return {@code true} if this task threw an exception or was cancelled
      */
     public final boolean isCompletedAbnormally() {
-        return status < NORMAL;
+        return (status & ABNORMAL) != 0;
     }
 
     /**
@@ -904,7 +916,7 @@ public abstract class ForkJoinTask<V> implements Future<V>, Serializable {
      * exception and was not cancelled
      */
     public final boolean isCompletedNormally() {
-        return (status & DONE_MASK) == NORMAL;
+        return (status & (DONE | ABNORMAL)) == DONE;
     }
 
     /**
@@ -915,9 +927,9 @@ public abstract class ForkJoinTask<V> implements Future<V>, Serializable {
      * @return the exception, or {@code null} if none
      */
     public final Throwable getException() {
-        int s = status & DONE_MASK;
-        return ((s >= NORMAL)    ? null :
-                (s == CANCELLED) ? new CancellationException() :
+        int s = status;
+        return ((s & ABNORMAL) == 0 ? null :
+                (s & THROWN)   == 0 ? new CancellationException() :
                 getThrowableException());
     }
 
@@ -961,7 +973,7 @@ public abstract class ForkJoinTask<V> implements Future<V>, Serializable {
             setExceptionalCompletion(rex);
             return;
         }
-        setCompletion(NORMAL);
+        setDone();
     }
 
     /**
@@ -973,7 +985,7 @@ public abstract class ForkJoinTask<V> implements Future<V>, Serializable {
      * @since 1.8
      */
     public final void quietlyComplete() {
-        setCompletion(NORMAL);
+        setDone();
     }
 
     /**
@@ -990,11 +1002,12 @@ public abstract class ForkJoinTask<V> implements Future<V>, Serializable {
     public final V get() throws InterruptedException, ExecutionException {
         int s = (Thread.currentThread() instanceof ForkJoinWorkerThread) ?
             doJoin() : externalInterruptibleAwaitDone();
-        if ((s &= DONE_MASK) == CANCELLED)
-            throw new CancellationException();
-        if (s == EXCEPTIONAL)
+        if ((s & THROWN) != 0)
             throw new ExecutionException(getThrowableException());
-        return getRawResult();
+        else if ((s & ABNORMAL) != 0)
+            throw new CancellationException();
+        else
+            return getRawResult();
     }
 
     /**
@@ -1034,7 +1047,7 @@ public abstract class ForkJoinTask<V> implements Future<V>, Serializable {
                 while ((s = status) >= 0 &&
                        (ns = deadline - System.nanoTime()) > 0L) {
                     if ((ms = TimeUnit.NANOSECONDS.toMillis(ns)) > 0L &&
-                        STATUS.compareAndSet(this, s, s | SIGNAL)) {
+                        (s = (int)STATUS.getAndBitwiseOr(this, SIGNAL)) >= 0) {
                         synchronized (this) {
                             if (status >= 0)
                                 wait(ms); // OK to throw InterruptedException
@@ -1046,15 +1059,13 @@ public abstract class ForkJoinTask<V> implements Future<V>, Serializable {
             }
         }
         if (s >= 0)
-            s = status;
-        if ((s &= DONE_MASK) != NORMAL) {
-            if (s == CANCELLED)
-                throw new CancellationException();
-            if (s != EXCEPTIONAL)
-                throw new TimeoutException();
+            throw new TimeoutException();
+        else if ((s & THROWN) != 0)
             throw new ExecutionException(getThrowableException());
-        }
-        return getRawResult();
+        else if ((s & ABNORMAL) != 0)
+            throw new CancellationException();
+        else
+            return getRawResult();
     }
 
     /**
@@ -1110,7 +1121,7 @@ public abstract class ForkJoinTask<V> implements Future<V>, Serializable {
      * setRawResult(null)}.
      */
     public void reinitialize() {
-        if ((status & DONE_MASK) == EXCEPTIONAL)
+        if ((status & THROWN) != 0)
             clearExceptionalCompletion();
         else
             status = 0;
@@ -1327,8 +1338,8 @@ public abstract class ForkJoinTask<V> implements Future<V>, Serializable {
      */
     public final short setForkJoinTaskTag(short newValue) {
         for (int s;;) {
-            if (STATUS.compareAndSet(this, s = status,
-                                     (s & ~SMASK) | (newValue & SMASK)))
+            if (STATUS.weakCompareAndSet(this, s = status,
+                                         (s & ~SMASK) | (newValue & SMASK)))
                 return (short)s;
         }
     }
@@ -1351,8 +1362,8 @@ public abstract class ForkJoinTask<V> implements Future<V>, Serializable {
         for (int s;;) {
             if ((short)(s = status) != expect)
                 return false;
-            if (STATUS.compareAndSet(this, s,
-                                     (s & ~SMASK) | (update & SMASK)))
+            if (STATUS.weakCompareAndSet(this, s,
+                                         (s & ~SMASK) | (update & SMASK)))
                 return true;
         }
     }
diff --git a/src/java.base/share/classes/java/util/concurrent/SubmissionPublisher.java b/src/java.base/share/classes/java/util/concurrent/SubmissionPublisher.java
index 81f6c6c0825..713c3fbd395 100644
--- a/src/java.base/share/classes/java/util/concurrent/SubmissionPublisher.java
+++ b/src/java.base/share/classes/java/util/concurrent/SubmissionPublisher.java
@@ -1252,18 +1252,20 @@ public class SubmissionPublisher<T> implements Publisher<T>,
                         head = h += taken;
                         d = subtractDemand(taken);
                     }
-                    else if ((empty = (t == h)) && (c & COMPLETE) != 0) {
-                        closeOnComplete(s);          // end of stream
-                        break;
-                    }
                     else if ((d = demand) == 0L && (c & REQS) != 0)
                         weakCasCtl(c, c & ~REQS);    // exhausted demand
                     else if (d != 0L && (c & REQS) == 0)
                         weakCasCtl(c, c | REQS);     // new demand
-                    else if (t == (t = tail) && (empty || d == 0L)) {
-                        int bit = ((c & ACTIVE) != 0) ? ACTIVE : RUN;
-                        if (weakCasCtl(c, c & ~bit) && bit == RUN)
-                            break;                   // un-keep-alive or exit
+                    else if (t == (t = tail)) {      // stability check
+                        if ((empty = (t == h)) && (c & COMPLETE) != 0) {
+                            closeOnComplete(s);      // end of stream
+                            break;
+                        }
+                        else if (empty || d == 0L) {
+                            int bit = ((c & ACTIVE) != 0) ? ACTIVE : RUN;
+                            if (weakCasCtl(c, c & ~bit) && bit == RUN)
+                                break;               // un-keep-alive or exit
+                        }
                     }
                 }
             }
diff --git a/src/java.base/share/classes/java/util/concurrent/atomic/DoubleAccumulator.java b/src/java.base/share/classes/java/util/concurrent/atomic/DoubleAccumulator.java
index 20c9efa5f2e..180073006f9 100644
--- a/src/java.base/share/classes/java/util/concurrent/atomic/DoubleAccumulator.java
+++ b/src/java.base/share/classes/java/util/concurrent/atomic/DoubleAccumulator.java
@@ -105,20 +105,20 @@ public class DoubleAccumulator extends Striped64 implements Serializable {
      * @param x the value
      */
     public void accumulate(double x) {
-        Cell[] as; long b, v, r; int m; Cell a;
-        if ((as = cells) != null
+        Cell[] cs; long b, v, r; int m; Cell c;
+        if ((cs = cells) != null
             || ((r = doubleToRawLongBits
                 (function.applyAsDouble(longBitsToDouble(b = base), x))) != b
                 && !casBase(b, r))) {
             boolean uncontended = true;
-            if (as == null
-                || (m = as.length - 1) < 0
-                || (a = as[getProbe() & m]) == null
+            if (cs == null
+                || (m = cs.length - 1) < 0
+                || (c = cs[getProbe() & m]) == null
                 || !(uncontended =
                      ((r = doubleToRawLongBits
                        (function.applyAsDouble
-                        (longBitsToDouble(v = a.value), x))) == v)
-                     || a.cas(v, r)))
+                        (longBitsToDouble(v = c.value), x))) == v)
+                     || c.cas(v, r)))
                 doubleAccumulate(x, function, uncontended);
         }
     }
@@ -133,13 +133,13 @@ public class DoubleAccumulator extends Striped64 implements Serializable {
      * @return the current value
      */
     public double get() {
-        Cell[] as = cells;
+        Cell[] cs = cells;
         double result = longBitsToDouble(base);
-        if (as != null) {
-            for (Cell a : as)
-                if (a != null)
+        if (cs != null) {
+            for (Cell c : cs)
+                if (c != null)
                     result = function.applyAsDouble
-                        (result, longBitsToDouble(a.value));
+                        (result, longBitsToDouble(c.value));
         }
         return result;
     }
@@ -153,12 +153,12 @@ public class DoubleAccumulator extends Striped64 implements Serializable {
      * updating.
      */
     public void reset() {
-        Cell[] as = cells;
+        Cell[] cs = cells;
         base = identity;
-        if (as != null) {
-            for (Cell a : as)
-                if (a != null)
-                    a.reset(identity);
+        if (cs != null) {
+            for (Cell c : cs)
+                if (c != null)
+                    c.reset(identity);
         }
     }
 
@@ -173,14 +173,12 @@ public class DoubleAccumulator extends Striped64 implements Serializable {
      * @return the value before reset
      */
     public double getThenReset() {
-        Cell[] as = cells;
-        double result = longBitsToDouble(base);
-        base = identity;
-        if (as != null) {
-            for (Cell a : as) {
-                if (a != null) {
-                    double v = longBitsToDouble(a.value);
-                    a.reset(identity);
+        Cell[] cs = cells;
+        double result = longBitsToDouble(getAndSetBase(identity));
+        if (cs != null) {
+            for (Cell c : cs) {
+                if (c != null) {
+                    double v = longBitsToDouble(c.getAndSet(identity));
                     result = function.applyAsDouble(result, v);
                 }
             }
diff --git a/src/java.base/share/classes/java/util/concurrent/atomic/DoubleAdder.java b/src/java.base/share/classes/java/util/concurrent/atomic/DoubleAdder.java
index 57bd8c581a4..3f3343f5c6f 100644
--- a/src/java.base/share/classes/java/util/concurrent/atomic/DoubleAdder.java
+++ b/src/java.base/share/classes/java/util/concurrent/atomic/DoubleAdder.java
@@ -87,15 +87,15 @@ public class DoubleAdder extends Striped64 implements Serializable {
      * @param x the value to add
      */
     public void add(double x) {
-        Cell[] as; long b, v; int m; Cell a;
-        if ((as = cells) != null ||
+        Cell[] cs; long b, v; int m; Cell c;
+        if ((cs = cells) != null ||
             !casBase(b = base,
                      Double.doubleToRawLongBits
                      (Double.longBitsToDouble(b) + x))) {
             boolean uncontended = true;
-            if (as == null || (m = as.length - 1) < 0 ||
-                (a = as[getProbe() & m]) == null ||
-                !(uncontended = a.cas(v = a.value,
+            if (cs == null || (m = cs.length - 1) < 0 ||
+                (c = cs[getProbe() & m]) == null ||
+                !(uncontended = c.cas(v = c.value,
                                       Double.doubleToRawLongBits
                                       (Double.longBitsToDouble(v) + x))))
                 doubleAccumulate(x, null, uncontended);
@@ -115,12 +115,12 @@ public class DoubleAdder extends Striped64 implements Serializable {
      * @return the sum
      */
     public double sum() {
-        Cell[] as = cells;
+        Cell[] cs = cells;
         double sum = Double.longBitsToDouble(base);
-        if (as != null) {
-            for (Cell a : as)
-                if (a != null)
-                    sum += Double.longBitsToDouble(a.value);
+        if (cs != null) {
+            for (Cell c : cs)
+                if (c != null)
+                    sum += Double.longBitsToDouble(c.value);
         }
         return sum;
     }
@@ -133,12 +133,12 @@ public class DoubleAdder extends Striped64 implements Serializable {
      * known that no threads are concurrently updating.
      */
     public void reset() {
-        Cell[] as = cells;
+        Cell[] cs = cells;
         base = 0L; // relies on fact that double 0 must have same rep as long
-        if (as != null) {
-            for (Cell a : as)
-                if (a != null)
-                    a.reset();
+        if (cs != null) {
+            for (Cell c : cs)
+                if (c != null)
+                    c.reset();
         }
     }
 
@@ -153,16 +153,12 @@ public class DoubleAdder extends Striped64 implements Serializable {
      * @return the sum
      */
     public double sumThenReset() {
-        Cell[] as = cells;
-        double sum = Double.longBitsToDouble(base);
-        base = 0L;
-        if (as != null) {
-            for (Cell a : as) {
-                if (a != null) {
-                    long v = a.value;
-                    a.reset();
-                    sum += Double.longBitsToDouble(v);
-                }
+        Cell[] cs = cells;
+        double sum = Double.longBitsToDouble(getAndSetBase(0L));
+        if (cs != null) {
+            for (Cell c : cs) {
+                if (c != null)
+                    sum += Double.longBitsToDouble(c.getAndSet(0L));
             }
         }
         return sum;
diff --git a/src/java.base/share/classes/java/util/concurrent/atomic/LongAccumulator.java b/src/java.base/share/classes/java/util/concurrent/atomic/LongAccumulator.java
index 71fa3e40651..ed9b4bac45f 100644
--- a/src/java.base/share/classes/java/util/concurrent/atomic/LongAccumulator.java
+++ b/src/java.base/share/classes/java/util/concurrent/atomic/LongAccumulator.java
@@ -103,17 +103,17 @@ public class LongAccumulator extends Striped64 implements Serializable {
      * @param x the value
      */
     public void accumulate(long x) {
-        Cell[] as; long b, v, r; int m; Cell a;
-        if ((as = cells) != null
+        Cell[] cs; long b, v, r; int m; Cell c;
+        if ((cs = cells) != null
             || ((r = function.applyAsLong(b = base, x)) != b
                 && !casBase(b, r))) {
             boolean uncontended = true;
-            if (as == null
-                || (m = as.length - 1) < 0
-                || (a = as[getProbe() & m]) == null
+            if (cs == null
+                || (m = cs.length - 1) < 0
+                || (c = cs[getProbe() & m]) == null
                 || !(uncontended =
-                     (r = function.applyAsLong(v = a.value, x)) == v
-                     || a.cas(v, r)))
+                     (r = function.applyAsLong(v = c.value, x)) == v
+                     || c.cas(v, r)))
                 longAccumulate(x, function, uncontended);
         }
     }
@@ -128,12 +128,12 @@ public class LongAccumulator extends Striped64 implements Serializable {
      * @return the current value
      */
     public long get() {
-        Cell[] as = cells;
+        Cell[] cs = cells;
         long result = base;
-        if (as != null) {
-            for (Cell a : as)
-                if (a != null)
-                    result = function.applyAsLong(result, a.value);
+        if (cs != null) {
+            for (Cell c : cs)
+                if (c != null)
+                    result = function.applyAsLong(result, c.value);
         }
         return result;
     }
@@ -147,12 +147,12 @@ public class LongAccumulator extends Striped64 implements Serializable {
      * updating.
      */
     public void reset() {
-        Cell[] as = cells;
+        Cell[] cs = cells;
         base = identity;
-        if (as != null) {
-            for (Cell a : as)
-                if (a != null)
-                    a.reset(identity);
+        if (cs != null) {
+            for (Cell c : cs)
+                if (c != null)
+                    c.reset(identity);
         }
     }
 
@@ -167,14 +167,12 @@ public class LongAccumulator extends Striped64 implements Serializable {
      * @return the value before reset
      */
     public long getThenReset() {
-        Cell[] as = cells;
-        long result = base;
-        base = identity;
-        if (as != null) {
-            for (Cell a : as) {
-                if (a != null) {
-                    long v = a.value;
-                    a.reset(identity);
+        Cell[] cs = cells;
+        long result = getAndSetBase(identity);
+        if (cs != null) {
+            for (Cell c : cs) {
+                if (c != null) {
+                    long v = c.getAndSet(identity);
                     result = function.applyAsLong(result, v);
                 }
             }
diff --git a/src/java.base/share/classes/java/util/concurrent/atomic/LongAdder.java b/src/java.base/share/classes/java/util/concurrent/atomic/LongAdder.java
index 0248fad9c5f..e0fdc18c7d7 100644
--- a/src/java.base/share/classes/java/util/concurrent/atomic/LongAdder.java
+++ b/src/java.base/share/classes/java/util/concurrent/atomic/LongAdder.java
@@ -83,12 +83,12 @@ public class LongAdder extends Striped64 implements Serializable {
      * @param x the value to add
      */
     public void add(long x) {
-        Cell[] as; long b, v; int m; Cell a;
-        if ((as = cells) != null || !casBase(b = base, b + x)) {
+        Cell[] cs; long b, v; int m; Cell c;
+        if ((cs = cells) != null || !casBase(b = base, b + x)) {
             boolean uncontended = true;
-            if (as == null || (m = as.length - 1) < 0 ||
-                (a = as[getProbe() & m]) == null ||
-                !(uncontended = a.cas(v = a.value, v + x)))
+            if (cs == null || (m = cs.length - 1) < 0 ||
+                (c = cs[getProbe() & m]) == null ||
+                !(uncontended = c.cas(v = c.value, v + x)))
                 longAccumulate(x, null, uncontended);
         }
     }
@@ -117,12 +117,12 @@ public class LongAdder extends Striped64 implements Serializable {
      * @return the sum
      */
     public long sum() {
-        Cell[] as = cells;
+        Cell[] cs = cells;
         long sum = base;
-        if (as != null) {
-            for (Cell a : as)
-                if (a != null)
-                    sum += a.value;
+        if (cs != null) {
+            for (Cell c : cs)
+                if (c != null)
+                    sum += c.value;
         }
         return sum;
     }
@@ -135,12 +135,12 @@ public class LongAdder extends Striped64 implements Serializable {
      * known that no threads are concurrently updating.
      */
     public void reset() {
-        Cell[] as = cells;
+        Cell[] cs = cells;
         base = 0L;
-        if (as != null) {
-            for (Cell a : as)
-                if (a != null)
-                    a.reset();
+        if (cs != null) {
+            for (Cell c : cs)
+                if (c != null)
+                    c.reset();
         }
     }
 
@@ -155,15 +155,12 @@ public class LongAdder extends Striped64 implements Serializable {
      * @return the sum
      */
     public long sumThenReset() {
-        Cell[] as = cells;
-        long sum = base;
-        base = 0L;
-        if (as != null) {
-            for (Cell a : as) {
-                if (a != null) {
-                    sum += a.value;
-                    a.reset();
-                }
+        Cell[] cs = cells;
+        long sum = getAndSetBase(0L);
+        if (cs != null) {
+            for (Cell c : cs) {
+                if (c != null)
+                    sum += c.getAndSet(0L);
             }
         }
         return sum;
diff --git a/src/java.base/share/classes/java/util/concurrent/atomic/Striped64.java b/src/java.base/share/classes/java/util/concurrent/atomic/Striped64.java
index 95030782a71..ddb1e53a3d0 100644
--- a/src/java.base/share/classes/java/util/concurrent/atomic/Striped64.java
+++ b/src/java.base/share/classes/java/util/concurrent/atomic/Striped64.java
@@ -133,6 +133,9 @@ abstract class Striped64 extends Number {
         final void reset(long identity) {
             VALUE.setVolatile(this, identity);
         }
+        final long getAndSet(long val) {
+            return (long)VALUE.getAndSet(this, val);
+        }
 
         // VarHandle mechanics
         private static final VarHandle VALUE;
@@ -178,6 +181,10 @@ abstract class Striped64 extends Number {
         return BASE.compareAndSet(this, cmp, val);
     }
 
+    final long getAndSetBase(long val) {
+        return (long)BASE.getAndSet(this, val);
+    }
+
     /**
      * CASes the cellsBusy field from 0 to 1 to acquire lock.
      */
@@ -228,9 +235,9 @@ abstract class Striped64 extends Number {
         }
         boolean collide = false;                // True if last slot nonempty
         done: for (;;) {
-            Cell[] as; Cell a; int n; long v;
-            if ((as = cells) != null && (n = as.length) > 0) {
-                if ((a = as[(n - 1) & h]) == null) {
+            Cell[] cs; Cell c; int n; long v;
+            if ((cs = cells) != null && (n = cs.length) > 0) {
+                if ((c = cs[(n - 1) & h]) == null) {
                     if (cellsBusy == 0) {       // Try to attach new Cell
                         Cell r = new Cell(x);   // Optimistically create
                         if (cellsBusy == 0 && casCellsBusy()) {
@@ -252,17 +259,17 @@ abstract class Striped64 extends Number {
                 }
                 else if (!wasUncontended)       // CAS already known to fail
                     wasUncontended = true;      // Continue after rehash
-                else if (a.cas(v = a.value,
+                else if (c.cas(v = c.value,
                                (fn == null) ? v + x : fn.applyAsLong(v, x)))
                     break;
-                else if (n >= NCPU || cells != as)
+                else if (n >= NCPU || cells != cs)
                     collide = false;            // At max size or stale
                 else if (!collide)
                     collide = true;
                 else if (cellsBusy == 0 && casCellsBusy()) {
                     try {
-                        if (cells == as)        // Expand table unless stale
-                            cells = Arrays.copyOf(as, n << 1);
+                        if (cells == cs)        // Expand table unless stale
+                            cells = Arrays.copyOf(cs, n << 1);
                     } finally {
                         cellsBusy = 0;
                     }
@@ -271,9 +278,9 @@ abstract class Striped64 extends Number {
                 }
                 h = advanceProbe(h);
             }
-            else if (cellsBusy == 0 && cells == as && casCellsBusy()) {
+            else if (cellsBusy == 0 && cells == cs && casCellsBusy()) {
                 try {                           // Initialize table
-                    if (cells == as) {
+                    if (cells == cs) {
                         Cell[] rs = new Cell[2];
                         rs[h & 1] = new Cell(x);
                         cells = rs;
@@ -312,9 +319,9 @@ abstract class Striped64 extends Number {
         }
         boolean collide = false;                // True if last slot nonempty
         done: for (;;) {
-            Cell[] as; Cell a; int n; long v;
-            if ((as = cells) != null && (n = as.length) > 0) {
-                if ((a = as[(n - 1) & h]) == null) {
+            Cell[] cs; Cell c; int n; long v;
+            if ((cs = cells) != null && (n = cs.length) > 0) {
+                if ((c = cs[(n - 1) & h]) == null) {
                     if (cellsBusy == 0) {       // Try to attach new Cell
                         Cell r = new Cell(Double.doubleToRawLongBits(x));
                         if (cellsBusy == 0 && casCellsBusy()) {
@@ -336,16 +343,16 @@ abstract class Striped64 extends Number {
                 }
                 else if (!wasUncontended)       // CAS already known to fail
                     wasUncontended = true;      // Continue after rehash
-                else if (a.cas(v = a.value, apply(fn, v, x)))
+                else if (c.cas(v = c.value, apply(fn, v, x)))
                     break;
-                else if (n >= NCPU || cells != as)
+                else if (n >= NCPU || cells != cs)
                     collide = false;            // At max size or stale
                 else if (!collide)
                     collide = true;
                 else if (cellsBusy == 0 && casCellsBusy()) {
                     try {
-                        if (cells == as)        // Expand table unless stale
-                            cells = Arrays.copyOf(as, n << 1);
+                        if (cells == cs)        // Expand table unless stale
+                            cells = Arrays.copyOf(cs, n << 1);
                     } finally {
                         cellsBusy = 0;
                     }
@@ -354,9 +361,9 @@ abstract class Striped64 extends Number {
                 }
                 h = advanceProbe(h);
             }
-            else if (cellsBusy == 0 && cells == as && casCellsBusy()) {
+            else if (cellsBusy == 0 && cells == cs && casCellsBusy()) {
                 try {                           // Initialize table
-                    if (cells == as) {
+                    if (cells == cs) {
                         Cell[] rs = new Cell[2];
                         rs[h & 1] = new Cell(Double.doubleToRawLongBits(x));
                         cells = rs;
diff --git a/src/java.base/share/classes/java/util/jar/JarFile.java b/src/java.base/share/classes/java/util/jar/JarFile.java
index 09060f9d209..bea26632957 100644
--- a/src/java.base/share/classes/java/util/jar/JarFile.java
+++ b/src/java.base/share/classes/java/util/jar/JarFile.java
@@ -43,17 +43,12 @@ import java.security.CodeSource;
 import java.security.cert.Certificate;
 import java.util.ArrayList;
 import java.util.Enumeration;
-import java.util.Iterator;
 import java.util.List;
 import java.util.Locale;
-import java.util.Map;
 import java.util.NoSuchElementException;
 import java.util.Objects;
-import java.util.Spliterator;
-import java.util.Spliterators;
-import java.util.stream.Collector;
+import java.util.function.Function;
 import java.util.stream.Stream;
-import java.util.stream.StreamSupport;
 import java.util.zip.ZipEntry;
 import java.util.zip.ZipException;
 import java.util.zip.ZipFile;
@@ -566,7 +561,14 @@ class JarFile extends ZipFile {
      * given entry name or {@code null} if not found.
      */
     private JarFileEntry getEntry0(String name) {
-        return (JarFileEntry)JUZFA.getEntry(this, name, JarFileEntry::new);
+        // Not using a lambda/method reference here to optimize startup time
+        Function<String, JarEntry> newJarFileEntryFn = new Function<>() {
+            @Override
+            public JarEntry apply(String name) {
+                return new JarFileEntry(name);
+            }
+        };
+        return (JarFileEntry)JUZFA.getEntry(this, name, newJarFileEntryFn);
     }
 
     private String getBasename(String name) {
diff --git a/src/java.base/share/classes/java/util/stream/Collectors.java b/src/java.base/share/classes/java/util/stream/Collectors.java
index 02f9ada39c0..26d98bf6d42 100644
--- a/src/java.base/share/classes/java/util/stream/Collectors.java
+++ b/src/java.base/share/classes/java/util/stream/Collectors.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2017, 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
@@ -116,6 +116,8 @@ public final class Collectors {
             = Collections.unmodifiableSet(EnumSet.of(Collector.Characteristics.UNORDERED,
                                                      Collector.Characteristics.IDENTITY_FINISH));
     static final Set<Collector.Characteristics> CH_NOID = Collections.emptySet();
+    static final Set<Collector.Characteristics> CH_UNORDERED_NOID
+            = Collections.unmodifiableSet(EnumSet.of(Collector.Characteristics.UNORDERED));
 
     private Collectors() { }
 
@@ -278,6 +280,26 @@ public final class Collectors {
                                    CH_ID);
     }
 
+    /**
+     * Returns a {@code Collector} that accumulates the input elements into an
+     * <a href="../List.html#unmodifiable">unmodifiable List</a> in encounter
+     * order. The returned Collector disallows null values and will throw
+     * {@code NullPointerException} if it is presented with a null value.
+     *
+     * @param <T> the type of the input elements
+     * @return a {@code Collector} that accumulates the input elements into an
+     * <a href="../List.html#unmodifiable">unmodifiable List</a> in encounter order
+     * @since 10
+     */
+    @SuppressWarnings("unchecked")
+    public static <T>
+    Collector<T, ?, List<T>> toUnmodifiableList() {
+        return new CollectorImpl<>((Supplier<List<T>>) ArrayList::new, List::add,
+                                   (left, right) -> { left.addAll(right); return left; },
+                                   list -> (List<T>)List.of(list.toArray()),
+                                   CH_NOID);
+    }
+
     /**
      * Returns a {@code Collector} that accumulates the input elements into a
      * new {@code Set}. There are no guarantees on the type, mutability,
@@ -305,6 +327,36 @@ public final class Collectors {
                                    CH_UNORDERED_ID);
     }
 
+    /**
+     * Returns a {@code Collector} that accumulates the input elements into an
+     * <a href="../Set.html#unmodifiable">unmodifiable Set</a>. The returned
+     * Collector disallows null values and will throw {@code NullPointerException}
+     * if it is presented with a null value. If the input contains duplicate elements,
+     * an arbitrary element of the duplicates is preserved.
+     *
+     * <p>This is an {@link Collector.Characteristics#UNORDERED unordered}
+     * Collector.
+     *
+     * @param <T> the type of the input elements
+     * @return a {@code Collector} that accumulates the input elements into an
+     * <a href="../Set.html#unmodifiable">unmodifiable Set</a>
+     * @since 10
+     */
+    @SuppressWarnings("unchecked")
+    public static <T>
+    Collector<T, ?, Set<T>> toUnmodifiableSet() {
+        return new CollectorImpl<>((Supplier<Set<T>>) HashSet::new, Set::add,
+                                   (left, right) -> {
+                                       if (left.size() < right.size()) {
+                                           right.addAll(left); return right;
+                                       } else {
+                                           left.addAll(right); return left;
+                                       }
+                                   },
+                                   set -> (Set<T>)Set.of(set.toArray()),
+                                   CH_UNORDERED_NOID);
+    }
+
     /**
      * Returns a {@code Collector} that concatenates the input elements into a
      * {@code String}, in encounter order.
@@ -1353,7 +1405,7 @@ public final class Collectors {
      * <p>If the mapped keys contain duplicates (according to
      * {@link Object#equals(Object)}), an {@code IllegalStateException} is
      * thrown when the collection operation is performed.  If the mapped keys
-     * may have duplicates, use {@link #toMap(Function, Function, BinaryOperator)}
+     * might have duplicates, use {@link #toMap(Function, Function, BinaryOperator)}
      * instead.
      *
      * <p>There are no guarantees on the type, mutability, serializability,
@@ -1410,6 +1462,45 @@ public final class Collectors {
                                    CH_ID);
     }
 
+    /**
+     * Returns a {@code Collector} that accumulates the input elements into an
+     * <a href="../Map.html#unmodifiable">unmodifiable Map</a>,
+     * whose keys and values are the result of applying the provided
+     * mapping functions to the input elements.
+     *
+     * <p>If the mapped keys contain duplicates (according to
+     * {@link Object#equals(Object)}), an {@code IllegalStateException} is
+     * thrown when the collection operation is performed.  If the mapped keys
+     * might have duplicates, use {@link #toUnmodifiableMap(Function, Function, BinaryOperator)}
+     * to handle merging of the values.
+     *
+     * <p>The returned Collector disallows null keys and values. If either mapping function
+     * returns null, {@code NullPointerException} will be thrown.
+     *
+     * @param <T> the type of the input elements
+     * @param <K> the output type of the key mapping function
+     * @param <U> the output type of the value mapping function
+     * @param keyMapper a mapping function to produce keys, must be non-null
+     * @param valueMapper a mapping function to produce values, must be non-null
+     * @return a {@code Collector} that accumulates the input elements into an
+     * <a href="../Map.html#unmodifiable">unmodifiable Map</a>, whose keys and values
+     * are the result of applying the provided mapping functions to the input elements
+     * @throws NullPointerException if either keyMapper or valueMapper is null
+     *
+     * @see #toUnmodifiableMap(Function, Function, BinaryOperator)
+     * @since 10
+     */
+    @SuppressWarnings({"rawtypes", "unchecked"})
+    public static <T, K, U>
+    Collector<T, ?, Map<K,U>> toUnmodifiableMap(Function<? super T, ? extends K> keyMapper,
+                                                Function<? super T, ? extends U> valueMapper) {
+        Objects.requireNonNull(keyMapper, "keyMapper");
+        Objects.requireNonNull(valueMapper, "valueMapper");
+        return collectingAndThen(
+                toMap(keyMapper, valueMapper),
+                map -> (Map<K,U>)Map.ofEntries(map.entrySet().toArray(new Map.Entry[0])));
+    }
+
     /**
      * Returns a {@code Collector} that accumulates elements into a
      * {@code Map} whose keys and values are the result of applying the provided
@@ -1473,6 +1564,51 @@ public final class Collectors {
         return toMap(keyMapper, valueMapper, mergeFunction, HashMap::new);
     }
 
+
+    /**
+     * Returns a {@code Collector} that accumulates the input elements into an
+     * <a href="../Map.html#unmodifiable">unmodifiable Map</a>,
+     * whose keys and values are the result of applying the provided
+     * mapping functions to the input elements.
+     *
+     * <p>If the mapped
+     * keys contain duplicates (according to {@link Object#equals(Object)}),
+     * the value mapping function is applied to each equal element, and the
+     * results are merged using the provided merging function.
+     *
+     * <p>The returned Collector disallows null keys and values. If either mapping function
+     * returns null, {@code NullPointerException} will be thrown.
+     *
+     * @param <T> the type of the input elements
+     * @param <K> the output type of the key mapping function
+     * @param <U> the output type of the value mapping function
+     * @param keyMapper a mapping function to produce keys, must be non-null
+     * @param valueMapper a mapping function to produce values, must be non-null
+     * @param mergeFunction a merge function, used to resolve collisions between
+     *                      values associated with the same key, as supplied
+     *                      to {@link Map#merge(Object, Object, BiFunction)},
+     *                      must be non-null
+     * @return a {@code Collector} that accumulates the input elements into an
+     * <a href="../Map.html#unmodifiable">unmodifiable Map</a>, whose keys and values
+     * are the result of applying the provided mapping functions to the input elements
+     * @throws NullPointerException if the keyMapper, valueMapper, or mergeFunction is null
+     *
+     * @see #toUnmodifiableMap(Function, Function)
+     * @since 10
+     */
+    @SuppressWarnings({"rawtypes", "unchecked"})
+    public static <T, K, U>
+    Collector<T, ?, Map<K,U>> toUnmodifiableMap(Function<? super T, ? extends K> keyMapper,
+                                                Function<? super T, ? extends U> valueMapper,
+                                                BinaryOperator<U> mergeFunction) {
+        Objects.requireNonNull(keyMapper, "keyMapper");
+        Objects.requireNonNull(valueMapper, "valueMapper");
+        Objects.requireNonNull(mergeFunction, "mergeFunction");
+        return collectingAndThen(
+                toMap(keyMapper, valueMapper, mergeFunction, HashMap::new),
+                map -> (Map<K,U>)Map.ofEntries(map.entrySet().toArray(new Map.Entry[0])));
+    }
+
     /**
      * Returns a {@code Collector} that accumulates elements into a
      * {@code Map} whose keys and values are the result of applying the provided
diff --git a/src/java.base/share/classes/java/util/stream/Stream.java b/src/java.base/share/classes/java/util/stream/Stream.java
index 01b6ac5a43a..f313fc6aeff 100644
--- a/src/java.base/share/classes/java/util/stream/Stream.java
+++ b/src/java.base/share/classes/java/util/stream/Stream.java
@@ -671,7 +671,8 @@ public interface Stream<T> extends BaseStream<T, Stream<T>> {
      * <p>This is a <a href="package-summary.html#StreamOps">terminal
      * operation</a>.
      *
-     * @return an array containing the elements of this stream
+     * @return an array, whose {@linkplain Class#getComponentType runtime component
+     * type} is {@code Object}, containing the elements of this stream
      */
     Object[] toArray();
 
@@ -694,13 +695,13 @@ public interface Stream<T> extends BaseStream<T, Stream<T>> {
      *                          .toArray(Person[]::new);
      * }</pre>
      *
-     * @param <A> the element type of the resulting array
+     * @param <A> the component type of the resulting array
      * @param generator a function which produces a new array of the desired
      *                  type and the provided length
      * @return an array containing the elements in this stream
-     * @throws ArrayStoreException if the runtime type of the array returned
-     * from the array generator is not a supertype of the runtime type of every
-     * element in this stream
+     * @throws ArrayStoreException if the runtime type of any element of this
+     *         stream is not assignable to the {@linkplain Class#getComponentType
+     *         runtime component type} of the generated array
      */
     <A> A[] toArray(IntFunction<A[]> generator);
 
diff --git a/src/java.base/share/classes/jdk/internal/loader/Loader.java b/src/java.base/share/classes/jdk/internal/loader/Loader.java
index dc46f6630b0..7c67c007575 100644
--- a/src/java.base/share/classes/jdk/internal/loader/Loader.java
+++ b/src/java.base/share/classes/jdk/internal/loader/Loader.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -28,7 +28,6 @@ package jdk.internal.loader;
 import java.io.File;
 import java.io.FilePermission;
 import java.io.IOException;
-import java.io.UncheckedIOException;
 import java.lang.module.Configuration;
 import java.lang.module.ModuleDescriptor;
 import java.lang.module.ModuleReader;
@@ -58,12 +57,8 @@ import java.util.List;
 import java.util.Map;
 import java.util.Objects;
 import java.util.Optional;
-import java.util.Spliterator;
-import java.util.Spliterators;
 import java.util.concurrent.ConcurrentHashMap;
-import java.util.function.Supplier;
 import java.util.stream.Stream;
-import java.util.stream.StreamSupport;
 
 import jdk.internal.misc.SharedSecrets;
 import jdk.internal.module.Resources;
@@ -403,12 +398,15 @@ public final class Loader extends SecureClassLoader {
 
         // this loader
         URL url = findResource(name);
-        if (url != null) {
-            return url;
-        } else {
+        if (url == null) {
             // parent loader
-            return parent.getResource(name);
+            if (parent != null) {
+                url = parent.getResource(name);
+            } else {
+                url = BootLoader.findResource(name);
+            }
         }
+        return url;
     }
 
     @Override
@@ -419,7 +417,12 @@ public final class Loader extends SecureClassLoader {
         List<URL> urls = findResourcesAsList(name);
 
         // parent loader
-        Enumeration<URL> e = parent.getResources(name);
+        Enumeration<URL> e;
+        if (parent != null) {
+            e = parent.getResources(name);
+        } else {
+            e = BootLoader.findResources(name);
+        }
 
         // concat the URLs with the URLs returned by the parent
         return new Enumeration<>() {
@@ -439,25 +442,6 @@ public final class Loader extends SecureClassLoader {
         };
     }
 
-    @Override
-    public Stream<URL> resources(String name) {
-        Objects.requireNonNull(name);
-        // ordering not specified
-        int characteristics = (Spliterator.NONNULL | Spliterator.IMMUTABLE |
-                               Spliterator.SIZED | Spliterator.SUBSIZED);
-        Supplier<Spliterator<URL>> supplier = () -> {
-            try {
-                List<URL> urls = findResourcesAsList(name);
-                return Spliterators.spliterator(urls, characteristics);
-            } catch (IOException e) {
-                throw new UncheckedIOException(e);
-            }
-        };
-        Stream<URL> s1 = StreamSupport.stream(supplier, characteristics, false);
-        Stream<URL> s2 = parent.resources(name);
-        return Stream.concat(s1, s2);
-    }
-
     /**
      * Finds the resources with the given name in this class loader.
      */
diff --git a/src/java.base/share/classes/jdk/internal/misc/JavaIOFileDescriptorAccess.java b/src/java.base/share/classes/jdk/internal/misc/JavaIOFileDescriptorAccess.java
index 017aefb16af..a82d04d6eed 100644
--- a/src/java.base/share/classes/jdk/internal/misc/JavaIOFileDescriptorAccess.java
+++ b/src/java.base/share/classes/jdk/internal/misc/JavaIOFileDescriptorAccess.java
@@ -25,6 +25,7 @@
 package jdk.internal.misc;
 
 import java.io.FileDescriptor;
+import java.io.IOException;
 
 /*
  * @author Chris Hegarty
@@ -35,7 +36,8 @@ public interface JavaIOFileDescriptorAccess {
     public int get(FileDescriptor fdo);
     public void setAppend(FileDescriptor fdo, boolean append);
     public boolean getAppend(FileDescriptor fdo);
-    public void close(FileDescriptor fdo);
+    public void close(FileDescriptor fdo) throws IOException;
+    public void registerCleanup(FileDescriptor fdo);
 
     // Only valid on Windows
     public void setHandle(FileDescriptor fdo, long handle);
diff --git a/src/java.base/share/classes/jdk/internal/misc/JavaLangAccess.java b/src/java.base/share/classes/jdk/internal/misc/JavaLangAccess.java
index 90ec8f40761..7561b2109ad 100644
--- a/src/java.base/share/classes/jdk/internal/misc/JavaLangAccess.java
+++ b/src/java.base/share/classes/jdk/internal/misc/JavaLangAccess.java
@@ -123,16 +123,6 @@ public interface JavaLangAccess {
      */
     void registerShutdownHook(int slot, boolean registerShutdownInProgress, Runnable hook);
 
-    /**
-     * Returns a new string backed by the provided character array. The
-     * character array is not copied and must never be modified after the
-     * String is created, in order to fulfill String's contract.
-     *
-     * @param chars the character array to back the string
-     * @return a newly created string whose content is the character array
-     */
-    String newStringUnsafe(char[] chars);
-
     /**
      * Returns a new Thread with the given Runnable and an
      * inherited AccessControlContext.
diff --git a/src/java.base/share/classes/jdk/internal/module/ModuleInfo.java b/src/java.base/share/classes/jdk/internal/module/ModuleInfo.java
index f6f1bb07e16..942af76b69d 100644
--- a/src/java.base/share/classes/jdk/internal/module/ModuleInfo.java
+++ b/src/java.base/share/classes/jdk/internal/module/ModuleInfo.java
@@ -63,6 +63,9 @@ import static jdk.internal.module.ClassFileConstants.*;
 
 public final class ModuleInfo {
 
+    private final int JAVA_MIN_SUPPORTED_VERSION = 53;
+    private final int JAVA_MAX_SUPPORTED_VERSION = 54;
+
     private static final JavaLangModuleAccess JLMA
         = SharedSecrets.getJavaLangModuleAccess();
 
@@ -188,8 +191,10 @@ public final class ModuleInfo {
 
         int minor_version = in.readUnsignedShort();
         int major_version = in.readUnsignedShort();
-        if (major_version < 53) {
-            throw invalidModuleDescriptor("Must be >= 53.0");
+        if (major_version < JAVA_MIN_SUPPORTED_VERSION ||
+                major_version > JAVA_MAX_SUPPORTED_VERSION) {
+            throw invalidModuleDescriptor("Unsupported major.minor version "
+                                          + major_version + "." + minor_version);
         }
 
         ConstantPool cpool = new ConstantPool(in);
@@ -245,7 +250,7 @@ public final class ModuleInfo {
             switch (attribute_name) {
 
                 case MODULE :
-                    builder = readModuleAttribute(in, cpool);
+                    builder = readModuleAttribute(in, cpool, major_version);
                     break;
 
                 case MODULE_PACKAGES :
@@ -334,7 +339,7 @@ public final class ModuleInfo {
      * Reads the Module attribute, returning the ModuleDescriptor.Builder to
      * build the corresponding ModuleDescriptor.
      */
-    private Builder readModuleAttribute(DataInput in, ConstantPool cpool)
+    private Builder readModuleAttribute(DataInput in, ConstantPool cpool, int major)
         throws IOException
     {
         // module_name
@@ -390,8 +395,21 @@ public final class ModuleInfo {
                 JLMA.requires(builder, mods, dn, vs);
             }
 
-            if (dn.equals("java.base"))
+            if (dn.equals("java.base")) {
+                if (major >= 54
+                    && (mods.contains(Requires.Modifier.TRANSITIVE)
+                        || mods.contains(Requires.Modifier.STATIC))) {
+                    String flagName;
+                    if (mods.contains(Requires.Modifier.TRANSITIVE)) {
+                        flagName = "ACC_TRANSITIVE";
+                    } else {
+                        flagName = "ACC_STATIC_PHASE";
+                    }
+                    throw invalidModuleDescriptor("The requires entry for java.base"
+                                                  + " has " + flagName + " set");
+                }
                 requiresJavaBase = true;
+            }
         }
         if (mn.equals("java.base")) {
             if (requires_count > 0) {
diff --git a/src/java.base/share/classes/jdk/internal/module/ModuleInfoWriter.java b/src/java.base/share/classes/jdk/internal/module/ModuleInfoWriter.java
index 2649e3e5b42..d1da3356ac7 100644
--- a/src/java.base/share/classes/jdk/internal/module/ModuleInfoWriter.java
+++ b/src/java.base/share/classes/jdk/internal/module/ModuleInfoWriter.java
@@ -80,7 +80,7 @@ public final class ModuleInfoWriter {
      */
     private static byte[] toModuleInfo(ModuleDescriptor md, ModuleTarget target) {
         ClassWriter cw = new ClassWriter(0);
-        cw.visit(Opcodes.V9, ACC_MODULE, "module-info", null, null, null);
+        cw.visit(Opcodes.V10, ACC_MODULE, "module-info", null, null, null);
 
         int moduleFlags = md.modifiers().stream()
                 .map(MODULE_MODS_TO_FLAGS::get)
diff --git a/src/java.base/share/classes/jdk/internal/org/objectweb/asm/ClassReader.java b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/ClassReader.java
index 3854a3eddb8..47e1f6d4b15 100644
--- a/src/java.base/share/classes/jdk/internal/org/objectweb/asm/ClassReader.java
+++ b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/ClassReader.java
@@ -185,7 +185,7 @@ public class ClassReader {
     public ClassReader(final byte[] b, final int off, final int len) {
         this.b = b;
         // checks the class version
-        if (readShort(off + 6) > Opcodes.V9) {
+        if (readShort(off + 6) > Opcodes.V10) {
             throw new IllegalArgumentException();
         }
         // parses the constant pool
diff --git a/src/java.base/share/classes/jdk/internal/org/objectweb/asm/Opcodes.java b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/Opcodes.java
index 4dc3ff19c5c..7ce9d9210c8 100644
--- a/src/java.base/share/classes/jdk/internal/org/objectweb/asm/Opcodes.java
+++ b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/Opcodes.java
@@ -89,6 +89,7 @@ public interface Opcodes {
     int V1_7 = 0 << 16 | 51;
     int V1_8 = 0 << 16 | 52;
     int V9 = 0 << 16 | 53;
+    int V10 = 0 << 16 | 54;
 
     // access flags
 
diff --git a/src/java.base/share/classes/sun/nio/ch/FileChannelImpl.java b/src/java.base/share/classes/sun/nio/ch/FileChannelImpl.java
index 5e48758bac0..274a17851e2 100644
--- a/src/java.base/share/classes/sun/nio/ch/FileChannelImpl.java
+++ b/src/java.base/share/classes/sun/nio/ch/FileChannelImpl.java
@@ -27,6 +27,7 @@ package sun.nio.ch;
 
 import java.io.FileDescriptor;
 import java.io.IOException;
+import java.io.UncheckedIOException;
 import java.lang.ref.Cleaner.Cleanable;
 import java.nio.ByteBuffer;
 import java.nio.MappedByteBuffer;
@@ -109,7 +110,12 @@ public class FileChannelImpl
         }
 
         public void run() {
-            fdAccess.close(fd);
+            try {
+                fdAccess.close(fd);
+            } catch (IOException ioe) {
+                // Rethrow as unchecked so the exception can be propagated as needed
+                throw new UncheckedIOException("close", ioe);
+            }
         }
     }
 
@@ -188,7 +194,11 @@ public class FileChannelImpl
         } else if (closer != null) {
             // Perform the cleaning action so it is not redone when
             // this channel becomes phantom reachable.
-            closer.clean();
+            try {
+                closer.clean();
+            } catch (UncheckedIOException uioe) {
+                throw uioe.getCause();
+            }
         } else {
             fdAccess.close(fd);
         }
diff --git a/src/java.base/share/classes/sun/security/internal/spec/TlsMasterSecretParameterSpec.java b/src/java.base/share/classes/sun/security/internal/spec/TlsMasterSecretParameterSpec.java
index 832b38edd76..e563360481b 100644
--- a/src/java.base/share/classes/sun/security/internal/spec/TlsMasterSecretParameterSpec.java
+++ b/src/java.base/share/classes/sun/security/internal/spec/TlsMasterSecretParameterSpec.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2005, 2017, 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
@@ -48,6 +48,7 @@ public class TlsMasterSecretParameterSpec implements AlgorithmParameterSpec {
     private final SecretKey premasterSecret;
     private final int majorVersion, minorVersion;
     private final byte[] clientRandom, serverRandom;
+    private final byte[] extendedMasterSecretSessionHash;
     private final String prfHashAlg;
     private final int prfHashLength;
     private final int prfBlockSize;
@@ -80,6 +81,50 @@ public class TlsMasterSecretParameterSpec implements AlgorithmParameterSpec {
             int majorVersion, int minorVersion,
             byte[] clientRandom, byte[] serverRandom,
             String prfHashAlg, int prfHashLength, int prfBlockSize) {
+        this(premasterSecret, majorVersion, minorVersion,
+                clientRandom, serverRandom,
+                new byte[0],
+                prfHashAlg, prfHashLength, prfBlockSize);
+    }
+
+    /**
+     * Constructs a new TlsMasterSecretParameterSpec.
+     *
+     * <p>The <code>getAlgorithm()</code> method of <code>premasterSecret</code>
+     * should return <code>"TlsRsaPremasterSecret"</code> if the key exchange
+     * algorithm was RSA and <code>"TlsPremasterSecret"</code> otherwise.
+     *
+     * @param premasterSecret the premaster secret
+     * @param majorVersion the major number of the protocol version
+     * @param minorVersion the minor number of the protocol version
+     * @param extendedMasterSecretSessionHash the session hash for
+     *        Extended Master Secret
+     * @param prfHashAlg the name of the TLS PRF hash algorithm to use.
+     *        Used only for TLS 1.2+.  TLS1.1 and earlier use a fixed PRF.
+     * @param prfHashLength the output length of the TLS PRF hash algorithm.
+     *        Used only for TLS 1.2+.
+     * @param prfBlockSize the input block size of the TLS PRF hash algorithm.
+     *        Used only for TLS 1.2+.
+     *
+     * @throws NullPointerException if premasterSecret is null
+     * @throws IllegalArgumentException if minorVersion or majorVersion are
+     *   negative or larger than 255
+     */
+    public TlsMasterSecretParameterSpec(SecretKey premasterSecret,
+            int majorVersion, int minorVersion,
+            byte[] extendedMasterSecretSessionHash,
+            String prfHashAlg, int prfHashLength, int prfBlockSize) {
+        this(premasterSecret, majorVersion, minorVersion,
+                new byte[0], new byte[0],
+                extendedMasterSecretSessionHash,
+                prfHashAlg, prfHashLength, prfBlockSize);
+    }
+
+    private TlsMasterSecretParameterSpec(SecretKey premasterSecret,
+            int majorVersion, int minorVersion,
+            byte[] clientRandom, byte[] serverRandom,
+            byte[] extendedMasterSecretSessionHash,
+            String prfHashAlg, int prfHashLength, int prfBlockSize) {
         if (premasterSecret == null) {
             throw new NullPointerException("premasterSecret must not be null");
         }
@@ -88,6 +133,9 @@ public class TlsMasterSecretParameterSpec implements AlgorithmParameterSpec {
         this.minorVersion = checkVersion(minorVersion);
         this.clientRandom = clientRandom.clone();
         this.serverRandom = serverRandom.clone();
+        this.extendedMasterSecretSessionHash =
+                (extendedMasterSecretSessionHash != null ?
+                        extendedMasterSecretSessionHash.clone() : new byte[0]);
         this.prfHashAlg = prfHashAlg;
         this.prfHashLength = prfHashLength;
         this.prfBlockSize = prfBlockSize;
@@ -146,6 +194,17 @@ public class TlsMasterSecretParameterSpec implements AlgorithmParameterSpec {
         return serverRandom.clone();
     }
 
+    /**
+     * Returns a copy of the Extended Master Secret session hash.
+     *
+     * @return a copy of the Extended Master Secret session hash, or an empty
+     *         array if no extended master secret session hash was provided
+     *         at instantiation time
+     */
+    public byte[] getExtendedMasterSecretSessionHash() {
+        return extendedMasterSecretSessionHash.clone();
+    }
+
     /**
      * Obtains the PRF hash algorithm to use in the PRF calculation.
      *
diff --git a/src/java.base/share/classes/sun/security/jca/ProviderList.java b/src/java.base/share/classes/sun/security/jca/ProviderList.java
index 81f8a6b8ef2..68597c891b2 100644
--- a/src/java.base/share/classes/sun/security/jca/ProviderList.java
+++ b/src/java.base/share/classes/sun/security/jca/ProviderList.java
@@ -308,7 +308,7 @@ public final class ProviderList {
         }
         if (debug != null) {
             debug.println("Loading all providers");
-            new Exception("Call trace").printStackTrace();
+            new Exception("Debug Info. Call trace:").printStackTrace();
         }
         int n = 0;
         for (int i = 0; i < configs.length; i++) {
diff --git a/src/java.base/share/classes/sun/security/pkcs/PKCS8Key.java b/src/java.base/share/classes/sun/security/pkcs/PKCS8Key.java
index ee2c6e31959..3301b72f400 100644
--- a/src/java.base/share/classes/sun/security/pkcs/PKCS8Key.java
+++ b/src/java.base/share/classes/sun/security/pkcs/PKCS8Key.java
@@ -346,7 +346,6 @@ public class PKCS8Key implements PrivateKey {
             }
 
         } catch (IOException e) {
-            // e.printStackTrace ();
             throw new InvalidKeyException("IOException : " +
                                           e.getMessage());
         }
diff --git a/src/java.base/share/classes/sun/security/provider/AuthPolicyFile.java b/src/java.base/share/classes/sun/security/provider/AuthPolicyFile.java
index 46cdcd9b092..1626f48ccb7 100644
--- a/src/java.base/share/classes/sun/security/provider/AuthPolicyFile.java
+++ b/src/java.base/share/classes/sun/security/provider/AuthPolicyFile.java
@@ -187,6 +187,7 @@ public class AuthPolicyFile extends javax.security.auth.Policy {
             } catch (Exception e) {
                 // ignore, treat it like we have no keystore
                 if (debug != null) {
+                    debug.println("Debug info only. No keystore.");
                     e.printStackTrace();
                 }
                 return null;
@@ -261,7 +262,7 @@ public class AuthPolicyFile extends javax.security.auth.Policy {
                 loaded_one = true;
             } catch (Exception e) {
                 if (debug != null) {
-                    debug.println("error reading policy " + e);
+                    debug.println("Debug info only. Error reading policy " + e);
                     e.printStackTrace();
                 }
                 // ignore that policy
diff --git a/src/java.base/share/classes/sun/security/provider/DSA.java b/src/java.base/share/classes/sun/security/provider/DSA.java
index 9fc47851baf..705974ac6ad 100644
--- a/src/java.base/share/classes/sun/security/provider/DSA.java
+++ b/src/java.base/share/classes/sun/security/provider/DSA.java
@@ -490,18 +490,6 @@ abstract class DSA extends SignatureSpi {
         return printable;
     }
 
-    private static void debug(Exception e) {
-        if (debug) {
-            e.printStackTrace();
-        }
-    }
-
-    private static void debug(String s) {
-        if (debug) {
-            System.err.println(s);
-        }
-    }
-
     /**
      * Standard SHA224withDSA implementation as defined in FIPS186-3.
      */
diff --git a/src/java.base/share/classes/sun/security/provider/PolicyFile.java b/src/java.base/share/classes/sun/security/provider/PolicyFile.java
index 3319f91a7c0..ee761d754f5 100644
--- a/src/java.base/share/classes/sun/security/provider/PolicyFile.java
+++ b/src/java.base/share/classes/sun/security/provider/PolicyFile.java
@@ -512,7 +512,8 @@ public class PolicyFile extends java.security.Policy {
                         }
                     } catch (Exception e) {
                         if (debug != null) {
-                            debug.println("error reading policy "+e);
+                            debug.println(
+                                "Debug info only. Error reading policy " +e);
                             e.printStackTrace();
                         }
                         // ignore that policy
@@ -559,6 +560,7 @@ public class PolicyFile extends java.security.Policy {
             } catch (Exception e) {
                 // ignore, treat it like we have no keystore
                 if (debug != null) {
+                    debug.println("Debug info only. Ignoring exception.");
                     e.printStackTrace();
                 }
             }
diff --git a/src/java.base/share/classes/sun/security/ssl/ClientHandshaker.java b/src/java.base/share/classes/sun/security/ssl/ClientHandshaker.java
index 1d6c74eab3a..cc12ab581c2 100644
--- a/src/java.base/share/classes/sun/security/ssl/ClientHandshaker.java
+++ b/src/java.base/share/classes/sun/security/ssl/ClientHandshaker.java
@@ -655,7 +655,8 @@ final class ClientHandshaker extends Handshaker {
 
                 // validate subject identity
                 ClientKeyExchangeService p =
-                        ClientKeyExchangeService.find(sessionSuite.keyExchange.name);
+                        ClientKeyExchangeService.find(
+                                sessionSuite.keyExchange.name);
                 if (p != null) {
                     Principal localPrincipal = session.getLocalPrincipal();
 
@@ -663,8 +664,9 @@ final class ClientHandshaker extends Handshaker {
                         if (debug != null && Debug.isOn("session"))
                             System.out.println("Subject identity is same");
                     } else {
-                        throw new SSLProtocolException("Server resumed" +
-                                " session with wrong subject identity or no subject");
+                        throw new SSLProtocolException(
+                                "Server resumed session with " +
+                                "wrong subject identity or no subject");
                     }
                 }
 
@@ -707,6 +709,54 @@ final class ClientHandshaker extends Handshaker {
         }   // Otherwise, using the value negotiated during the original
             // session initiation
 
+        // check the "extended_master_secret" extension
+        ExtendedMasterSecretExtension extendedMasterSecretExt =
+                (ExtendedMasterSecretExtension)mesg.extensions.get(
+                        ExtensionType.EXT_EXTENDED_MASTER_SECRET);
+        if (extendedMasterSecretExt != null) {
+            // Is it the expected server extension?
+            if (!useExtendedMasterSecret ||
+                    !mesgVersion.useTLS10PlusSpec() || !requestedToUseEMS) {
+                fatalSE(Alerts.alert_unsupported_extension,
+                        "Server sent the extended_master_secret " +
+                        "extension improperly");
+            }
+
+            // For abbreviated handshake, if the original session did not use
+            // the "extended_master_secret" extension but the new ServerHello
+            // contains the extension, the client MUST abort the handshake.
+            if (resumingSession && (session != null) &&
+                    !session.getUseExtendedMasterSecret()) {
+                fatalSE(Alerts.alert_unsupported_extension,
+                        "Server sent an unexpected extended_master_secret " +
+                        "extension on session resumption");
+            }
+        } else {
+            if (useExtendedMasterSecret && !allowLegacyMasterSecret) {
+                // For full handshake, if a client receives a ServerHello
+                // without the extension, it SHOULD abort the handshake if
+                // it does not wish to interoperate with legacy servers.
+                fatalSE(Alerts.alert_handshake_failure,
+                    "Extended Master Secret extension is required");
+            }
+
+            if (resumingSession && (session != null)) {
+                if (session.getUseExtendedMasterSecret()) {
+                    // For abbreviated handshake, if the original session used
+                    // the "extended_master_secret" extension but the new
+                    // ServerHello does not contain the extension, the client
+                    // MUST abort the handshake.
+                    fatalSE(Alerts.alert_handshake_failure,
+                            "Missing Extended Master Secret extension " +
+                            "on session resumption");
+                } else if (useExtendedMasterSecret && !allowLegacyResumption) {
+                    // Unlikely, abbreviated handshake should be discarded.
+                    fatalSE(Alerts.alert_handshake_failure,
+                        "Extended Master Secret extension is required");
+                }
+            }
+        }
+
         // check the ALPN extension
         ALPNExtension serverHelloALPN =
             (ALPNExtension) mesg.extensions.get(ExtensionType.EXT_ALPN);
@@ -777,7 +827,8 @@ final class ClientHandshaker extends Handshaker {
                     && (type != ExtensionType.EXT_ALPN)
                     && (type != ExtensionType.EXT_RENEGOTIATION_INFO)
                     && (type != ExtensionType.EXT_STATUS_REQUEST)
-                    && (type != ExtensionType.EXT_STATUS_REQUEST_V2)) {
+                    && (type != ExtensionType.EXT_STATUS_REQUEST_V2)
+                    && (type != ExtensionType.EXT_EXTENDED_MASTER_SECRET)) {
                 // Note: Better to check client requested extensions rather
                 // than all supported extensions.
                 fatalSE(Alerts.alert_unsupported_extension,
@@ -788,7 +839,8 @@ final class ClientHandshaker extends Handshaker {
         // Create a new session, we need to do the full handshake
         session = new SSLSessionImpl(protocolVersion, cipherSuite,
                             getLocalSupportedSignAlgs(),
-                            mesg.sessionId, getHostSE(), getPortSE());
+                            mesg.sessionId, getHostSE(), getPortSE(),
+                            (extendedMasterSecretExt != null));
         session.setRequestedServerNames(requestedServerNames);
         session.setNegotiatedMaxFragSize(requestedMFLength);
         session.setMaximumPacketSize(maximumPacketSize);
@@ -1430,6 +1482,44 @@ final class ClientHandshaker extends Handshaker {
                 session = null;
             }
 
+            if ((session != null) && useExtendedMasterSecret) {
+                boolean isTLS10Plus = sessionVersion.useTLS10PlusSpec();
+                if (isTLS10Plus && !session.getUseExtendedMasterSecret()) {
+                    if (!allowLegacyResumption) {
+                        // perform full handshake instead
+                        //
+                        // The client SHOULD NOT offer an abbreviated handshake
+                        // to resume a session that does not use an extended
+                        // master secret.  Instead, it SHOULD offer a full
+                        // handshake.
+                        session = null;
+                    }
+                }
+
+                if ((session != null) && !allowUnsafeServerCertChange) {
+                    // It is fine to move on with abbreviate handshake if
+                    // endpoint identification is enabled.
+                    String identityAlg = getEndpointIdentificationAlgorithmSE();
+                    if ((identityAlg == null || identityAlg.length() == 0)) {
+                        if (isTLS10Plus) {
+                            if (!session.getUseExtendedMasterSecret()) {
+                                // perform full handshake instead
+                                session = null;
+                            }   // Otherwise, use extended master secret.
+                        } else {
+                            // The extended master secret extension does not
+                            // apply to SSL 3.0.  Perform a full handshake
+                            // instead.
+                            //
+                            // Note that the useExtendedMasterSecret is
+                            // extended to protect SSL 3.0 connections,
+                            // by discarding abbreviate handshake.
+                            session = null;
+                        }
+                    }
+                }
+            }
+
             if (session != null) {
                 if (debug != null) {
                     if (Debug.isOn("handshake") || Debug.isOn("session")) {
@@ -1539,6 +1629,14 @@ final class ClientHandshaker extends Handshaker {
             clientHelloMessage.addSignatureAlgorithmsExtension(localSignAlgs);
         }
 
+        // add Extended Master Secret extension
+        if (useExtendedMasterSecret && maxProtocolVersion.useTLS10PlusSpec()) {
+            if ((session == null) || session.getUseExtendedMasterSecret()) {
+                clientHelloMessage.addExtendedMasterSecretExtension();
+                requestedToUseEMS = true;
+            }
+        }
+
         // add server_name extension
         if (enableSNIExtension) {
             if (session != null) {
@@ -1647,10 +1745,14 @@ final class ClientHandshaker extends Handshaker {
         // Allow server certificate change in client side during renegotiation
         // after a session-resumption abbreviated initial handshake?
         //
-        // DO NOT need to check allowUnsafeServerCertChange here. We only
+        // DO NOT need to check allowUnsafeServerCertChange here.  We only
         // reserve server certificates when allowUnsafeServerCertChange is
         // flase.
-        if (reservedServerCerts != null) {
+        //
+        // Allow server certificate change if it is negotiated to use the
+        // extended master secret.
+        if ((reservedServerCerts != null) &&
+                !session.getUseExtendedMasterSecret()) {
             // It is not necessary to check the certificate update if endpoint
             // identification is enabled.
             String identityAlg = getEndpointIdentificationAlgorithmSE();
diff --git a/src/java.base/share/classes/sun/security/ssl/ExtendedMasterSecretExtension.java b/src/java.base/share/classes/sun/security/ssl/ExtendedMasterSecretExtension.java
new file mode 100644
index 00000000000..bf7f7aefd75
--- /dev/null
+++ b/src/java.base/share/classes/sun/security/ssl/ExtendedMasterSecretExtension.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2017, Red Hat, Inc. and/or its affiliates.
+ *
+ * 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.security.ssl;
+
+import java.io.IOException;
+import javax.net.ssl.SSLProtocolException;
+
+/**
+ * Extended Master Secret TLS extension (TLS 1.0+). This extension
+ * defines how to calculate the TLS connection master secret and
+ * mitigates some types of man-in-the-middle attacks.
+ *
+ * See further information in
+ * <a href="https://tools.ietf.org/html/rfc7627">RFC 7627</a>.
+ *
+ * @author Martin Balao (mbalao@redhat.com)
+ */
+final class ExtendedMasterSecretExtension extends HelloExtension {
+    ExtendedMasterSecretExtension() {
+        super(ExtensionType.EXT_EXTENDED_MASTER_SECRET);
+    }
+
+    ExtendedMasterSecretExtension(HandshakeInStream s,
+            int len) throws IOException {
+        super(ExtensionType.EXT_EXTENDED_MASTER_SECRET);
+
+        if (len != 0) {
+            throw new SSLProtocolException("Invalid " + type + " extension");
+        }
+    }
+
+    @Override
+    int length() {
+        return 4;       // 4: extension type and length fields
+    }
+
+    @Override
+    void send(HandshakeOutStream s) throws IOException {
+        s.putInt16(type.id);    // ExtensionType extension_type;
+        s.putInt16(0);          // extension_data length
+    }
+
+    @Override
+    public String toString() {
+        return "Extension " + type;
+    }
+}
+
diff --git a/src/java.base/share/classes/sun/security/ssl/ExtensionType.java b/src/java.base/share/classes/sun/security/ssl/ExtensionType.java
index 5338807bbdc..0b908a2aa7d 100644
--- a/src/java.base/share/classes/sun/security/ssl/ExtensionType.java
+++ b/src/java.base/share/classes/sun/security/ssl/ExtensionType.java
@@ -43,7 +43,7 @@ final class ExtensionType {
         return name;
     }
 
-    static List<ExtensionType> knownExtensions = new ArrayList<>(15);
+    static List<ExtensionType> knownExtensions = new ArrayList<>(16);
 
     static ExtensionType get(int id) {
         for (ExtensionType ext : knownExtensions) {
@@ -105,6 +105,10 @@ final class ExtensionType {
     static final ExtensionType EXT_STATUS_REQUEST_V2 =
             e(0x0011, "status_request_v2");      // IANA registry value: 17
 
+    // extensions defined in RFC 7627
+    static final ExtensionType EXT_EXTENDED_MASTER_SECRET =
+            e(0x0017, "extended_master_secret"); // IANA registry value: 23
+
     // extensions defined in RFC 5746
     static final ExtensionType EXT_RENEGOTIATION_INFO =
             e(0xff01, "renegotiation_info");     // IANA registry value: 65281
diff --git a/src/java.base/share/classes/sun/security/ssl/HandshakeMessage.java b/src/java.base/share/classes/sun/security/ssl/HandshakeMessage.java
index cf3648685ba..952f8808b4d 100644
--- a/src/java.base/share/classes/sun/security/ssl/HandshakeMessage.java
+++ b/src/java.base/share/classes/sun/security/ssl/HandshakeMessage.java
@@ -389,6 +389,10 @@ static final class ClientHello extends HandshakeMessage {
         extensions.add(signatureAlgorithm);
     }
 
+    void addExtendedMasterSecretExtension() {
+        extensions.add(new ExtendedMasterSecretExtension());
+    }
+
     void addMFLExtension(int maximumPacketSize) {
         HelloExtension maxFragmentLength =
                 new MaxFragmentLengthExtension(maximumPacketSize);
@@ -1441,7 +1445,7 @@ class ECDH_ServerKeyExchange extends ServerKeyExchange {
         } else {
             sig = getSignature(privateKey.getAlgorithm());
         }
-        sig.initSign(privateKey);  // where is the SecureRandom?
+        sig.initSign(privateKey, sr);
 
         updateSignature(sig, clntNonce, svrNonce);
         signatureBytes = sig.sign();
diff --git a/src/java.base/share/classes/sun/security/ssl/Handshaker.java b/src/java.base/share/classes/sun/security/ssl/Handshaker.java
index f2813d57dd7..f2b66c8e5ff 100644
--- a/src/java.base/share/classes/sun/security/ssl/Handshaker.java
+++ b/src/java.base/share/classes/sun/security/ssl/Handshaker.java
@@ -30,12 +30,6 @@ import java.io.*;
 import java.util.*;
 import java.security.*;
 import java.nio.ByteBuffer;
-import java.security.NoSuchAlgorithmException;
-import java.security.AccessController;
-import java.security.AlgorithmConstraints;
-import java.security.AccessControlContext;
-import java.security.PrivilegedExceptionAction;
-import java.security.PrivilegedActionException;
 import java.util.function.BiFunction;
 
 import javax.crypto.*;
@@ -225,6 +219,20 @@ abstract class Handshaker {
             Debug.getBooleanProperty(
                 "jdk.tls.rejectClientInitiatedRenegotiation", false);
 
+    // To switch off the extended_master_secret extension.
+    static final boolean useExtendedMasterSecret;
+
+    // Allow session resumption without Extended Master Secret extension.
+    static final boolean allowLegacyResumption =
+            Debug.getBooleanProperty("jdk.tls.allowLegacyResumption", true);
+
+    // Allow full handshake without Extended Master Secret extension.
+    static final boolean allowLegacyMasterSecret =
+            Debug.getBooleanProperty("jdk.tls.allowLegacyMasterSecret", true);
+
+    // Is it requested to use extended master secret extension?
+    boolean requestedToUseEMS = false;
+
     // need to dispose the object when it is invalidated
     boolean invalidated;
 
@@ -233,6 +241,24 @@ abstract class Handshaker {
      */
     final boolean isDTLS;
 
+    // Is the extended_master_secret extension supported?
+    static {
+        boolean supportExtendedMasterSecret = true;
+        try {
+            KeyGenerator kg =
+                JsseJce.getKeyGenerator("SunTlsExtendedMasterSecret");
+        } catch (NoSuchAlgorithmException nae) {
+            supportExtendedMasterSecret = false;
+        }
+
+        if (supportExtendedMasterSecret) {
+            useExtendedMasterSecret = Debug.getBooleanProperty(
+                    "jdk.tls.useExtendedMasterSecret", true);
+        } else {
+            useExtendedMasterSecret = false;
+        }
+    }
+
     Handshaker(SSLSocketImpl c, SSLContextImpl context,
             ProtocolList enabledProtocols, boolean needCertVerify,
             boolean isClient, ProtocolVersion activeProtocolVersion,
@@ -243,7 +269,7 @@ abstract class Handshaker {
         init(context, enabledProtocols, needCertVerify, isClient,
             activeProtocolVersion, isInitialHandshake, secureRenegotiation,
             clientVerifyData, serverVerifyData);
-    }
+   }
 
     Handshaker(SSLEngineImpl engine, SSLContextImpl context,
             ProtocolList enabledProtocols, boolean needCertVerify,
@@ -1226,6 +1252,7 @@ abstract class Handshaker {
      * SHA1 hashes are of (different) constant strings, the pre-master
      * secret, and the nonces provided by the client and the server.
      */
+    @SuppressWarnings("deprecation")
     private SecretKey calculateMasterSecret(SecretKey preMasterSecret,
             ProtocolVersion requestedVersion) {
 
@@ -1276,11 +1303,37 @@ abstract class Handshaker {
         int prfHashLength = prf.getPRFHashLength();
         int prfBlockSize = prf.getPRFBlockSize();
 
-        @SuppressWarnings("deprecation")
-        TlsMasterSecretParameterSpec spec = new TlsMasterSecretParameterSpec(
-                preMasterSecret, (majorVersion & 0xFF), (minorVersion & 0xFF),
-                clnt_random.random_bytes, svr_random.random_bytes,
-                prfHashAlg, prfHashLength, prfBlockSize);
+        TlsMasterSecretParameterSpec spec;
+        if (session.getUseExtendedMasterSecret()) {
+            // reset to use the extended master secret algorithm
+            masterAlg = "SunTlsExtendedMasterSecret";
+
+            byte[] sessionHash = null;
+            if (protocolVersion.useTLS12PlusSpec()) {
+                sessionHash = handshakeHash.getFinishedHash();
+            } else {
+                // TLS 1.0/1.1, DTLS 1.0
+                sessionHash = new byte[36];
+                try {
+                    handshakeHash.getMD5Clone().digest(sessionHash, 0, 16);
+                    handshakeHash.getSHAClone().digest(sessionHash, 16, 20);
+                } catch (DigestException de) {
+                    throw new ProviderException(de);
+                }
+            }
+
+            spec = new TlsMasterSecretParameterSpec(
+                    preMasterSecret,
+                    (majorVersion & 0xFF), (minorVersion & 0xFF),
+                    sessionHash,
+                    prfHashAlg, prfHashLength, prfBlockSize);
+        } else {
+            spec = new TlsMasterSecretParameterSpec(
+                    preMasterSecret,
+                    (majorVersion & 0xFF), (minorVersion & 0xFF),
+                    clnt_random.random_bytes, svr_random.random_bytes,
+                    prfHashAlg, prfHashLength, prfBlockSize);
+        }
 
         try {
             KeyGenerator kg = JsseJce.getKeyGenerator(masterAlg);
diff --git a/src/java.base/share/classes/sun/security/ssl/HelloExtensions.java b/src/java.base/share/classes/sun/security/ssl/HelloExtensions.java
index 82975c962f5..013b4fb7aa7 100644
--- a/src/java.base/share/classes/sun/security/ssl/HelloExtensions.java
+++ b/src/java.base/share/classes/sun/security/ssl/HelloExtensions.java
@@ -93,6 +93,8 @@ final class HelloExtensions {
                 extension = new CertStatusReqExtension(s, extlen);
             } else if (extType == ExtensionType.EXT_STATUS_REQUEST_V2) {
                 extension = new CertStatusReqListV2Extension(s, extlen);
+            } else if (extType == ExtensionType.EXT_EXTENDED_MASTER_SECRET) {
+                extension = new ExtendedMasterSecretExtension(s, extlen);
             } else {
                 extension = new UnknownExtension(s, extlen, extType);
             }
diff --git a/src/java.base/share/classes/sun/security/ssl/SSLSessionImpl.java b/src/java.base/share/classes/sun/security/ssl/SSLSessionImpl.java
index 59472f18b84..c747844f1ff 100644
--- a/src/java.base/share/classes/sun/security/ssl/SSLSessionImpl.java
+++ b/src/java.base/share/classes/sun/security/ssl/SSLSessionImpl.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1996, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1996, 2017, 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
@@ -91,6 +91,7 @@ final class SSLSessionImpl extends ExtendedSSLSession {
     private byte                compressionMethod;
     private CipherSuite         cipherSuite;
     private SecretKey           masterSecret;
+    private final boolean       useExtendedMasterSecret;
 
     /*
      * Information not part of the SSLv3 protocol spec, but used
@@ -148,7 +149,7 @@ final class SSLSessionImpl extends ExtendedSSLSession {
      */
     private SSLSessionImpl() {
         this(ProtocolVersion.NONE, CipherSuite.C_NULL, null,
-            new SessionId(false, null), null, -1);
+            new SessionId(false, null), null, -1, false);
     }
 
     /*
@@ -158,9 +159,11 @@ final class SSLSessionImpl extends ExtendedSSLSession {
      */
     SSLSessionImpl(ProtocolVersion protocolVersion, CipherSuite cipherSuite,
             Collection<SignatureAndHashAlgorithm> algorithms,
-            SecureRandom generator, String host, int port) {
+            SecureRandom generator, String host, int port,
+            boolean useExtendedMasterSecret) {
         this(protocolVersion, cipherSuite, algorithms,
-             new SessionId(defaultRejoinable, generator), host, port);
+             new SessionId(defaultRejoinable, generator), host, port,
+             useExtendedMasterSecret);
     }
 
     /*
@@ -168,7 +171,8 @@ final class SSLSessionImpl extends ExtendedSSLSession {
      */
     SSLSessionImpl(ProtocolVersion protocolVersion, CipherSuite cipherSuite,
             Collection<SignatureAndHashAlgorithm> algorithms,
-            SessionId id, String host, int port) {
+            SessionId id, String host, int port,
+            boolean useExtendedMasterSecret) {
         this.protocolVersion = protocolVersion;
         sessionId = id;
         peerCerts = null;
@@ -182,6 +186,7 @@ final class SSLSessionImpl extends ExtendedSSLSession {
             SignatureAndHashAlgorithm.getAlgorithmNames(algorithms);
         negotiatedMaxFragLen = -1;
         statusResponses = null;
+        this.useExtendedMasterSecret = useExtendedMasterSecret;
 
         if (debug != null && Debug.isOn("session")) {
             System.out.println("%% Initialized:  " + this);
@@ -203,6 +208,10 @@ final class SSLSessionImpl extends ExtendedSSLSession {
         return masterSecret;
     }
 
+    boolean getUseExtendedMasterSecret() {
+        return useExtendedMasterSecret;
+    }
+
     void setPeerCertificates(X509Certificate[] peer) {
         if (peerCerts == null) {
             peerCerts = peer;
diff --git a/src/java.base/share/classes/sun/security/ssl/ServerHandshaker.java b/src/java.base/share/classes/sun/security/ssl/ServerHandshaker.java
index 85b96cd3f8b..c6555e8a84e 100644
--- a/src/java.base/share/classes/sun/security/ssl/ServerHandshaker.java
+++ b/src/java.base/share/classes/sun/security/ssl/ServerHandshaker.java
@@ -529,6 +529,27 @@ final class ServerHandshaker extends Handshaker {
             }
         }
 
+        // check out the "extended_master_secret" extension
+        if (useExtendedMasterSecret) {
+            ExtendedMasterSecretExtension extendedMasterSecretExtension =
+                    (ExtendedMasterSecretExtension)mesg.extensions.get(
+                            ExtensionType.EXT_EXTENDED_MASTER_SECRET);
+            if (extendedMasterSecretExtension != null) {
+                requestedToUseEMS = true;
+            } else if (mesg.protocolVersion.useTLS10PlusSpec()) {
+                if (!allowLegacyMasterSecret) {
+                    // For full handshake, if the server receives a ClientHello
+                    // without the extension, it SHOULD abort the handshake if
+                    // it does not wish to interoperate with legacy clients.
+                    //
+                    // As if extended master extension is required for full
+                    // handshake, it MUST be used in abbreviated handshake too.
+                    fatalSE(Alerts.alert_handshake_failure,
+                        "Extended Master Secret extension is required");
+                }
+            }
+        }
+
         // check the ALPN extension
         ALPNExtension clientHelloALPN = (ALPNExtension)
             mesg.extensions.get(ExtensionType.EXT_ALPN);
@@ -592,11 +613,45 @@ final class ServerHandshaker extends Handshaker {
                 if (resumingSession) {
                     ProtocolVersion oldVersion = previous.getProtocolVersion();
                     // cannot resume session with different version
-                    if (oldVersion != protocolVersion) {
+                    if (oldVersion != mesg.protocolVersion) {
                         resumingSession = false;
                     }
                 }
 
+                if (resumingSession && useExtendedMasterSecret) {
+                    if (requestedToUseEMS &&
+                            !previous.getUseExtendedMasterSecret()) {
+                        // For abbreviated handshake request, If the original
+                        // session did not use the "extended_master_secret"
+                        // extension but the new ClientHello contains the
+                        // extension, then the server MUST NOT perform the
+                        // abbreviated handshake.  Instead, it SHOULD continue
+                        // with a full handshake.
+                        resumingSession = false;
+                    } else if (!requestedToUseEMS &&
+                            previous.getUseExtendedMasterSecret()) {
+                        // For abbreviated handshake request, if the original
+                        // session used the "extended_master_secret" extension
+                        // but the new ClientHello does not contain it, the
+                        // server MUST abort the abbreviated handshake.
+                        fatalSE(Alerts.alert_handshake_failure,
+                                "Missing Extended Master Secret extension " +
+                                "on session resumption");
+                    } else if (!requestedToUseEMS &&
+                            !previous.getUseExtendedMasterSecret()) {
+                        // For abbreviated handshake request, if neither the
+                        // original session nor the new ClientHello uses the
+                        // extension, the server SHOULD abort the handshake.
+                        if (!allowLegacyResumption) {
+                            fatalSE(Alerts.alert_handshake_failure,
+                                "Missing Extended Master Secret extension " +
+                                "on session resumption");
+                        } else {  // Otherwise, continue with a full handshake.
+                            resumingSession = false;
+                        }
+                    }
+                }
+
                 // cannot resume session with different server name indication
                 if (resumingSession) {
                     List<SNIServerName> oldServerNames =
@@ -630,7 +685,7 @@ final class ServerHandshaker extends Handshaker {
                 if (resumingSession) {
                     CipherSuite suite = previous.getSuite();
                     ClientKeyExchangeService p =
-                            ClientKeyExchangeService.find(suite.keyExchange.name);
+                        ClientKeyExchangeService.find(suite.keyExchange.name);
                     if (p != null) {
                         Principal localPrincipal = previous.getLocalPrincipal();
 
@@ -784,7 +839,9 @@ final class ServerHandshaker extends Handshaker {
             session = new SSLSessionImpl(protocolVersion, CipherSuite.C_NULL,
                         getLocalSupportedSignAlgs(),
                         sslContext.getSecureRandom(),
-                        getHostAddressSE(), getPortSE());
+                        getHostAddressSE(), getPortSE(),
+                        (requestedToUseEMS &&
+                                protocolVersion.useTLS10PlusSpec()));
 
             if (protocolVersion.useTLS12PlusSpec()) {
                 if (peerSupportedSignAlgs != null) {
@@ -886,6 +943,10 @@ final class ServerHandshaker extends Handshaker {
             m1.extensions.add(maxFragLenExt);
         }
 
+        if (session.getUseExtendedMasterSecret()) {
+            m1.extensions.add(new ExtendedMasterSecretExtension());
+        }
+
         StaplingParameters staplingParams = processStapling(mesg);
         if (staplingParams != null) {
             // We now can safely assert status_request[_v2] in our
@@ -963,7 +1024,8 @@ final class ServerHandshaker extends Handshaker {
          * defined in the protocol spec are explicitly stated to require
          * using RSA certificates.
          */
-        if (ClientKeyExchangeService.find(cipherSuite.keyExchange.name) != null) {
+        if (ClientKeyExchangeService.find(
+                cipherSuite.keyExchange.name) != null) {
             // No external key exchange provider needs a cert now.
         } else if ((keyExchange != K_DH_ANON) && (keyExchange != K_ECDH_ANON)) {
             if (certs == null) {
diff --git a/src/java.base/share/classes/sun/security/tools/KeyStoreUtil.java b/src/java.base/share/classes/sun/security/tools/KeyStoreUtil.java
index 0aca1f39bd5..bc93634c642 100644
--- a/src/java.base/share/classes/sun/security/tools/KeyStoreUtil.java
+++ b/src/java.base/share/classes/sun/security/tools/KeyStoreUtil.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2005, 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2005, 2017, 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
@@ -93,8 +93,9 @@ public class KeyStoreUtil {
      * MSCAPI KeyStores
      */
     public static boolean isWindowsKeyStore(String storetype) {
-        return storetype.equalsIgnoreCase("Windows-MY")
-                || storetype.equalsIgnoreCase("Windows-ROOT");
+        return storetype != null
+                && (storetype.equalsIgnoreCase("Windows-MY")
+                    || storetype.equalsIgnoreCase("Windows-ROOT"));
     }
 
     /**
diff --git a/src/java.base/share/classes/sun/security/tools/keytool/Main.java b/src/java.base/share/classes/sun/security/tools/keytool/Main.java
index f30eec637fb..e5b503c3151 100644
--- a/src/java.base/share/classes/sun/security/tools/keytool/Main.java
+++ b/src/java.base/share/classes/sun/security/tools/keytool/Main.java
@@ -134,8 +134,6 @@ public final class Main {
     private Set<Pair <String, String>> providers = null;
     private Set<Pair <String, String>> providerClasses = null;
     private String storetype = null;
-    private boolean hasStoretypeOption = false;
-    private boolean hasSrcStoretypeOption = false;
     private String srcProviderName = null;
     private String providerName = null;
     private String pathlist = null;
@@ -549,14 +547,12 @@ public final class Main {
                 passwords.add(storePass);
             } else if (collator.compare(flags, "-storetype") == 0 ||
                     collator.compare(flags, "-deststoretype") == 0) {
-                storetype = args[++i];
-                hasStoretypeOption = true;
+                storetype = KeyStoreUtil.niceStoreTypeName(args[++i]);
             } else if (collator.compare(flags, "-srcstorepass") == 0) {
                 srcstorePass = getPass(modifier, args[++i]);
                 passwords.add(srcstorePass);
             } else if (collator.compare(flags, "-srcstoretype") == 0) {
-                srcstoretype = args[++i];
-                hasSrcStoretypeOption = true;
+                srcstoretype = KeyStoreUtil.niceStoreTypeName(args[++i]);
             } else if (collator.compare(flags, "-srckeypass") == 0) {
                 srckeyPass = getPass(modifier, args[++i]);
                 passwords.add(srckeyPass);
@@ -708,16 +704,6 @@ public final class Main {
             ksfname = KeyStoreUtil.getCacerts();
         }
 
-        if (storetype == null) {
-            storetype = KeyStore.getDefaultType();
-        }
-        storetype = KeyStoreUtil.niceStoreTypeName(storetype);
-
-        if (srcstoretype == null) {
-            srcstoretype = KeyStore.getDefaultType();
-        }
-        srcstoretype = KeyStoreUtil.niceStoreTypeName(srcstoretype);
-
         if (P11KEYSTORE.equalsIgnoreCase(storetype) ||
                 KeyStoreUtil.isWindowsKeyStore(storetype)) {
             token = true;
@@ -742,11 +728,6 @@ public final class Main {
                         (".storepasswd.and.keypasswd.commands.not.supported.if.storetype.is.{0}"), storetype));
         }
 
-        if (P12KEYSTORE.equalsIgnoreCase(storetype) && command == KEYPASSWD) {
-            throw new UnsupportedOperationException(rb.getString
-                        (".keypasswd.commands.not.supported.if.storetype.is.PKCS12"));
-        }
-
         if (token && (keyPass != null || newPass != null || destKeyPass != null)) {
             throw new IllegalArgumentException(MessageFormat.format(rb.getString
                 (".keypass.and.new.can.not.be.specified.if.storetype.is.{0}"), storetype));
@@ -923,9 +904,13 @@ public final class Main {
         // Create new keystore
         // Probe for keystore type when filename is available
         if (ksfile != null && ksStream != null && providerName == null &&
-                hasStoretypeOption == false && !inplaceImport) {
+                storetype == null && !inplaceImport) {
             keyStore = KeyStore.getInstance(ksfile, storePass);
+            storetype = keyStore.getType();
         } else {
+            if (storetype == null) {
+                storetype = KeyStore.getDefaultType();
+            }
             if (providerName == null) {
                 keyStore = KeyStore.getInstance(storetype);
             } else {
@@ -964,6 +949,11 @@ public final class Main {
             }
         }
 
+        if (P12KEYSTORE.equalsIgnoreCase(storetype) && command == KEYPASSWD) {
+            throw new UnsupportedOperationException(rb.getString
+                    (".keypasswd.commands.not.supported.if.storetype.is.PKCS12"));
+        }
+
         // All commands that create or modify the keystore require a keystore
         // password.
 
@@ -2123,9 +2113,13 @@ public final class Main {
         try {
             // Probe for keystore type when filename is available
             if (srcksfile != null && is != null && srcProviderName == null &&
-                hasSrcStoretypeOption == false) {
+                    srcstoretype == null) {
                 store = KeyStore.getInstance(srcksfile, srcstorePass);
+                srcstoretype = store.getType();
             } else {
+                if (srcstoretype == null) {
+                    srcstoretype = KeyStore.getDefaultType();
+                }
                 if (srcProviderName == null) {
                     store = KeyStore.getInstance(srcstoretype);
                 } else {
diff --git a/src/java.base/share/classes/sun/security/util/AnchorCertificates.java b/src/java.base/share/classes/sun/security/util/AnchorCertificates.java
index e260dd44ac0..af77f8fda57 100644
--- a/src/java.base/share/classes/sun/security/util/AnchorCertificates.java
+++ b/src/java.base/share/classes/sun/security/util/AnchorCertificates.java
@@ -75,8 +75,8 @@ public class AnchorCertificates {
                 } catch (Exception e) {
                     if (debug != null) {
                         debug.println("Error parsing cacerts");
+                        e.printStackTrace();
                     }
-                    e.printStackTrace();
                 }
                 return null;
             }
diff --git a/src/java.base/share/classes/sun/security/util/SignatureFileVerifier.java b/src/java.base/share/classes/sun/security/util/SignatureFileVerifier.java
index 5a459267130..35b06aa554c 100644
--- a/src/java.base/share/classes/sun/security/util/SignatureFileVerifier.java
+++ b/src/java.base/share/classes/sun/security/util/SignatureFileVerifier.java
@@ -386,8 +386,9 @@ public class SignatureFileVerifier {
                     if (e.getMessage() != null) {
                         debug.println(key + ":  " + e.getMessage());
                     } else {
-                        debug.println(key + ":  " + algorithm +
-                                " was disabled, no exception msg given.");
+                        debug.println("Debug info only. " +  key + ":  " +
+                            algorithm +
+                            " was disabled, no exception msg given.");
                         e.printStackTrace();
                     }
                 }
diff --git a/src/java.base/share/classes/sun/security/x509/CertificateExtensions.java b/src/java.base/share/classes/sun/security/x509/CertificateExtensions.java
index f26244e7c82..e76a8b331f3 100644
--- a/src/java.base/share/classes/sun/security/x509/CertificateExtensions.java
+++ b/src/java.base/share/classes/sun/security/x509/CertificateExtensions.java
@@ -124,7 +124,8 @@ public class CertificateExtensions implements CertAttrSet<Extension> {
                 unparseableExtensions.put(ext.getExtensionId().toString(),
                         new UnparseableExtension(ext, e));
                 if (debug != null) {
-                    debug.println("Error parsing extension: " + ext);
+                    debug.println("Debug info only." +
+                       " Error parsing extension: " + ext);
                     e.printStackTrace();
                     HexDumpEncoder h = new HexDumpEncoder();
                     System.err.println(h.encodeBuffer(ext.getExtensionValue()));
diff --git a/src/java.base/share/classes/sun/security/x509/X509Key.java b/src/java.base/share/classes/sun/security/x509/X509Key.java
index a11fa27dff8..6a0008a68c1 100644
--- a/src/java.base/share/classes/sun/security/x509/X509Key.java
+++ b/src/java.base/share/classes/sun/security/x509/X509Key.java
@@ -392,7 +392,6 @@ public class X509Key implements PublicKey {
                 throw new InvalidKeyException ("excess key data");
 
         } catch (IOException e) {
-            // e.printStackTrace ();
             throw new InvalidKeyException("IOException: " +
                                           e.getMessage());
         }
diff --git a/src/java.base/share/lib/security/default.policy b/src/java.base/share/lib/security/default.policy
index c5d6fd9bf9b..a4a9cbcbccc 100644
--- a/src/java.base/share/lib/security/default.policy
+++ b/src/java.base/share/lib/security/default.policy
@@ -155,7 +155,10 @@ grant codeBase "jrt:/jdk.internal.vm.compiler" {
 };
 
 grant codeBase "jrt:/jdk.internal.vm.compiler.management" {
-    permission java.security.AllPermission;
+    permission java.lang.RuntimePermission "accessClassInPackage.org.graalvm.compiler.hotspot";
+    permission java.lang.RuntimePermission "accessClassInPackage.jdk.vm.ci.runtime";
+    permission java.lang.RuntimePermission "accessClassInPackage.sun.management.spi";
+    permission java.lang.RuntimePermission "sun.management.spi.PlatformMBeanProvider.subclass";
 };
 
 grant codeBase "jrt:/jdk.jsobject" {
diff --git a/src/java.base/share/native/include/classfile_constants.h b/src/java.base/share/native/include/classfile_constants.h
index e6490a74cda..3b63f31c6e8 100644
--- a/src/java.base/share/native/include/classfile_constants.h
+++ b/src/java.base/share/native/include/classfile_constants.h
@@ -31,7 +31,7 @@ extern "C" {
 #endif
 
 /* Classfile version number for this information */
-#define JVM_CLASSFILE_MAJOR_VERSION 53
+#define JVM_CLASSFILE_MAJOR_VERSION 54
 #define JVM_CLASSFILE_MINOR_VERSION 0
 
 /* Flags */
diff --git a/src/java.base/share/native/libjava/System.c b/src/java.base/share/native/libjava/System.c
index 80ab07191de..07e638862db 100644
--- a/src/java.base/share/native/libjava/System.c
+++ b/src/java.base/share/native/libjava/System.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1994, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1994, 2017, 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,7 +114,7 @@ Java_java_lang_System_identityHashCode(JNIEnv *env, jobject this, jobject x)
 #define VENDOR_URL_BUG "http://bugreport.java.com/bugreport/"
 #endif
 
-#define JAVA_MAX_SUPPORTED_VERSION 53
+#define JAVA_MAX_SUPPORTED_VERSION 54
 #define JAVA_MAX_SUPPORTED_MINOR_VERSION 0
 
 #ifdef JAVA_SPECIFICATION_VENDOR /* Third party may NOT overwrite this. */
diff --git a/src/java.base/unix/classes/java/io/FileDescriptor.java b/src/java.base/unix/classes/java/io/FileDescriptor.java
index 6c20e9d5763..5e0b80eb625 100644
--- a/src/java.base/unix/classes/java/io/FileDescriptor.java
+++ b/src/java.base/unix/classes/java/io/FileDescriptor.java
@@ -25,10 +25,14 @@
 
 package java.io;
 
+import java.lang.ref.Cleaner;
 import java.util.ArrayList;
 import java.util.List;
+
 import jdk.internal.misc.JavaIOFileDescriptorAccess;
 import jdk.internal.misc.SharedSecrets;
+import jdk.internal.ref.CleanerFactory;
+import jdk.internal.ref.PhantomCleanable;
 
 /**
  * Instances of the file descriptor class serve as an opaque handle
@@ -64,7 +68,7 @@ public final class FileDescriptor {
         SharedSecrets.setJavaIOFileDescriptorAccess(
                 new JavaIOFileDescriptorAccess() {
                     public void set(FileDescriptor fdo, int fd) {
-                        fdo.fd = fd;
+                        fdo.set(fd);
                     }
 
                     public int get(FileDescriptor fdo) {
@@ -79,10 +83,14 @@ public final class FileDescriptor {
                         return fdo.append;
                     }
 
-                    public void close(FileDescriptor fdo) {
+                    public void close(FileDescriptor fdo) throws IOException {
                         fdo.close();
                     }
 
+                    public void registerCleanup(FileDescriptor fdo) {
+                        fdo.registerCleanup();
+                    }
+
                     public void setHandle(FileDescriptor fdo, long handle) {
                         throw new UnsupportedOperationException();
                     }
@@ -94,6 +102,11 @@ public final class FileDescriptor {
         );
     }
 
+    /**
+     * Cleanup in case FileDescriptor is not explicitly closed.
+     */
+    private FDCleanup cleanup;
+
     /**
      * Constructs an (invalid) FileDescriptor
      * object.
@@ -177,6 +190,34 @@ public final class FileDescriptor {
     /* This routine initializes JNI field offsets for the class */
     private static native void initIDs();
 
+    /**
+     * Set the fd.
+     * If setting to -1, clear the cleaner.
+     * The {@link #registerCleanup()} method should be called for new fds.
+     * @param fd the fd or -1 to indicate closed
+     */
+    @SuppressWarnings("unchecked")
+    synchronized void set(int fd) {
+        if (fd == -1 && cleanup != null) {
+            cleanup.clear();
+            cleanup = null;
+        }
+        this.fd = fd;
+    }
+
+    /**
+     * Register a cleanup for the current raw fd.
+     * Used directly in java.io and indirectly via fdAccess.
+     * The cleanup should be registered after the fd is set in the FileDescriptor.
+     */
+    @SuppressWarnings("unchecked")
+    synchronized void registerCleanup() {
+        if (cleanup != null) {
+            cleanup.clear();
+        }
+        cleanup = FDCleanup.create(this);
+    }
+
     /**
      * Returns true, if the file was opened for appending.
      */
@@ -185,9 +226,30 @@ public final class FileDescriptor {
     /**
      * Close the raw file descriptor or handle, if it has not already been closed
      * and set the fd and handle to -1.
+     * Clear the cleaner so the close does not happen twice.
      * Package private to allow it to be used in java.io.
+     * @throws IOException if close fails
      */
-    native void close();
+    @SuppressWarnings("unchecked")
+    synchronized void close() throws IOException {
+        if (cleanup != null) {
+            cleanup.clear();
+            cleanup = null;
+        }
+        close0();
+    }
+
+    /*
+     * Close the raw file descriptor or handle, if it has not already been closed
+     * and set the fd and handle to -1.
+     */
+    private native void close0() throws IOException;
+
+    /*
+     * Raw close of the file descriptor.
+     * Used only for last chance cleanup.
+     */
+    private static native void cleanupClose0(int fd) throws IOException;
 
     /*
      * Package private methods to track referents.
@@ -252,4 +314,45 @@ public final class FileDescriptor {
             }
         }
     }
+
+    /**
+     * Cleanup for a FileDescriptor when it becomes phantom reachable.
+     * Create a cleanup if fd != -1.
+     * Subclassed from {@code PhantomCleanable} so that {@code clear} can be
+     * called to disable the cleanup when the fd is closed by any means other
+     * than calling {@link FileDescriptor#close}.
+     * Otherwise, it may close the native fd after it has been reused.
+     */
+    static final class FDCleanup extends PhantomCleanable<Object> {
+        private final int fd;
+
+        static FDCleanup create(FileDescriptor fdo) {
+            return fdo.fd == -1
+                ? null
+                : new FDCleanup(fdo, CleanerFactory.cleaner(), fdo.fd);
+        }
+
+        /**
+         * Constructor for a phantom cleanable reference.
+         * @param obj the object to monitor
+         * @param cleaner the cleaner
+         * @param fd file descriptor to close
+         */
+        private FDCleanup(Object obj, Cleaner cleaner, int fd) {
+            super(obj, cleaner);
+            this.fd = fd;
+        }
+
+        /**
+         * Close the native fd.
+         */
+        @Override
+        protected void performCleanup() {
+            try {
+                cleanupClose0(fd);
+            } catch (IOException ioe) {
+                throw new UncheckedIOException("close", ioe);
+            }
+        }
+    }
 }
diff --git a/src/java.base/unix/native/libjava/FileDescriptor_md.c b/src/java.base/unix/native/libjava/FileDescriptor_md.c
index fcde1ef7f93..49669384489 100644
--- a/src/java.base/unix/native/libjava/FileDescriptor_md.c
+++ b/src/java.base/unix/native/libjava/FileDescriptor_md.c
@@ -71,8 +71,17 @@ Java_java_io_FileDescriptor_getAppend(JNIEnv *env, jclass fdClass, jint fd) {
     return ((flags & O_APPEND) == 0) ? JNI_FALSE : JNI_TRUE;
 }
 
+JNIEXPORT void JNICALL
+Java_java_io_FileDescriptor_cleanupClose0(JNIEnv *env, jclass fdClass, jint fd) {
+    if (fd != -1) {
+        if (close(fd) == -1) {
+            JNU_ThrowIOExceptionWithLastError(env, "close failed");
+        }
+    }
+}
+
 // instance method close0 for FileDescriptor
 JNIEXPORT void JNICALL
-Java_java_io_FileDescriptor_close(JNIEnv *env, jobject this) {
+Java_java_io_FileDescriptor_close0(JNIEnv *env, jobject this) {
     fileDescriptorClose(env, this);
 }
diff --git a/src/java.base/unix/native/libjava/io_util_md.c b/src/java.base/unix/native/libjava/io_util_md.c
index 64374b50196..1ec21237856 100644
--- a/src/java.base/unix/native/libjava/io_util_md.c
+++ b/src/java.base/unix/native/libjava/io_util_md.c
@@ -139,6 +139,11 @@ fileDescriptorClose(JNIEnv *env, jobject this)
     if ((*env)->ExceptionOccurred(env)) {
         return;
     }
+
+    if (fd == -1) {
+        return;     // already closed and set to -1
+    }
+
     /* Set the fd to -1 before closing it so that the timing window
      * of other threads using the wrong fd (closed but recycled fd,
      * that gets re-opened with some other filename) is reduced.
diff --git a/src/java.base/unix/native/libnio/fs/UnixNativeDispatcher.c b/src/java.base/unix/native/libnio/fs/UnixNativeDispatcher.c
index 0e7e2563994..53bb1c1009f 100644
--- a/src/java.base/unix/native/libnio/fs/UnixNativeDispatcher.c
+++ b/src/java.base/unix/native/libnio/fs/UnixNativeDispatcher.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2008, 2017, 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
@@ -476,10 +476,14 @@ static void prepAttributes(JNIEnv* env, struct stat64* buf, jobject attrs) {
     (*env)->SetLongField(env, attrs, attrs_st_birthtime_sec, (jlong)buf->st_birthtime);
 #endif
 
-#if (_POSIX_C_SOURCE >= 200809L) || defined(__solaris__)
+#ifndef MACOSX
     (*env)->SetLongField(env, attrs, attrs_st_atime_nsec, (jlong)buf->st_atim.tv_nsec);
     (*env)->SetLongField(env, attrs, attrs_st_mtime_nsec, (jlong)buf->st_mtim.tv_nsec);
     (*env)->SetLongField(env, attrs, attrs_st_ctime_nsec, (jlong)buf->st_ctim.tv_nsec);
+#else
+    (*env)->SetLongField(env, attrs, attrs_st_atime_nsec, (jlong)buf->st_atimespec.tv_nsec);
+    (*env)->SetLongField(env, attrs, attrs_st_mtime_nsec, (jlong)buf->st_mtimespec.tv_nsec);
+    (*env)->SetLongField(env, attrs, attrs_st_ctime_nsec, (jlong)buf->st_ctimespec.tv_nsec);
 #endif
 }
 
diff --git a/src/java.base/windows/classes/java/io/FileDescriptor.java b/src/java.base/windows/classes/java/io/FileDescriptor.java
index 21fbd951fa5..ecc1934795d 100644
--- a/src/java.base/windows/classes/java/io/FileDescriptor.java
+++ b/src/java.base/windows/classes/java/io/FileDescriptor.java
@@ -25,10 +25,14 @@
 
 package java.io;
 
+import java.lang.ref.Cleaner;
 import java.util.ArrayList;
 import java.util.List;
+
 import jdk.internal.misc.JavaIOFileDescriptorAccess;
 import jdk.internal.misc.SharedSecrets;
+import jdk.internal.ref.CleanerFactory;
+import jdk.internal.ref.PhantomCleanable;
 
 /**
  * Instances of the file descriptor class serve as an opaque handle
@@ -81,12 +85,16 @@ public final class FileDescriptor {
                         return fdo.append;
                     }
 
-                    public void close(FileDescriptor fdo) {
+                    public void close(FileDescriptor fdo) throws IOException {
                         fdo.close();
                     }
 
+                    public void registerCleanup(FileDescriptor fdo) {
+                        fdo.registerCleanup();
+                    }
+
                     public void setHandle(FileDescriptor fdo, long handle) {
-                        fdo.handle = handle;
+                        fdo.setHandle(handle);
                     }
 
                     public long getHandle(FileDescriptor fdo) {
@@ -96,6 +104,11 @@ public final class FileDescriptor {
         );
     }
 
+    /**
+     * Cleanup in case FileDescriptor is not explicitly closed.
+     */
+    private FDCleanup cleanup;
+
     /**
      * Constructs an (invalid) FileDescriptor
      * object.
@@ -149,7 +162,7 @@ public final class FileDescriptor {
      * relevant device(s).  In particular, if this FileDescriptor
      * refers to a physical storage medium, such as a file in a file
      * system, sync will not return until all in-memory modified copies
-     * of buffers associated with this FileDesecriptor have been
+     * of buffers associated with this FileDescriptor have been
      * written to the physical medium.
      *
      * sync is meant to be used by code that requires physical
@@ -175,20 +188,69 @@ public final class FileDescriptor {
     /* This routine initializes JNI field offsets for the class */
     private static native void initIDs();
 
-    private static native long set(int d);
-
     private static FileDescriptor standardStream(int fd) {
         FileDescriptor desc = new FileDescriptor();
-        desc.handle = set(fd);
+        desc.handle = getHandle(fd);
         return desc;
     }
 
+    private static native long getHandle(int d);
+
+    /**
+     * Set the handle.
+     * If setting to -1, clear the cleaner.
+     * The {@link #registerCleanup()} method should be called for new handles.
+     * @param handle the handle or -1 to indicate closed
+     */
+    @SuppressWarnings("unchecked")
+    void setHandle(long handle) {
+        if (handle == -1 && cleanup != null) {
+            cleanup.clear();
+            cleanup = null;
+        }
+        this.handle = handle;
+    }
+
+    /**
+     * Register a cleanup for the current handle.
+     * Used directly in java.io and indirectly via fdAccess.
+     * The cleanup should be registered after the handle is set in the FileDescriptor.
+     */
+    @SuppressWarnings("unchecked")
+    synchronized void registerCleanup() {
+        if (cleanup != null) {
+            cleanup.clear();
+        }
+        cleanup = FDCleanup.create(this);
+    }
+
     /**
      * Close the raw file descriptor or handle, if it has not already been closed
      * and set the fd and handle to -1.
+     * Clear the cleaner so the close does not happen twice.
      * Package private to allow it to be used in java.io.
+     * @throws IOException if close fails
      */
-    native void close();
+    @SuppressWarnings("unchecked")
+    synchronized void close() throws IOException {
+        if (cleanup != null) {
+            cleanup.clear();
+            cleanup = null;
+        }
+        close0();
+    }
+
+    /*
+     * Close the raw file descriptor or handle, if it has not already been closed
+     * and set the fd and handle to -1.
+     */
+    private native void close0() throws IOException;
+
+    /*
+    * Raw close of the file handle.
+    * Used only for last chance cleanup.
+    */
+    private static native void cleanupClose0(long handle) throws IOException;
 
     /*
      * Package private methods to track referents.
@@ -253,4 +315,45 @@ public final class FileDescriptor {
             }
         }
     }
+
+    /**
+     * Cleanup for a FileDescriptor when it becomes phantom reachable.
+     * Create a cleanup if handle != -1.
+     * Subclassed from {@code PhantomCleanable} so that {@code clear} can be
+     * called to disable the cleanup when the fd is closed by any means other
+     * than calling {@link FileDescriptor#close}.
+     * Otherwise, it may close the handle after it has been reused.
+     */
+    static final class FDCleanup extends PhantomCleanable<Object> {
+        private final long handle;
+
+        static FDCleanup create(FileDescriptor fdo) {
+            return fdo.handle == -1
+                    ? null
+                    : new FDCleanup(fdo, CleanerFactory.cleaner(), fdo.handle);
+        }
+
+        /**
+         * Constructor for a phantom cleanable reference.
+         * @param obj the object to monitor
+         * @param cleaner the cleaner
+         * @param handle file handle to close
+         */
+        private FDCleanup(Object obj, Cleaner cleaner, long handle) {
+            super(obj, cleaner);
+            this.handle = handle;
+        }
+
+        /**
+         * Close the native handle.
+         */
+        @Override
+        protected void performCleanup() {
+            try {
+                cleanupClose0(handle);
+            } catch (IOException ioe) {
+                throw new UncheckedIOException("close", ioe);
+            }
+        }
+    }
 }
diff --git a/src/java.base/windows/classes/sun/nio/ch/FileDispatcherImpl.java b/src/java.base/windows/classes/sun/nio/ch/FileDispatcherImpl.java
index 23adf004e6d..9424d36f892 100644
--- a/src/java.base/windows/classes/sun/nio/ch/FileDispatcherImpl.java
+++ b/src/java.base/windows/classes/sun/nio/ch/FileDispatcherImpl.java
@@ -114,6 +114,7 @@ class FileDispatcherImpl extends FileDispatcher {
         FileDescriptor result = new FileDescriptor();
         long handle = duplicateHandle(fdAccess.getHandle(fd));
         fdAccess.setHandle(result, handle);
+        fdAccess.registerCleanup(result);
         return result;
     }
 
diff --git a/src/java.base/windows/classes/sun/nio/ch/WindowsAsynchronousFileChannelImpl.java b/src/java.base/windows/classes/sun/nio/ch/WindowsAsynchronousFileChannelImpl.java
index 30f62f22f71..2a4fda647d8 100644
--- a/src/java.base/windows/classes/sun/nio/ch/WindowsAsynchronousFileChannelImpl.java
+++ b/src/java.base/windows/classes/sun/nio/ch/WindowsAsynchronousFileChannelImpl.java
@@ -139,7 +139,7 @@ public class WindowsAsynchronousFileChannelImpl
         invalidateAllLocks();
 
         // close the file
-        close0(handle);
+        nd.close(fdObj);
 
         // waits until all I/O operations have completed
         ioCache.close();
@@ -728,8 +728,6 @@ public class WindowsAsynchronousFileChannelImpl
     private static native int lockFile(long handle, long position, long size,
         boolean shared, long overlapped) throws IOException;
 
-    private static native void close0(long handle);
-
     static {
         IOUtil.load();
     }
diff --git a/src/java.base/windows/classes/sun/nio/fs/WindowsChannelFactory.java b/src/java.base/windows/classes/sun/nio/fs/WindowsChannelFactory.java
index 16d65c78a30..3e219c98f24 100644
--- a/src/java.base/windows/classes/sun/nio/fs/WindowsChannelFactory.java
+++ b/src/java.base/windows/classes/sun/nio/fs/WindowsChannelFactory.java
@@ -216,8 +216,7 @@ class WindowsChannelFactory {
         } catch (IOException x) {
             // IOException is thrown if the file handle cannot be associated
             // with the completion port. All we can do is close the file.
-            long handle = fdAccess.getHandle(fdObj);
-            CloseHandle(handle);
+            fdAccess.close(fdObj);
             throw x;
         }
     }
@@ -347,6 +346,7 @@ class WindowsChannelFactory {
         FileDescriptor fdObj = new FileDescriptor();
         fdAccess.setHandle(fdObj, handle);
         fdAccess.setAppend(fdObj, flags.append);
+        fdAccess.registerCleanup(fdObj);
         return fdObj;
     }
 }
diff --git a/src/java.base/windows/native/libjava/FileDescriptor_md.c b/src/java.base/windows/native/libjava/FileDescriptor_md.c
index 0cd3a7321a7..8b64741b08a 100644
--- a/src/java.base/windows/native/libjava/FileDescriptor_md.c
+++ b/src/java.base/windows/native/libjava/FileDescriptor_md.c
@@ -57,7 +57,7 @@ Java_java_io_FileDescriptor_initIDs(JNIEnv *env, jclass fdClass) {
 }
 
 JNIEXPORT jlong JNICALL
-Java_java_io_FileDescriptor_set(JNIEnv *env, jclass fdClass, jint fd) {
+Java_java_io_FileDescriptor_getHandle(JNIEnv *env, jclass fdClass, jint fd) {
     SET_HANDLE(fd);
 }
 
@@ -73,8 +73,17 @@ Java_java_io_FileDescriptor_sync(JNIEnv *env, jobject this) {
     }
 }
 
+JNIEXPORT void JNICALL
+Java_java_io_FileDescriptor_cleanupClose0(JNIEnv *env, jclass fdClass, jlong handle) {
+    if (handle != -1) {
+        if (CloseHandle((HANDLE)handle) == -1) {
+            JNU_ThrowIOExceptionWithLastError(env, "close failed");
+        }
+    }
+}
+
 // instance method close0 for FileDescriptor
 JNIEXPORT void JNICALL
-Java_java_io_FileDescriptor_close(JNIEnv *env, jobject this) {
+Java_java_io_FileDescriptor_close0(JNIEnv *env, jobject this) {
     fileDescriptorClose(env, this);
 }
diff --git a/src/java.base/windows/native/libnio/ch/WindowsAsynchronousFileChannelImpl.c b/src/java.base/windows/native/libnio/ch/WindowsAsynchronousFileChannelImpl.c
index e24ca6e52b2..2269231f410 100644
--- a/src/java.base/windows/native/libnio/ch/WindowsAsynchronousFileChannelImpl.c
+++ b/src/java.base/windows/native/libnio/ch/WindowsAsynchronousFileChannelImpl.c
@@ -121,13 +121,3 @@ Java_sun_nio_ch_WindowsAsynchronousFileChannelImpl_lockFile(JNIEnv *env, jobject
     return 0;
 }
 
-JNIEXPORT void JNICALL
-Java_sun_nio_ch_WindowsAsynchronousFileChannelImpl_close0(JNIEnv* env, jclass this,
-    jlong handle)
-{
-    HANDLE h = (HANDLE)jlong_to_ptr(handle);
-    BOOL result = CloseHandle(h);
-    if (result == 0) {
-        JNU_ThrowIOExceptionWithLastError(env, "Close failed");
-    }
-}
diff --git a/src/java.compiler/share/classes/javax/lang/model/util/AbstractAnnotationValueVisitor9.java b/src/java.compiler/share/classes/javax/lang/model/util/AbstractAnnotationValueVisitor9.java
index 46b2875b42d..c05736913e7 100644
--- a/src/java.compiler/share/classes/javax/lang/model/util/AbstractAnnotationValueVisitor9.java
+++ b/src/java.compiler/share/classes/javax/lang/model/util/AbstractAnnotationValueVisitor9.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2017, 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,7 +32,7 @@ import javax.annotation.processing.SupportedSourceVersion;
 /**
  * A skeletal visitor for annotation values with default behavior
  * appropriate for the {@link SourceVersion#RELEASE_9 RELEASE_9}
- * source version.
+ * and {@link SourceVersion#RELEASE_10 RELEASE_10} source versions.
  *
  * <p> <b>WARNING:</b> The {@code AnnotationValueVisitor} interface
  * implemented by this class may have methods added to it in the
@@ -59,7 +59,7 @@ import javax.annotation.processing.SupportedSourceVersion;
  * @see AbstractAnnotationValueVisitor8
  * @since 9
  */
-@SupportedSourceVersion(RELEASE_9)
+@SupportedSourceVersion(RELEASE_10)
 public abstract class AbstractAnnotationValueVisitor9<R, P> extends AbstractAnnotationValueVisitor8<R, P> {
 
     /**
diff --git a/src/java.compiler/share/classes/javax/lang/model/util/AbstractElementVisitor9.java b/src/java.compiler/share/classes/javax/lang/model/util/AbstractElementVisitor9.java
index 9adc95b49cb..556c897c950 100644
--- a/src/java.compiler/share/classes/javax/lang/model/util/AbstractElementVisitor9.java
+++ b/src/java.compiler/share/classes/javax/lang/model/util/AbstractElementVisitor9.java
@@ -34,7 +34,7 @@ import static javax.lang.model.SourceVersion.*;
 /**
  * A skeletal visitor of program elements with default behavior
  * appropriate for the {@link SourceVersion#RELEASE_9 RELEASE_9}
- * source version.
+ * and {@link SourceVersion#RELEASE_10 RELEASE_10} source versions.
  *
  * <p> <b>WARNING:</b> The {@code ElementVisitor} interface
  * implemented by this class may have methods added to it in the
@@ -65,7 +65,7 @@ import static javax.lang.model.SourceVersion.*;
  * @since 9
  * @spec JPMS
  */
-@SupportedSourceVersion(RELEASE_9)
+@SupportedSourceVersion(RELEASE_10)
 public abstract class AbstractElementVisitor9<R, P> extends AbstractElementVisitor8<R, P> {
     /**
      * Constructor for concrete subclasses to call.
diff --git a/src/java.compiler/share/classes/javax/lang/model/util/AbstractTypeVisitor9.java b/src/java.compiler/share/classes/javax/lang/model/util/AbstractTypeVisitor9.java
index d268e867d0a..b38a032ada7 100644
--- a/src/java.compiler/share/classes/javax/lang/model/util/AbstractTypeVisitor9.java
+++ b/src/java.compiler/share/classes/javax/lang/model/util/AbstractTypeVisitor9.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2017, 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
@@ -27,13 +27,13 @@ package javax.lang.model.util;
 
 import javax.annotation.processing.SupportedSourceVersion;
 import javax.lang.model.type.*;
-
+import javax.lang.model.SourceVersion;
 import static javax.lang.model.SourceVersion.*;
 
 /**
  * A skeletal visitor of types with default behavior appropriate for
- * the {@link javax.lang.model.SourceVersion#RELEASE_9 RELEASE_9}
- * source version.
+ * the {@link SourceVersion#RELEASE_9 RELEASE_9}
+ * and {@link SourceVersion#RELEASE_10 RELEASE_10} source versions.
  *
  * <p> <b>WARNING:</b> The {@code TypeVisitor} interface implemented
  * by this class may have methods added to it in the future to
@@ -63,7 +63,7 @@ import static javax.lang.model.SourceVersion.*;
  * @see AbstractTypeVisitor8
  * @since 9
  */
-@SupportedSourceVersion(RELEASE_9)
+@SupportedSourceVersion(RELEASE_10)
 public abstract class AbstractTypeVisitor9<R, P> extends AbstractTypeVisitor8<R, P> {
     /**
      * Constructor for concrete subclasses to call.
diff --git a/src/java.compiler/share/classes/javax/lang/model/util/ElementKindVisitor9.java b/src/java.compiler/share/classes/javax/lang/model/util/ElementKindVisitor9.java
index 9237cc11361..7fc8140e8ec 100644
--- a/src/java.compiler/share/classes/javax/lang/model/util/ElementKindVisitor9.java
+++ b/src/java.compiler/share/classes/javax/lang/model/util/ElementKindVisitor9.java
@@ -33,7 +33,8 @@ import javax.lang.model.SourceVersion;
 /**
  * A visitor of program elements based on their {@linkplain
  * ElementKind kind} with default behavior appropriate for the {@link
- * SourceVersion#RELEASE_9 RELEASE_9} source version.  For {@linkplain
+ * SourceVersion#RELEASE_9 RELEASE_9} and {@link
+ * SourceVersion#RELEASE_10 RELEASE_10} source versions. For {@linkplain
  * Element elements} <code><i>Xyz</i></code> that may have more than one
  * kind, the <code>visit<i>Xyz</i></code> methods in this class delegate
  * to the <code>visit<i>Xyz</i>As<i>Kind</i></code> method corresponding to the
@@ -77,7 +78,7 @@ import javax.lang.model.SourceVersion;
  * @since 9
  * @spec JPMS
  */
-@SupportedSourceVersion(RELEASE_9)
+@SupportedSourceVersion(RELEASE_10)
 public class ElementKindVisitor9<R, P> extends ElementKindVisitor8<R, P> {
     /**
      * Constructor for concrete subclasses; uses {@code null} for the
diff --git a/src/java.compiler/share/classes/javax/lang/model/util/ElementScanner9.java b/src/java.compiler/share/classes/javax/lang/model/util/ElementScanner9.java
index dba0a96e383..a5e61046646 100644
--- a/src/java.compiler/share/classes/javax/lang/model/util/ElementScanner9.java
+++ b/src/java.compiler/share/classes/javax/lang/model/util/ElementScanner9.java
@@ -34,7 +34,8 @@ import static javax.lang.model.SourceVersion.*;
 /**
  * A scanning visitor of program elements with default behavior
  * appropriate for the {@link SourceVersion#RELEASE_9 RELEASE_9}
- * source version.  The <code>visit<i>Xyz</i></code> methods in this
+ * and {@link SourceVersion#RELEASE_10 RELEASE_10} source versions.
+ * The <code>visit<i>Xyz</i></code> methods in this
  * class scan their component elements by calling {@code scan} on
  * their {@linkplain Element#getEnclosedElements enclosed elements},
  * {@linkplain ExecutableElement#getParameters parameters}, etc., as
@@ -90,7 +91,7 @@ import static javax.lang.model.SourceVersion.*;
  * @since 9
  * @spec JPMS
  */
-@SupportedSourceVersion(RELEASE_9)
+@SupportedSourceVersion(RELEASE_10)
 public class ElementScanner9<R, P> extends ElementScanner8<R, P> {
     /**
      * Constructor for concrete subclasses; uses {@code null} for the
diff --git a/src/java.compiler/share/classes/javax/lang/model/util/SimpleAnnotationValueVisitor9.java b/src/java.compiler/share/classes/javax/lang/model/util/SimpleAnnotationValueVisitor9.java
index d5cf8e0cfd0..adb172e092e 100644
--- a/src/java.compiler/share/classes/javax/lang/model/util/SimpleAnnotationValueVisitor9.java
+++ b/src/java.compiler/share/classes/javax/lang/model/util/SimpleAnnotationValueVisitor9.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2017, 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,7 +32,8 @@ import static javax.lang.model.SourceVersion.*;
 /**
  * A simple visitor for annotation values with default behavior
  * appropriate for the {@link SourceVersion#RELEASE_9 RELEASE_9}
- * source version.  Visit methods call {@link #defaultAction
+ * and {@link SourceVersion#RELEASE_10 RELEASE_10} source versions.
+ * Visit methods call {@link #defaultAction
  * defaultAction} passing their arguments to {@code defaultAction}'s
  * corresponding parameters.
  *
@@ -66,7 +67,7 @@ import static javax.lang.model.SourceVersion.*;
  * @see SimpleAnnotationValueVisitor8
  * @since 9
  */
-@SupportedSourceVersion(RELEASE_9)
+@SupportedSourceVersion(RELEASE_10)
 public class SimpleAnnotationValueVisitor9<R, P> extends SimpleAnnotationValueVisitor8<R, P> {
     /**
      * Constructor for concrete subclasses; uses {@code null} for the
diff --git a/src/java.compiler/share/classes/javax/lang/model/util/SimpleElementVisitor9.java b/src/java.compiler/share/classes/javax/lang/model/util/SimpleElementVisitor9.java
index 5c62d078a4c..a73988fb3b4 100644
--- a/src/java.compiler/share/classes/javax/lang/model/util/SimpleElementVisitor9.java
+++ b/src/java.compiler/share/classes/javax/lang/model/util/SimpleElementVisitor9.java
@@ -33,7 +33,7 @@ import static javax.lang.model.SourceVersion.*;
 /**
  * A simple visitor of program elements with default behavior
  * appropriate for the {@link SourceVersion#RELEASE_9 RELEASE_9}
- * source version.
+ * and {@link SourceVersion#RELEASE_10 RELEASE_10} source versions.
  *
  * Visit methods corresponding to {@code RELEASE_9} and earlier
  * language constructs call {@link #defaultAction defaultAction},
@@ -73,7 +73,7 @@ import static javax.lang.model.SourceVersion.*;
  * @since 9
  * @spec JPMS
  */
-@SupportedSourceVersion(RELEASE_9)
+@SupportedSourceVersion(RELEASE_10)
 public class SimpleElementVisitor9<R, P> extends SimpleElementVisitor8<R, P> {
     /**
      * Constructor for concrete subclasses; uses {@code null} for the
diff --git a/src/java.compiler/share/classes/javax/lang/model/util/SimpleTypeVisitor9.java b/src/java.compiler/share/classes/javax/lang/model/util/SimpleTypeVisitor9.java
index 566888c871a..41521e20509 100644
--- a/src/java.compiler/share/classes/javax/lang/model/util/SimpleTypeVisitor9.java
+++ b/src/java.compiler/share/classes/javax/lang/model/util/SimpleTypeVisitor9.java
@@ -32,7 +32,8 @@ import static javax.lang.model.SourceVersion.*;
 
 /**
  * A simple visitor of types with default behavior appropriate for the
- * {@link SourceVersion#RELEASE_9 RELEASE_9} source version.
+ * {@link SourceVersion#RELEASE_9 RELEASE_9} and
+ * {@link SourceVersion#RELEASE_10 RELEASE_10} source versions.
  *
  * Visit methods corresponding to {@code RELEASE_9} and earlier
  * language constructs call {@link #defaultAction defaultAction},
@@ -71,7 +72,7 @@ import static javax.lang.model.SourceVersion.*;
  * @see SimpleTypeVisitor7
  * @since 9
  */
-@SupportedSourceVersion(RELEASE_9)
+@SupportedSourceVersion(RELEASE_10)
 public class SimpleTypeVisitor9<R, P> extends SimpleTypeVisitor8<R, P> {
     /**
      * Constructor for concrete subclasses; uses {@code null} for the
diff --git a/src/java.compiler/share/classes/javax/lang/model/util/TypeKindVisitor9.java b/src/java.compiler/share/classes/javax/lang/model/util/TypeKindVisitor9.java
index 5e5c52e5a80..321a7fa311a 100644
--- a/src/java.compiler/share/classes/javax/lang/model/util/TypeKindVisitor9.java
+++ b/src/java.compiler/share/classes/javax/lang/model/util/TypeKindVisitor9.java
@@ -33,7 +33,8 @@ import static javax.lang.model.SourceVersion.*;
 /**
  * A visitor of types based on their {@linkplain TypeKind kind} with
  * default behavior appropriate for the {@link SourceVersion#RELEASE_9
- * RELEASE_9} source version.  For {@linkplain
+ * RELEASE_9} and {@link SourceVersion#RELEASE_10 RELEASE_10} source
+ * versions. For {@linkplain
  * TypeMirror types} <code><i>Xyz</i></code> that may have more than one
  * kind, the <code>visit<i>Xyz</i></code> methods in this class delegate
  * to the <code>visit<i>Xyz</i>As<i>Kind</i></code> method corresponding to the
@@ -74,7 +75,7 @@ import static javax.lang.model.SourceVersion.*;
  * @see TypeKindVisitor8
  * @since 9
  */
-@SupportedSourceVersion(RELEASE_9)
+@SupportedSourceVersion(RELEASE_10)
 public class TypeKindVisitor9<R, P> extends TypeKindVisitor8<R, P> {
     /**
      * Constructor for concrete subclasses to call; uses {@code null}
diff --git a/src/java.logging/share/classes/java/util/logging/Level.java b/src/java.logging/share/classes/java/util/logging/Level.java
index 2af2c6d83d6..dbc27124d33 100644
--- a/src/java.logging/share/classes/java/util/logging/Level.java
+++ b/src/java.logging/share/classes/java/util/logging/Level.java
@@ -389,14 +389,15 @@ public class Level implements java.io.Serializable {
         try {
             int x = Integer.parseInt(name);
             level = KnownLevel.findByValue(x, KnownLevel::mirrored);
-            if (!level.isPresent()) {
-                // add new Level
-                Level levelObject = new Level(name, x);
-                // There's no need to use a reachability fence here because
-                // KnownLevel keeps a strong reference on the level when
-                // level.getClass() == Level.class.
-                return KnownLevel.findByValue(x, KnownLevel::mirrored).get();
+            if (level.isPresent()) {
+                return level.get();
             }
+            // add new Level
+            Level levelObject = new Level(name, x);
+            // There's no need to use a reachability fence here because
+            // KnownLevel keeps a strong reference on the level when
+            // level.getClass() == Level.class.
+            return KnownLevel.findByValue(x, KnownLevel::mirrored).get();
         } catch (NumberFormatException ex) {
             // Not an integer.
             // Drop through.
diff --git a/src/java.logging/share/classes/java/util/logging/LogManager.java b/src/java.logging/share/classes/java/util/logging/LogManager.java
index 31c5e74c624..e864f5ebabf 100644
--- a/src/java.logging/share/classes/java/util/logging/LogManager.java
+++ b/src/java.logging/share/classes/java/util/logging/LogManager.java
@@ -388,15 +388,23 @@ public class LogManager {
                         // create root logger before reading primordial
                         // configuration - to ensure that it will be added
                         // before the global logger, and not after.
-                        owner.rootLogger = owner.new RootLogger();
+                        final Logger root = owner.rootLogger = owner.new RootLogger();
 
                         // Read configuration.
                         owner.readPrimordialConfiguration();
 
                         // Create and retain Logger for the root of the namespace.
-                        owner.addLogger(owner.rootLogger);
-                        if (!owner.rootLogger.isLevelInitialized()) {
-                            owner.rootLogger.setLevel(defaultLevel);
+                        owner.addLogger(root);
+
+                        // For backward compatibility: add any handlers configured using
+                        // ".handlers"
+                        owner.createLoggerHandlers("", ".handlers")
+                                .stream()
+                                .forEach(root::addHandler);
+
+                        // Initialize level if not yet initialized
+                        if (!root.isLevelInitialized()) {
+                            root.setLevel(defaultLevel);
                         }
 
                         // Adding the global Logger.
@@ -1708,7 +1716,7 @@ public class LogManager {
          * @param k a property key in the configuration
          * @param previous the old configuration
          * @param next the new configuration (modified by this function)
-         * @param remappingFunction the mapping function.
+         * @param mappingFunction the mapping function.
          */
         static void merge(String k, Properties previous, Properties next,
                           BiFunction<String, String, String> mappingFunction) {
diff --git a/src/java.management/share/classes/com/sun/jmx/remote/security/FileLoginModule.java b/src/java.management/share/classes/com/sun/jmx/remote/security/FileLoginModule.java
index 9c5ebb6ec85..79b6dcf3b43 100644
--- a/src/java.management/share/classes/com/sun/jmx/remote/security/FileLoginModule.java
+++ b/src/java.management/share/classes/com/sun/jmx/remote/security/FileLoginModule.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2004, 2008, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2004, 2017, 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
@@ -22,22 +22,17 @@
  * or visit www.oracle.com if you need additional information or have any
  * questions.
  */
-
 package com.sun.jmx.remote.security;
 
 import com.sun.jmx.mbeanserver.GetPropertyAction;
 import com.sun.jmx.mbeanserver.Util;
-import java.io.BufferedInputStream;
 import java.io.File;
-import java.io.FileInputStream;
 import java.io.FilePermission;
 import java.io.IOException;
 import java.security.AccessControlException;
 import java.security.AccessController;
 import java.util.Arrays;
-import java.util.Hashtable;
 import java.util.Map;
-import java.util.Properties;
 
 import javax.security.auth.*;
 import javax.security.auth.callback.*;
@@ -59,10 +54,7 @@ import com.sun.jmx.remote.util.EnvHelp;
  * the access control file for JMX remote management or in a Java security
  * policy.
  *
- * <p> The password file comprises a list of key-value pairs as specified in
- * {@link Properties}. The key represents a user's name and the value is its
- * associated cleartext password. By default, the following password file is
- * used:
+ * By default, the following password file is used:
  * <pre>
  *     ${java.home}/conf/management/jmxremote.password
  * </pre>
@@ -105,6 +97,11 @@ import com.sun.jmx.remote.util.EnvHelp;
  * <dd> if <code>true</code>, this module clears the username and password
  *      stored in the module's shared state after both phases of authentication
  *      (login and commit) have completed.</dd>
+ *
+ * <dt> <code>hashPasswords</code> </dt>
+ * <dd> if <code>true</code>, this module replaces each clear text password
+ * with its hash, if present. </dd>
+ *
  * </dl>
  */
 public class FileLoginModule implements LoginModule {
@@ -135,6 +132,7 @@ public class FileLoginModule implements LoginModule {
     private boolean tryFirstPass = false;
     private boolean storePass = false;
     private boolean clearPass = false;
+    private boolean hashPasswords = false;
 
     // Authentication status
     private boolean succeeded = false;
@@ -154,7 +152,7 @@ public class FileLoginModule implements LoginModule {
     private String passwordFileDisplayName;
     private boolean userSuppliedPasswordFile;
     private boolean hasJavaHomePermission;
-    private Properties userCredentials;
+    private HashedPasswordManager hashPwdMgr;
 
     /**
      * Initialize this <code>LoginModule</code>.
@@ -186,6 +184,8 @@ public class FileLoginModule implements LoginModule {
                 "true".equalsIgnoreCase((String)options.get("storePass"));
         clearPass =
                 "true".equalsIgnoreCase((String)options.get("clearPass"));
+        hashPasswords
+                = "true".equalsIgnoreCase((String) options.get("hashPasswords"));
 
         passwordFile = (String)options.get("passwordFile");
         passwordFileDisplayName = passwordFile;
@@ -221,17 +221,28 @@ public class FileLoginModule implements LoginModule {
     public boolean login() throws LoginException {
 
         try {
-            loadPasswordFile();
+            synchronized (this) {
+                if (hashPwdMgr == null) {
+                    hashPwdMgr = new HashedPasswordManager(passwordFile, hashPasswords);
+                }
+            }
+            hashPwdMgr.loadPasswords();
         } catch (IOException ioe) {
             LoginException le = new LoginException(
                     "Error: unable to load the password file: " +
                     passwordFileDisplayName);
             throw EnvHelp.initCause(le, ioe);
-        }
-
-        if (userCredentials == null) {
-            throw new LoginException
-                ("Error: unable to locate the users' credentials.");
+        } catch (SecurityException e) {
+            if (userSuppliedPasswordFile || hasJavaHomePermission) {
+                throw e;
+            } else {
+                final FilePermission fp
+                        = new FilePermission(passwordFileDisplayName, "read");
+                AccessControlException ace = new AccessControlException(
+                        "access denied " + fp.toString());
+                ace.initCause(e);
+                throw ace;
+            }
         }
 
         if (logger.debugOn()) {
@@ -437,12 +448,7 @@ public class FileLoginModule implements LoginModule {
         // get the username and password
         getUsernamePassword(usePasswdFromSharedState);
 
-        String localPassword;
-
-        // userCredentials is initialized in login()
-        if (((localPassword = userCredentials.getProperty(username)) == null) ||
-            (! localPassword.equals(new String(password)))) {
-
+        if (!hashPwdMgr.authenticate(username, password)) {
             // username not found or passwords do not match
             if (logger.debugOn()) {
                 logger.debug("login", "Invalid username or password");
@@ -468,38 +474,6 @@ public class FileLoginModule implements LoginModule {
         }
     }
 
-    /*
-     * Read the password file.
-     */
-    private void loadPasswordFile() throws IOException {
-        FileInputStream fis;
-        try {
-            fis = new FileInputStream(passwordFile);
-        } catch (SecurityException e) {
-            if (userSuppliedPasswordFile || hasJavaHomePermission) {
-                throw e;
-            } else {
-                final FilePermission fp =
-                        new FilePermission(passwordFileDisplayName, "read");
-                AccessControlException ace = new AccessControlException(
-                        "access denied " + fp.toString());
-                ace.setStackTrace(e.getStackTrace());
-                throw ace;
-            }
-        }
-        try {
-            final BufferedInputStream bis = new BufferedInputStream(fis);
-            try {
-                userCredentials = new Properties();
-                userCredentials.load(bis);
-            } finally {
-                bis.close();
-            }
-        } finally {
-            fis.close();
-        }
-    }
-
     /**
      * Get the username and password.
      * This method does not return any value.
diff --git a/src/java.management/share/classes/com/sun/jmx/remote/security/HashedPasswordManager.java b/src/java.management/share/classes/com/sun/jmx/remote/security/HashedPasswordManager.java
new file mode 100644
index 00000000000..1404e2d458e
--- /dev/null
+++ b/src/java.management/share/classes/com/sun/jmx/remote/security/HashedPasswordManager.java
@@ -0,0 +1,333 @@
+/*
+ * Copyright (c) 2017, 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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 com.sun.jmx.remote.security;
+
+import com.sun.jmx.remote.util.ClassLogger;
+
+import java.io.*;
+import java.nio.ByteBuffer;
+import java.nio.CharBuffer;
+import java.nio.channels.FileLock;
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
+import java.util.Arrays;
+import java.util.Base64;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+/**
+ * HashedPasswordManager loads passwords from the password file and optionally
+ * hashes them.
+ * <p>
+ * <p>
+ * This class accepts Unicode UTF-8 encoded file
+ * <p>
+ * <p>
+ * Each entry in the password file contains a username followed by a password.
+ * Password can be in clear text or as a hash. Hashed passwords must follow the
+ * below format. hashedPassword = base64_encoded_64_byte_salt W
+ * base64_encoded_hash W hash_algorithm where, W = spaces,
+ * base64_encoded_64_byte_salt = 64 byte random salt, base64_encoded_hash =
+ * hash_algorithm(password + salt), hash_algorithm = Algorithm string as
+ * specified in
+ * <a href="{@docRoot}/../specs/security/standard-names.html#messagedigest-algorithms">
+ * </a>
+ * hash_algorithm is an optional field. If not specified, SHA3-512 will be
+ * assumed.
+ * <p>
+ * <p>
+ * If passwords are in clear, they will be over-written by their hash if hashing
+ * is requested by setting com.sun.management.jmxremote.password.toHashes
+ * property to true in the management.properties file and if the password file
+ * is writable and if the system security policy allows writing into the
+ * password file, if a security manager is configured
+ * <p>
+ * <p>
+ * In order to change the password for a role, replace the hashed password entry
+ * with a new clear text password or a new hashed password. If the new password
+ * is in clear, it will be replaced with its hash when a new login attempt is
+ * made.
+ * <p>
+ * <p>
+ * A given role should have at most one entry in this file. If a role has no
+ * entry, it has no access. If multiple entries are found for the same role
+ * name, then the last one will be used.
+ * <p>
+ * <p>
+ * <p>
+ * A user generated hashed password file can also be used instead of a
+ * clear-text password file. If generated by the user, hashed passwords must
+ * follow the format specified above.
+ */
+final public class HashedPasswordManager {
+
+    private static final class UserCredentials {
+
+        private final String userName;
+        private final String hashAlgorithm;
+        private final String b64Salt;
+        private final String b64Hash;
+
+        public UserCredentials(String userName, String hashAlgorithm, String b64Salt, String b64Hash) {
+            this.userName = userName;
+            this.hashAlgorithm = hashAlgorithm;
+            this.b64Salt = b64Salt;
+            this.b64Hash = b64Hash;
+        }
+
+        @Override
+        public String toString() {
+            return userName + " " + b64Salt + " " + b64Hash + " " + hashAlgorithm;
+        }
+    }
+
+    private static final String DefaultHashAlgorithm = "SHA3-512";
+    private static final int DefaultSaltLength = 64;
+
+    private final SecureRandom random = new SecureRandom();
+    private final Map<String, UserCredentials> userCredentialsMap = new HashMap<>();
+    private final String passwordFile;
+    private final boolean shouldHashPasswords;
+    private boolean isLogged = false;
+
+    private static final ClassLogger logger
+            = new ClassLogger("javax.management.remote.misc",
+                    "HashedPasswordManager");
+
+    /**
+     * Creates a new password manager for the input password file
+     *
+     * @param filename UTF-8 encoded input file to read passwords from
+     * @param shouldHashPasswords Request for clear passwords to be hashed
+     */
+    public HashedPasswordManager(String filename, boolean shouldHashPasswords) {
+        this.passwordFile = filename;
+        this.shouldHashPasswords = shouldHashPasswords;
+    }
+
+    private String[] getHash(String algorithm, String password) {
+        try {
+            byte[] salt = new byte[DefaultSaltLength];
+            random.nextBytes(salt);
+
+            MessageDigest digest = MessageDigest.getInstance(algorithm);
+            digest.reset();
+            digest.update(salt);
+            byte[] hash = digest.digest(password.getBytes(StandardCharsets.UTF_8));
+            String saltStr = Base64.getEncoder().encodeToString(salt);
+            String hashStr = Base64.getEncoder().encodeToString(hash);
+
+            return new String[]{saltStr, hashStr};
+        } catch (NoSuchAlgorithmException ex) {
+            if (logger.debugOn()) {
+                logger.debug("getHash", "Invalid algorithm : " + algorithm);
+            }
+            // We should never reach here as default Hash Algorithm
+            // must be always present
+            return new String[]{"", ""};
+        }
+    }
+
+    private String[] readPasswordFile() throws IOException {
+        synchronized (HashedPasswordManager.class) {
+            byte[] data;
+            File f = new File(passwordFile);
+            try (FileInputStream fin = new FileInputStream(f);
+                    FileLock lock = fin.getChannel().lock(0L, Long.MAX_VALUE, true)) {
+                data = new byte[(int) f.length()];
+                int read = fin.read(data);
+                if (read != data.length) {
+                    throw new IOException("Failed to read data from the password file");
+                }
+                lock.release();
+            }
+            String str = new String(data, StandardCharsets.UTF_8);
+            return str.split("\\r?\\n");
+        }
+    }
+
+    private void writePasswordFile(String input) throws IOException {
+        synchronized (HashedPasswordManager.class) {
+            try (FileOutputStream fout = new FileOutputStream(passwordFile);
+                    OutputStreamWriter out = new OutputStreamWriter(fout, StandardCharsets.UTF_8);
+                    FileLock lock = fout.getChannel().lock()) {
+                out.write(input);
+                lock.release();
+            }
+        }
+    }
+
+    /**
+     * Authenticate the supplied credentials against the one present in the file
+     *
+     * @param userName Input username
+     * @param inputPassword Input password
+     * @return true if authentication succeeds, false otherwise
+     */
+    public synchronized boolean authenticate(String userName, char[] inputPassword) {
+        if (userCredentialsMap.containsKey(userName)) {
+            try {
+                UserCredentials us = userCredentialsMap.get(userName);
+                byte[] salt = Base64.getDecoder().decode(us.b64Salt);
+                byte[] targetHash = Base64.getDecoder().decode(us.b64Hash);
+                MessageDigest digest = MessageDigest.getInstance(us.hashAlgorithm);
+                digest.reset();
+                digest.update(salt);
+                ByteBuffer byteBuffer = Charset.forName("UTF-8").encode(CharBuffer.wrap(inputPassword));
+                byte[] passwordBytes = new byte[byteBuffer.limit()];
+                byteBuffer.get(passwordBytes);
+                byte[] hash = digest.digest(passwordBytes);
+                return Arrays.equals(hash, targetHash);
+            } catch (NoSuchAlgorithmException ex) {
+                if (logger.debugOn()) {
+                    logger.debug("authenticate", "Unrecognized hash algorithm : "
+                            + userCredentialsMap.get(userName).hashAlgorithm
+                            + " - for user : " + userName);
+                }
+                return false;
+            }
+        } else {
+            if (logger.debugOn()) {
+                logger.debug("authenticate", "Unknown user : " + userName);
+            }
+            return false;
+        }
+    }
+
+    /**
+     * Load passwords from the password file.
+     * <p>
+     * <p>
+     * This method should be called for every login attempt to load new/changed
+     * credentials, if any.
+     * <p>
+     * <p>
+     * If hashing is requested, clear passwords will be over-written with their
+     * SHA3-512 hash
+     *
+     * @throws IOException If unable to access the file
+     * @throws SecurityException If read/write file permissions are not granted
+     */
+    public synchronized void loadPasswords()
+            throws IOException, SecurityException {
+
+        SecurityManager security = System.getSecurityManager();
+        if (security != null) {
+            security.checkRead(passwordFile);
+        }
+
+        AtomicBoolean hasClearPasswords = new AtomicBoolean(false);
+        StringBuilder sbuf = new StringBuilder();
+        final String header = "# The passwords in this file are hashed.\n"
+                + "# In order to change the password for a role, replace the hashed "
+                + "password entry\n"
+                + "# with a clear text password or a new hashed password. "
+                + "If the new password is in clear,\n# it will be replaced with its "
+                + "hash when a new login attempt is made.\n\n";
+
+        userCredentialsMap.clear();
+        Arrays.stream(readPasswordFile()).forEach(line -> {
+            if (line.trim().startsWith("#")) {   // Ignore comments
+                sbuf.append(line).append("\n");
+                return;
+            }
+            String[] tokens = line.split("\\s+");
+            switch (tokens.length) {
+                case 2: {
+                    // Password is in clear
+                    String[] b64str = getHash(DefaultHashAlgorithm, tokens[1]);
+                    UserCredentials us = new UserCredentials(tokens[0], DefaultHashAlgorithm, b64str[0], b64str[1]);
+                    sbuf.append(us.userName).append(" ").append(us.b64Salt).
+                            append(" ").append(us.b64Hash).append(" ").
+                            append(us.hashAlgorithm).append("\n");
+                    if (userCredentialsMap.get(tokens[0]) != null) {
+                        if (logger.debugOn()) {
+                            logger.debug("loadPasswords", "Ignoring entry for role : " + tokens[0]);
+                        }
+                    }
+                    userCredentialsMap.put(tokens[0], us);
+                    hasClearPasswords.set(true);
+                    if (logger.debugOn()) {
+                        logger.debug("loadPasswords",
+                                "Found atleast one clear password");
+                    }
+                    break;
+                }
+                case 3:
+                case 4: {
+                    // Passwords are hashed
+                    UserCredentials us = new UserCredentials(tokens[0], (tokens.length == 4 ? tokens[3] : DefaultHashAlgorithm),
+                            tokens[1], tokens[2]);
+                    sbuf.append(line).append("\n");
+                    if (userCredentialsMap.get(tokens[0]) != null) {
+                        if (logger.debugOn()) {
+                            logger.debug("loadPasswords", "Ignoring entry for role : " + tokens[0]);
+                        }
+                    }
+                    userCredentialsMap.put(tokens[0], us);
+                    break;
+                }
+                default:
+                    sbuf.append(line).append("\n");
+                    break;
+            }
+        });
+
+        if (!shouldHashPasswords && hasClearPasswords.get()) {
+            if (logger.debugOn()) {
+                logger.debug("loadPasswords",
+                        "Passwords in " + passwordFile + " are in clear but are requested "
+                        + "not to be hashed !!!");
+            }
+        }
+
+        // Check if header needs to be inserted
+        if (sbuf.indexOf("# The passwords in this file are hashed") != 0) {
+            sbuf.insert(0, header);
+        }
+
+        // Even if we are unable to write hashed passwords to password file,
+        // passwords will be hashed in memory so that jvm heap dump should not
+        // give away clear passwords
+        if (shouldHashPasswords && hasClearPasswords.get()) {
+            if (new File(passwordFile).canWrite()) {
+                writePasswordFile(sbuf.toString());
+                if (logger.debugOn()) {
+                    logger.debug("loadPasswords",
+                            "Wrote hashed passwords to file : " + passwordFile);
+                }
+            } else if (logger.debugOn() && !isLogged) {
+                isLogged = true;
+                logger.debug("loadPasswords",
+                        "Passwords in " + passwordFile + " are in clear and password file is read-only. "
+                        + "Passwords cannot be hashed !!!!");
+            }
+        }
+    }
+}
diff --git a/src/java.management/share/classes/com/sun/jmx/remote/security/JMXPluggableAuthenticator.java b/src/java.management/share/classes/com/sun/jmx/remote/security/JMXPluggableAuthenticator.java
index f7ab5d70fb6..dadef33e5dc 100644
--- a/src/java.management/share/classes/com/sun/jmx/remote/security/JMXPluggableAuthenticator.java
+++ b/src/java.management/share/classes/com/sun/jmx/remote/security/JMXPluggableAuthenticator.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2004, 2008, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2004, 2017, 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
@@ -34,8 +34,6 @@ import java.security.PrivilegedExceptionAction;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.Map;
-import java.util.Properties;
-import javax.management.remote.JMXPrincipal;
 import javax.management.remote.JMXAuthenticator;
 import javax.security.auth.AuthPermission;
 import javax.security.auth.Subject;
@@ -91,10 +89,12 @@ public final class JMXPluggableAuthenticator implements JMXAuthenticator {
 
         String loginConfigName = null;
         String passwordFile = null;
+        String hashPasswords = null;
 
         if (env != null) {
             loginConfigName = (String) env.get(LOGIN_CONFIG_PROP);
             passwordFile = (String) env.get(PASSWORD_FILE_PROP);
+            hashPasswords = (String) env.get(HASH_PASSWORDS);
         }
 
         try {
@@ -114,6 +114,7 @@ public final class JMXPluggableAuthenticator implements JMXAuthenticator {
                 }
 
                 final String pf = passwordFile;
+                final String hashPass = hashPasswords;
                 try {
                     loginContext = AccessController.doPrivileged(
                         new PrivilegedExceptionAction<LoginContext>() {
@@ -122,7 +123,7 @@ public final class JMXPluggableAuthenticator implements JMXAuthenticator {
                                                 LOGIN_CONFIG_NAME,
                                                 null,
                                                 new JMXCallbackHandler(),
-                                                new FileLoginConfig(pf));
+                                                new FileLoginConfig(pf, hashPass));
                             }
                         });
                 } catch (PrivilegedActionException pae) {
@@ -250,6 +251,8 @@ public final class JMXPluggableAuthenticator implements JMXAuthenticator {
     private static final String LOGIN_CONFIG_NAME = "JMXPluggableAuthenticator";
     private static final String PASSWORD_FILE_PROP =
         "jmx.remote.x.password.file";
+    private static final String HASH_PASSWORDS =
+        "jmx.remote.x.password.toHashes";
     private static final ClassLogger logger =
         new ClassLogger("javax.management.remote.misc", LOGIN_CONFIG_NAME);
 
@@ -303,19 +306,22 @@ private static class FileLoginConfig extends Configuration {
 
     // The option that identifies the password file to use
     private static final String PASSWORD_FILE_OPTION = "passwordFile";
+    private static final String HASH_PASSWORDS = "hashPasswords";
 
     /**
      * Creates an instance of <code>FileLoginConfig</code>
      *
      * @param passwordFile A filepath that identifies the password file to use.
      *                     If null then the default password file is used.
+     * @param hashPasswords Flag to indicate if the password file needs to be hashed
      */
-    public FileLoginConfig(String passwordFile) {
+    public FileLoginConfig(String passwordFile, String hashPasswords) {
 
         Map<String, String> options;
         if (passwordFile != null) {
             options = new HashMap<String, String>(1);
             options.put(PASSWORD_FILE_OPTION, passwordFile);
+            options.put(HASH_PASSWORDS, hashPasswords);
         } else {
             options = Collections.emptyMap();
         }
diff --git a/src/java.sql/share/classes/java/sql/Date.java b/src/java.sql/share/classes/java/sql/Date.java
index a6d8d8f4016..f8da3e7f94b 100644
--- a/src/java.sql/share/classes/java/sql/Date.java
+++ b/src/java.sql/share/classes/java/sql/Date.java
@@ -27,8 +27,6 @@ package java.sql;
 
 import java.time.Instant;
 import java.time.LocalDate;
-import jdk.internal.misc.SharedSecrets;
-import jdk.internal.misc.JavaLangAccess;
 
 /**
  * <P>A thin wrapper around a millisecond value that allows
@@ -46,8 +44,6 @@ import jdk.internal.misc.JavaLangAccess;
  */
 public class Date extends java.util.Date {
 
-    private static final JavaLangAccess jla = SharedSecrets.getJavaLangAccess();
-
     /**
      * Constructs a <code>Date</code> object initialized with the given
      * year, month, and day.
@@ -168,7 +164,7 @@ public class Date extends java.util.Date {
         buf[7] = '-';
         Date.formatDecimalInt(day, buf, 8, 2);
 
-        return jla.newStringUnsafe(buf);
+        return new String(buf);
     }
 
     /**
diff --git a/src/java.sql/share/classes/java/sql/Time.java b/src/java.sql/share/classes/java/sql/Time.java
index 3d1f237933c..281d031c7c1 100644
--- a/src/java.sql/share/classes/java/sql/Time.java
+++ b/src/java.sql/share/classes/java/sql/Time.java
@@ -27,8 +27,6 @@ package java.sql;
 
 import java.time.Instant;
 import java.time.LocalTime;
-import jdk.internal.misc.SharedSecrets;
-import jdk.internal.misc.JavaLangAccess;
 
 /**
  * <P>A thin wrapper around the <code>java.util.Date</code> class that allows the JDBC
@@ -43,8 +41,6 @@ import jdk.internal.misc.JavaLangAccess;
  */
 public class Time extends java.util.Date {
 
-    private static final JavaLangAccess jla = SharedSecrets.getJavaLangAccess();
-
     /**
      * Constructs a <code>Time</code> object initialized with the
      * given values for the hour, minute, and second.
@@ -134,7 +130,7 @@ public class Time extends java.util.Date {
         buf[5] = ':';
         Date.formatDecimalInt(second, buf, 6, 2);
 
-        return jla.newStringUnsafe(buf);
+        return new String(buf);
     }
 
     // Override all the date operations inherited from java.util.Date;
diff --git a/src/java.sql/share/classes/java/sql/Timestamp.java b/src/java.sql/share/classes/java/sql/Timestamp.java
index 968d6e76b9f..4b4c334dd45 100644
--- a/src/java.sql/share/classes/java/sql/Timestamp.java
+++ b/src/java.sql/share/classes/java/sql/Timestamp.java
@@ -27,8 +27,6 @@ package java.sql;
 
 import java.time.Instant;
 import java.time.LocalDateTime;
-import jdk.internal.misc.SharedSecrets;
-import jdk.internal.misc.JavaLangAccess;
 
 /**
  * <P>A thin wrapper around {@code java.util.Date} that allows
@@ -74,8 +72,6 @@ import jdk.internal.misc.JavaLangAccess;
  */
 public class Timestamp extends java.util.Date {
 
-    private static final JavaLangAccess jla = SharedSecrets.getJavaLangAccess();
-
     /**
      * Constructs a {@code Timestamp} object initialized
      * with the given values.
@@ -313,7 +309,7 @@ public class Timestamp extends java.util.Date {
         buf[yearSize + 15] = '.';
         Date.formatDecimalInt(tmpNanos, buf, yearSize + 16, 9 - trailingZeros);
 
-        return jla.newStringUnsafe(buf);
+        return new String(buf);
     }
 
     /**
diff --git a/src/jdk.aot/share/classes/jdk.tools.jaotc.binformat/src/jdk/tools/jaotc/binformat/BinaryContainer.java b/src/jdk.aot/share/classes/jdk.tools.jaotc.binformat/src/jdk/tools/jaotc/binformat/BinaryContainer.java
index 0971479577e..b0e8799559f 100644
--- a/src/jdk.aot/share/classes/jdk.tools.jaotc.binformat/src/jdk/tools/jaotc/binformat/BinaryContainer.java
+++ b/src/jdk.aot/share/classes/jdk.tools.jaotc.binformat/src/jdk/tools/jaotc/binformat/BinaryContainer.java
@@ -65,6 +65,8 @@ public final class BinaryContainer implements SymbolTable {
 
     private final int codeEntryAlignment;
 
+    private final boolean threadLocalHandshakes;
+
     /**
      * Container holding code bits and any other related information.
      */
@@ -279,6 +281,8 @@ public final class BinaryContainer implements SymbolTable {
 
         this.codeEntryAlignment = graalHotSpotVMConfig.codeEntryAlignment;
 
+        this.threadLocalHandshakes = graalHotSpotVMConfig.threadLocalHandshakes;
+
         // Section unique name is limited to 8 characters due to limitation on Windows.
         // Name could be longer but only first 8 characters are stored on Windows.
 
@@ -323,7 +327,8 @@ public final class BinaryContainer implements SymbolTable {
                                    TieredAOT.getValue(graalOptions),
                                    graalHotSpotVMConfig.enableContended,
                                    graalHotSpotVMConfig.restrictContended,
-                                   graphBuilderConfig.omitAssertions()
+                                   graphBuilderConfig.omitAssertions(),
+                                   graalHotSpotVMConfig.threadLocalHandshakes
         };
 
         int[] intFlags         = { graalHotSpotVMConfig.getOopEncoding().getShift(),
@@ -434,6 +439,11 @@ public final class BinaryContainer implements SymbolTable {
         return codeEntryAlignment;
     }
 
+    public boolean getThreadLocalHandshakes() {
+        return threadLocalHandshakes;
+    }
+
+
     /**
      * Gets the global AOT symbol associated with the function name.
      *
diff --git a/src/jdk.aot/share/classes/jdk.tools.jaotc/src/jdk/tools/jaotc/MarkProcessor.java b/src/jdk.aot/share/classes/jdk.tools.jaotc/src/jdk/tools/jaotc/MarkProcessor.java
index 397d86e7378..16d5dadffc5 100644
--- a/src/jdk.aot/share/classes/jdk.tools.jaotc/src/jdk/tools/jaotc/MarkProcessor.java
+++ b/src/jdk.aot/share/classes/jdk.tools.jaotc/src/jdk/tools/jaotc/MarkProcessor.java
@@ -45,6 +45,7 @@ final class MarkProcessor {
      * @param methodInfo compiled method info
      * @param mark mark being processed
      */
+    @SuppressWarnings("fallthrough")
     void process(CompiledMethodInfo methodInfo, Mark mark) {
         MarkId markId = MarkId.getEnum((int) mark.id);
         switch (markId) {
@@ -53,6 +54,11 @@ final class MarkProcessor {
                 break;
             case POLL_FAR:
             case POLL_RETURN_FAR:
+                if (binaryContainer.getThreadLocalHandshakes()) {
+                    // skip relocation
+                    break;
+                }
+                // fallthrough
             case CARD_TABLE_ADDRESS:
             case HEAP_TOP_ADDRESS:
             case HEAP_END_ADDRESS:
diff --git a/src/jdk.attach/share/classes/sun/tools/attach/HotSpotVirtualMachine.java b/src/jdk.attach/share/classes/sun/tools/attach/HotSpotVirtualMachine.java
index 9ab9357ad05..a475bf42a38 100644
--- a/src/jdk.attach/share/classes/sun/tools/attach/HotSpotVirtualMachine.java
+++ b/src/jdk.attach/share/classes/sun/tools/attach/HotSpotVirtualMachine.java
@@ -86,18 +86,23 @@ public abstract class HotSpotVirtualMachine extends VirtualMachine {
     private void loadAgentLibrary(String agentLibrary, boolean isAbsolute, String options)
         throws AgentLoadException, AgentInitializationException, IOException
     {
+        String msgPrefix = "return code: ";
         InputStream in = execute("load",
                                  agentLibrary,
                                  isAbsolute ? "true" : "false",
                                  options);
-        try {
-            int result = readInt(in);
-            if (result != 0) {
-                throw new AgentInitializationException("Agent_OnAttach failed", result);
+        try (BufferedReader reader = new BufferedReader(new InputStreamReader(in))) {
+            String result = reader.readLine();
+            if (result == null) {
+                throw new AgentLoadException("Target VM did not respond");
+            } else if (result.startsWith(msgPrefix)) {
+                int retCode = Integer.parseInt(result.substring(msgPrefix.length()));
+                if (retCode != 0) {
+                    throw new AgentInitializationException("Agent_OnAttach failed", retCode);
+                }
+            } else {
+                throw new AgentLoadException(result);
             }
-        } finally {
-            in.close();
-
         }
     }
 
diff --git a/src/jdk.compiler/share/classes/com/sun/source/doctree/DocTreeVisitor.java b/src/jdk.compiler/share/classes/com/sun/source/doctree/DocTreeVisitor.java
index 29ccba7474d..b11ea0de1ee 100644
--- a/src/jdk.compiler/share/classes/com/sun/source/doctree/DocTreeVisitor.java
+++ b/src/jdk.compiler/share/classes/com/sun/source/doctree/DocTreeVisitor.java
@@ -145,11 +145,19 @@ public interface DocTreeVisitor<R,P> {
 
     /**
      * Visits a HiddenTree node.
+     *
+     * @implSpec Visits a {@code HiddenTree} node
+     * by calling {@code visitOther(node, p)}.
+     *
      * @param node the node being visited
      * @param p a parameter value
      * @return a result value
+     *
+     * @since 9
      */
-    R visitHidden(HiddenTree node, P p);
+    default R visitHidden(HiddenTree node, P p)  {
+        return visitOther(node, p);
+    }
 
     /**
      * Visits an IdentifierTree node.
@@ -161,11 +169,19 @@ public interface DocTreeVisitor<R,P> {
 
     /**
      * Visits an IndexTree node.
+     *
+     * @implSpec Visits an {@code IndexTree} node
+     * by calling {@code visitOther(node, p)}.
+     *
      * @param node the node being visited
      * @param p a parameter value
      * @return a result value
+     *
+     * @since 9
      */
-    R visitIndex(IndexTree node, P p);
+    default R visitIndex(IndexTree node, P p) {
+        return visitOther(node, p);
+    }
 
     /**
      * Visits an InheritDocTree node.
@@ -201,11 +217,19 @@ public interface DocTreeVisitor<R,P> {
 
     /**
      * Visits a ProvidesTree node.
+     *
+     * @implSpec Visits a {@code ProvidesTree} node
+     * by calling {@code visitOther(node, p)}.
+     *
      * @param node the node being visited
      * @param p a parameter value
      * @return a result value
+     *
+     * @since 9
      */
-    R visitProvides(ProvidesTree node, P p);
+    default R visitProvides(ProvidesTree node, P p) {
+        return visitOther(node, p);
+    }
 
     /**
      * Visits a ReferenceTree node.
@@ -320,11 +344,19 @@ public interface DocTreeVisitor<R,P> {
 
     /**
      * Visits a UsesTree node.
+     *
+     * @implSpec Visits a {@code UsesTree} node
+     * by calling {@code visitOther(node, p)}.
+     *
      * @param node the node being visited
      * @param p a parameter value
      * @return a result value
+     *
+     * @since 9
      */
-    R visitUses(UsesTree node, P p);
+    default R visitUses(UsesTree node, P p) {
+        return visitOther(node, p);
+    }
 
     /**
      * Visits a ValueTree node.
diff --git a/src/jdk.compiler/share/classes/com/sun/source/util/SimpleDocTreeVisitor.java b/src/jdk.compiler/share/classes/com/sun/source/util/SimpleDocTreeVisitor.java
index d180accc329..07abab5de04 100644
--- a/src/jdk.compiler/share/classes/com/sun/source/util/SimpleDocTreeVisitor.java
+++ b/src/jdk.compiler/share/classes/com/sun/source/util/SimpleDocTreeVisitor.java
@@ -220,6 +220,8 @@ public class SimpleDocTreeVisitor<R,P> implements DocTreeVisitor<R, P> {
      * @param node {@inheritDoc}
      * @param p {@inheritDoc}
      * @return the result of {@code defaultAction}
+     *
+     * @since 9
      */
     @Override
     public R visitHidden(HiddenTree node, P p) {
@@ -244,6 +246,8 @@ public class SimpleDocTreeVisitor<R,P> implements DocTreeVisitor<R, P> {
      * @param node {@inheritDoc}
      * @param p {@inheritDoc}
      * @return  the result of {@code defaultAction}
+     *
+     * @since 9
      */
     @Override
     public R visitIndex(IndexTree node, P p) {
@@ -304,6 +308,8 @@ public class SimpleDocTreeVisitor<R,P> implements DocTreeVisitor<R, P> {
      * @param node {@inheritDoc}
      * @param p {@inheritDoc}
      * @return  the result of {@code defaultAction}
+     *
+     * @since 9
      */
     @Override
     public R visitProvides(ProvidesTree node, P p) {
@@ -473,6 +479,8 @@ public class SimpleDocTreeVisitor<R,P> implements DocTreeVisitor<R, P> {
      * @param node {@inheritDoc}
      * @param p {@inheritDoc}
      * @return  the result of {@code defaultAction}
+     *
+     * @since 9
      */
     @Override
     public R visitUses(UsesTree node, P p) {
diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/api/JavacTool.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/api/JavacTool.java
index e0a2607c393..1c474b05c30 100644
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/api/JavacTool.java
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/api/JavacTool.java
@@ -219,7 +219,7 @@ public final class JavacTool implements JavaCompiler {
         Set<Option> recognizedOptions = Option.getJavacToolOptions();
         for (Option o : recognizedOptions) {
             if (o.matches(option)) {
-                return o.hasArg() ? 1 : 0;
+                return o.hasSeparateArg() ? 1 : 0;
             }
         }
         return -1;
diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/LambdaToMethod.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/LambdaToMethod.java
index f041a5638ac..b4365e3365d 100644
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/LambdaToMethod.java
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/LambdaToMethod.java
@@ -510,6 +510,28 @@ public class LambdaToMethod extends TreeTranslator {
         }
     }
 
+    /**
+     * Translate instance creation expressions with implicit enclosing instances
+     * @param tree
+     */
+    @Override
+    public void visitNewClass(JCNewClass tree) {
+        if (context == null || !analyzer.lambdaNewClassFilter(context, tree)) {
+            super.visitNewClass(tree);
+        } else {
+            int prevPos = make.pos;
+            try {
+                make.at(tree);
+
+                LambdaTranslationContext lambdaContext = (LambdaTranslationContext) context;
+                tree = lambdaContext.translate(tree);
+                super.visitNewClass(tree);
+            } finally {
+                make.at(prevPos);
+            }
+        }
+    }
+
     @Override
     public void visitVarDef(JCVariableDecl tree) {
         LambdaTranslationContext lambdaContext = (LambdaTranslationContext)context;
@@ -984,6 +1006,7 @@ public class LambdaToMethod extends TreeTranslator {
                 //create the instance creation expression
                 //note that method reference syntax does not allow an explicit
                 //enclosing class (so the enclosing class is null)
+                // but this may need to be patched up later with the proxy for the outer this
                 JCNewClass newClass = make.NewClass(null,
                         List.nil(),
                         make.Type(tree.getQualifierExpression().type),
@@ -2129,6 +2152,21 @@ public class LambdaToMethod extends TreeTranslator {
                 return null;
             }
 
+            /* Translate away naked new instance creation expressions with implicit enclosing instances,
+               anchoring them to synthetic parameters that stand proxy for the qualified outer this handle.
+            */
+            public JCNewClass translate(JCNewClass newClass) {
+                Assert.check(newClass.clazz.type.tsym.hasOuterInstance() && newClass.encl == null);
+                Map<Symbol, Symbol> m = translatedSymbols.get(LambdaSymbolKind.CAPTURED_OUTER_THIS);
+                final Type enclosingType = newClass.clazz.type.getEnclosingType();
+                if (m.containsKey(enclosingType.tsym)) {
+                      Symbol tSym = m.get(enclosingType.tsym);
+                      JCExpression encl = make.Ident(tSym).setType(enclosingType);
+                      newClass.encl = encl;
+                }
+                return newClass;
+            }
+
             /**
              * The translatedSym is not complete/accurate until the analysis is
              * finished.  Once the analysis is finished, the translatedSym is
diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Lower.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Lower.java
index f95f6640506..fe9f0945797 100644
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Lower.java
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Lower.java
@@ -3236,12 +3236,10 @@ public class Lower extends TreeTranslator {
                                                     make.Ident(index)).setType(elemtype);
             JCVariableDecl loopvardef = (JCVariableDecl)make.VarDef(tree.var.mods,
                                                   tree.var.name,
-                                                  tree.var.vartype, null).setType(tree.var.type);
+                                                  tree.var.vartype, loopvarinit).setType(tree.var.type);
             loopvardef.sym = tree.var.sym;
 
-            JCStatement loopVarAssign = make.Assignment(tree.var.sym, loopvarinit);
-            JCBlock body = make.
-                Block(0, List.of(loopVarAssign, tree.body));
+            JCBlock body = make.Block(0, List.of(loopvardef, tree.body));
 
             arraycachedef = translate(arraycachedef);
             result = translate(make.
@@ -3251,12 +3249,7 @@ public class Lower extends TreeTranslator {
                                        body));
             patchTargets(body, tree, result);
             JCStatement nullAssignToArr = make.Assignment(arraycache, make.Literal(BOT, null).setType(syms.botType));
-            JCStatement nullAssignToLoopVar = tree.var.type.isPrimitive() ?
-                    null :
-                    make.Assignment(tree.var.sym, make.Literal(BOT, null).setType(syms.botType));
-            result = nullAssignToLoopVar == null ?
-                    make.Block(0, List.of(arraycachedef, loopvardef, (JCStatement)result, nullAssignToArr)):
-                    make.Block(0, List.of(arraycachedef, loopvardef, (JCStatement)result, nullAssignToArr, nullAssignToLoopVar));
+            result = make.Block(0, List.of(arraycachedef, (JCStatement)result, nullAssignToArr));
         }
         /** Patch up break and continue targets. */
         private void patchTargets(JCTree body, final JCTree src, final JCTree dest) {
@@ -3310,7 +3303,7 @@ public class Lower extends TreeTranslator {
                                             types.erasure(types.asSuper(iterator.type.getReturnType(), syms.iteratorType.tsym)),
                                             currentMethodSym);
 
-             JCStatement init = make.
+            JCStatement init = make.
                 VarDef(itvar, make.App(make.Select(tree.expr, iterator)
                      .setType(types.erasure(iterator.type))));
 
@@ -3331,10 +3324,9 @@ public class Lower extends TreeTranslator {
             JCVariableDecl indexDef = (JCVariableDecl)make.VarDef(tree.var.mods,
                                                   tree.var.name,
                                                   tree.var.vartype,
-                                                  null).setType(tree.var.type);
+                                                  vardefinit).setType(tree.var.type);
             indexDef.sym = tree.var.sym;
-            JCStatement loopVarAssign = make.Assignment(tree.var.sym, vardefinit);
-            JCBlock body = make.Block(0, List.of(loopVarAssign, tree.body));
+            JCBlock body = make.Block(0, List.of(indexDef, tree.body));
             body.endpos = TreeInfo.endPos(tree.body);
             init = translate(init);
             result = translate(make.
@@ -3344,12 +3336,7 @@ public class Lower extends TreeTranslator {
                         body));
             patchTargets(body, tree, result);
             JCStatement nullAssignToIterator = make.Assignment(itvar, make.Literal(BOT, null).setType(syms.botType));
-            JCStatement nullAssignToLoopVar = tree.var.type.isPrimitive() ?
-                    null :
-                    make.Assignment(tree.var.sym, make.Literal(BOT, null).setType(syms.botType));
-            result = nullAssignToLoopVar == null ?
-                    make.Block(0, List.of(init, indexDef, (JCStatement)result, nullAssignToIterator)):
-                    make.Block(0, List.of(init, indexDef, (JCStatement)result, nullAssignToIterator, nullAssignToLoopVar));
+            result = make.Block(0, List.of(init, (JCStatement)result, nullAssignToIterator));
         }
 
     public void visitVarDef(JCVariableDecl tree) {
diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassFile.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassFile.java
index 6429cf8f16f..6e80995e509 100644
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassFile.java
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassFile.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1999, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1999, 2017, 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
@@ -110,7 +110,8 @@ public class ClassFile {
         V50(50, 0),   // JDK 1.6: stackmaps
         V51(51, 0),   // JDK 1.7
         V52(52, 0),   // JDK 1.8: lambda, type annos, param names
-        V53(53, 0);   // JDK 1.9: modules, indy string concat
+        V53(53, 0),   // JDK 1.9: modules, indy string concat
+        V54(54, 0);   // JDK 10
         Version(int major, int minor) {
             this.major = major;
             this.minor = minor;
diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassReader.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassReader.java
index a0495a969bb..ec321fad4e5 100644
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassReader.java
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassReader.java
@@ -2684,7 +2684,7 @@ public class ClassReader {
 
         minorVersion = nextChar();
         majorVersion = nextChar();
-        int maxMajor = 53; // Version.MAX().major;  //******* TEMPORARY *******
+        int maxMajor = Version.MAX().major;
         int maxMinor = Version.MAX().minor;
         if (majorVersion > maxMajor ||
             majorVersion * 1000 + minorVersion <
diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassWriter.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassWriter.java
index f23385c7c21..a0a1453c1c2 100644
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassWriter.java
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassWriter.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1999, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1999, 2017, 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
@@ -1815,15 +1815,8 @@ public class ClassWriter extends ClassFile {
         acount += writeExtraClassAttributes(c);
 
         poolbuf.appendInt(JAVA_MAGIC);
-
-        if (c.owner.kind == MDL) {
-            // temporarily overide to force use of v53 for module-info.class
-            poolbuf.appendChar(0);
-            poolbuf.appendChar(53);
-        } else {
-            poolbuf.appendChar(target.minorVersion);
-            poolbuf.appendChar(target.majorVersion);
-        }
+        poolbuf.appendChar(target.minorVersion);
+        poolbuf.appendChar(target.majorVersion);
 
         writePool(c.pool);
 
diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Target.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Target.java
index 1d1fdf1e11d..b0413afb743 100644
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Target.java
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/Target.java
@@ -63,8 +63,8 @@ public enum Target {
     /** JDK 9. */
     JDK1_9("1.9", 53, 0),
 
-    /** JDK 10, initially an alias for 9 */
-    JDK1_10("1.10", 53, 0);
+    /** JDK 10. */
+    JDK1_10("1.10", 54, 0);
 
     private static final Context.Key<Target> targetKey = new Context.Key<>();
 
diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/main/Option.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/main/Option.java
index 548ee7ca01a..9210793710b 100644
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/main/Option.java
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/main/Option.java
@@ -1025,6 +1025,11 @@ public enum Option {
         return (argKind != ArgKind.NONE);
     }
 
+    public boolean hasSeparateArg() {
+        return getArgKind() == ArgKind.REQUIRED &&
+               !primaryName.endsWith(":") && !primaryName.endsWith("=");
+    }
+
     public boolean matches(String option) {
         for (String name: names) {
             if (matches(option, name))
diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/model/JavacElements.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/model/JavacElements.java
index d8d4687a1db..324b0b13240 100644
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/model/JavacElements.java
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/model/JavacElements.java
@@ -197,9 +197,18 @@ public class JavacElements implements Elements {
         for (ModuleSymbol msym : modules.allModules()) {
             S sym = nameToSymbol(msym, nameStr, clazz);
 
-            if (sym != null) {
-                if (!allowModules || clazz == ClassSymbol.class || !sym.members().isEmpty()) {
-                    //do not add packages without members:
+            if (sym == null)
+                continue;
+
+            if (clazz == ClassSymbol.class) {
+                // Always include classes
+                found.add(sym);
+            } else if (clazz == PackageSymbol.class) {
+                // In module mode, ignore the "spurious" empty packages that "enclose" module-specific packages.
+                // For example, if a module contains classes or package info in package p.q.r, it will also appear
+                // to have additional packages p.q and p, even though these packages have no content other
+                // than the subpackage.  We don't want those empty packages showing up in searches for p or p.q.
+                if (!sym.members().isEmpty() || ((PackageSymbol) sym).package_info != null) {
                     found.add(sym);
                 }
             }
diff --git a/src/jdk.compiler/share/classes/jdk/internal/shellsupport/doc/JavadocHelper.java b/src/jdk.compiler/share/classes/jdk/internal/shellsupport/doc/JavadocHelper.java
index 0edc8458478..1f4b162e1fd 100644
--- a/src/jdk.compiler/share/classes/jdk/internal/shellsupport/doc/JavadocHelper.java
+++ b/src/jdk.compiler/share/classes/jdk/internal/shellsupport/doc/JavadocHelper.java
@@ -77,6 +77,7 @@ import com.sun.source.tree.ClassTree;
 import com.sun.source.tree.CompilationUnitTree;
 import com.sun.source.tree.MethodTree;
 import com.sun.source.tree.VariableTree;
+import com.sun.source.util.DocSourcePositions;
 import com.sun.source.util.DocTreePath;
 import com.sun.source.util.DocTreeScanner;
 import com.sun.source.util.DocTrees;
@@ -194,6 +195,8 @@ public abstract class JavadocHelper implements AutoCloseable {
             String docComment = trees.getDocComment(el);
 
             if (docComment == null && element.getKind() == ElementKind.METHOD) {
+                //if a method does not have a javadoc,
+                //try to use javadoc from the methods overridden by this method:
                 ExecutableElement executableElement = (ExecutableElement) element;
                 Iterable<Element> superTypes =
                         () -> superTypeForInheritDoc(task, element.getEnclosingElement()).iterator();
@@ -215,27 +218,65 @@ public abstract class JavadocHelper implements AutoCloseable {
                 }
             }
 
+            if (docComment == null)
+                return null;
+
             Pair<DocCommentTree, Integer> parsed = parseDocComment(task, docComment);
             DocCommentTree docCommentTree = parsed.fst;
             int offset = parsed.snd;
             IOException[] exception = new IOException[1];
-            Map<int[], String> replace = new TreeMap<>((span1, span2) -> span2[0] - span1[0]);
+            Comparator<int[]> spanComp =
+                    (span1, span2) -> span1[0] != span2[0] ? span2[0] - span1[0]
+                                                           : span2[1] - span1[0];
+            //spans in the docComment that should be replaced with the given Strings:
+            Map<int[], List<String>> replace = new TreeMap<>(spanComp);
+            DocSourcePositions sp = trees.getSourcePositions();
 
+            //fill in missing elements and resolve {@inheritDoc}
+            //if an element is (silently) missing in the javadoc, a synthetic {@inheritDoc}
+            //is created for it.
             new DocTreeScanner<Void, Void>() {
+                /* enclosing doctree that may contain {@inheritDoc} (explicit or synthetic)*/
                 private Stack<DocTree> interestingParent = new Stack<>();
+                /* current top-level DocCommentTree*/
                 private DocCommentTree dcTree;
-                private JavacTask inheritedJavacTask;
-                private TreePath inheritedTreePath;
+                /* javadoc from a super method from which we may copy elements.*/
                 private String inherited;
+                /* JavacTask from which inherited originates.*/
+                private JavacTask inheritedJavacTask;
+                /* TreePath to the super method from which inherited originates.*/
+                private TreePath inheritedTreePath;
+                /* Synthetic trees that contain {@inheritDoc} and
+                 * texts which which they should be replaced.*/
                 private Map<DocTree, String> syntheticTrees = new IdentityHashMap<>();
-                private long lastPos = 0;
+                /* Position on which the synthetic trees should be inserted.*/
+                private long insertPos = offset;
                 @Override @DefinedBy(Api.COMPILER_TREE)
                 public Void visitDocComment(DocCommentTree node, Void p) {
                     dcTree = node;
                     interestingParent.push(node);
                     try {
-                        scan(node.getFirstSentence(), p);
-                        scan(node.getBody(), p);
+                        if (node.getFullBody().isEmpty()) {
+                            //there is no body in the javadoc, add synthetic {@inheritDoc}, which
+                            //will be automatically filled in visitInheritDoc:
+                            DocCommentTree dc = parseDocComment(task, "{@inheritDoc}").fst;
+                            syntheticTrees.put(dc, "*\n");
+                            interestingParent.push(dc);
+                            boolean prevInSynthetic = inSynthetic;
+                            try {
+                                inSynthetic = true;
+                                scan(dc.getFirstSentence(), p);
+                                scan(dc.getBody(), p);
+                            } finally {
+                                inSynthetic = prevInSynthetic;
+                                interestingParent.pop();
+                            }
+                        } else {
+                            scan(node.getFirstSentence(), p);
+                            scan(node.getBody(), p);
+                        }
+                        //add missing @param, @throws and @return, augmented with {@inheritDoc}
+                        //which will be resolved in visitInheritDoc:
                         List<DocTree> augmentedBlockTags = new ArrayList<>(node.getBlockTags());
                         if (element.getKind() == ElementKind.METHOD) {
                             ExecutableElement executableElement = (ExecutableElement) element;
@@ -269,19 +310,19 @@ public abstract class JavadocHelper implements AutoCloseable {
 
                             for (String missingParam : missingParams) {
                                 DocTree syntheticTag = parseBlockTag(task, "@param " + missingParam + " {@inheritDoc}");
-                                syntheticTrees.put(syntheticTag, "@param " + missingParam + " ");
+                                syntheticTrees.put(syntheticTag, "@param " + missingParam + " *\n");
                                 insertTag(augmentedBlockTags, syntheticTag, parameters, throwsList);
                             }
 
                             for (String missingThrow : missingThrows) {
                                 DocTree syntheticTag = parseBlockTag(task, "@throws " + missingThrow + " {@inheritDoc}");
-                                syntheticTrees.put(syntheticTag, "@throws " + missingThrow + " ");
+                                syntheticTrees.put(syntheticTag, "@throws " + missingThrow + " *\n");
                                 insertTag(augmentedBlockTags, syntheticTag, parameters, throwsList);
                             }
 
                             if (!hasReturn) {
                                 DocTree syntheticTag = parseBlockTag(task, "@return {@inheritDoc}");
-                                syntheticTrees.put(syntheticTag, "@return ");
+                                syntheticTrees.put(syntheticTag, "@return *\n");
                                 insertTag(augmentedBlockTags, syntheticTag, parameters, throwsList);
                             }
                         }
@@ -320,26 +361,32 @@ public abstract class JavadocHelper implements AutoCloseable {
                 }
                 @Override @DefinedBy(Api.COMPILER_TREE)
                 public Void visitInheritDoc(InheritDocTree node, Void p) {
+                    //replace (schedule replacement into the replace map)
+                    //{@inheritDoc} with the corresponding text from an overridden method
+
+                    //first, fill in inherited, inheritedJavacTask and inheritedTreePath if not
+                    //done yet:
                     if (inherited == null) {
                         try {
                             if (element.getKind() == ElementKind.METHOD) {
                                 ExecutableElement executableElement = (ExecutableElement) element;
-                                Iterable<Element> superTypes = () -> superTypeForInheritDoc(task, element.getEnclosingElement()).iterator();
-                                OUTER: for (Element sup : superTypes) {
-                                   for (ExecutableElement supMethod : ElementFilter.methodsIn(sup.getEnclosedElements())) {
-                                       if (task.getElements().overrides(executableElement, supMethod, (TypeElement) executableElement.getEnclosingElement())) {
-                                           Pair<JavacTask, TreePath> source = getSourceElement(task, supMethod);
+                                Iterable<ExecutableElement> superMethods =
+                                        () -> superMethodsForInheritDoc(task, executableElement).
+                                              iterator();
+                                for (Element supMethod : superMethods) {
+                                   Pair<JavacTask, TreePath> source =
+                                           getSourceElement(task, supMethod);
 
-                                           if (source != null) {
-                                               String overriddenComment = getResolvedDocComment(source.fst, source.snd);
+                                   if (source != null) {
+                                       String overriddenComment =
+                                               getResolvedDocComment(source.fst,
+                                                                     source.snd);
 
-                                               if (overriddenComment != null) {
-                                                   inheritedJavacTask = source.fst;
-                                                   inheritedTreePath = source.snd;
-                                                   inherited = overriddenComment;
-                                                   break OUTER;
-                                               }
-                                           }
+                                       if (overriddenComment != null) {
+                                           inheritedJavacTask = source.fst;
+                                           inheritedTreePath = source.snd;
+                                           inherited = overriddenComment;
+                                           break;
                                        }
                                    }
                                 }
@@ -357,6 +404,8 @@ public abstract class JavadocHelper implements AutoCloseable {
                     DocCommentTree inheritedDocTree = parsed.fst;
                     int offset = parsed.snd;
                     List<List<? extends DocTree>> inheritedText = new ArrayList<>();
+                    //find the corresponding piece in the inherited javadoc
+                    //(interesting parent keeps the enclosing tree):
                     DocTree parent = interestingParent.peek();
                     switch (parent.getKind()) {
                         case DOC_COMMENT:
@@ -401,18 +450,29 @@ public abstract class JavadocHelper implements AutoCloseable {
                         long end = Long.MIN_VALUE;
 
                         for (DocTree t : inheritedText.get(0)) {
-                            start = Math.min(start, trees.getSourcePositions().getStartPosition(null, inheritedDocTree, t) - offset);
-                            end   = Math.max(end,   trees.getSourcePositions().getEndPosition(null, inheritedDocTree, t) - offset);
+                            start = Math.min(start,
+                                             sp.getStartPosition(null, inheritedDocTree, t) - offset);
+                            end   = Math.max(end,
+                                             sp.getEndPosition(null, inheritedDocTree, t) - offset);
                         }
-                        String text = inherited.substring((int) start, (int) end);
+                        String text = end >= 0 ? inherited.substring((int) start, (int) end) : "";
 
                         if (syntheticTrees.containsKey(parent)) {
-                            replace.put(new int[] {(int) lastPos + 1, (int) lastPos}, "\n" + syntheticTrees.get(parent) + text);
+                            //if the {@inheritDoc} is inside a synthetic tree, don't delete anything,
+                            //but insert the required text
+                            //(insertPos is the position at which new stuff should be added):
+                            int[] span = new int[] {(int) insertPos, (int) insertPos};
+                            replace.computeIfAbsent(span, s -> new ArrayList<>())
+                                    .add(syntheticTrees.get(parent).replace("*", text));
                         } else {
-                            long inheritedStart = trees.getSourcePositions().getStartPosition(null, dcTree, node);
-                            long inheritedEnd   = trees.getSourcePositions().getEndPosition(null, dcTree, node);
+                            //replace the {@inheritDoc} with the full text from
+                            //the overridden method:
+                            long inheritedStart = sp.getStartPosition(null, dcTree, node);
+                            long inheritedEnd   = sp.getEndPosition(null, dcTree, node);
+                            int[] span = new int[] {(int) inheritedStart, (int) inheritedEnd};
 
-                            replace.put(new int[] {(int) inheritedStart, (int) inheritedEnd}, text);
+                            replace.computeIfAbsent(span, s -> new ArrayList<>())
+                                    .add(text);
                         }
                     }
                     return super.visitInheritDoc(node, p);
@@ -428,13 +488,31 @@ public abstract class JavadocHelper implements AutoCloseable {
                         inSynthetic |= syntheticTrees.containsKey(tree);
                         return super.scan(tree, p);
                     } finally {
-                        if (!inSynthetic) {
-                            lastPos = trees.getSourcePositions().getEndPosition(null, dcTree, tree);
+                        if (!inSynthetic && tree != null) {
+                            //for nonsynthetic trees, preserve the ending position as the future
+                            //insertPos (as future missing elements should be inserted behind
+                            //this tree)
+                            //if there is a newline immediately behind this tree, insert behind
+                            //the newline:
+                            long endPos = sp.getEndPosition(null, dcTree, tree);
+                            if (endPos >= 0) {
+                                if (endPos - offset + 1 < docComment.length() &&
+                                    docComment.charAt((int) (endPos - offset + 1)) == '\n') {
+                                    endPos++;
+                                }
+                                if (endPos - offset < docComment.length()) {
+                                    insertPos = endPos + 1;
+                                } else {
+                                    insertPos = endPos;
+                                }
+                            }
                         }
                         inSynthetic = prevInSynthetic;
                     }
                 }
 
+                /* Insert a synthetic tag (toInsert) into the list of tags at
+                 * an appropriate position.*/
                 private void insertTag(List<DocTree> tags, DocTree toInsert, List<String> parameters, List<String> throwsTypes) {
                     Comparator<DocTree> comp = (tag1, tag2) -> {
                         if (tag1.getKind() == tag2.getKind()) {
@@ -479,16 +557,30 @@ public abstract class JavadocHelper implements AutoCloseable {
             if (replace.isEmpty())
                 return docComment;
 
+            //do actually replace {@inheritDoc} with the new text (as scheduled by the visitor
+            //above):
             StringBuilder replacedInheritDoc = new StringBuilder(docComment);
 
-            for (Entry<int[], String> e : replace.entrySet()) {
-                replacedInheritDoc.delete(e.getKey()[0] - offset, e.getKey()[1] - offset + 1);
-                replacedInheritDoc.insert(e.getKey()[0] - offset, e.getValue());
+            for (Entry<int[], List<String>> e : replace.entrySet()) {
+                replacedInheritDoc.delete(e.getKey()[0] - offset, e.getKey()[1] - offset);
+                replacedInheritDoc.insert(e.getKey()[0] - offset,
+                                          e.getValue().stream().collect(Collectors.joining("")));
             }
 
             return replacedInheritDoc.toString();
         }
 
+        /* Find methods from which the given method may inherit javadoc, in the proper order.*/
+        private Stream<ExecutableElement> superMethodsForInheritDoc(JavacTask task,
+                                                                     ExecutableElement method) {
+            TypeElement type = (TypeElement) method.getEnclosingElement();
+
+            return this.superTypeForInheritDoc(task, type)
+                       .flatMap(sup -> ElementFilter.methodsIn(sup.getEnclosedElements()).stream())
+                       .filter(supMethod -> task.getElements().overrides(method, supMethod, type));
+        }
+
+        /* Find types from which methods in type may inherit javadoc, in the proper order.*/
         private Stream<Element> superTypeForInheritDoc(JavacTask task, Element type) {
             TypeElement clazz = (TypeElement) type;
             Stream<Element> result = interfaces(clazz);
@@ -529,7 +621,7 @@ public abstract class JavadocHelper implements AutoCloseable {
                 };
                 DocCommentTree tree = trees.getDocCommentTree(fo);
                 int offset = (int) trees.getSourcePositions().getStartPosition(null, tree, tree);
-                offset += "<body>".length() + 1;
+                offset += "<body>".length();
                 return Pair.of(tree, offset);
             } catch (URISyntaxException ex) {
                 throw new IllegalStateException(ex);
diff --git a/src/jdk.dynalink/share/classes/jdk/dynalink/beans/BeanLinker.java b/src/jdk.dynalink/share/classes/jdk/dynalink/beans/BeanLinker.java
index 7f2aa301be9..b2389d61066 100644
--- a/src/jdk.dynalink/share/classes/jdk/dynalink/beans/BeanLinker.java
+++ b/src/jdk.dynalink/share/classes/jdk/dynalink/beans/BeanLinker.java
@@ -205,9 +205,8 @@ class BeanLinker extends AbstractJavaLinker implements TypeBasedGuardingDynamicL
             return nextComponent;
         }
 
-        return guardComponentWithRangeCheck(gicact, linkerServices,
-                callSiteDescriptor, nextComponent, new Binder(linkerServices, callSiteType, typedName),
-                isFixedKey ? NULL_GETTER_1 : NULL_GETTER_2);
+        return guardComponentWithRangeCheck(gicact, callSiteType, nextComponent,
+                new Binder(linkerServices, callSiteType, typedName), isFixedKey ? NULL_GETTER_1 : NULL_GETTER_2);
     }
 
     private static class GuardedInvocationComponentAndCollectionType {
@@ -276,21 +275,19 @@ class BeanLinker extends AbstractJavaLinker implements TypeBasedGuardingDynamicL
     }
 
     private static GuardedInvocationComponent guardComponentWithRangeCheck(
-            final GuardedInvocationComponentAndCollectionType gicact, final LinkerServices linkerServices,
-            final CallSiteDescriptor callSiteDescriptor, final GuardedInvocationComponent nextComponent, final Binder binder,
-            final MethodHandle noOp) {
-        final MethodType callSiteType = callSiteDescriptor.getMethodType();
+            final GuardedInvocationComponentAndCollectionType gicact, final MethodType callSiteType,
+            final GuardedInvocationComponent nextComponent, final Binder binder, final MethodHandle noOp) {
 
         final MethodHandle checkGuard;
         switch(gicact.collectionType) {
             case LIST:
-                checkGuard = convertArgToNumber(RANGE_CHECK_LIST, linkerServices, callSiteDescriptor);
+                checkGuard = binder.convertArgToNumber(RANGE_CHECK_LIST);
                 break;
             case MAP:
-                checkGuard = linkerServices.filterInternalObjects(CONTAINS_MAP);
+                checkGuard = binder.linkerServices.filterInternalObjects(CONTAINS_MAP);
                 break;
             case ARRAY:
-                checkGuard = convertArgToNumber(RANGE_CHECK_ARRAY, linkerServices, callSiteDescriptor);
+                checkGuard = binder.convertArgToNumber(RANGE_CHECK_ARRAY);
                 break;
             default:
                 throw new AssertionError();
@@ -301,7 +298,7 @@ class BeanLinker extends AbstractJavaLinker implements TypeBasedGuardingDynamicL
         if (nextComponent != null) {
             finalNextComponent = nextComponent;
         } else {
-            finalNextComponent = createGuardedInvocationComponentAsType(noOp, callSiteType, linkerServices);
+            finalNextComponent = createGuardedInvocationComponentAsType(noOp, callSiteType, binder.linkerServices);
         }
 
         final GuardedInvocationComponent gic = gicact.gic;
@@ -377,18 +374,6 @@ class BeanLinker extends AbstractJavaLinker implements TypeBasedGuardingDynamicL
         return intIndex;
     }
 
-    private static MethodHandle convertArgToNumber(final MethodHandle mh, final LinkerServices ls, final CallSiteDescriptor desc) {
-        final Class<?> sourceType = desc.getMethodType().parameterType(1);
-        if(TypeUtilities.isMethodInvocationConvertible(sourceType, Number.class)) {
-            return mh;
-        } else if(ls.canConvert(sourceType, Number.class)) {
-            final MethodHandle converter = ls.getTypeConverter(sourceType, Number.class);
-            return MethodHandles.filterArguments(mh, 1, converter.asType(converter.type().changeReturnType(
-                    mh.type().parameterType(1))));
-        }
-        return mh;
-    }
-
     /**
      * Contains methods to adapt an item getter/setter method handle to the requested type, optionally binding it to a
      * fixed key first.
@@ -412,6 +397,18 @@ class BeanLinker extends AbstractJavaLinker implements TypeBasedGuardingDynamicL
             return bindToFixedKey(Guards.asType(handle, methodType));
         }
 
+        /*private*/ MethodHandle convertArgToNumber(final MethodHandle mh) {
+            final Class<?> sourceType = methodType.parameterType(1);
+            if(TypeUtilities.isMethodInvocationConvertible(sourceType, Number.class)) {
+                return mh;
+            } else if(linkerServices.canConvert(sourceType, Number.class)) {
+                final MethodHandle converter = linkerServices.getTypeConverter(sourceType, Number.class);
+                return MethodHandles.filterArguments(mh, 1, converter.asType(converter.type().changeReturnType(
+                        mh.type().parameterType(1))));
+            }
+            return mh;
+        }
+
         private MethodHandle bindToFixedKey(final MethodHandle handle) {
             return fixedKey == null ? handle : MethodHandles.insertArguments(handle, 1, fixedKey);
         }
@@ -506,8 +503,7 @@ class BeanLinker extends AbstractJavaLinker implements TypeBasedGuardingDynamicL
             return gic.replaceInvocation(binder.bind(invocation));
         }
 
-        return guardComponentWithRangeCheck(gicact, linkerServices, callSiteDescriptor,
-                nextComponent, binder, isFixedKey ? NO_OP_SETTER_2 : NO_OP_SETTER_3);
+        return guardComponentWithRangeCheck(gicact, callSiteType, nextComponent, binder, isFixedKey ? NO_OP_SETTER_2 : NO_OP_SETTER_3);
     }
 
     private static final MethodHandle GET_COLLECTION_LENGTH = Lookup.PUBLIC.findVirtual(Collection.class, "size",
diff --git a/src/jdk.hotspot.agent/macosx/native/libsaproc/BsdDebuggerLocal.c b/src/jdk.hotspot.agent/macosx/native/libsaproc/BsdDebuggerLocal.c
index 85b07f76440..fadf211ac0f 100644
--- a/src/jdk.hotspot.agent/macosx/native/libsaproc/BsdDebuggerLocal.c
+++ b/src/jdk.hotspot.agent/macosx/native/libsaproc/BsdDebuggerLocal.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2002, 2007, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2002, 2017, 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
@@ -298,9 +298,6 @@ JNIEXPORT jlongArray JNICALL Java_sun_jvm_hotspot_debugger_bsd_BsdDebuggerLocal_
 #ifdef i386
 #define NPRGREG sun_jvm_hotspot_debugger_x86_X86ThreadContext_NPRGREG
 #endif
-#ifdef ia64
-#define NPRGREG IA64_REG_COUNT
-#endif
 #ifdef amd64
 #define NPRGREG sun_jvm_hotspot_debugger_amd64_AMD64ThreadContext_NPRGREG
 #endif
@@ -335,14 +332,6 @@ JNIEXPORT jlongArray JNICALL Java_sun_jvm_hotspot_debugger_bsd_BsdDebuggerLocal_
 
 #endif /* i386 */
 
-#if ia64
-  regs = (*env)->GetLongArrayElements(env, array, &isCopy);
-  int i;
-  for (i = 0; i < NPRGREG; i++ ) {
-    regs[i] = 0xDEADDEAD;
-  }
-#endif /* ia64 */
-
 #ifdef amd64
 #define REG_INDEX(reg) sun_jvm_hotspot_debugger_amd64_AMD64ThreadContext_##reg
 
diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/HotSpotAgent.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/HotSpotAgent.java
index bb92d20b1c8..33f20eadbb8 100644
--- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/HotSpotAgent.java
+++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/HotSpotAgent.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2000, 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2000, 2017, 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
@@ -35,7 +35,6 @@ import sun.jvm.hotspot.debugger.MachineDescription;
 import sun.jvm.hotspot.debugger.MachineDescriptionAMD64;
 import sun.jvm.hotspot.debugger.MachineDescriptionPPC64;
 import sun.jvm.hotspot.debugger.MachineDescriptionAArch64;
-import sun.jvm.hotspot.debugger.MachineDescriptionIA64;
 import sun.jvm.hotspot.debugger.MachineDescriptionIntelX86;
 import sun.jvm.hotspot.debugger.MachineDescriptionSPARC32Bit;
 import sun.jvm.hotspot.debugger.MachineDescriptionSPARC64Bit;
@@ -556,10 +555,8 @@ public class HotSpotAgent {
             machDesc = new MachineDescriptionIntelX86();
         } else if (cpu.equals("amd64")) {
             machDesc = new MachineDescriptionAMD64();
-        } else if (cpu.equals("ia64")) {
-            machDesc = new MachineDescriptionIA64();
         } else {
-            throw new DebuggerException("Win32 supported under x86, amd64 and ia64 only");
+            throw new DebuggerException("Win32 supported under x86 and amd64 only");
         }
 
         // Note we do not use a cache for the local debugger in server
@@ -586,8 +583,6 @@ public class HotSpotAgent {
 
         if (cpu.equals("x86")) {
             machDesc = new MachineDescriptionIntelX86();
-        } else if (cpu.equals("ia64")) {
-            machDesc = new MachineDescriptionIA64();
         } else if (cpu.equals("amd64")) {
             machDesc = new MachineDescriptionAMD64();
         } else if (cpu.equals("ppc64")) {
diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/bsd/BsdDebuggerLocal.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/bsd/BsdDebuggerLocal.java
index 45697a83808..655b450c3fc 100644
--- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/bsd/BsdDebuggerLocal.java
+++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/bsd/BsdDebuggerLocal.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2002, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2002, 2017, 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
@@ -216,11 +216,7 @@ public class BsdDebuggerLocal extends DebuggerBase implements BsdDebugger {
             // the UI. This is a cache of 4096 4K pages, or 16 MB. The page
             // size must be adjusted to be the hardware's page size.
             // (FIXME: should pick this up from the debugger.)
-            if (getCPU().equals("ia64")) {
-              initCache(16384, parseCacheNumPagesProperty(1024));
-            } else {
-              initCache(4096, parseCacheNumPagesProperty(4096));
-            }
+            initCache(4096, parseCacheNumPagesProperty(4096));
         }
 
         isDarwin = getOS().equals("darwin");
@@ -575,11 +571,6 @@ public class BsdDebuggerLocal extends DebuggerBase implements BsdDebugger {
 
     public CDebugger getCDebugger() {
       if (cdbg == null) {
-         String cpu = getCPU();
-         if (cpu.equals("ia64") ) {
-            // IA-64 is not supported because of stack-walking issues
-            return null;
-         }
          cdbg = new BsdCDebugger(this);
       }
       return cdbg;
diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/ia64/IA64ThreadContext.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/ia64/IA64ThreadContext.java
deleted file mode 100644
index 8a8ce383f2e..00000000000
--- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/ia64/IA64ThreadContext.java
+++ /dev/null
@@ -1,187 +0,0 @@
-/*
- * 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
- * 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.debugger.ia64;
-
-import sun.jvm.hotspot.debugger.*;
-import sun.jvm.hotspot.debugger.cdbg.*;
-
-/** Specifies the thread context on ia64 platform; only a sub-portion
-    of the context is guaranteed to be present on all operating
-    systems. */
-
-public abstract class IA64ThreadContext implements ThreadContext {
-  // Refer to winnt.h CONTEXT structure - Nov 2001 edition Platform SDK
-  // only a relevant subset of CONTEXT structure is used here.
-  // For eg. floating point registers are ignored.
-
-  // NOTE: the indices for the various registers must be maintained as
-  // listed across various operating systems. However, only a
-  // subset of the registers' values are guaranteed to be present
-
-  // global registers r0-r31
-  public static final int GR0  = 0;
-  public static final int GR1  = 1;
-  public static final int GR2  = 2;
-  public static final int GR3  = 3;
-  public static final int GR4  = 4;
-  public static final int GR5  = 5;
-  public static final int GR6  = 6;
-  public static final int GR7  = 7;
-  public static final int GR8  = 8;
-  public static final int GR9  = 9;
-  public static final int GR10 = 10;
-  public static final int GR11 = 11;
-  public static final int GR12 = 12;
-  public static final int SP = GR12;
-  public static final int GR13 = 13;
-  public static final int GR14 = 14;
-  public static final int GR15 = 15;
-  public static final int GR16 = 16;
-  public static final int GR17 = 17;
-  public static final int GR18 = 18;
-  public static final int GR19 = 19;
-  public static final int GR20 = 20;
-  public static final int GR21 = 21;
-  public static final int GR22 = 22;
-  public static final int GR23 = 23;
-  public static final int GR24 = 24;
-  public static final int GR25 = 25;
-  public static final int GR26 = 26;
-  public static final int GR27 = 27;
-  public static final int GR28 = 28;
-  public static final int GR29 = 29;
-  public static final int GR30 = 30;
-  public static final int GR31 = 31;
-
-  // Nat bits for r1-r31
-  public static final int INT_NATS = 32;
-
-  // predicates
-  public static final int PREDS    = 33;
-
-  // branch registers
-  public static final int BR0      = 34;
-  public static final int BR_RP    = BR0;
-  public static final int BR1      = 35;
-  public static final int BR2      = 36;
-  public static final int BR3      = 37;
-  public static final int BR4      = 38;
-  public static final int BR5      = 39;
-  public static final int BR6      = 40;
-  public static final int BR7      = 41;
-
-  // application registers
-  public static final int AP_UNAT  = 42; // User Nat Collection register
-  public static final int AP_LC    = 43; // Loop counter register
-  public static final int AP_EC    = 43; // Epilog counter register
-  public static final int AP_CCV   = 45; // CMPXCHG value register
-  public static final int AP_DCR   = 46; // Default control register
-
-  // register stack info
-  public static final int RS_PFS   = 47; // Previous function state
-  public static final int AP_PFS   = RS_PFS;
-  public static final int RS_BSP   = 48; // Backing store pointer
-  public static final int AR_BSP   = RS_BSP;
-  public static final int RS_BSPSTORE = 49;
-  public static final int AP_BSPSTORE = RS_BSPSTORE;
-  public static final int RS_RSC   = 50;     // RSE configuration
-  public static final int AP_RSC   = RS_RSC;
-  public static final int RS_RNAT  = 51; // RSE Nat collection register
-  public static final int AP_RNAT  = RS_RNAT;
-
-  // trap status register
-  public static final int ST_IPSR  = 52; // Interuption Processor Status
-  public static final int ST_IIP   = 53; // Interruption IP
-  public static final int ST_IFS   = 54; // Interruption Function State
-
-  // debug registers
-  public static final int DB_I0    = 55;
-  public static final int DB_I1    = 56;
-  public static final int DB_I2    = 57;
-  public static final int DB_I3    = 58;
-  public static final int DB_I4    = 59;
-  public static final int DB_I5    = 60;
-  public static final int DB_I6    = 61;
-  public static final int DB_I7    = 62;
-
-  public static final int DB_D0    = 63;
-  public static final int DB_D1    = 64;
-  public static final int DB_D2    = 65;
-  public static final int DB_D3    = 66;
-  public static final int DB_D4    = 67;
-  public static final int DB_D5    = 68;
-  public static final int DB_D6    = 69;
-  public static final int DB_D7    = 70;
-
-  public static final int NPRGREG  = 71;
-
-  private static final String[] regNames = {
-     "GR0", "GR1", "GR2", "GR3", "GR4", "GR5", "GR6", "GR7", "GR8",
-     "GR9", "GR10", "GR11", "GR12", "GR13", "GR14", "GR15", "GR16",
-     "GR17","GR18", "GR19", "GR20", "GR21", "GR22", "GR23", "GR24",
-     "GR25","GR26", "GR27", "GR28", "GR29", "GR30", "GR31",
-     "INT_NATS", "PREDS",
-     "BR0", "BR1", "BR2", "BR3", "BR4", "BR5", "BR6", "BR7",
-     "AP_UNAT", "AP_LC", "AP_EC", "AP_CCV", "AP_DCR",
-     "RS_FPS", "RS_BSP", "RS_BSPSTORE", "RS_RSC", "RS_RNAT",
-     "ST_IPSR", "ST_IIP", "ST_IFS",
-     "DB_I0", "DB_I1", "DB_I2", "DB_I3", "DB_I4", "DB_I5", "DB_I6", "DB_I7",
-     "DB_D0", "DB_D1", "DB_D2", "DB_D3", "DB_D4", "DB_D5", "DB_D6", "DB_D7"
-  };
-
-  private long[] data;
-
-  public IA64ThreadContext() {
-    data = new long[NPRGREG];
-  }
-
-  public int getNumRegisters() {
-    return NPRGREG;
-  }
-
-  public String getRegisterName(int index) {
-    return regNames[index];
-  }
-
-  public void setRegister(int index, long value) {
-    data[index] = value;
-  }
-
-  public long getRegister(int index) {
-    return data[index];
-  }
-
-  public CFrame getTopFrame(Debugger dbg) {
-    return null;
-  }
-
-  /** This can't be implemented in this class since we would have to
-      tie the implementation to, for example, the debugging system */
-  public abstract void setRegisterAsAddress(int index, Address value);
-
-  /** This can't be implemented in this class since we would have to
-      tie the implementation to, for example, the debugging system */
-  public abstract Address getRegisterAsAddress(int index);
-}
diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/linux/LinuxDebuggerLocal.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/linux/LinuxDebuggerLocal.java
index f282a228df6..0d47eb03630 100644
--- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/linux/LinuxDebuggerLocal.java
+++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/linux/LinuxDebuggerLocal.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2002, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2002, 2017, 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
@@ -210,11 +210,7 @@ public class LinuxDebuggerLocal extends DebuggerBase implements LinuxDebugger {
             // the UI. This is a cache of 4096 4K pages, or 16 MB. The page
             // size must be adjusted to be the hardware's page size.
             // (FIXME: should pick this up from the debugger.)
-            if (getCPU().equals("ia64")) {
-              initCache(16384, parseCacheNumPagesProperty(1024));
-            } else {
-              initCache(4096, parseCacheNumPagesProperty(4096));
-            }
+            initCache(4096, parseCacheNumPagesProperty(4096));
         }
 
         workerThread = new LinuxDebuggerLocalWorkerThread(this);
@@ -560,11 +556,6 @@ public class LinuxDebuggerLocal extends DebuggerBase implements LinuxDebugger {
 
     public CDebugger getCDebugger() {
       if (cdbg == null) {
-         String cpu = getCPU();
-         if (cpu.equals("ia64") ) {
-            // IA-64 is not supported because of stack-walking issues
-            return null;
-         }
          cdbg = new LinuxCDebugger(this);
       }
       return cdbg;
diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/linux/LinuxThreadContextFactory.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/linux/LinuxThreadContextFactory.java
index 2225558000b..4b786eecc95 100644
--- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/linux/LinuxThreadContextFactory.java
+++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/linux/LinuxThreadContextFactory.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2002, 2012, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2002, 2017, 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
@@ -27,7 +27,6 @@ package sun.jvm.hotspot.debugger.linux;
 import java.lang.reflect.*;
 import sun.jvm.hotspot.debugger.*;
 import sun.jvm.hotspot.debugger.linux.amd64.*;
-import sun.jvm.hotspot.debugger.linux.ia64.*;
 import sun.jvm.hotspot.debugger.linux.x86.*;
 import sun.jvm.hotspot.debugger.linux.ppc64.*;
 import sun.jvm.hotspot.debugger.linux.sparc.*;
@@ -39,8 +38,6 @@ class LinuxThreadContextFactory {
          return new LinuxX86ThreadContext(dbg);
       } else if (cpu.equals("amd64")) {
          return new LinuxAMD64ThreadContext(dbg);
-      } else if (cpu.equals("ia64")) {
-         return new LinuxIA64ThreadContext(dbg);
       } else if (cpu.equals("sparc")) {
          return new LinuxSPARCThreadContext(dbg);
       }  else if (cpu.equals("ppc64")) {
diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/windbg/WindbgDebuggerLocal.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/windbg/WindbgDebuggerLocal.java
index afe11938c42..fa7837cec6e 100644
--- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/windbg/WindbgDebuggerLocal.java
+++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/windbg/WindbgDebuggerLocal.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2002, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2002, 2017, 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
@@ -30,10 +30,8 @@ import java.util.*;
 import sun.jvm.hotspot.debugger.*;
 import sun.jvm.hotspot.debugger.amd64.*;
 import sun.jvm.hotspot.debugger.x86.*;
-import sun.jvm.hotspot.debugger.ia64.*;
 import sun.jvm.hotspot.debugger.windbg.amd64.*;
 import sun.jvm.hotspot.debugger.windbg.x86.*;
-import sun.jvm.hotspot.debugger.windbg.ia64.*;
 import sun.jvm.hotspot.debugger.win32.coff.*;
 import sun.jvm.hotspot.debugger.cdbg.*;
 import sun.jvm.hotspot.debugger.cdbg.basic.BasicDebugEvent;
@@ -115,8 +113,6 @@ public class WindbgDebuggerLocal extends DebuggerBase implements WindbgDebugger
        threadFactory = new WindbgX86ThreadFactory(this);
     } else if (cpu.equals("amd64")) {
        threadFactory = new WindbgAMD64ThreadFactory(this);
-    } else if (cpu.equals("ia64")) {
-       threadFactory = new WindbgIA64ThreadFactory(this);
     }
 
     if (useCache) {
@@ -231,11 +227,7 @@ public class WindbgDebuggerLocal extends DebuggerBase implements WindbgDebugger
 
   public CDebugger getCDebugger() throws DebuggerException {
     if (cdbg == null) {
-      // FIXME: CDebugger is not yet supported for IA64 because
-      // of native stack walking issues.
-      if (! getCPU().equals("ia64")) {
-         cdbg = new WindbgCDebugger(this);
-      }
+      cdbg = new WindbgCDebugger(this);
     }
     return cdbg;
   }
diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/windbg/ia64/WindbgIA64Thread.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/windbg/ia64/WindbgIA64Thread.java
deleted file mode 100644
index 591ad9cf5a9..00000000000
--- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/windbg/ia64/WindbgIA64Thread.java
+++ /dev/null
@@ -1,103 +0,0 @@
-/*
- * Copyright (c) 2003, 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.debugger.windbg.ia64;
-
-import sun.jvm.hotspot.debugger.*;
-import sun.jvm.hotspot.debugger.ia64.*;
-import sun.jvm.hotspot.debugger.windbg.*;
-
-class WindbgIA64Thread implements ThreadProxy {
-  private WindbgDebugger debugger;
-  private long           sysId;
-  private boolean        gotID;
-  private long           id;
-
-  /** The address argument must be the address of the HANDLE of the
-      desired thread in the target process. */
-  WindbgIA64Thread(WindbgDebugger debugger, Address addr) {
-    this.debugger = debugger;
-    // FIXME: size of data fetched here should be configurable.
-    // However, making it so would produce a dependency on the "types"
-    // package from the debugger package, which is not desired.
-
-    // another hack here is that we use sys thread id instead of handle.
-    // windbg can't get details based on handles it seems.
-    // I assume that osThread_win32 thread struct has _thread_id (which
-    // sys thread id) just after handle field.
-
-    this.sysId   = (int) addr.addOffsetTo(debugger.getAddressSize()).getCIntegerAt(0, 4, true);
-    gotID = false;
-  }
-
-  WindbgIA64Thread(WindbgDebugger debugger, long sysId) {
-    this.debugger = debugger;
-    this.sysId    = sysId;
-    gotID         = false;
-  }
-
-  public ThreadContext getContext() throws IllegalThreadStateException {
-    long[] data = debugger.getThreadIntegerRegisterSet(getThreadID());
-    WindbgIA64ThreadContext context = new WindbgIA64ThreadContext(debugger);
-    for (int i = 0; i < data.length; i++) {
-      context.setRegister(i, data[i]);
-    }
-    return context;
-  }
-
-  public boolean canSetContext() throws DebuggerException {
-    return false;
-  }
-
-  public void setContext(ThreadContext thrCtx)
-    throws IllegalThreadStateException, DebuggerException {
-    throw new DebuggerException("Unimplemented");
-  }
-
-  public boolean equals(Object obj) {
-    if ((obj == null) || !(obj instanceof WindbgIA64Thread)) {
-      return false;
-    }
-
-    return (((WindbgIA64Thread) obj).getThreadID() == getThreadID());
-  }
-
-  public int hashCode() {
-    return (int) getThreadID();
-  }
-
-  public String toString() {
-    return Long.toString(getThreadID());
-  }
-
-  /** Retrieves the thread ID of this thread by examining the Thread
-      Information Block. */
-  private long getThreadID() {
-    if (!gotID) {
-       id = debugger.getThreadIdFromSysId(sysId);
-    }
-
-    return id;
-  }
-}
diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/InstanceKlass.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/InstanceKlass.java
index 1456c019951..4bcd98bd3da 100644
--- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/InstanceKlass.java
+++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/InstanceKlass.java
@@ -474,20 +474,48 @@ public class InstanceKlass extends Klass {
   }
 
   // same as enum InnerClassAttributeOffset in VM code.
-  public static interface InnerClassAttributeOffset {
+  private static class InnerClassAttributeOffset {
     // from JVM spec. "InnerClasses" attribute
-    public static final int innerClassInnerClassInfoOffset = 0;
-    public static final int innerClassOuterClassInfoOffset = 1;
-    public static final int innerClassInnerNameOffset = 2;
-    public static final int innerClassAccessFlagsOffset = 3;
-    public static final int innerClassNextOffset = 4;
-  };
+    public static int innerClassInnerClassInfoOffset;
+    public static int innerClassOuterClassInfoOffset;
+    public static int innerClassInnerNameOffset;
+    public static int innerClassAccessFlagsOffset;
+    public static int innerClassNextOffset;
+    static {
+      VM.registerVMInitializedObserver(new Observer() {
+          public void update(Observable o, Object data) {
+              initialize(VM.getVM().getTypeDataBase());
+          }
+      });
+    }
 
-  public static interface EnclosingMethodAttributeOffset {
-    public static final int enclosing_method_class_index_offset = 0;
-    public static final int enclosing_method_method_index_offset = 1;
-    public static final int enclosing_method_attribute_size = 2;
-  };
+    private static synchronized void initialize(TypeDataBase db) {
+      innerClassInnerClassInfoOffset = db.lookupIntConstant(
+          "InstanceKlass::inner_class_inner_class_info_offset").intValue();
+      innerClassOuterClassInfoOffset = db.lookupIntConstant(
+          "InstanceKlass::inner_class_outer_class_info_offset").intValue();
+      innerClassInnerNameOffset = db.lookupIntConstant(
+          "InstanceKlass::inner_class_inner_name_offset").intValue();
+      innerClassAccessFlagsOffset = db.lookupIntConstant(
+          "InstanceKlass::inner_class_access_flags_offset").intValue();
+      innerClassNextOffset = db.lookupIntConstant(
+          "InstanceKlass::inner_class_next_offset").intValue();
+    }
+  }
+
+  private static class EnclosingMethodAttributeOffset {
+    public static int enclosingMethodAttributeSize;
+    static {
+      VM.registerVMInitializedObserver(new Observer() {
+          public void update(Observable o, Object data) {
+              initialize(VM.getVM().getTypeDataBase());
+          }
+      });
+    }
+    private static synchronized void initialize(TypeDataBase db) {
+      enclosingMethodAttributeSize = db.lookupIntConstant("InstanceKlass::enclosing_method_attribute_size").intValue();
+    }
+  }
 
   // refer to compute_modifier_flags in VM code.
   public long computeModifierFlags() {
@@ -498,11 +526,11 @@ public class InstanceKlass extends Klass {
     if (length > 0) {
        if (Assert.ASSERTS_ENABLED) {
           Assert.that(length % InnerClassAttributeOffset.innerClassNextOffset == 0 ||
-                      length % InnerClassAttributeOffset.innerClassNextOffset == EnclosingMethodAttributeOffset.enclosing_method_attribute_size,
+                      length % InnerClassAttributeOffset.innerClassNextOffset == EnclosingMethodAttributeOffset.enclosingMethodAttributeSize,
                       "just checking");
        }
        for (int i = 0; i < length; i += InnerClassAttributeOffset.innerClassNextOffset) {
-          if (i == length - EnclosingMethodAttributeOffset.enclosing_method_attribute_size) {
+          if (i == length - EnclosingMethodAttributeOffset.enclosingMethodAttributeSize) {
               break;
           }
           int ioff = innerClassList.at(i +
@@ -547,11 +575,11 @@ public class InstanceKlass extends Klass {
     if (length > 0) {
        if (Assert.ASSERTS_ENABLED) {
          Assert.that(length % InnerClassAttributeOffset.innerClassNextOffset == 0 ||
-                     length % InnerClassAttributeOffset.innerClassNextOffset == EnclosingMethodAttributeOffset.enclosing_method_attribute_size,
+                     length % InnerClassAttributeOffset.innerClassNextOffset == EnclosingMethodAttributeOffset.enclosingMethodAttributeSize,
                      "just checking");
        }
        for (int i = 0; i < length; i += InnerClassAttributeOffset.innerClassNextOffset) {
-         if (i == length - EnclosingMethodAttributeOffset.enclosing_method_attribute_size) {
+         if (i == length - EnclosingMethodAttributeOffset.enclosingMethodAttributeSize) {
              break;
          }
          int ioff = innerClassList.at(i +
diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/Oop.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/Oop.java
index 241e5be3756..c0776dbad2b 100644
--- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/Oop.java
+++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/Oop.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2000, 2017, 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
@@ -202,8 +202,7 @@ public class Oop {
 
   public boolean verify() { return true;}
 
-  // Package-private routine to speed up ObjectHeap.newOop
-  static Klass getKlassForOopHandle(OopHandle handle) {
+  public static Klass getKlassForOopHandle(OopHandle handle) {
     if (handle == null) {
       return null;
     }
diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/java_lang_Class.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/java_lang_Class.java
index 4eab2fe6c91..1e3014655d0 100644
--- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/java_lang_Class.java
+++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/java_lang_Class.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2017, 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
@@ -41,6 +41,7 @@ public class java_lang_Class {
 
   // java.lang.Class fields
   static int klassOffset;
+  static int arrayKlassOffset;
   static IntField oopSizeField;
 
   static {
@@ -56,6 +57,7 @@ public class java_lang_Class {
     // find them from InstanceKlass for java.lang.Class.
     Type jlc = db.lookupType("java_lang_Class");
     klassOffset = (int) jlc.getCIntegerField("_klass_offset").getValue();
+    arrayKlassOffset = (int) jlc.getCIntegerField("_array_klass_offset").getValue();
     int oopSizeOffset = (int) jlc.getCIntegerField("_oop_size_offset").getValue();
     oopSizeField = new IntField(new NamedFieldIdentifier("oop_size"), oopSizeOffset, true);
   }
@@ -69,4 +71,23 @@ public class java_lang_Class {
   public static long getOopSize(Oop aClass) {
     return java_lang_Class.oopSizeField.getValue(aClass);
   }
+
+  /**
+   * Returns the Java name for this Java mirror
+   */
+  public static String asExternalName(Oop aClass) {
+    Klass k = java_lang_Class.asKlass(aClass);
+    if (k == null) { // primitive array
+      BasicType type = BasicType.T_VOID;
+      ArrayKlass ak = (ArrayKlass)Metadata.instantiateWrapperFor(
+                             aClass.getHandle().getAddressAt(arrayKlassOffset));
+      if (ak != null) {
+        type = BasicType.intToBasicType(ak.getElementType());
+      }
+      return type.getName();
+    } else {
+      return k.getName().asString();
+    }
+  }
+
 }
diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/BasicType.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/BasicType.java
index 8331b5d34ca..7c25edc41d6 100644
--- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/BasicType.java
+++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/BasicType.java
@@ -24,113 +24,160 @@
 
 package sun.jvm.hotspot.runtime;
 
+import java.util.Observer;
+import sun.jvm.hotspot.types.TypeDataBase;
+
+
 /** Encapsulates the BasicType enum in globalDefinitions.hpp in the
     VM. */
 
 public class BasicType {
-  public static final int tBoolean     = 4;
-  public static final int tChar        = 5;
-  public static final int tFloat       = 6;
-  public static final int tDouble      = 7;
-  public static final int tByte        = 8;
-  public static final int tShort       = 9;
-  public static final int tInt         = 10;
-  public static final int tLong        = 11;
-  public static final int tObject      = 12;
-  public static final int tArray       = 13;
-  public static final int tVoid        = 14;
-  public static final int tAddress     = 15;
-  public static final int tNarrowOop   = 16;
-  public static final int tMetadata    = 17;
-  public static final int tNarrowKlass = 18;
-  public static final int tConflict    = 19;
-  public static final int tIllegal     = 99;
+  public static final BasicType T_BOOLEAN = new BasicType();
+  public static final BasicType T_CHAR = new BasicType();
+  public static final BasicType T_FLOAT = new BasicType();
+  public static final BasicType T_DOUBLE = new BasicType();
+  public static final BasicType T_BYTE = new BasicType();
+  public static final BasicType T_SHORT = new BasicType();
+  public static final BasicType T_INT = new BasicType();
+  public static final BasicType T_LONG = new BasicType();
+  public static final BasicType T_OBJECT = new BasicType();
+  public static final BasicType T_ARRAY = new BasicType();
+  public static final BasicType T_VOID = new BasicType();
+  public static final BasicType T_ADDRESS = new BasicType();
+  public static final BasicType T_NARROWOOP = new BasicType();
+  public static final BasicType T_METADATA = new BasicType();
+  public static final BasicType T_NARROWKLASS = new BasicType();
+  public static final BasicType T_CONFLICT = new BasicType();
+  public static final BasicType T_ILLEGAL = new BasicType();
 
-  public static final BasicType T_BOOLEAN = new BasicType(tBoolean);
-  public static final BasicType T_CHAR = new BasicType(tChar);
-  public static final BasicType T_FLOAT = new BasicType(tFloat);
-  public static final BasicType T_DOUBLE = new BasicType(tDouble);
-  public static final BasicType T_BYTE = new BasicType(tByte);
-  public static final BasicType T_SHORT = new BasicType(tShort);
-  public static final BasicType T_INT = new BasicType(tInt);
-  public static final BasicType T_LONG = new BasicType(tLong);
-  public static final BasicType T_OBJECT = new BasicType(tObject);
-  public static final BasicType T_ARRAY = new BasicType(tArray);
-  public static final BasicType T_VOID = new BasicType(tVoid);
-  public static final BasicType T_ADDRESS = new BasicType(tAddress);
-  public static final BasicType T_NARROWOOP = new BasicType(tNarrowOop);
-  public static final BasicType T_METADATA = new BasicType(tMetadata);
-  public static final BasicType T_NARROWKLASS = new BasicType(tNarrowKlass);
-  public static final BasicType T_CONFLICT = new BasicType(tConflict);
-  public static final BasicType T_ILLEGAL = new BasicType(tIllegal);
+  static {
+    VM.registerVMInitializedObserver(
+        (o, d) -> initialize(VM.getVM().getTypeDataBase()));
+  }
+
+  private static synchronized void initialize(TypeDataBase db) {
+    T_BOOLEAN.setType(db.lookupIntConstant("T_BOOLEAN").intValue());
+    T_CHAR.setType(db.lookupIntConstant("T_CHAR").intValue());
+    T_FLOAT.setType(db.lookupIntConstant("T_FLOAT").intValue());
+    T_DOUBLE.setType(db.lookupIntConstant("T_DOUBLE").intValue());
+    T_BYTE.setType(db.lookupIntConstant("T_BYTE").intValue());
+    T_SHORT.setType(db.lookupIntConstant("T_SHORT").intValue());
+    T_INT.setType(db.lookupIntConstant("T_INT").intValue());
+    T_LONG.setType(db.lookupIntConstant("T_LONG").intValue());
+    T_OBJECT.setType(db.lookupIntConstant("T_OBJECT").intValue());
+    T_ARRAY.setType(db.lookupIntConstant("T_ARRAY").intValue());
+    T_VOID.setType(db.lookupIntConstant("T_VOID").intValue());
+    T_ADDRESS.setType(db.lookupIntConstant("T_ADDRESS").intValue());
+    T_NARROWOOP.setType(db.lookupIntConstant("T_NARROWOOP").intValue());
+    T_METADATA.setType(db.lookupIntConstant("T_METADATA").intValue());
+    T_NARROWKLASS.setType(db.lookupIntConstant("T_NARROWKLASS").intValue());
+    T_CONFLICT.setType(db.lookupIntConstant("T_CONFLICT").intValue());
+    T_ILLEGAL.setType(db.lookupIntConstant("T_ILLEGAL").intValue());
+  }
 
   public static int getTBoolean() {
-    return tBoolean;
+    return T_BOOLEAN.getType();
   }
 
   public static int getTChar() {
-    return tChar;
+    return T_CHAR.getType();
   }
 
   public static int getTFloat() {
-    return tFloat;
+    return T_FLOAT.getType();
   }
 
   public static int getTDouble() {
-    return tDouble;
+    return T_DOUBLE.getType();
   }
 
   public static int getTByte() {
-    return tByte;
+    return T_BYTE.getType();
   }
 
   public static int getTShort() {
-    return tShort;
+    return T_SHORT.getType();
   }
 
   public static int getTInt() {
-    return tInt;
+    return T_INT.getType();
   }
 
   public static int getTLong() {
-    return tLong;
+    return T_LONG.getType();
   }
 
   public static int getTObject() {
-    return tObject;
+    return T_OBJECT.getType();
   }
 
   public static int getTArray() {
-    return tArray;
+    return T_ARRAY.getType();
   }
 
   public static int getTVoid() {
-    return tVoid;
+    return T_VOID.getType();
   }
 
   public static int getTAddress() {
-    return tAddress;
+    return T_ADDRESS.getType();
   }
 
   public static int getTNarrowOop() {
-    return tNarrowOop;
+    return T_NARROWOOP.getType();
   }
 
   public static int getTMetadata() {
-    return tMetadata;
+    return T_METADATA.getType();
   }
 
   public static int getTNarrowKlass() {
-    return tNarrowKlass;
+    return T_NARROWKLASS.getType();
   }
 
   /** For stack value type with conflicting contents */
   public static int getTConflict() {
-    return tConflict;
+    return T_CONFLICT.getType();
   }
 
   public static int getTIllegal() {
-    return tIllegal;
+    return T_ILLEGAL.getType();
+  }
+
+  public static BasicType intToBasicType(int i) {
+    if (i == T_BOOLEAN.getType()) {
+      return T_BOOLEAN;
+    } else if (i == T_CHAR.getType()) {
+      return T_CHAR;
+    } else if (i == T_FLOAT.getType()) {
+      return T_FLOAT;
+    } else if (i == T_DOUBLE.getType()) {
+      return T_DOUBLE;
+    } else if (i == T_BYTE.getType()) {
+      return T_BYTE;
+    } else if (i == T_SHORT.getType()) {
+      return T_SHORT;
+    } else if (i == T_INT.getType()) {
+      return T_INT;
+    } else if (i == T_LONG.getType()) {
+      return T_LONG;
+    } else if (i == T_OBJECT.getType()) {
+      return T_OBJECT;
+    } else if (i == T_ARRAY.getType()) {
+      return T_ARRAY;
+    } else if (i == T_VOID.getType()) {
+      return T_VOID;
+    } else if (i == T_ADDRESS.getType()) {
+      return T_ADDRESS;
+    } else if (i == T_NARROWOOP.getType()) {
+      return T_NARROWOOP;
+    } else if (i == T_METADATA.getType()) {
+      return T_METADATA;
+    } else if (i == T_NARROWKLASS.getType()) {
+      return T_NARROWKLASS;
+    } else {
+      return T_ILLEGAL;
+    }
   }
 
   public static BasicType charToBasicType(char c) {
@@ -158,8 +205,49 @@ public class BasicType {
     return type;
   }
 
+  public String getName() {
+    if (type == T_BOOLEAN.getType()) {
+      return "boolean";
+    } else if (type == T_CHAR.getType()) {
+      return "char";
+    } else if (type == T_FLOAT.getType()) {
+      return "float";
+    } else if (type == T_DOUBLE.getType()) {
+      return "double";
+    } else if (type == T_BYTE.getType()) {
+      return "byte";
+    } else if (type == T_SHORT.getType()) {
+      return "short";
+    } else if (type == T_INT.getType()) {
+      return "int";
+    } else if (type == T_LONG.getType()) {
+      return "long";
+    } else if (type == T_OBJECT.getType()) {
+      return "object";
+    } else if (type == T_ARRAY.getType()) {
+      return "array";
+    } else if (type == T_VOID.getType()) {
+      return "void";
+    } else if (type == T_ADDRESS.getType()) {
+      return "address";
+    } else if (type == T_NARROWOOP.getType()) {
+      return "narrow oop";
+    } else if (type == T_METADATA.getType()) {
+      return "metadata";
+    } else if (type == T_NARROWKLASS.getType()) {
+      return "narrow klass";
+    } else if (type == T_CONFLICT.getType()) {
+      return "conflict";
+    } else {
+      return "ILLEGAL TYPE";
+    }
+  }
+
   //-- Internals only below this point
-  private BasicType(int type) {
+  private BasicType() {
+  }
+
+  private void setType(int type) {
     this.type = type;
   }
 
diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/BasicTypeSize.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/BasicTypeSize.java
index 2a18795ab99..e09d6f5e1ce 100644
--- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/BasicTypeSize.java
+++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/BasicTypeSize.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2001, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2001, 2017, 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
@@ -24,22 +24,48 @@
 
 package sun.jvm.hotspot.runtime;
 
+import java.util.Observer;
+import sun.jvm.hotspot.types.TypeDataBase;
+
+
 /** Encapsulates the BasicTypeSize enum in globalDefinitions.hpp in
     the VM. */
 
 public class BasicTypeSize {
-  private static boolean initialized = false;
-  private static int tBooleanSize = 1;
-  private static int tCharSize    = 1;
-  private static int tFloatSize   = 1;
-  private static int tDoubleSize  = 2;
-  private static int tByteSize    = 1;
-  private static int tShortSize   = 1;
-  private static int tIntSize     = 1;
-  private static int tLongSize    = 2;
-  private static int tObjectSize  = 1;
-  private static int tArraySize   = 1;
-  private static int tVoidSize    = 0;
+  private static int tBooleanSize;
+  private static int tCharSize;
+  private static int tFloatSize;
+  private static int tDoubleSize;
+  private static int tByteSize;
+  private static int tShortSize;
+  private static int tIntSize;
+  private static int tLongSize;
+  private static int tObjectSize;
+  private static int tArraySize;
+  private static int tNarrowOopSize;
+  private static int tNarrowKlassSize;
+  private static int tVoidSize;
+
+  static {
+    VM.registerVMInitializedObserver(
+        (o, d) -> initialize(VM.getVM().getTypeDataBase()));
+  }
+
+  private static synchronized void initialize(TypeDataBase db) {
+    tBooleanSize     = db.lookupIntConstant("T_BOOLEAN_size").intValue();
+    tCharSize        = db.lookupIntConstant("T_INT_size").intValue();
+    tFloatSize       = db.lookupIntConstant("T_FLOAT_size").intValue();
+    tDoubleSize      = db.lookupIntConstant("T_DOUBLE_size").intValue();
+    tByteSize        = db.lookupIntConstant("T_BYTE_size").intValue();
+    tShortSize       = db.lookupIntConstant("T_SHORT_size").intValue();
+    tIntSize         = db.lookupIntConstant("T_INT_size").intValue();
+    tLongSize        = db.lookupIntConstant("T_LONG_size").intValue();
+    tObjectSize      = db.lookupIntConstant("T_OBJECT_size").intValue();
+    tArraySize       = db.lookupIntConstant("T_ARRAY_size").intValue();
+    tNarrowOopSize   = db.lookupIntConstant("T_NARROWOOP_size").intValue();
+    tNarrowKlassSize = db.lookupIntConstant("T_NARROWKLASS_size").intValue();
+    tVoidSize        = db.lookupIntConstant("T_VOID_size").intValue();
+  }
 
   public static int getTBooleanSize() {
     return tBooleanSize;
@@ -81,6 +107,14 @@ public class BasicTypeSize {
     return tArraySize;
   }
 
+  public static int getTNarrowOopSize() {
+    return tNarrowOopSize;
+  }
+
+  public static int getTNarrowKlassSize() {
+    return tNarrowKlassSize;
+  }
+
   public static int getTVoidSize() {
     return tVoidSize;
   }
diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/CompiledVFrame.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/CompiledVFrame.java
index ba42e8da19f..a2d67b3fe3e 100644
--- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/CompiledVFrame.java
+++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/CompiledVFrame.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2000, 2009, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2000, 2017, 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
@@ -127,12 +127,12 @@ public class CompiledVFrame extends JavaVFrame {
   }
 
   /** Returns List<MonitorInfo> */
-  public List   getMonitors() {
+  public List<MonitorInfo> getMonitors() {
     List monitors = getScope().getMonitors();
     if (monitors == null) {
-      return new ArrayList();
+      return new ArrayList<>();
     }
-    List result = new ArrayList(monitors.size());
+    List<MonitorInfo> result = new ArrayList<>(monitors.size());
     for (int i = 0; i < monitors.size(); i++) {
       MonitorValue mv = (MonitorValue) monitors.get(i);
       ScopeValue ov = mv.owner();
diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/InterpretedVFrame.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/InterpretedVFrame.java
index 180de93a366..75750548ef5 100644
--- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/InterpretedVFrame.java
+++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/InterpretedVFrame.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2000, 2009, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2000, 2017, 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
@@ -108,8 +108,8 @@ public class InterpretedVFrame extends JavaVFrame {
   }
 
   /** Returns List<MonitorInfo> */
-  public List   getMonitors() {
-    List result = new ArrayList(5);
+  public List<MonitorInfo> getMonitors() {
+    List<MonitorInfo> result = new ArrayList<>(5);
     for (BasicObjectLock current = getFrame().interpreterFrameMonitorEnd();
          current.address().lessThan(getFrame().interpreterFrameMonitorBegin().address());
          current = getFrame().nextMonitorInInterpreterFrame(current)) {
diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/JavaVFrame.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/JavaVFrame.java
index a45cbc3640c..5486d6c2bc3 100644
--- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/JavaVFrame.java
+++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/JavaVFrame.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2000, 2007, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2000, 2017, 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,14 +28,19 @@ import java.io.*;
 import java.util.*;
 import sun.jvm.hotspot.oops.*;
 import sun.jvm.hotspot.utilities.*;
+import sun.jvm.hotspot.debugger.*;
 
 public abstract class JavaVFrame extends VFrame {
+
+  private static final String ADDRESS_FORMAT = VM.getVM().isLP64() ? "0x%016x"
+                                                                   : "0x%08x";
+
   /** JVM state */
   public abstract Method getMethod();
   public abstract int    getBCI();
   public abstract StackValueCollection getLocals();
   public abstract StackValueCollection getExpressions();
-  public abstract List   getMonitors();    // List<MonitorInfo>
+  public abstract List<MonitorInfo> getMonitors();
 
   /** Test operation */
   public boolean isJavaFrame() { return true; }
@@ -49,9 +54,112 @@ public abstract class JavaVFrame extends VFrame {
   // FIXME: not yet implemented
   //  public Address getPendingMonitor(int frameCount);
 
+  public void printLockedObjectClassName(PrintStream tty,
+                                         OopHandle hobj, String lockState) {
+    if (hobj.asLongValue() != 0L) {
+      tty.format("\t- %s <" + ADDRESS_FORMAT + "> ",
+                 lockState, hobj.asLongValue());
+
+      Klass klass = Oop.getKlassForOopHandle(hobj);
+      String klassName = klass.getName().asString();
+      tty.print("(a ");
+      if (klassName.equals("java/lang/Class")) {
+        Oop obj = VM.getVM().getObjectHeap().newOop(hobj);
+        klassName = java_lang_Class.asExternalName(obj);
+        tty.print("java.lang.Class for ");
+      }
+      tty.println(klassName.replace('/', '.') + ")");
+    }
+  }
+
+  private String identifyLockState(MonitorInfo monitor, String waitingState) {
+    Mark mark = new Mark(monitor.owner());
+    if (mark.hasMonitor() &&
+        ( // we have marked ourself as pending on this monitor
+          mark.monitor().equals(thread.getCurrentPendingMonitor()) ||
+          // we are not the owner of this monitor
+          !mark.monitor().isEntered(thread)
+        )) {
+      return waitingState;
+    }
+    return "locked";
+  }
+
   /** Printing used during stack dumps */
-  // FIXME: not yet implemented
-  //  void print_lock_info(int frame_count);
+  public void printLockInfo(PrintStream tty, int frameCount) {
+    // If this is the first frame and it is java.lang.Object.wait(...)
+    // then print out the receiver. Locals are not always available,
+    // e.g., compiled native frames have no scope so there are no locals.
+    if (frameCount == 0) {
+      if (getMethod().getName().asString().equals("wait") &&
+          getMethod().getMethodHolder().getName().asString().equals("java/lang/Object")) {
+        String waitState = "waiting on"; // assume we are waiting
+        // If earlier in the output we reported java.lang.Thread.State ==
+        // "WAITING (on object monitor)" and now we report "waiting on", then
+        // we are still waiting for notification or timeout. Otherwise if
+        // we earlier reported java.lang.Thread.State == "BLOCKED (on object
+        // monitor)", then we are actually waiting to re-lock the monitor.
+        // At this level we can't distinguish the two cases to report
+        // "waited on" rather than "waiting on" for the second case.
+        StackValueCollection locs = getLocals();
+        if (!locs.isEmpty()) {
+          StackValue sv = locs.get(0);
+          if (sv.getType() == BasicType.getTObject()) {
+            OopHandle o = sv.getObject();
+            printLockedObjectClassName(tty, o, waitState);
+          }
+        } else {
+          tty.println("\t- " + waitState + " <no object reference available>");
+        }
+      } else if (thread.getCurrentParkBlocker() != null) {
+        Oop obj = thread.getCurrentParkBlocker();
+        Klass k = obj.getKlass();
+        tty.format("\t- parking to wait for <" + ADDRESS_FORMAT + "> (a %s)",
+                   obj.getHandle().asLongValue(), k.getName().asString());
+        tty.println();
+      }
+    }
+
+    // Print out all monitors that we have locked, or are trying to lock,
+    // including re-locking after being notified or timing out in a wait().
+    List<MonitorInfo> mons = getMonitors();
+    if (!mons.isEmpty()) {
+      boolean foundFirstMonitor = false;
+      for (int index = mons.size() - 1; index >= 0; index--) {
+        MonitorInfo monitor = mons.get(index);
+        if (monitor.eliminated() && isCompiledFrame()) { // Eliminated in compiled code
+          if (monitor.ownerIsScalarReplaced()) {
+            Klass k = Oop.getKlassForOopHandle(monitor.ownerKlass());
+            tty.println("\t- eliminated <owner is scalar replaced> (a " + k.getName().asString() + ")");
+          } else if (monitor.owner() != null) {
+            printLockedObjectClassName(tty, monitor.owner(), "eliminated");
+          }
+          continue;
+        }
+        if (monitor.owner() != null) {
+          // the monitor is associated with an object, i.e., it is locked
+          String lockState = "locked";
+          if (!foundFirstMonitor && frameCount == 0) {
+            // If this is the first frame and we haven't found an owned
+            // monitor before, then we need to see if we have completed
+            // the lock or if we are blocked trying to acquire it. Only
+            // an inflated monitor that is first on the monitor list in
+            // the first frame can block us on a monitor enter.
+            lockState = identifyLockState(monitor, "waiting to lock");
+          } else if (frameCount != 0) {
+            // This is not the first frame so we either own this monitor
+            // or we owned the monitor before and called wait(). Because
+            // wait() could have been called on any monitor in a lower
+            // numbered frame on the stack, we have to check all the
+            // monitors on the list for this frame.
+            lockState = identifyLockState(monitor, "waiting to re-lock in wait()");
+          }
+          printLockedObjectClassName(tty, monitor.owner(), lockState);
+          foundFirstMonitor = true;
+        }
+      }
+    }
+  }
 
   /** Printing operations */
 
@@ -73,22 +181,6 @@ public abstract class JavaVFrame extends VFrame {
 
     printStackValuesOn(tty, "locals",      getLocals());
     printStackValuesOn(tty, "expressions", getExpressions());
-
-    // List<MonitorInfo>
-    // FIXME: not yet implemented
-    //    List list = getMonitors();
-    //    if (list.isEmpty()) {
-    //      return;
-    //    }
-    //    for (int index = 0; index < list.size(); index++) {
-    //      MonitorInfo monitor = (MonitorInfo) list.get(index);
-    //      tty.print("\t  obj\t");
-    //      monitor.getOwner().printValueOn(tty);
-    //      tty.println();
-    //      tty.print("\t  ");
-    //      monitor.lock().printOn(tty);
-    //      tty.println();
-    //    }
   }
 
   public void printActivation(int index) {
diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/ObjectSynchronizer.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/ObjectSynchronizer.java
index aee92a4f654..e5f72b0783c 100644
--- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/ObjectSynchronizer.java
+++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/ObjectSynchronizer.java
@@ -44,9 +44,7 @@ public class ObjectSynchronizer {
     Type type;
     try {
       type = db.lookupType("ObjectSynchronizer");
-      AddressField blockListField;
-      blockListField = type.getAddressField("gBlockList");
-      gBlockListAddr = blockListField.getValue();
+      gBlockList = type.getAddressField("gBlockList").getValue();
       blockSize = db.lookupIntConstant("ObjectSynchronizer::_BLOCKSIZE").intValue();
       defaultCacheLineSize = db.lookupIntConstant("DEFAULT_CACHE_LINE_SIZE").intValue();
     } catch (RuntimeException e) { }
@@ -84,7 +82,7 @@ public class ObjectSynchronizer {
   }
 
   public static Iterator objectMonitorIterator() {
-    if (gBlockListAddr != null) {
+    if (gBlockList != null) {
       return new ObjectMonitorIterator();
     } else {
       return null;
@@ -97,7 +95,7 @@ public class ObjectSynchronizer {
     // and are not included by this Iterator. May add them later.
 
     ObjectMonitorIterator() {
-      blockAddr = gBlockListAddr;
+      blockAddr = gBlockList;
       index = blockSize - 1;
       block = new ObjectMonitor(blockAddr);
     }
@@ -131,7 +129,7 @@ public class ObjectSynchronizer {
     private Address blockAddr;
   }
 
-  private static Address gBlockListAddr;
+  private static Address gBlockList;
   private static int blockSize;
   private static int defaultCacheLineSize;
   private static long objectMonitorTypeSize;
diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/PerfDataEntry.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/PerfDataEntry.java
index 5d16b7026b5..a8da801532d 100644
--- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/PerfDataEntry.java
+++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/PerfDataEntry.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2004, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2004, 2017, 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
@@ -92,14 +92,29 @@ public class PerfDataEntry extends VMObject {
         return (flags() & 0x1) != 0;
     }
 
-    // NOTE: Keep this in sync with PerfData::Units enum in VM code
-    public interface PerfDataUnits {
-        public static final int U_None   = 1;
-        public static final int U_Bytes  = 2;
-        public static final int U_Ticks  = 3;
-        public static final int U_Events = 4;
-        public static final int U_String = 5;
-        public static final int U_Hertz  = 6;
+    private static class PerfDataUnits {
+        public static int U_None;
+        public static int U_Bytes;
+        public static int U_Ticks;
+        public static int U_Events;
+        public static int U_String;
+        public static int U_Hertz;
+
+        static {
+            VM.registerVMInitializedObserver(new Observer() {
+                public void update(Observable o, Object data) {
+                    initialize(VM.getVM().getTypeDataBase());
+                }
+            });
+        }
+        private static synchronized void initialize(TypeDataBase db) {
+            U_None = db.lookupIntConstant("PerfData::U_None");
+            U_Bytes = db.lookupIntConstant("PerfData::U_Bytes");
+            U_Ticks = db.lookupIntConstant("PerfData::U_Ticks");
+            U_Events = db.lookupIntConstant("PerfData::U_Events");
+            U_String = db.lookupIntConstant("PerfData::U_String");
+            U_Hertz = db.lookupIntConstant("PerfData::U_Hertz");
+        }
     }
 
     // returns one of the constants in PerfDataUnits
@@ -107,13 +122,6 @@ public class PerfDataEntry extends VMObject {
         return (int) dataUnitsField.getValue(addr);
     }
 
-    // NOTE: Keep this in sync with PerfData::Variability enum in VM code
-    public interface PerfDataVariability {
-        public static final int V_Constant  = 1;
-        public static final int V_Monotonic = 2;
-        public static final int V_Variable  = 3;
-    }
-
     // returns one of the constants in PerfDataVariability
     public int dataVariability() {
         return (int) dataVariabilityField.getValue(addr);
@@ -131,7 +139,7 @@ public class PerfDataEntry extends VMObject {
     public boolean booleanValue() {
         if (Assert.ASSERTS_ENABLED) {
             Assert.that(vectorLength() == 0 &&
-                        dataType() == BasicType.tBoolean, "not a boolean");
+                        dataType() == BasicType.getTBoolean(), "not a boolean");
         }
         return addr.getJBooleanAt(dataOffset());
     }
@@ -139,7 +147,7 @@ public class PerfDataEntry extends VMObject {
     public char charValue() {
         if (Assert.ASSERTS_ENABLED) {
             Assert.that(vectorLength() == 0 &&
-                        dataType() == BasicType.tChar, "not a char");
+                        dataType() == BasicType.getTChar(), "not a char");
         }
         return addr.getJCharAt(dataOffset());
     }
@@ -147,7 +155,7 @@ public class PerfDataEntry extends VMObject {
     public byte byteValue() {
         if (Assert.ASSERTS_ENABLED) {
             Assert.that(vectorLength() == 0 &&
-                        dataType() == BasicType.tByte, "not a byte");
+                        dataType() == BasicType.getTByte(), "not a byte");
         }
         return addr.getJByteAt(dataOffset());
 
@@ -156,7 +164,7 @@ public class PerfDataEntry extends VMObject {
     public short shortValue() {
         if (Assert.ASSERTS_ENABLED) {
             Assert.that(vectorLength() == 0 &&
-                        dataType() == BasicType.tShort, "not a short");
+                        dataType() == BasicType.getTShort(), "not a short");
         }
         return addr.getJShortAt(dataOffset());
     }
@@ -164,7 +172,7 @@ public class PerfDataEntry extends VMObject {
     public int intValue() {
         if (Assert.ASSERTS_ENABLED) {
             Assert.that(vectorLength() == 0 &&
-                        dataType() == BasicType.tInt, "not an int");
+                        dataType() == BasicType.getTInt(), "not an int");
         }
         return addr.getJIntAt(dataOffset());
     }
@@ -172,7 +180,7 @@ public class PerfDataEntry extends VMObject {
     public long longValue() {
         if (Assert.ASSERTS_ENABLED) {
             Assert.that(vectorLength() == 0 &&
-                        dataType() == BasicType.tLong, "not a long");
+                        dataType() == BasicType.getTLong(), "not a long");
         }
         return addr.getJLongAt(dataOffset());
     }
@@ -180,7 +188,7 @@ public class PerfDataEntry extends VMObject {
     public float floatValue() {
         if (Assert.ASSERTS_ENABLED) {
             Assert.that(vectorLength() == 0 &&
-                        dataType() == BasicType.tFloat, "not a float");
+                        dataType() == BasicType.getTFloat(), "not a float");
         }
         return addr.getJFloatAt(dataOffset());
     }
@@ -188,7 +196,7 @@ public class PerfDataEntry extends VMObject {
     public double doubleValue() {
         if (Assert.ASSERTS_ENABLED) {
             Assert.that(vectorLength() == 0 &&
-                        dataType() == BasicType.tDouble, "not a double");
+                        dataType() == BasicType.getTDouble(), "not a double");
         }
         return addr.getJDoubleAt(dataOffset());
     }
@@ -197,7 +205,7 @@ public class PerfDataEntry extends VMObject {
         int len = vectorLength();
         if (Assert.ASSERTS_ENABLED) {
             Assert.that(len > 0 &&
-                        dataType() == BasicType.tBoolean, "not a boolean vector");
+                        dataType() == BasicType.getTBoolean(), "not a boolean vector");
         }
         boolean[] res = new boolean[len];
         final int off = dataOffset();
@@ -212,7 +220,7 @@ public class PerfDataEntry extends VMObject {
         int len = vectorLength();
         if (Assert.ASSERTS_ENABLED) {
             Assert.that(len > 0 &&
-                        dataType() == BasicType.tChar, "not a char vector");
+                        dataType() == BasicType.getTChar(), "not a char vector");
         }
         char[] res = new char[len];
         final int off = dataOffset();
@@ -227,7 +235,7 @@ public class PerfDataEntry extends VMObject {
         int len = vectorLength();
         if (Assert.ASSERTS_ENABLED) {
             Assert.that(len > 0 &&
-                        dataType() == BasicType.tByte, "not a byte vector");
+                        dataType() == BasicType.getTByte(), "not a byte vector");
         }
         byte[] res = new byte[len];
         final int off = dataOffset();
@@ -242,7 +250,7 @@ public class PerfDataEntry extends VMObject {
         int len = vectorLength();
         if (Assert.ASSERTS_ENABLED) {
             Assert.that(len > 0 &&
-                        dataType() == BasicType.tShort, "not a short vector");
+                        dataType() == BasicType.getTShort(), "not a short vector");
         }
         short[] res = new short[len];
         final int off = dataOffset();
@@ -257,7 +265,7 @@ public class PerfDataEntry extends VMObject {
         int len = vectorLength();
         if (Assert.ASSERTS_ENABLED) {
             Assert.that(len > 0 &&
-                        dataType() == BasicType.tInt, "not an int vector");
+                        dataType() == BasicType.getTInt(), "not an int vector");
         }
         int[] res = new int[len];
         final int off = dataOffset();
@@ -272,7 +280,7 @@ public class PerfDataEntry extends VMObject {
         int len = vectorLength();
         if (Assert.ASSERTS_ENABLED) {
             Assert.that(len > 0 &&
-                        dataType() == BasicType.tLong, "not a long vector");
+                        dataType() == BasicType.getTLong(), "not a long vector");
         }
         long[] res = new long[len];
         final int off = dataOffset();
@@ -287,7 +295,7 @@ public class PerfDataEntry extends VMObject {
         int len = vectorLength();
         if (Assert.ASSERTS_ENABLED) {
             Assert.that(len > 0 &&
-                        dataType() == BasicType.tFloat, "not a float vector");
+                        dataType() == BasicType.getTFloat(), "not a float vector");
         }
         float[] res = new float[len];
         final int off = dataOffset();
@@ -302,7 +310,7 @@ public class PerfDataEntry extends VMObject {
         int len = vectorLength();
         if (Assert.ASSERTS_ENABLED) {
             Assert.that(len > 0 &&
-                        dataType() == BasicType.tDouble, "not a double vector");
+                        dataType() == BasicType.getTDouble(), "not a double vector");
         }
         double[] res = new double[len];
         final int off = dataOffset();
@@ -319,38 +327,27 @@ public class PerfDataEntry extends VMObject {
         int len = vectorLength();
         String str = null;
         if (len == 0) { // scalar
-            switch (dataType) {
-            case BasicType.tBoolean:
+            if (dataType == BasicType.getTBoolean()) {
                 str = Boolean.toString(booleanValue());
-                break;
-            case BasicType.tChar:
+            } else if (dataType == BasicType.getTChar()) {
                 str = "'" + Character.toString(charValue()) + "'";
-                break;
-            case BasicType.tByte:
+            } else if (dataType == BasicType.getTByte()) {
                 str = Byte.toString(byteValue());
-                break;
-            case BasicType.tShort:
+            } else if (dataType == BasicType.getTShort()) {
                 str = Short.toString(shortValue());
-                break;
-            case BasicType.tInt:
+            } else if (dataType ==  BasicType.getTInt()) {
                 str = Integer.toString(intValue());
-                break;
-            case BasicType.tLong:
+            } else if (dataType == BasicType.getTLong()) {
                 str = Long.toString(longValue());
-                break;
-            case BasicType.tFloat:
+            } else if (dataType == BasicType.getTFloat()) {
                 str = Float.toString(floatValue());
-                break;
-            case BasicType.tDouble:
+            } else if (dataType == BasicType.getTDouble()) {
                 str = Double.toString(doubleValue());
-                break;
-            default:
+            } else {
                 str = "<unknown scalar value>";
-                break;
             }
         } else { // vector
-            switch (dataType) {
-            case BasicType.tBoolean: {
+            if (dataType == BasicType.getTBoolean()) {
                 boolean[] res = booleanArrayValue();
                 StringBuffer buf = new StringBuffer();
                 buf.append('[');
@@ -360,26 +357,17 @@ public class PerfDataEntry extends VMObject {
                 }
                 buf.append(']');
                 str = buf.toString();
-                break;
-            }
-
-            case BasicType.tChar: {
+            } else if (dataType == BasicType.getTChar()) {
                 // char[] is returned as a String
                 str = new String(charArrayValue());
-                break;
-            }
-
-            case BasicType.tByte: {
+            } else if (dataType == BasicType.getTByte()) {
                 // byte[] is returned as a String
                 try {
                     str = new String(byteArrayValue(), "US-ASCII");
                 } catch (java.io.UnsupportedEncodingException e) {
                     str = "can't decode string : " + e.getMessage();
                 }
-                break;
-            }
-
-            case BasicType.tShort: {
+            } else if (dataType == BasicType.getTShort()) {
                 short[] res = shortArrayValue();
                 StringBuffer buf = new StringBuffer();
                 buf.append('[');
@@ -389,10 +377,7 @@ public class PerfDataEntry extends VMObject {
                 }
                 buf.append(']');
                 str = buf.toString();
-                break;
-            }
-
-            case BasicType.tInt: {
+            } else if (dataType ==  BasicType.getTInt()) {
                 int[] res = intArrayValue();
                 StringBuffer buf = new StringBuffer();
                 buf.append('[');
@@ -402,10 +387,7 @@ public class PerfDataEntry extends VMObject {
                 }
                 buf.append(']');
                 str = buf.toString();
-                break;
-            }
-
-            case BasicType.tLong: {
+            } else if (dataType == BasicType.getTLong()) {
                 long[] res = longArrayValue();
                 StringBuffer buf = new StringBuffer();
                 buf.append('[');
@@ -415,10 +397,7 @@ public class PerfDataEntry extends VMObject {
                 }
                 buf.append(']');
                 str = buf.toString();
-                break;
-            }
-
-            case BasicType.tFloat: {
+            } else if (dataType == BasicType.getTFloat()) {
                 float[] res = floatArrayValue();
                 StringBuffer buf = new StringBuffer();
                 buf.append('[');
@@ -428,10 +407,7 @@ public class PerfDataEntry extends VMObject {
                 }
                 buf.append(']');
                 str = buf.toString();
-                break;
-            }
-
-            case BasicType.tDouble: {
+            } else if (dataType == BasicType.getTDouble()) {
                 double[] res = doubleArrayValue();
                 StringBuffer buf = new StringBuffer();
                 buf.append('[');
@@ -441,33 +417,22 @@ public class PerfDataEntry extends VMObject {
                 }
                 buf.append(']');
                 str = buf.toString();
-                break;
-            }
-
-            default:
+            } else {
                 str = "<unknown vector value>";
-                break;
             }
         }
 
         // add units
-        switch (dataUnits()) {
-        case PerfDataUnits.U_None:
-            break;
-        case PerfDataUnits.U_Bytes:
+        int dataUnitsValue = dataUnits();
+
+        if (dataUnitsValue == PerfDataUnits.U_Bytes) {
             str += " byte(s)";
-            break;
-        case PerfDataUnits.U_Ticks:
+        } else if (dataUnitsValue == PerfDataUnits.U_Ticks) {
             str += " tick(s)";
-            break;
-        case PerfDataUnits.U_Events:
+        } else if (dataUnitsValue == PerfDataUnits.U_Events) {
             str += " event(s)";
-            break;
-        case PerfDataUnits.U_String:
-            break;
-        case PerfDataUnits.U_Hertz:
+        } else if (dataUnitsValue == PerfDataUnits.U_Hertz) {
             str += " Hz";
-            break;
         }
 
         return str;
diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/VFrame.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/VFrame.java
index 2506d417371..f4d7827254c 100644
--- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/VFrame.java
+++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/VFrame.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2000, 2006, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2000, 2017, 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
@@ -117,10 +117,7 @@ public class VFrame {
       return null;
     }
     Frame s = fr.realSender(tempMap);
-    // ia64 in 1.4.1 only has java frames and no entryFrame
-    // so "s" can be null here for the first frame.
     if (s == null) {
-      Assert.that(VM.getVM().getCPU().equals("ia64"), "Only ia64 should have null here");
       return null;
     }
     if (s.isFirstFrame()) {
diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/tools/StackTrace.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/tools/StackTrace.java
index 7774fb68df0..0072a5bf64b 100644
--- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/tools/StackTrace.java
+++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/tools/StackTrace.java
@@ -76,6 +76,8 @@ public class StackTrace extends Tool {
                 if (cur.isJavaThread()) {
                     cur.printThreadInfoOn(tty);
                     try {
+                        int count = 0;
+
                         for (JavaVFrame vf = cur.getLastJavaVFrameDbg(); vf != null; vf = vf.javaSender()) {
                             Method method = vf.getMethod();
                             tty.print(" - " + method.externalNameAndSignature() +
@@ -109,6 +111,7 @@ public class StackTrace extends Tool {
                             }
 
                             tty.println(")");
+                            vf.printLockInfo(tty, count++);
                         }
                     } catch (Exception e) {
                         tty.println("Error occurred during stack walking:");
diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/tools/jcore/PackageNameFilter.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/tools/jcore/PackageNameFilter.java
index 1701c11b02d..e0312ff5a4d 100644
--- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/tools/jcore/PackageNameFilter.java
+++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/tools/jcore/PackageNameFilter.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2002, 2005, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2002, 2017, 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
@@ -50,10 +50,14 @@ public class PackageNameFilter implements ClassFilter
     }
 
     public boolean canInclude(InstanceKlass kls) {
-        String klassName = kls.getName().asString().replace('/', '.');
+        if (pkgList == null) {
+            // Dump everything
+            return true;
+        }
         final int len = pkgList.length;
         if (len == 0)
             return true;
+        String klassName = kls.getName().asString().replace('/', '.');
         for (int i=0; i < len; i++)
             if (klassName.startsWith((String) pkgList[i] )) return true;
         return false;
diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/ui/classbrowser/HTMLGenerator.java b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/ui/classbrowser/HTMLGenerator.java
index d2d639214af..f37d15e696f 100644
--- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/ui/classbrowser/HTMLGenerator.java
+++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/ui/classbrowser/HTMLGenerator.java
@@ -1910,6 +1910,7 @@ public class HTMLGenerator implements /* imports */ ClassConstants {
       buf.append(thread.getThreadState().toString());
       buf.br();
       buf.beginTag("pre");
+      int count = 0;
       for (JavaVFrame vf = thread.getLastJavaVFrameDbg(); vf != null; vf = vf.javaSender()) {
          Method method = vf.getMethod();
          buf.append(" - ");
@@ -1954,6 +1955,19 @@ public class HTMLGenerator implements /* imports */ ClassConstants {
          }
          buf.append(")");
          buf.br();
+
+         ByteArrayOutputStream bytes = new ByteArrayOutputStream();
+         PrintStream printStream = new PrintStream(bytes);
+         try (printStream) {
+             vf.printLockInfo(printStream, count++);
+             for (String line : bytes.toString().split("\n")) {
+                 if (genHTML) {
+                     line = line.replace("<", "&lt;").replace(">", "&gt;");
+                 }
+                 buf.append(line);
+                 buf.br();
+             }
+         }
       }
 
       buf.endTag("pre");
diff --git a/src/jdk.hotspot.agent/solaris/native/libsaproc/libproc.h b/src/jdk.hotspot.agent/solaris/native/libsaproc/libproc.h
index a91f72a7e31..b926ad94102 100644
--- a/src/jdk.hotspot.agent/solaris/native/libsaproc/libproc.h
+++ b/src/jdk.hotspot.agent/solaris/native/libsaproc/libproc.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2002, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2002, 2017, 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,14 +77,14 @@ extern  int     _libproc_debug; /* set non-zero to enable debugging fprintfs */
 typedef uint32_t syscall_t;     /* holds a syscall instruction */
 #endif  /* sparc */
 
-#if defined(__i386) || defined(__ia64)
+#if defined(__i386)
 #define R_PC    EIP
 #define R_SP    UESP
 #define R_RVAL1 EAX             /* register holding a function return value */
 #define R_RVAL2 EDX             /* 32 more bits for a 64-bit return value */
 #define SYSCALL 0x9a            /* syscall (lcall) instruction opcode */
 typedef uchar_t syscall_t[7];   /* holds a syscall instruction */
-#endif  /* __i386 || __ia64 */
+#endif  /* __i386 */
 
 #define R_RVAL  R_RVAL1         /* simple function return value register */
 
diff --git a/src/jdk.httpserver/share/classes/sun/net/httpserver/ServerImpl.java b/src/jdk.httpserver/share/classes/sun/net/httpserver/ServerImpl.java
index 10c3539b84b..ee758e4da94 100644
--- a/src/jdk.httpserver/share/classes/sun/net/httpserver/ServerImpl.java
+++ b/src/jdk.httpserver/share/classes/sun/net/httpserver/ServerImpl.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2005, 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2005, 2017, 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
@@ -551,9 +551,11 @@ class ServerImpl implements TimeSource {
                 requestLine = req.requestLine();
                 if (requestLine == null) {
                     /* connection closed */
+                    logger.log(Level.DEBUG, "no request line: closing");
                     closeConnection(connection);
                     return;
                 }
+                logger.log(Level.DEBUG, "Exchange request line: {0}", requestLine);
                 int space = requestLine.indexOf (' ');
                 if (space == -1) {
                     reject (Code.HTTP_BAD_REQUEST,
@@ -797,7 +799,8 @@ class ServerImpl implements TimeSource {
     // fashion.
 
     void requestCompleted (HttpConnection c) {
-        assert c.getState() == State.REQUEST;
+        State s = c.getState();
+        assert s == State.REQUEST : "State is not REQUEST ("+s+")";
         reqConnections.remove (c);
         c.rspStartedTime = getTime();
         rspConnections.add (c);
@@ -806,7 +809,8 @@ class ServerImpl implements TimeSource {
 
     // called after response has been sent
     void responseCompleted (HttpConnection c) {
-        assert c.getState() == State.RESPONSE;
+        State s = c.getState();
+        assert s == State.RESPONSE : "State is not RESPONSE ("+s+")";
         rspConnections.remove (c);
         c.setState (State.IDLE);
     }
diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/AbstractAsyncSSLConnection.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/AbstractAsyncSSLConnection.java
index 07223e61f25..67dd2aa5023 100644
--- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/AbstractAsyncSSLConnection.java
+++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/AbstractAsyncSSLConnection.java
@@ -28,9 +28,19 @@ package jdk.incubator.http;
 import java.io.IOException;
 import java.net.InetSocketAddress;
 import java.nio.ByteBuffer;
+import java.nio.channels.SocketChannel;
+import java.util.Arrays;
+import java.util.List;
 import java.util.concurrent.CompletableFuture;
+import javax.net.ssl.SNIHostName;
+import javax.net.ssl.SSLContext;
 import javax.net.ssl.SSLEngine;
-import jdk.incubator.http.internal.common.ExceptionallyCloseable;
+import javax.net.ssl.SSLEngineResult;
+import javax.net.ssl.SSLParameters;
+
+import jdk.incubator.http.internal.common.SSLTube;
+import jdk.incubator.http.internal.common.Log;
+import jdk.incubator.http.internal.common.Utils;
 
 
 /**
@@ -52,34 +62,127 @@ import jdk.incubator.http.internal.common.ExceptionallyCloseable;
  *
  */
 abstract class AbstractAsyncSSLConnection extends HttpConnection
-               implements AsyncConnection, ExceptionallyCloseable {
+{
+    protected final SSLEngine engine;
+    protected final String serverName;
+    protected final SSLParameters sslParameters;
 
-
-    AbstractAsyncSSLConnection(InetSocketAddress addr, HttpClientImpl client) {
+    AbstractAsyncSSLConnection(InetSocketAddress addr,
+                               HttpClientImpl client,
+                               String serverName,
+                               String[] alpn) {
         super(addr, client);
+        this.serverName = serverName;
+        SSLContext context = client.theSSLContext();
+        sslParameters = createSSLParameters(client, serverName, alpn);
+        Log.logParams(sslParameters);
+        engine = createEngine(context, sslParameters);
     }
 
-    abstract SSLEngine getEngine();
-    abstract AsyncSSLDelegate sslDelegate();
     abstract HttpConnection plainConnection();
-    abstract HttpConnection downgrade();
+    abstract SSLTube getConnectionFlow();
+
+    final CompletableFuture<String> getALPN() {
+        assert connected();
+        return getConnectionFlow().getALPN();
+    }
+
+    final SSLEngine getEngine() { return engine; }
+
+    private static SSLParameters createSSLParameters(HttpClientImpl client,
+                                                     String serverName,
+                                                     String[] alpn) {
+        SSLParameters sslp = client.sslParameters();
+        SSLParameters sslParameters = Utils.copySSLParameters(sslp);
+        if (alpn != null) {
+            Log.logSSL("AbstractAsyncSSLConnection: Setting application protocols: {0}",
+                       Arrays.toString(alpn));
+            sslParameters.setApplicationProtocols(alpn);
+        } else {
+            Log.logSSL("AbstractAsyncSSLConnection: no applications set!");
+        }
+        if (serverName != null) {
+            sslParameters.setServerNames(List.of(new SNIHostName(serverName)));
+        }
+        return sslParameters;
+    }
+
+    private static SSLEngine createEngine(SSLContext context,
+                                          SSLParameters sslParameters) {
+        SSLEngine engine = context.createSSLEngine();
+        engine.setUseClientMode(true);
+        engine.setSSLParameters(sslParameters);
+        return engine;
+    }
 
     @Override
     final boolean isSecure() {
         return true;
     }
 
-    // Blocking read functions not used here
-    @Override
-    protected final ByteBuffer readImpl() throws IOException {
-        throw new UnsupportedOperationException("Not supported.");
+    // Support for WebSocket/RawChannelImpl which unfortunately
+    // still depends on synchronous read/writes.
+    // It should be removed when RawChannelImpl moves to using asynchronous APIs.
+    static final class SSLConnectionChannel extends DetachedConnectionChannel {
+        final DetachedConnectionChannel delegate;
+        final SSLDelegate sslDelegate;
+        SSLConnectionChannel(DetachedConnectionChannel delegate, SSLDelegate sslDelegate) {
+            this.delegate = delegate;
+            this.sslDelegate = sslDelegate;
+        }
+
+        SocketChannel channel() {
+            return delegate.channel();
+        }
+
+        @Override
+        ByteBuffer read() throws IOException {
+            SSLDelegate.WrapperResult r = sslDelegate.recvData(ByteBuffer.allocate(8192));
+            // TODO: check for closure
+            int n = r.result.bytesProduced();
+            if (n > 0) {
+                return r.buf;
+            } else if (n == 0) {
+                return Utils.EMPTY_BYTEBUFFER;
+            } else {
+                return null;
+            }
+        }
+        @Override
+        long write(ByteBuffer[] buffers, int start, int number) throws IOException {
+            long l = SSLDelegate.countBytes(buffers, start, number);
+            SSLDelegate.WrapperResult r = sslDelegate.sendData(buffers, start, number);
+            if (r.result.getStatus() == SSLEngineResult.Status.CLOSED) {
+                if (l > 0) {
+                    throw new IOException("SSLHttpConnection closed");
+                }
+            }
+            return l;
+        }
+        @Override
+        public void shutdownInput() throws IOException {
+            delegate.shutdownInput();
+        }
+        @Override
+        public void shutdownOutput() throws IOException {
+            delegate.shutdownOutput();
+        }
+        @Override
+        public void close() {
+            delegate.close();
+        }
     }
 
-    // whenReceivedResponse only used in HTTP/1.1 (Http1Exchange)
-    // AbstractAsyncSSLConnection is only used with HTTP/2
+    // Support for WebSocket/RawChannelImpl which unfortunately
+    // still depends on synchronous read/writes.
+    // It should be removed when RawChannelImpl moves to using asynchronous APIs.
     @Override
-    final CompletableFuture<Void> whenReceivingResponse() {
-        throw new UnsupportedOperationException("Not supported.");
+    DetachedConnectionChannel detachChannel() {
+        assert client() != null;
+        DetachedConnectionChannel detachedChannel = plainConnection().detachChannel();
+        SSLDelegate sslDelegate = new SSLDelegate(engine,
+                                                  detachedChannel.channel());
+        return new SSLConnectionChannel(detachedChannel, sslDelegate);
     }
 
 }
diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/common/BufferHandler.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/AbstractSubscription.java
similarity index 68%
rename from src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/common/BufferHandler.java
rename to src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/AbstractSubscription.java
index 73ae0462799..859146c1db4 100644
--- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/common/BufferHandler.java
+++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/AbstractSubscription.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2017, 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
@@ -23,16 +23,23 @@
  * questions.
  */
 
-package jdk.incubator.http.internal.common;
+package jdk.incubator.http;
 
-import java.nio.ByteBuffer;
+import java.util.concurrent.Flow;
+import jdk.incubator.http.internal.common.Demand;
 
 /**
- * Implemented by buffer pools.
+ * A {@link Flow.Subscription} wrapping a {@link Demand} instance.
+ *
  */
-public interface BufferHandler {
+abstract class AbstractSubscription implements Flow.Subscription {
 
-    ByteBuffer getBuffer();
+    private final Demand demand = new Demand();
+
+    /**
+     * Returns the subscription's demand.
+     * @return the subscription's demand.
+     */
+    protected Demand demand() { return demand; }
 
-    void returnBuffer(ByteBuffer buffer);
 }
diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/AsyncConnection.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/AsyncConnection.java
deleted file mode 100644
index 39514653c34..00000000000
--- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/AsyncConnection.java
+++ /dev/null
@@ -1,112 +0,0 @@
-/*
- * Copyright (c) 2015, 2016, 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.  Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * 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 jdk.incubator.http;
-
-import jdk.incubator.http.internal.common.ByteBufferReference;
-
-import java.io.IOException;
-import java.nio.ByteBuffer;
-import java.util.function.Consumer;
-import java.util.function.Supplier;
-
-/**
- * Implemented by classes that offer an asynchronous interface.
- *
- * PlainHttpConnection, AsyncSSLConnection AsyncSSLDelegate.
- *
- * setAsyncCallbacks() is called to set the callback for reading
- * and error notification. Reads all happen on the selector thread, which
- * must not block.
- *
- * Writing uses the same write() methods as used in blocking mode.
- * Queues are employed on the writing side to buffer data while it is waiting
- * to be sent. This strategy relies on HTTP/2 protocol flow control to stop
- * outgoing queue from continually growing. Writes can be initiated by the
- * calling thread, but if socket becomes full then the queue is emptied by
- * the selector thread
- */
-interface AsyncConnection {
-
-    /**
-     * Enables asynchronous sending and receiving mode. The given async
-     * receiver will receive all incoming data. asyncInput() will be called
-     * to trigger reads. asyncOutput() will be called to drive writes.
-     *
-     * The errorReceiver callback must be called when any fatal exception
-     * occurs. Connection is assumed to be closed afterwards.
-     */
-    void setAsyncCallbacks(Consumer<ByteBufferReference> asyncReceiver,
-                           Consumer<Throwable> errorReceiver,
-                           Supplier<ByteBufferReference> readBufferSupplier);
-
-
-
-    /**
-     * Does whatever is required to start reading. Usually registers
-     * an event with the selector thread.
-     */
-    void startReading();
-
-    /**
-     * Cancel asynchronous reading. Used to downgrade a HTTP/2 connection to HTTP/1
-     */
-    void stopAsyncReading();
-
-    /**
-     * In async mode, this method puts buffers at the end of the send queue.
-     * When in async mode, calling this method should later be followed by
-     * subsequent flushAsync invocation.
-     * That allows multiple threads to put buffers into the queue while some other
-     * thread is writing.
-     */
-    void writeAsync(ByteBufferReference[] buffers) throws IOException;
-
-    /**
-     * Re-enable asynchronous reads through the callback
-     */
-    void enableCallback();
-
-    /**
-     * In async mode, this method may put buffers at the beginning of send queue,
-     * breaking frames sequence and allowing to write these buffers before other
-     * buffers in the queue.
-     * When in async mode, calling this method should later be followed by
-     * subsequent flushAsync invocation.
-     * That allows multiple threads to put buffers into the queue while some other
-     * thread is writing.
-     */
-    void writeAsyncUnordered(ByteBufferReference[] buffers) throws IOException;
-
-    /**
-     * This method should be called after any writeAsync/writeAsyncUnordered
-     * invocation.
-     * If there is a race to flushAsync from several threads one thread
-     * (race winner) capture flush operation and write the whole queue content.
-     * Other threads (race losers) exits from the method (not blocking)
-     * and continue execution.
-     */
-    void flushAsync() throws IOException;
-}
diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/AsyncEvent.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/AsyncEvent.java
index e45aacfdfcf..655439934ea 100644
--- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/AsyncEvent.java
+++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/AsyncEvent.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2017, 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
@@ -25,23 +25,24 @@
 
 package jdk.incubator.http;
 
+import java.io.IOException;
 import java.nio.channels.SelectableChannel;
 
 /**
  * Event handling interface from HttpClientImpl's selector.
  *
- * If BLOCKING is set, then the channel will be put in blocking
- * mode prior to handle() being called. If false, then it remains non-blocking.
- *
  * If REPEATING is set then the event is not cancelled after being posted.
  */
 abstract class AsyncEvent {
 
-    public static final int BLOCKING = 0x1; // non blocking if not set
     public static final int REPEATING = 0x2; // one off event if not set
 
     protected final int flags;
 
+    AsyncEvent() {
+        this(0);
+    }
+
     AsyncEvent(int flags) {
         this.flags = flags;
     }
@@ -55,12 +56,13 @@ abstract class AsyncEvent {
     /** Called when event occurs */
     public abstract void handle();
 
-    /** Called when selector is shutting down. Abort all exchanges. */
-    public abstract void abort();
-
-    public boolean blocking() {
-        return (flags & BLOCKING) != 0;
-    }
+    /**
+     * Called when an error occurs during registration, or when the selector has
+     * been shut down. Aborts all exchanges.
+     *
+     * @param ioe  the IOException, or null
+     */
+    public abstract void abort(IOException ioe);
 
     public boolean repeating() {
         return (flags & REPEATING) != 0;
diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/AsyncSSLConnection.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/AsyncSSLConnection.java
index f6c095526ee..cc77383d1a5 100644
--- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/AsyncSSLConnection.java
+++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/AsyncSSLConnection.java
@@ -26,37 +26,29 @@
 package jdk.incubator.http;
 
 import java.io.IOException;
+import java.lang.System.Logger.Level;
 import java.net.InetSocketAddress;
-import java.nio.ByteBuffer;
 import java.nio.channels.SocketChannel;
 import java.util.concurrent.CompletableFuture;
-import java.util.function.Consumer;
-import java.util.function.Supplier;
-import javax.net.ssl.SSLEngine;
-
-import jdk.incubator.http.internal.common.ByteBufferReference;
+import jdk.incubator.http.internal.common.SSLTube;
 import jdk.incubator.http.internal.common.Utils;
 
+
 /**
  * Asynchronous version of SSLConnection.
  */
 class AsyncSSLConnection extends AbstractAsyncSSLConnection {
 
-    final AsyncSSLDelegate sslDelegate;
     final PlainHttpConnection plainConnection;
-    final String serverName;
+    final PlainHttpPublisher writePublisher;
+    private volatile SSLTube flow;
 
-    AsyncSSLConnection(InetSocketAddress addr, HttpClientImpl client, String[] ap) {
-        super(addr, client);
+    AsyncSSLConnection(InetSocketAddress addr,
+                       HttpClientImpl client,
+                       String[] alpn) {
+        super(addr, client, Utils.getServerName(addr), alpn);
         plainConnection = new PlainHttpConnection(addr, client);
-        serverName = Utils.getServerName(addr);
-        sslDelegate = new AsyncSSLDelegate(plainConnection, client, ap, serverName);
-    }
-
-    @Override
-    synchronized void configureMode(Mode mode) throws IOException {
-        super.configureMode(mode);
-        plainConnection.configureMode(mode);
+        writePublisher = new PlainHttpPublisher();
     }
 
     @Override
@@ -64,30 +56,26 @@ class AsyncSSLConnection extends AbstractAsyncSSLConnection {
         return plainConnection;
     }
 
-    @Override
-    AsyncSSLDelegate sslDelegate() {
-        return sslDelegate;
-    }
-
-    @Override
-    public void connect() throws IOException, InterruptedException {
-        plainConnection.connect();
-        configureMode(Mode.ASYNC);
-        startReading();
-        sslDelegate.connect();
-    }
-
     @Override
     public CompletableFuture<Void> connectAsync() {
-        // not used currently
-        throw new InternalError();
+        return plainConnection
+                .connectAsync()
+                .thenApply( unused -> {
+                    // create the SSLTube wrapping the SocketTube, with the given engine
+                    flow = new SSLTube(engine,
+                                       client().theExecutor(),
+                                       plainConnection.getConnectionFlow());
+                    return null; } );
     }
 
     @Override
     boolean connected() {
-        return plainConnection.connected() && sslDelegate.connected();
+        return plainConnection.connected();
     }
 
+    @Override
+    HttpPublisher publisher() { return writePublisher; }
+
     @Override
     boolean isProxied() {
         return false;
@@ -98,98 +86,30 @@ class AsyncSSLConnection extends AbstractAsyncSSLConnection {
         return plainConnection.channel();
     }
 
-    @Override
-    public void enableCallback() {
-        sslDelegate.enableCallback();
-    }
-
     @Override
     ConnectionPool.CacheKey cacheKey() {
         return ConnectionPool.cacheKey(address, null);
     }
 
-    @Override
-    long write(ByteBuffer[] buffers, int start, int number)
-        throws IOException
-    {
-        ByteBuffer[] bufs = Utils.reduce(buffers, start, number);
-        long n = Utils.remaining(bufs);
-        sslDelegate.writeAsync(ByteBufferReference.toReferences(bufs));
-        sslDelegate.flushAsync();
-        return n;
-    }
-
-    @Override
-    long write(ByteBuffer buffer) throws IOException {
-        long n = buffer.remaining();
-        sslDelegate.writeAsync(ByteBufferReference.toReferences(buffer));
-        sslDelegate.flushAsync();
-        return n;
-    }
-
-    @Override
-    public void writeAsyncUnordered(ByteBufferReference[] buffers) throws IOException {
-        assert getMode() == Mode.ASYNC;
-        sslDelegate.writeAsyncUnordered(buffers);
-    }
-
-    @Override
-    public void writeAsync(ByteBufferReference[] buffers) throws IOException {
-        assert getMode() == Mode.ASYNC;
-        sslDelegate.writeAsync(buffers);
-    }
-
-    @Override
-    public void flushAsync() throws IOException {
-        sslDelegate.flushAsync();
-    }
-
-    @Override
-    public void closeExceptionally(Throwable cause) {
-        Utils.close(cause, sslDelegate, plainConnection.channel());
-    }
-
     @Override
     public void close() {
-        Utils.close(sslDelegate, plainConnection.channel());
+        plainConnection.close();
     }
 
     @Override
     void shutdownInput() throws IOException {
+        debug.log(Level.DEBUG, "plainConnection.channel().shutdownInput()");
         plainConnection.channel().shutdownInput();
     }
 
     @Override
     void shutdownOutput() throws IOException {
+        debug.log(Level.DEBUG, "plainConnection.channel().shutdownOutput()");
         plainConnection.channel().shutdownOutput();
     }
 
-    @Override
-    SSLEngine getEngine() {
-        return sslDelegate.getEngine();
-    }
-
-    @Override
-    public void setAsyncCallbacks(Consumer<ByteBufferReference> asyncReceiver,
-                                  Consumer<Throwable> errorReceiver,
-                                  Supplier<ByteBufferReference> readBufferSupplier) {
-        sslDelegate.setAsyncCallbacks(asyncReceiver, errorReceiver, readBufferSupplier);
-        plainConnection.setAsyncCallbacks(sslDelegate::asyncReceive, errorReceiver, sslDelegate::getNetBuffer);
-    }
-
-    @Override
-    public void startReading() {
-        plainConnection.startReading();
-        sslDelegate.startReading();
-    }
-
-    @Override
-    public void stopAsyncReading() {
-        plainConnection.stopAsyncReading();
-    }
-
-    @Override
-    SSLConnection downgrade() {
-        return new SSLConnection(this);
-    }
+   @Override
+   SSLTube getConnectionFlow() {
+       return flow;
+   }
 }
diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/AsyncSSLDelegate.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/AsyncSSLDelegate.java
deleted file mode 100644
index e3341c148c2..00000000000
--- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/AsyncSSLDelegate.java
+++ /dev/null
@@ -1,692 +0,0 @@
-/*
- * Copyright (c) 2015, 2016, 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.  Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * 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 jdk.incubator.http;
-
-import java.io.IOException;
-import java.nio.ByteBuffer;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-import java.util.concurrent.Semaphore;
-import java.util.concurrent.CompletableFuture;
-import java.util.function.Consumer;
-import java.util.function.Supplier;
-
-import static javax.net.ssl.SSLEngineResult.Status.*;
-import javax.net.ssl.*;
-
-import jdk.incubator.http.internal.common.AsyncWriteQueue;
-import jdk.incubator.http.internal.common.ByteBufferPool;
-import jdk.incubator.http.internal.common.ByteBufferReference;
-import jdk.incubator.http.internal.common.Log;
-import jdk.incubator.http.internal.common.Queue;
-import jdk.incubator.http.internal.common.Utils;
-import static javax.net.ssl.SSLEngineResult.HandshakeStatus.*;
-import jdk.incubator.http.internal.common.ExceptionallyCloseable;
-
-/**
- * Asynchronous wrapper around SSLEngine. send and receive is fully non
- * blocking. When handshaking is required, a thread is created to perform
- * the handshake and application level sends do not take place during this time.
- *
- * Is implemented using queues and functions operating on the receiving end
- * of each queue.
- *
- * Application writes to:
- *        ||
- *        \/
- *     appOutputQ
- *        ||
- *        \/
- * appOutputQ read by "upperWrite" method which does SSLEngine.wrap
- * and does async write to PlainHttpConnection
- *
- * Reading side is as follows
- * --------------------------
- *
- * "upperRead" method reads off channelInputQ and calls SSLEngine.unwrap and
- * when decrypted data is returned, it is passed to the user's Consumer<ByteBuffer>
- *        /\
- *        ||
- *     channelInputQ
- *        /\
- *        ||
- * "asyncReceive" method puts buffers into channelInputQ. It is invoked from
- * OP_READ events from the selector.
- *
- * Whenever handshaking is required, the doHandshaking() method is called
- * which creates a thread to complete the handshake. It takes over the
- * channelInputQ from upperRead, and puts outgoing packets on channelOutputQ.
- * Selector events are delivered to asyncReceive and lowerWrite as normal.
- *
- * Errors
- *
- * Any exception thrown by the engine or channel, causes all Queues to be closed
- * the channel to be closed, and the error is reported to the user's
- * Consumer<Throwable>
- */
-class AsyncSSLDelegate implements ExceptionallyCloseable, AsyncConnection {
-
-    // outgoing buffers put in this queue first and may remain here
-    // while SSL handshaking happening.
-    final AsyncWriteQueue appOutputQ = new AsyncWriteQueue(this::upperWrite);
-
-    // Bytes read into this queue before being unwrapped. Backup on this
-    // Q should only happen when the engine is stalled due to delegated tasks
-    final Queue<ByteBufferReference> channelInputQ;
-
-    // input occurs through the read() method which is expected to be called
-    // when the selector signals some data is waiting to be read. All incoming
-    // handshake data is handled in this method, which means some calls to
-    // read() may return zero bytes of user data. This is not a sign of spinning,
-    // just that the handshake mechanics are being executed.
-
-    final SSLEngine engine;
-    final SSLParameters sslParameters;
-    final HttpConnection lowerOutput;
-    final HttpClientImpl client;
-    final String serverName;
-    // should be volatile to provide proper synchronization(visibility) action
-    volatile Consumer<ByteBufferReference> asyncReceiver;
-    volatile Consumer<Throwable> errorHandler;
-    volatile boolean connected = false;
-
-    // Locks.
-    final Object reader = new Object();
-    // synchronizing handshake state
-    final Semaphore handshaker = new Semaphore(1);
-    final String[] alpn;
-
-    // alpn[] may be null. upcall is callback which receives incoming decoded bytes off socket
-
-    AsyncSSLDelegate(HttpConnection lowerOutput, HttpClientImpl client, String[] alpn, String sname)
-    {
-        SSLContext context = client.sslContext();
-        this.serverName = sname;
-        engine = context.createSSLEngine();
-        engine.setUseClientMode(true);
-        SSLParameters sslp = client.sslParameters()
-                                   .orElseGet(context::getSupportedSSLParameters);
-        sslParameters = Utils.copySSLParameters(sslp);
-        if (alpn != null) {
-            Log.logSSL("AsyncSSLDelegate: Setting application protocols: " + Arrays.toString(alpn));
-            sslParameters.setApplicationProtocols(alpn);
-        } else {
-            Log.logSSL("AsyncSSLDelegate: no applications set!");
-        }
-        if (serverName != null) {
-            SNIHostName sn = new SNIHostName(serverName);
-            sslParameters.setServerNames(List.of(sn));
-        }
-        logParams(sslParameters);
-        engine.setSSLParameters(sslParameters);
-        this.lowerOutput = lowerOutput;
-        this.client = client;
-        this.channelInputQ = new Queue<>();
-        this.channelInputQ.registerPutCallback(this::upperRead);
-        this.alpn = alpn;
-    }
-
-    @Override
-    public void writeAsync(ByteBufferReference[] src) throws IOException {
-        appOutputQ.put(src);
-    }
-
-    @Override
-    public void writeAsyncUnordered(ByteBufferReference[] buffers) throws IOException {
-        appOutputQ.putFirst(buffers);
-    }
-
-    @Override
-    public void flushAsync() throws IOException {
-        if (appOutputQ.flush()) {
-            lowerOutput.flushAsync();
-        }
-    }
-
-    SSLEngine getEngine() {
-        return engine;
-    }
-
-    @Override
-    public void closeExceptionally(Throwable t) {
-        Utils.close(t, appOutputQ, channelInputQ, lowerOutput);
-    }
-
-    @Override
-    public void close() {
-        Utils.close(appOutputQ, channelInputQ, lowerOutput);
-    }
-
-    // The code below can be uncommented to shake out
-    // the implementation by inserting random delays and trigger
-    // handshake in the SelectorManager thread (upperRead)
-    // static final java.util.Random random =
-    //    new java.util.Random(System.currentTimeMillis());
-
-    /**
-     * Attempts to wrap buffers from appOutputQ and place them on the
-     * channelOutputQ for writing. If handshaking is happening, then the
-     * process stalls and last buffers taken off the appOutputQ are put back
-     * into it until handshaking completes.
-     *
-     * This same method is called to try and resume output after a blocking
-     * handshaking operation has completed.
-     */
-    private boolean upperWrite(ByteBufferReference[] refs, AsyncWriteQueue delayCallback) {
-        // currently delayCallback is not used. Use it when it's needed to execute handshake in another thread.
-        try {
-            ByteBuffer[] buffers = ByteBufferReference.toBuffers(refs);
-            int bytes = Utils.remaining(buffers);
-            while (bytes > 0) {
-                EngineResult r = wrapBuffers(buffers);
-                int bytesProduced = r.bytesProduced();
-                int bytesConsumed = r.bytesConsumed();
-                bytes -= bytesConsumed;
-                if (bytesProduced > 0) {
-                    lowerOutput.writeAsync(new ByteBufferReference[]{r.destBuffer});
-                }
-
-                // The code below can be uncommented to shake out
-                // the implementation by inserting random delays and trigger
-                // handshake in the SelectorManager thread (upperRead)
-
-                // int sleep = random.nextInt(100);
-                // if (sleep > 20) {
-                //   Thread.sleep(sleep);
-                // }
-
-                // handshaking is happening or is needed
-                if (r.handshaking()) {
-                    Log.logTrace("Write: needs handshake");
-                    doHandshakeNow("Write");
-                }
-            }
-            ByteBufferReference.clear(refs);
-        } catch (Throwable t) {
-            closeExceptionally(t);
-            errorHandler.accept(t);
-        }
-        // We always return true: either all the data was sent, or
-        // an exception happened and we have closed the queue.
-        return true;
-    }
-
-    // Connecting at this level means the initial handshake has completed.
-    // This means that the initial SSL parameters are available including
-    // ALPN result.
-    void connect() throws IOException, InterruptedException {
-        doHandshakeNow("Init");
-        connected = true;
-    }
-
-    boolean connected() {
-        return connected;
-    }
-
-    private void startHandshake(String tag) {
-        Runnable run = () -> {
-            try {
-                doHandshakeNow(tag);
-            } catch (Throwable t) {
-                Log.logTrace("{0}: handshake failed: {1}", tag, t);
-                closeExceptionally(t);
-                errorHandler.accept(t);
-            }
-        };
-        client.executor().execute(run);
-    }
-
-    private void doHandshakeNow(String tag)
-        throws IOException, InterruptedException
-    {
-        handshaker.acquire();
-        try {
-            channelInputQ.disableCallback();
-            lowerOutput.flushAsync();
-            Log.logTrace("{0}: Starting handshake...", tag);
-            doHandshakeImpl();
-            Log.logTrace("{0}: Handshake completed", tag);
-            // don't unblock the channel here, as we aren't sure yet, whether ALPN
-            // negotiation succeeded. Caller will call enableCallback() externally
-        } finally {
-            handshaker.release();
-        }
-    }
-
-    public void enableCallback() {
-        channelInputQ.enableCallback();
-    }
-
-     /**
-     * Executes entire handshake in calling thread.
-     * Returns after handshake is completed or error occurs
-     */
-    private void doHandshakeImpl() throws IOException {
-        engine.beginHandshake();
-        while (true) {
-            SSLEngineResult.HandshakeStatus status = engine.getHandshakeStatus();
-            switch(status) {
-                case NEED_TASK: {
-                    List<Runnable> tasks = obtainTasks();
-                    for (Runnable task : tasks) {
-                        task.run();
-                    }
-                } break;
-                case NEED_WRAP:
-                    handshakeWrapAndSend();
-                    break;
-                case NEED_UNWRAP: case NEED_UNWRAP_AGAIN:
-                    handshakeReceiveAndUnWrap();
-                    break;
-                case FINISHED:
-                    return;
-                case NOT_HANDSHAKING:
-                    return;
-                default:
-                    throw new InternalError("Unexpected Handshake Status: "
-                                             + status);
-            }
-        }
-    }
-
-    // acknowledge a received CLOSE request from peer
-    void doClosure() throws IOException {
-        //while (!wrapAndSend(emptyArray))
-            //;
-    }
-
-    List<Runnable> obtainTasks() {
-        List<Runnable> l = new ArrayList<>();
-        Runnable r;
-        while ((r = engine.getDelegatedTask()) != null) {
-            l.add(r);
-        }
-        return l;
-    }
-
-    @Override
-    public void setAsyncCallbacks(Consumer<ByteBufferReference> asyncReceiver,
-                                  Consumer<Throwable> errorReceiver,
-                                  Supplier<ByteBufferReference> readBufferSupplier) {
-        this.asyncReceiver = asyncReceiver;
-        this.errorHandler = errorReceiver;
-        // readBufferSupplier is not used,
-        // because of AsyncSSLDelegate has its own appBufferPool
-    }
-
-    @Override
-    public void startReading() {
-        // maybe this class does not need to implement AsyncConnection
-    }
-
-    @Override
-    public void stopAsyncReading() {
-        // maybe this class does not need to implement AsyncConnection
-    }
-
-
-    static class EngineResult {
-        final SSLEngineResult result;
-        final ByteBufferReference destBuffer;
-
-
-        // normal result
-        EngineResult(SSLEngineResult result) {
-            this(result, null);
-        }
-
-        EngineResult(SSLEngineResult result, ByteBufferReference destBuffer) {
-            this.result = result;
-            this.destBuffer = destBuffer;
-        }
-
-        boolean handshaking() {
-            SSLEngineResult.HandshakeStatus s = result.getHandshakeStatus();
-            return s != FINISHED && s != NOT_HANDSHAKING;
-        }
-
-        int bytesConsumed() {
-            return result.bytesConsumed();
-        }
-
-        int bytesProduced() {
-            return result.bytesProduced();
-        }
-
-        SSLEngineResult.HandshakeStatus handshakeStatus() {
-            return result.getHandshakeStatus();
-        }
-
-        SSLEngineResult.Status status() {
-            return result.getStatus();
-        }
-    }
-
-    EngineResult handshakeWrapAndSend() throws IOException {
-        EngineResult r = wrapBuffer(Utils.EMPTY_BYTEBUFFER);
-        if (r.bytesProduced() > 0) {
-            lowerOutput.writeAsync(new ByteBufferReference[]{r.destBuffer});
-            lowerOutput.flushAsync();
-        }
-        return r;
-    }
-
-    // called during handshaking. It blocks until a complete packet
-    // is available, unwraps it and returns.
-    void handshakeReceiveAndUnWrap() throws IOException {
-        ByteBufferReference ref = channelInputQ.take();
-        while (true) {
-            // block waiting for input
-            EngineResult r = unwrapBuffer(ref.get());
-            SSLEngineResult.Status status = r.status();
-            if (status == BUFFER_UNDERFLOW) {
-                // wait for another buffer to arrive
-                ByteBufferReference ref1 = channelInputQ.take();
-                ref = combine (ref, ref1);
-                continue;
-            }
-            // OK
-            // theoretically possible we could receive some user data
-            if (r.bytesProduced() > 0) {
-                asyncReceiver.accept(r.destBuffer);
-            } else {
-                r.destBuffer.clear();
-            }
-            // it is also possible that a delegated task could be needed
-            // even though they are handled in the calling function
-            if (r.handshakeStatus() == NEED_TASK) {
-                obtainTasks().stream().forEach((task) -> task.run());
-            }
-
-            if (!ref.get().hasRemaining()) {
-                ref.clear();
-                return;
-            }
-        }
-    }
-
-    EngineResult wrapBuffer(ByteBuffer src) throws SSLException {
-        ByteBuffer[] bufs = new ByteBuffer[1];
-        bufs[0] = src;
-        return wrapBuffers(bufs);
-    }
-
-    private final ByteBufferPool netBufferPool = new ByteBufferPool();
-    private final ByteBufferPool appBufferPool = new ByteBufferPool();
-
-    /**
-     * provides buffer of sslEngine@getPacketBufferSize().
-     * used for encrypted buffers after wrap or before unwrap.
-     * @return ByteBufferReference
-     */
-    public ByteBufferReference getNetBuffer() {
-        return netBufferPool.get(engine.getSession().getPacketBufferSize());
-    }
-
-    /**
-     * provides buffer of sslEngine@getApplicationBufferSize().
-     * @return ByteBufferReference
-     */
-    private ByteBufferReference getAppBuffer() {
-        return appBufferPool.get(engine.getSession().getApplicationBufferSize());
-    }
-
-    EngineResult wrapBuffers(ByteBuffer[] src) throws SSLException {
-        ByteBufferReference dst = getNetBuffer();
-        while (true) {
-            SSLEngineResult sslResult = engine.wrap(src, dst.get());
-            switch (sslResult.getStatus()) {
-                case BUFFER_OVERFLOW:
-                    // Shouldn't happen. We allocated buffer with packet size
-                    // get it again if net buffer size was changed
-                    dst = getNetBuffer();
-                    break;
-                case CLOSED:
-                case OK:
-                    dst.get().flip();
-                    return new EngineResult(sslResult, dst);
-                case BUFFER_UNDERFLOW:
-                    // Shouldn't happen.  Doesn't returns when wrap()
-                    // underflow handled externally
-                    return new EngineResult(sslResult);
-                default:
-                    assert false;
-            }
-        }
-    }
-
-    EngineResult unwrapBuffer(ByteBuffer srcbuf) throws IOException {
-        ByteBufferReference dst = getAppBuffer();
-        while (true) {
-            SSLEngineResult sslResult = engine.unwrap(srcbuf, dst.get());
-            switch (sslResult.getStatus()) {
-                case BUFFER_OVERFLOW:
-                    // may happen only if app size buffer was changed.
-                    // get it again if app buffer size changed
-                    dst = getAppBuffer();
-                    break;
-                case CLOSED:
-                    doClosure();
-                    throw new IOException("Engine closed");
-                case BUFFER_UNDERFLOW:
-                    dst.clear();
-                    return new EngineResult(sslResult);
-                case OK:
-                     dst.get().flip();
-                     return new EngineResult(sslResult, dst);
-            }
-        }
-    }
-
-    /**
-     * Asynchronous read input. Call this when selector fires.
-     * Unwrap done in upperRead because it also happens in
-     * doHandshake() when handshake taking place
-     */
-    public void asyncReceive(ByteBufferReference buffer) {
-        try {
-            channelInputQ.put(buffer);
-        } catch (Throwable t) {
-            closeExceptionally(t);
-            errorHandler.accept(t);
-        }
-    }
-
-    private ByteBufferReference pollInput() throws IOException {
-        return channelInputQ.poll();
-    }
-
-    private ByteBufferReference pollInput(ByteBufferReference next) throws IOException {
-        return next == null ? channelInputQ.poll() : next;
-    }
-
-    public void upperRead() {
-        ByteBufferReference src;
-        ByteBufferReference next = null;
-        synchronized (reader) {
-            try {
-                src = pollInput();
-                if (src == null) {
-                    return;
-                }
-                while (true) {
-                    EngineResult r = unwrapBuffer(src.get());
-                    switch (r.result.getStatus()) {
-                        case BUFFER_UNDERFLOW:
-                            // Buffer too small. Need to combine with next buf
-                            next = pollInput(next);
-                            if (next == null) {
-                                // no data available.
-                                // push buffer back until more data available
-                                channelInputQ.pushback(src);
-                                return;
-                            } else {
-                                src = shift(src, next);
-                                if (!next.get().hasRemaining()) {
-                                    next.clear();
-                                    next = null;
-                                }
-                            }
-                            break;
-                        case OK:
-                            // check for any handshaking work
-                            if (r.handshaking()) {
-                                // handshaking is happening or is needed
-                                // so we put the buffer back on Q to process again
-                                // later.
-                                Log.logTrace("Read: needs handshake");
-                                channelInputQ.pushback(src);
-                                startHandshake("Read");
-                                return;
-                            }
-                            asyncReceiver.accept(r.destBuffer);
-                    }
-                    if (src.get().hasRemaining()) {
-                        continue;
-                    }
-                    src.clear();
-                    src = pollInput(next);
-                    next = null;
-                    if (src == null) {
-                        return;
-                    }
-                }
-            } catch (Throwable t) {
-                closeExceptionally(t);
-                errorHandler.accept(t);
-            }
-        }
-    }
-
-    ByteBufferReference shift(ByteBufferReference ref1, ByteBufferReference ref2) {
-        ByteBuffer buf1 = ref1.get();
-        if (buf1.capacity() < engine.getSession().getPacketBufferSize()) {
-            ByteBufferReference newRef = getNetBuffer();
-            ByteBuffer newBuf = newRef.get();
-            newBuf.put(buf1);
-            buf1 = newBuf;
-            ref1.clear();
-            ref1 = newRef;
-        } else {
-            buf1.compact();
-        }
-        ByteBuffer buf2 = ref2.get();
-        Utils.copy(buf2, buf1, Math.min(buf1.remaining(), buf2.remaining()));
-        buf1.flip();
-        return ref1;
-    }
-
-
-    ByteBufferReference combine(ByteBufferReference ref1, ByteBufferReference ref2) {
-        ByteBuffer buf1 = ref1.get();
-        ByteBuffer buf2 = ref2.get();
-        int avail1 = buf1.capacity() - buf1.remaining();
-        if (buf2.remaining() < avail1) {
-            buf1.compact();
-            buf1.put(buf2);
-            buf1.flip();
-            ref2.clear();
-            return ref1;
-        }
-        int newsize = buf1.remaining() + buf2.remaining();
-        ByteBuffer newbuf = ByteBuffer.allocate(newsize); // getting rid of buffer pools
-        newbuf.put(buf1);
-        newbuf.put(buf2);
-        newbuf.flip();
-        ref1.clear();
-        ref2.clear();
-        return ByteBufferReference.of(newbuf);
-    }
-
-    SSLParameters getSSLParameters() {
-        return sslParameters;
-    }
-
-    static void logParams(SSLParameters p) {
-        if (!Log.ssl()) {
-            return;
-        }
-
-        if (p == null) {
-            Log.logSSL("SSLParameters: Null params");
-            return;
-        }
-
-        final StringBuilder sb = new StringBuilder("SSLParameters:");
-        final List<Object> params = new ArrayList<>();
-        if (p.getCipherSuites() != null) {
-            for (String cipher : p.getCipherSuites()) {
-                sb.append("\n    cipher: {")
-                  .append(params.size()).append("}");
-                params.add(cipher);
-            }
-        }
-
-        // SSLParameters.getApplicationProtocols() can't return null
-        // JDK 8 EXCL START
-        for (String approto : p.getApplicationProtocols()) {
-            sb.append("\n    application protocol: {")
-              .append(params.size()).append("}");
-            params.add(approto);
-        }
-        // JDK 8 EXCL END
-
-        if (p.getProtocols() != null) {
-            for (String protocol : p.getProtocols()) {
-                sb.append("\n    protocol: {")
-                  .append(params.size()).append("}");
-                params.add(protocol);
-            }
-        }
-
-        if (p.getServerNames() != null) {
-            for (SNIServerName sname : p.getServerNames()) {
-                 sb.append("\n    server name: {")
-                  .append(params.size()).append("}");
-                params.add(sname.toString());
-            }
-        }
-        sb.append('\n');
-
-        Log.logSSL(sb.toString(), params.toArray());
-    }
-
-    String getSessionInfo() {
-        StringBuilder sb = new StringBuilder();
-        String application = engine.getApplicationProtocol();
-        SSLSession sess = engine.getSession();
-        String cipher = sess.getCipherSuite();
-        String protocol = sess.getProtocol();
-        sb.append("Handshake complete alpn: ")
-          .append(application)
-          .append(", Cipher: ")
-          .append(cipher)
-          .append(", Protocol: ")
-          .append(protocol);
-        return sb.toString();
-    }
-}
diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/AsyncSSLTunnelConnection.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/AsyncSSLTunnelConnection.java
index 7afb87f5191..1497db8c050 100644
--- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/AsyncSSLTunnelConnection.java
+++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/AsyncSSLTunnelConnection.java
@@ -26,15 +26,11 @@
 package jdk.incubator.http;
 
 import java.io.IOException;
+import java.lang.System.Logger.Level;
 import java.net.InetSocketAddress;
-import java.nio.ByteBuffer;
 import java.nio.channels.SocketChannel;
 import java.util.concurrent.CompletableFuture;
-import java.util.function.Consumer;
-import java.util.function.Supplier;
-import javax.net.ssl.SSLEngine;
-import javax.net.ssl.SSLParameters;
-import jdk.incubator.http.internal.common.ByteBufferReference;
+import jdk.incubator.http.internal.common.SSLTube;
 import jdk.incubator.http.internal.common.Utils;
 
 /**
@@ -43,48 +39,43 @@ import jdk.incubator.http.internal.common.Utils;
 class AsyncSSLTunnelConnection extends AbstractAsyncSSLConnection {
 
     final PlainTunnelingConnection plainConnection;
-    final AsyncSSLDelegate sslDelegate;
-    final String serverName;
+    final PlainHttpPublisher writePublisher;
+    volatile SSLTube flow;
 
-    @Override
-    public void connect() throws IOException, InterruptedException {
-        plainConnection.connect();
-        configureMode(Mode.ASYNC);
-        startReading();
-        sslDelegate.connect();
-    }
-
-    @Override
-    boolean connected() {
-        return plainConnection.connected() && sslDelegate.connected();
+    AsyncSSLTunnelConnection(InetSocketAddress addr,
+                             HttpClientImpl client,
+                             String[] alpn,
+                             InetSocketAddress proxy)
+    {
+        super(addr, client, Utils.getServerName(addr), alpn);
+        this.plainConnection = new PlainTunnelingConnection(addr, proxy, client);
+        this.writePublisher = new PlainHttpPublisher();
     }
 
     @Override
     public CompletableFuture<Void> connectAsync() {
-        throw new InternalError();
-    }
-
-    AsyncSSLTunnelConnection(InetSocketAddress addr,
-                        HttpClientImpl client,
-                        String[] alpn,
-                        InetSocketAddress proxy)
-    {
-        super(addr, client);
-        this.serverName = Utils.getServerName(addr);
-        this.plainConnection = new PlainTunnelingConnection(addr, proxy, client);
-        this.sslDelegate = new AsyncSSLDelegate(plainConnection, client, alpn, serverName);
+        debug.log(Level.DEBUG, "Connecting plain tunnel connection");
+        // This will connect the PlainHttpConnection flow, so that
+        // its HttpSubscriber and HttpPublisher are subscribed to the
+        // SocketTube
+        return plainConnection
+                .connectAsync()
+                .thenApply( unused -> {
+                    debug.log(Level.DEBUG, "creating SSLTube");
+                    // create the SSLTube wrapping the SocketTube, with the given engine
+                    flow = new SSLTube(engine,
+                                       client().theExecutor(),
+                                       plainConnection.getConnectionFlow());
+                    return null;} );
     }
 
     @Override
-    synchronized void configureMode(Mode mode) throws IOException {
-        super.configureMode(mode);
-        plainConnection.configureMode(mode);
+    boolean connected() {
+        return plainConnection.connected(); // && sslDelegate.connected();
     }
 
     @Override
-    SSLParameters sslParameters() {
-        return sslDelegate.getSSLParameters();
-    }
+    HttpPublisher publisher() { return writePublisher; }
 
     @Override
     public String toString() {
@@ -96,53 +87,14 @@ class AsyncSSLTunnelConnection extends AbstractAsyncSSLConnection {
         return plainConnection;
     }
 
-    @Override
-    AsyncSSLDelegate sslDelegate() {
-        return sslDelegate;
-    }
-
     @Override
     ConnectionPool.CacheKey cacheKey() {
         return ConnectionPool.cacheKey(address, plainConnection.proxyAddr);
     }
 
-    @Override
-    long write(ByteBuffer[] buffers, int start, int number) throws IOException {
-        //debugPrint("Send", buffers, start, number);
-        ByteBuffer[] bufs = Utils.reduce(buffers, start, number);
-        long n = Utils.remaining(bufs);
-        sslDelegate.writeAsync(ByteBufferReference.toReferences(bufs));
-        sslDelegate.flushAsync();
-        return n;
-    }
-
-    @Override
-    long write(ByteBuffer buffer) throws IOException {
-        //debugPrint("Send", buffer);
-        long n = buffer.remaining();
-        sslDelegate.writeAsync(ByteBufferReference.toReferences(buffer));
-        sslDelegate.flushAsync();
-        return n;
-    }
-
-    @Override
-    public void writeAsync(ByteBufferReference[] buffers) throws IOException {
-        sslDelegate.writeAsync(buffers);
-    }
-
-    @Override
-    public void writeAsyncUnordered(ByteBufferReference[] buffers) throws IOException {
-        sslDelegate.writeAsyncUnordered(buffers);
-    }
-
-    @Override
-    public void flushAsync() throws IOException {
-        sslDelegate.flushAsync();
-    }
-
     @Override
     public void close() {
-        Utils.close(sslDelegate, plainConnection.channel());
+        plainConnection.close();
     }
 
     @Override
@@ -166,41 +118,7 @@ class AsyncSSLTunnelConnection extends AbstractAsyncSSLConnection {
     }
 
     @Override
-    public void setAsyncCallbacks(Consumer<ByteBufferReference> asyncReceiver,
-                                  Consumer<Throwable> errorReceiver,
-                                  Supplier<ByteBufferReference> readBufferSupplier) {
-        sslDelegate.setAsyncCallbacks(asyncReceiver, errorReceiver, readBufferSupplier);
-        plainConnection.setAsyncCallbacks(sslDelegate::asyncReceive, errorReceiver, sslDelegate::getNetBuffer);
-    }
-
-    @Override
-    public void startReading() {
-        plainConnection.startReading();
-        sslDelegate.startReading();
-    }
-
-    @Override
-    public void stopAsyncReading() {
-        plainConnection.stopAsyncReading();
-    }
-
-    @Override
-    public void enableCallback() {
-        sslDelegate.enableCallback();
-    }
-
-    @Override
-    public void closeExceptionally(Throwable cause) throws IOException {
-        Utils.close(cause, sslDelegate, plainConnection.channel());
-    }
-
-    @Override
-    SSLEngine getEngine() {
-        return sslDelegate.getEngine();
-    }
-
-    @Override
-    SSLTunnelConnection downgrade() {
-        return new SSLTunnelConnection(this);
-    }
+    SSLTube getConnectionFlow() {
+       return flow;
+   }
 }
diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/AsyncTriggerEvent.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/AsyncTriggerEvent.java
new file mode 100644
index 00000000000..5becb011232
--- /dev/null
+++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/AsyncTriggerEvent.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2017, 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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 jdk.incubator.http;
+
+import java.io.IOException;
+import java.nio.channels.SelectableChannel;
+import java.util.Objects;
+import java.util.function.Consumer;
+
+/**
+ * An asynchronous event which is triggered only once from the selector manager
+ * thread as soon as event registration are handled.
+ */
+final class AsyncTriggerEvent extends AsyncEvent{
+
+    private final Runnable trigger;
+    private final Consumer<? super IOException> errorHandler;
+    AsyncTriggerEvent(Consumer<? super IOException> errorHandler,
+                      Runnable trigger) {
+        super(0);
+        this.trigger = Objects.requireNonNull(trigger);
+        this.errorHandler = Objects.requireNonNull(errorHandler);
+    }
+    /** Returns null */
+    @Override
+    public SelectableChannel channel() { return null; }
+    /** Returns 0 */
+    @Override
+    public int interestOps() { return 0; }
+    @Override
+    public void handle() { trigger.run(); }
+    @Override
+    public void abort(IOException ioe) { errorHandler.accept(ioe); }
+    @Override
+    public boolean repeating() { return false; }
+}
diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/AuthenticationFilter.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/AuthenticationFilter.java
index a6ff07d884e..9659ce43022 100644
--- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/AuthenticationFilter.java
+++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/AuthenticationFilter.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2017, 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
@@ -54,6 +54,9 @@ class AuthenticationFilter implements HeaderFilter {
     static final int UNAUTHORIZED = 401;
     static final int PROXY_UNAUTHORIZED = 407;
 
+    // A public no-arg constructor is required by FilterFactory
+    public AuthenticationFilter() {}
+
     private PasswordAuthentication getCredentials(String header,
                                                   boolean proxy,
                                                   HttpRequestImpl req)
@@ -83,7 +86,7 @@ class AuthenticationFilter implements HeaderFilter {
     }
 
     private URI getProxyURI(HttpRequestImpl r) {
-        InetSocketAddress proxy = r.proxy(exchange.client());
+        InetSocketAddress proxy = r.proxy();
         if (proxy == null) {
             return null;
         }
@@ -216,7 +219,6 @@ class AuthenticationFilter implements HeaderFilter {
             return null;   // error gets returned to app
         }
 
-        String realm = parser.findValue("realm");
         AuthInfo au = proxy ? exchange.proxyauth : exchange.serverauth;
         if (au == null) {
             PasswordAuthentication pw = getCredentials(authval, proxy, req);
diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/BlockingPushPublisher.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/BlockingPushPublisher.java
deleted file mode 100644
index fa49fe5eb33..00000000000
--- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/BlockingPushPublisher.java
+++ /dev/null
@@ -1,126 +0,0 @@
-/*
- * Copyright (c) 2016, 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.  Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * 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 jdk.incubator.http;
-
-import java.util.Optional;
-import java.util.concurrent.Flow;
-import jdk.incubator.http.internal.common.Log;
-
-/**
- * A Publisher which is assumed to run in its own thread.
- *
- * acceptData() may therefore block while waiting for subscriber demand
- */
-class BlockingPushPublisher<T> extends AbstractPushPublisher<T> {
-    volatile Subscription subscription;
-    volatile Flow.Subscriber<? super T> subscriber;
-    volatile SubscriptionState state;
-    long demand;
-
-    @Override
-    public void subscribe(Flow.Subscriber<? super T> subscriber) {
-        state = SubscriptionState.OPENED;
-        subscription = new Subscription(subscriber);
-        subscriber.onSubscribe(subscription);
-    }
-
-    /**
-     * Entry point for supplying items to publisher. This call will block
-     * when no demand available.
-     */
-    @Override
-    public void acceptData(Optional<T> item) throws InterruptedException {
-        SubscriptionState s = this.state;
-
-        // do not use switch(this.state): this.state could be null.
-        if (s == SubscriptionState.CANCELLED) return;
-        if (s == SubscriptionState.DONE) {
-            throw new IllegalStateException("subscription complete");
-        }
-
-        if (!item.isPresent()) {
-            subscriber.onComplete();
-            this.state = SubscriptionState.DONE;
-        } else {
-            obtainPermit();
-            if (this.state == SubscriptionState.CANCELLED) return;
-            subscriber.onNext(item.get());
-        }
-    }
-
-    /**
-     * Terminates the publisher with given exception.
-     */
-    @Override
-    public void acceptError(Throwable t) {
-        if (this.state != SubscriptionState.OPENED) {
-            Log.logError(t);
-            return;
-        }
-        subscriber.onError(t);
-        cancel();
-    }
-
-    private synchronized void obtainPermit() throws InterruptedException {
-        while (demand == 0) {
-            wait();
-        }
-        if (this.state == SubscriptionState.DONE) {
-            throw new IllegalStateException("subscription complete");
-        }
-        demand --;
-    }
-
-    synchronized void addPermits(long n) {
-        long old = demand;
-        demand += n;
-        if (old == 0) {
-            notifyAll();
-        }
-    }
-
-    synchronized void cancel() {
-        this.state = SubscriptionState.CANCELLED;
-        notifyAll();
-    }
-
-    private class Subscription implements Flow.Subscription {
-
-        Subscription(Flow.Subscriber<? super T> subscriber) {
-            BlockingPushPublisher.this.subscriber = subscriber;
-        }
-
-        @Override
-        public void request(long n) {
-            addPermits(n);
-        }
-
-        @Override
-        public void cancel() {
-            BlockingPushPublisher.this.cancel();
-        }
-    }
-}
diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/BufferingSubscriber.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/BufferingSubscriber.java
new file mode 100644
index 00000000000..270da1688cc
--- /dev/null
+++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/BufferingSubscriber.java
@@ -0,0 +1,313 @@
+/*
+ * Copyright (c) 2017, 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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 jdk.incubator.http;
+
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.ListIterator;
+import java.util.Objects;
+import java.util.concurrent.CompletionStage;
+import java.util.concurrent.Flow;
+import java.util.concurrent.atomic.AtomicBoolean;
+import jdk.incubator.http.internal.common.Demand;
+import jdk.incubator.http.internal.common.SequentialScheduler;
+import jdk.incubator.http.internal.common.Utils;
+
+/**
+ * A buffering BodySubscriber. When subscribed, accumulates ( buffers ) a given
+ * amount ( in bytes ) of a publisher's data before pushing it to a downstream
+ * subscriber.
+ */
+class BufferingSubscriber<T> implements HttpResponse.BodySubscriber<T>
+{
+    /** The downstream consumer of the data. */
+    private final HttpResponse.BodySubscriber<T> downstreamSubscriber;
+    /** The amount of data to be accumulate before pushing downstream. */
+    private final int bufferSize;
+
+    /** The subscription, created lazily. */
+    private volatile Flow.Subscription subscription;
+    /** The downstream subscription, created lazily. */
+    private volatile DownstreamSubscription downstreamSubscription;
+
+    /** Must be held when accessing the internal buffers. */
+    private final Object buffersLock = new Object();
+    /** The internal buffers holding the buffered data. */
+    private ArrayList<ByteBuffer> internalBuffers;
+    /** The actual accumulated remaining bytes in internalBuffers. */
+    private int accumulatedBytes;
+
+    /** Holds the Throwable from upstream's onError. */
+    private volatile Throwable throwable;
+
+    /** State of the buffering subscriber:
+     *  1) [UNSUBSCRIBED] when initially created
+     *  2) [ACTIVE] when subscribed and can receive data
+     *  3) [ERROR | CANCELLED | COMPLETE] (terminal state)
+     */
+    static final int UNSUBSCRIBED = 0x01;
+    static final int ACTIVE       = 0x02;
+    static final int ERROR        = 0x04;
+    static final int CANCELLED    = 0x08;
+    static final int COMPLETE     = 0x10;
+
+    private volatile int state;
+
+    BufferingSubscriber(HttpResponse.BodySubscriber<T> downstreamSubscriber,
+                        int bufferSize) {
+        this.downstreamSubscriber = downstreamSubscriber;
+        this.bufferSize = bufferSize;
+        synchronized (buffersLock) {
+            internalBuffers = new ArrayList<>();
+        }
+        state = UNSUBSCRIBED;
+    }
+
+    /** Returns the number of bytes remaining in the given buffers. */
+    private static final long remaining(List<ByteBuffer> buffers) {
+        return buffers.stream().mapToLong(ByteBuffer::remaining).sum();
+    }
+
+    /**
+     * Tells whether, or not, there is at least a sufficient number of bytes
+     * accumulated in the internal buffers. If the subscriber is COMPLETE, and
+     * has some buffered data, then there is always enough ( to pass downstream ).
+     */
+    private final boolean hasEnoughAccumulatedBytes() {
+        assert Thread.holdsLock(buffersLock);
+        return accumulatedBytes >= bufferSize
+                || (state == COMPLETE && accumulatedBytes > 0);
+    }
+
+    /**
+     * Returns a new, unmodifiable, List<ByteBuffer> containing exactly the
+     * amount of data as required before pushing downstream. The amount of data
+     * may be less than required ( bufferSize ), in the case where the subscriber
+     * is COMPLETE.
+     */
+    private List<ByteBuffer> fromInternalBuffers() {
+        assert Thread.holdsLock(buffersLock);
+        int leftToFill = bufferSize;
+        int state = this.state;
+        assert (state == ACTIVE || state == CANCELLED)
+                ? accumulatedBytes >= leftToFill : true;
+        List<ByteBuffer> dsts = new ArrayList<>();
+
+        ListIterator<ByteBuffer> itr = internalBuffers.listIterator();
+        while (itr.hasNext()) {
+            ByteBuffer b = itr.next();
+            if (b.remaining() <= leftToFill) {
+                itr.remove();
+                if (b.position() != 0)
+                    b = b.slice();  // ensure position = 0 when propagated
+                dsts.add(b);
+                leftToFill -= b.remaining();
+                accumulatedBytes -= b.remaining();
+                if (leftToFill == 0)
+                    break;
+            } else {
+                int prevLimit = b.limit();
+                b.limit(b.position() + leftToFill);
+                ByteBuffer slice = b.slice();
+                dsts.add(slice);
+                b.limit(prevLimit);
+                b.position(b.position() + leftToFill);
+                accumulatedBytes -= leftToFill;
+                leftToFill = 0;
+                break;
+            }
+        }
+        assert (state == ACTIVE || state == CANCELLED)
+                ? leftToFill == 0 : state == COMPLETE;
+        assert (state == ACTIVE || state == CANCELLED)
+                ? remaining(dsts) == bufferSize : state == COMPLETE;
+        assert accumulatedBytes >= 0;
+        assert dsts.stream().noneMatch(b -> b.position() != 0);
+        return Collections.unmodifiableList(dsts);
+    }
+
+    /** Subscription that is passed to the downstream subscriber. */
+    private class DownstreamSubscription implements Flow.Subscription {
+        private final AtomicBoolean cancelled = new AtomicBoolean(); // false
+        private final Demand demand = new Demand();
+        private volatile boolean illegalArg;
+
+        @Override
+        public void request(long n) {
+            if (cancelled.get() || illegalArg) {
+                return;
+            }
+            if (n <= 0L) {
+                // pass the "bad" value upstream so the Publisher can deal with
+                // it appropriately, i.e. invoke onError
+                illegalArg = true;
+                subscription.request(n);
+                return;
+            }
+
+            demand.increase(n);
+
+            pushDemanded();
+        }
+
+        private final SequentialScheduler pushDemandedScheduler =
+                new SequentialScheduler(new PushDemandedTask());
+
+        void pushDemanded() {
+            if (cancelled.get())
+                return;
+            pushDemandedScheduler.runOrSchedule();
+        }
+
+        class PushDemandedTask extends SequentialScheduler.CompleteRestartableTask {
+            @Override
+            public void run() {
+                try {
+                    Throwable t = throwable;
+                    if (t != null) {
+                        pushDemandedScheduler.stop(); // stop the demand scheduler
+                        downstreamSubscriber.onError(t);
+                        return;
+                    }
+
+                    while (true) {
+                        List<ByteBuffer> item;
+                        synchronized (buffersLock) {
+                            if (cancelled.get())
+                                return;
+                            if (!hasEnoughAccumulatedBytes())
+                                break;
+                            if (!demand.tryDecrement())
+                                break;
+                            item = fromInternalBuffers();
+                        }
+                        assert item != null;
+
+                        downstreamSubscriber.onNext(item);
+                    }
+                    if (cancelled.get())
+                        return;
+
+                    // complete only if all data consumed
+                    boolean complete;
+                    synchronized (buffersLock) {
+                        complete = state == COMPLETE && internalBuffers.isEmpty();
+                    }
+                    if (complete) {
+                        assert internalBuffers.isEmpty();
+                        pushDemandedScheduler.stop(); // stop the demand scheduler
+                        downstreamSubscriber.onComplete();
+                        return;
+                    }
+                } catch (Throwable t) {
+                    cancel();  // cancel if there is any error
+                    throw t;
+                }
+
+                boolean requestMore = false;
+                synchronized (buffersLock) {
+                    if (!hasEnoughAccumulatedBytes() && !demand.isFulfilled()) {
+                        // request more upstream data
+                        requestMore = true;
+                    }
+                }
+                if (requestMore)
+                    subscription.request(1);
+            }
+        }
+
+        @Override
+        public void cancel() {
+            if (cancelled.compareAndExchange(false, true))
+                return;  // already cancelled
+
+            state = CANCELLED;  // set CANCELLED state of upstream subscriber
+            subscription.cancel();  // cancel upstream subscription
+            pushDemandedScheduler.stop(); // stop the demand scheduler
+        }
+    }
+
+    @Override
+    public void onSubscribe(Flow.Subscription subscription) {
+        Objects.requireNonNull(subscription);
+        if (this.subscription != null) {
+            subscription.cancel();
+            return;
+        }
+
+        int s = this.state;
+        assert s == UNSUBSCRIBED;
+        state = ACTIVE;
+        this.subscription = subscription;
+        downstreamSubscription = new DownstreamSubscription();
+        downstreamSubscriber.onSubscribe(downstreamSubscription);
+    }
+
+    @Override
+    public void onNext(List<ByteBuffer> item) {
+        Objects.requireNonNull(item);
+
+        int s = state;
+        if (s == CANCELLED)
+            return;
+
+        if (s != ACTIVE)
+            throw new InternalError("onNext on inactive subscriber");
+
+        synchronized (buffersLock) {
+            accumulatedBytes += Utils.accumulateBuffers(internalBuffers, item);
+        }
+
+        downstreamSubscription.pushDemanded();
+    }
+
+    @Override
+    public void onError(Throwable incomingThrowable) {
+        Objects.requireNonNull(incomingThrowable);
+        int s = state;
+        assert s == ACTIVE : "Expected ACTIVE, got:" + s;
+        state = ERROR;
+        Throwable t = this.throwable;
+        assert t == null : "Expected null, got:" + t;
+        this.throwable = incomingThrowable;
+        downstreamSubscription.pushDemanded();
+    }
+
+    @Override
+    public void onComplete() {
+        int s = state;
+        assert s == ACTIVE : "Expected ACTIVE, got:" + s;
+        state = COMPLETE;
+        downstreamSubscription.pushDemanded();
+    }
+
+    @Override
+    public CompletionStage<T> getBody() {
+        return downstreamSubscriber.getBody();
+    }
+}
diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/ConnectionPool.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/ConnectionPool.java
index df627809111..38f9b3d2546 100644
--- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/ConnectionPool.java
+++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/ConnectionPool.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2017, 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
@@ -25,14 +25,24 @@
 
 package jdk.incubator.http;
 
-import java.lang.ref.WeakReference;
+import java.io.IOException;
+import java.lang.System.Logger.Level;
 import java.net.InetSocketAddress;
+import java.nio.ByteBuffer;
+import java.time.Instant;
+import java.time.temporal.ChronoUnit;
+import java.util.ArrayList;
+import java.util.Collections;
 import java.util.HashMap;
+import java.util.Iterator;
 import java.util.LinkedList;
+import java.util.List;
 import java.util.ListIterator;
 import java.util.Objects;
-import java.util.concurrent.atomic.AtomicLong;
-import java.util.concurrent.atomic.AtomicReference;
+import java.util.Optional;
+import java.util.concurrent.Flow;
+import java.util.stream.Collectors;
+import jdk.incubator.http.internal.common.FlowTube;
 import jdk.incubator.http.internal.common.Utils;
 
 /**
@@ -40,34 +50,18 @@ import jdk.incubator.http.internal.common.Utils;
  */
 final class ConnectionPool {
 
-    // These counters are used to distribute ids for debugging
-    // The ACTIVE_CLEANER_COUNTER will tell how many CacheCleaner
-    // are active at a given time. It will increase when a new
-    // CacheCleaner is started and decrease when it exits.
-    static final AtomicLong ACTIVE_CLEANER_COUNTER = new AtomicLong();
-    // The POOL_IDS_COUNTER increases each time a new ConnectionPool
-    // is created. It may wrap and become negative but will never be
-    // decremented.
-    static final AtomicLong POOL_IDS_COUNTER = new AtomicLong();
-    // The cleanerCounter is used to name cleaner threads within a
-    // a connection pool, and increments monotically.
-    // It may wrap and become negative but will never be
-    // decremented.
-    final AtomicLong cleanerCounter = new AtomicLong();
-
     static final long KEEP_ALIVE = Utils.getIntegerNetProperty(
             "jdk.httpclient.keepalive.timeout", 1200); // seconds
+    static final boolean DEBUG = Utils.DEBUG; // Revisit: temporary dev flag.
+    final System.Logger debug = Utils.getDebugLogger(this::dbgString, DEBUG);
 
     // Pools of idle connections
 
-    final HashMap<CacheKey,LinkedList<HttpConnection>> plainPool;
-    final HashMap<CacheKey,LinkedList<HttpConnection>> sslPool;
-    // A monotically increasing id for this connection pool.
-    // It may be negative (that's OK)
-    // Mostly used for debugging purposes when looking at thread dumps.
-    // Global scope.
-    final long poolID = POOL_IDS_COUNTER.incrementAndGet();
-    final AtomicReference<CacheCleaner> cleanerRef;
+    private final HashMap<CacheKey,LinkedList<HttpConnection>> plainPool;
+    private final HashMap<CacheKey,LinkedList<HttpConnection>> sslPool;
+    private final ExpiryList expiryList;
+    private final String dbgTag; // used for debug
+    boolean stopped;
 
     /**
      * Entries in connection pool are keyed by destination address and/or
@@ -110,28 +104,26 @@ final class ConnectionPool {
         }
     }
 
-    static class ExpiryEntry {
-        final HttpConnection connection;
-        final long expiry; // absolute time in seconds of expiry time
-        ExpiryEntry(HttpConnection connection, long expiry) {
-            this.connection = connection;
-            this.expiry = expiry;
-        }
+    ConnectionPool(long clientId) {
+        this("ConnectionPool("+clientId+")");
     }
 
-    final LinkedList<ExpiryEntry> expiryList;
-
     /**
      * There should be one of these per HttpClient.
      */
-    ConnectionPool() {
+    private ConnectionPool(String tag) {
+        dbgTag = tag;
         plainPool = new HashMap<>();
         sslPool = new HashMap<>();
-        expiryList = new LinkedList<>();
-        cleanerRef = new AtomicReference<>();
+        expiryList = new ExpiryList();
+    }
+
+    final String dbgString() {
+        return dbgTag;
     }
 
     void start() {
+        assert !stopped : "Already stopped";
     }
 
     static CacheKey cacheKey(InetSocketAddress destination,
@@ -143,6 +135,7 @@ final class ConnectionPool {
     synchronized HttpConnection getConnection(boolean secure,
                                               InetSocketAddress addr,
                                               InetSocketAddress proxy) {
+        if (stopped) return null;
         CacheKey key = new CacheKey(addr, proxy);
         HttpConnection c = secure ? findConnection(key, sslPool)
                                   : findConnection(key, plainPool);
@@ -153,16 +146,49 @@ final class ConnectionPool {
     /**
      * Returns the connection to the pool.
      */
-    synchronized void returnToPool(HttpConnection conn) {
-        if (conn instanceof PlainHttpConnection) {
-            putConnection(conn, plainPool);
-        } else {
-            putConnection(conn, sslPool);
+    void returnToPool(HttpConnection conn) {
+        returnToPool(conn, Instant.now(), KEEP_ALIVE);
+    }
+
+    // Called also by whitebox tests
+    void returnToPool(HttpConnection conn, Instant now, long keepAlive) {
+
+        // Don't call registerCleanupTrigger while holding a lock,
+        // but register it before the connection is added to the pool,
+        // since we don't want to trigger the cleanup if the connection
+        // is not in the pool.
+        CleanupTrigger cleanup = registerCleanupTrigger(conn);
+
+        // it's possible that cleanup may have been called.
+        synchronized(this) {
+            if (cleanup.isDone()) {
+                return;
+            } else if (stopped) {
+                conn.close();
+                return;
+            }
+            if (conn instanceof PlainHttpConnection) {
+                putConnection(conn, plainPool);
+            } else {
+                assert conn.isSecure();
+                putConnection(conn, sslPool);
+            }
+            expiryList.add(conn, now, keepAlive);
         }
-        addToExpiryList(conn);
         //System.out.println("Return to pool: " + conn);
     }
 
+    private CleanupTrigger registerCleanupTrigger(HttpConnection conn) {
+        // Connect the connection flow to a pub/sub pair that will take the
+        // connection out of the pool and close it if anything happens
+        // while the connection is sitting in the pool.
+        CleanupTrigger cleanup = new CleanupTrigger(conn);
+        FlowTube flow = conn.getConnectionFlow();
+        debug.log(Level.DEBUG, "registering %s", cleanup);
+        flow.connectFlows(cleanup, cleanup);
+        return cleanup;
+    }
+
     private HttpConnection
     findConnection(CacheKey key,
                    HashMap<CacheKey,LinkedList<HttpConnection>> pool) {
@@ -171,20 +197,24 @@ final class ConnectionPool {
             return null;
         } else {
             HttpConnection c = l.removeFirst();
-            removeFromExpiryList(c);
+            expiryList.remove(c);
             return c;
         }
     }
 
     /* called from cache cleaner only  */
-    private void
+    private boolean
     removeFromPool(HttpConnection c,
                    HashMap<CacheKey,LinkedList<HttpConnection>> pool) {
         //System.out.println("cacheCleaner removing: " + c);
-        LinkedList<HttpConnection> l = pool.get(c.cacheKey());
-        assert l != null;
-        boolean wasPresent = l.remove(c);
-        assert wasPresent;
+        assert Thread.holdsLock(this);
+        CacheKey k = c.cacheKey();
+        List<HttpConnection> l = pool.get(k);
+        if (l == null || l.isEmpty()) {
+            pool.remove(k);
+            return false;
+        }
+        return l.remove(c);
     }
 
     private void
@@ -199,135 +229,262 @@ final class ConnectionPool {
         l.add(c);
     }
 
-    static String makeCleanerName(long poolId, long cleanerId) {
-        return "HTTP-Cache-cleaner-" + poolId + "-" + cleanerId;
+    /**
+     * Purge expired connection and return the number of milliseconds
+     * in which the next connection is scheduled to expire.
+     * If no connections are scheduled to be purged return 0.
+     * @return the delay in milliseconds in which the next connection will
+     *         expire.
+     */
+    long purgeExpiredConnectionsAndReturnNextDeadline() {
+        if (!expiryList.purgeMaybeRequired()) return 0;
+        return purgeExpiredConnectionsAndReturnNextDeadline(Instant.now());
     }
 
-    // only runs while entries exist in cache
-    final static class CacheCleaner extends Thread {
+    // Used for whitebox testing
+    long purgeExpiredConnectionsAndReturnNextDeadline(Instant now) {
+        long nextPurge = 0;
 
-        volatile boolean stopping;
-        // A monotically increasing id. May wrap and become negative (that's OK)
-        // Mostly used for debugging purposes when looking at thread dumps.
-        // Scoped per connection pool.
-        final long cleanerID;
-        // A reference to the owning ConnectionPool.
-        // This reference's referent may become null if the HttpClientImpl
-        // that owns this pool is GC'ed.
-        final WeakReference<ConnectionPool> ownerRef;
-
-        CacheCleaner(ConnectionPool owner) {
-            this(owner, owner.cleanerCounter.incrementAndGet());
-        }
-
-        CacheCleaner(ConnectionPool owner, long cleanerID) {
-            super(null, null, makeCleanerName(owner.poolID, cleanerID), 0, false);
-            this.cleanerID = cleanerID;
-            this.ownerRef = new WeakReference<>(owner);
-            setDaemon(true);
-        }
-
-        synchronized boolean stopping() {
-            return stopping || ownerRef.get() == null;
-        }
-
-        synchronized void stopCleaner() {
-            stopping = true;
-        }
-
-        @Override
-        public void run() {
-            ACTIVE_CLEANER_COUNTER.incrementAndGet();
-            try {
-                while (!stopping()) {
-                    try {
-                        Thread.sleep(3000);
-                    } catch (InterruptedException e) {}
-                    ConnectionPool owner = ownerRef.get();
-                    if (owner == null) return;
-                    owner.cleanCache(this);
-                    owner = null;
-                }
-            } finally {
-                ACTIVE_CLEANER_COUNTER.decrementAndGet();
-            }
-        }
-    }
-
-    synchronized void removeFromExpiryList(HttpConnection c) {
-        if (c == null) {
-            return;
-        }
-        ListIterator<ExpiryEntry> li = expiryList.listIterator();
-        while (li.hasNext()) {
-            ExpiryEntry e = li.next();
-            if (e.connection.equals(c)) {
-                li.remove();
-                return;
-            }
-        }
-        CacheCleaner cleaner = this.cleanerRef.get();
-        if (expiryList.isEmpty() && cleaner != null) {
-            this.cleanerRef.compareAndSet(cleaner, null);
-            cleaner.stopCleaner();
-            cleaner.interrupt();
-        }
-    }
-
-    private void cleanCache(CacheCleaner cleaner) {
-        long now = System.currentTimeMillis() / 1000;
-        LinkedList<HttpConnection> closelist = new LinkedList<>();
+        // We may be in the process of adding new elements
+        // to the expiry list - but those elements will not
+        // have outlast their keep alive timer yet since we're
+        // just adding them.
+        if (!expiryList.purgeMaybeRequired()) return nextPurge;
 
+        List<HttpConnection> closelist;
         synchronized (this) {
-            ListIterator<ExpiryEntry> li = expiryList.listIterator();
+            closelist = expiryList.purgeUntil(now);
+            for (HttpConnection c : closelist) {
+                if (c instanceof PlainHttpConnection) {
+                    boolean wasPresent = removeFromPool(c, plainPool);
+                    assert wasPresent;
+                } else {
+                    boolean wasPresent = removeFromPool(c, sslPool);
+                    assert wasPresent;
+                }
+            }
+            nextPurge = now.until(
+                    expiryList.nextExpiryDeadline().orElse(now),
+                    ChronoUnit.MILLIS);
+        }
+        closelist.forEach(this::close);
+        return nextPurge;
+    }
+
+    private void close(HttpConnection c) {
+        try {
+            c.close();
+        } catch (Throwable e) {} // ignore
+    }
+
+    void stop() {
+        List<HttpConnection> closelist = Collections.emptyList();
+        try {
+            synchronized (this) {
+                stopped = true;
+                closelist = expiryList.stream()
+                    .map(e -> e.connection)
+                    .collect(Collectors.toList());
+                expiryList.clear();
+                plainPool.clear();
+                sslPool.clear();
+            }
+        } finally {
+            closelist.forEach(this::close);
+        }
+    }
+
+    static final class ExpiryEntry {
+        final HttpConnection connection;
+        final Instant expiry; // absolute time in seconds of expiry time
+        ExpiryEntry(HttpConnection connection, Instant expiry) {
+            this.connection = connection;
+            this.expiry = expiry;
+        }
+    }
+
+    /**
+     * Manages a LinkedList of sorted ExpiryEntry. The entry with the closer
+     * deadline is at the tail of the list, and the entry with the farther
+     * deadline is at the head. In the most common situation, new elements
+     * will need to be added at the head (or close to it), and expired elements
+     * will need to be purged from the tail.
+     */
+    private static final class ExpiryList {
+        private final LinkedList<ExpiryEntry> list = new LinkedList<>();
+        private volatile boolean mayContainEntries;
+
+        // A loosely accurate boolean whose value is computed
+        // at the end of each operation performed on ExpiryList;
+        // Does not require synchronizing on the ConnectionPool.
+        boolean purgeMaybeRequired() {
+            return mayContainEntries;
+        }
+
+        // Returns the next expiry deadline
+        // should only be called while holding a synchronization
+        // lock on the ConnectionPool
+        Optional<Instant> nextExpiryDeadline() {
+            if (list.isEmpty()) return Optional.empty();
+            else return Optional.of(list.getLast().expiry);
+        }
+
+        // should only be called while holding a synchronization
+        // lock on the ConnectionPool
+        void add(HttpConnection conn) {
+            add(conn, Instant.now(), KEEP_ALIVE);
+        }
+
+        // Used by whitebox test.
+        void add(HttpConnection conn, Instant now, long keepAlive) {
+            Instant then = now.truncatedTo(ChronoUnit.SECONDS)
+                    .plus(keepAlive, ChronoUnit.SECONDS);
+
+            // Elements with the farther deadline are at the head of
+            // the list. It's more likely that the new element will
+            // have the farthest deadline, and will need to be inserted
+            // at the head of the list, so we're using an ascending
+            // list iterator to find the right insertion point.
+            ListIterator<ExpiryEntry> li = list.listIterator();
             while (li.hasNext()) {
                 ExpiryEntry entry = li.next();
-                if (entry.expiry <= now) {
+
+                if (then.isAfter(entry.expiry)) {
+                    li.previous();
+                    // insert here
+                    li.add(new ExpiryEntry(conn, then));
+                    mayContainEntries = true;
+                    return;
+                }
+            }
+            // last (or first) element of list (the last element is
+            // the first when the list is empty)
+            list.add(new ExpiryEntry(conn, then));
+            mayContainEntries = true;
+        }
+
+        // should only be called while holding a synchronization
+        // lock on the ConnectionPool
+        void remove(HttpConnection c) {
+            if (c == null || list.isEmpty()) return;
+            ListIterator<ExpiryEntry> li = list.listIterator();
+            while (li.hasNext()) {
+                ExpiryEntry e = li.next();
+                if (e.connection.equals(c)) {
+                    li.remove();
+                    mayContainEntries = !list.isEmpty();
+                    return;
+                }
+            }
+        }
+
+        // should only be called while holding a synchronization
+        // lock on the ConnectionPool.
+        // Purge all elements whose deadline is before now (now included).
+        List<HttpConnection> purgeUntil(Instant now) {
+            if (list.isEmpty()) return Collections.emptyList();
+
+            List<HttpConnection> closelist = new ArrayList<>();
+
+            // elements with the closest deadlines are at the tail
+            // of the queue, so we're going to use a descending iterator
+            // to remove them, and stop when we find the first element
+            // that has not expired yet.
+            Iterator<ExpiryEntry> li = list.descendingIterator();
+            while (li.hasNext()) {
+                ExpiryEntry entry = li.next();
+                // use !isAfter instead of isBefore in order to
+                // remove the entry if its expiry == now
+                if (!entry.expiry.isAfter(now)) {
                     li.remove();
                     HttpConnection c = entry.connection;
                     closelist.add(c);
-                    if (c instanceof PlainHttpConnection) {
-                        removeFromPool(c, plainPool);
-                    } else {
-                        removeFromPool(c, sslPool);
-                    }
-                }
-            }
-            if (expiryList.isEmpty() && cleaner != null) {
-                this.cleanerRef.compareAndSet(cleaner, null);
-                cleaner.stopCleaner();
+                } else break; // the list is sorted
             }
+            mayContainEntries = !list.isEmpty();
+            return closelist;
         }
-        for (HttpConnection c : closelist) {
-            //System.out.println ("KAC: closing " + c);
-            c.close();
+
+        // should only be called while holding a synchronization
+        // lock on the ConnectionPool
+        java.util.stream.Stream<ExpiryEntry> stream() {
+            return list.stream();
+        }
+
+        // should only be called while holding a synchronization
+        // lock on the ConnectionPool
+        void clear() {
+            list.clear();
+            mayContainEntries = false;
         }
     }
 
-    private synchronized void addToExpiryList(HttpConnection conn) {
-        long now = System.currentTimeMillis() / 1000;
-        long then = now + KEEP_ALIVE;
-        if (expiryList.isEmpty()) {
-            CacheCleaner cleaner = new CacheCleaner(this);
-            if (this.cleanerRef.compareAndSet(null, cleaner)) {
-                cleaner.start();
+    void cleanup(HttpConnection c, Throwable error) {
+        debug.log(Level.DEBUG,
+                  "%s : ConnectionPool.cleanup(%s)",
+                  String.valueOf(c.getConnectionFlow()),
+                  error);
+        synchronized(this) {
+            if (c instanceof PlainHttpConnection) {
+                removeFromPool(c, plainPool);
+            } else {
+                assert c.isSecure();
+                removeFromPool(c, sslPool);
             }
-            expiryList.add(new ExpiryEntry(conn, then));
-            return;
+            expiryList.remove(c);
         }
-
-        ListIterator<ExpiryEntry> li = expiryList.listIterator();
-        while (li.hasNext()) {
-            ExpiryEntry entry = li.next();
-
-            if (then > entry.expiry) {
-                li.previous();
-                // insert here
-                li.add(new ExpiryEntry(conn, then));
-                return;
-            }
-        }
-        // first element of list
-        expiryList.add(new ExpiryEntry(conn, then));
+        c.close();
     }
+
+    /**
+     * An object that subscribes to the flow while the connection is in
+     * the pool. Anything that comes in will cause the connection to be closed
+     * and removed from the pool.
+     */
+    private final class CleanupTrigger implements
+            FlowTube.TubeSubscriber, FlowTube.TubePublisher,
+            Flow.Subscription {
+
+        private final HttpConnection connection;
+        private volatile boolean done;
+
+        public CleanupTrigger(HttpConnection connection) {
+            this.connection = connection;
+        }
+
+        public boolean isDone() { return done;}
+
+        private void triggerCleanup(Throwable error) {
+            done = true;
+            cleanup(connection, error);
+        }
+
+        @Override public void request(long n) {}
+        @Override public void cancel() {}
+
+        @Override
+        public void onSubscribe(Flow.Subscription subscription) {
+            subscription.request(1);
+        }
+        @Override
+        public void onError(Throwable error) { triggerCleanup(error); }
+        @Override
+        public void onComplete() { triggerCleanup(null); }
+        @Override
+        public void onNext(List<ByteBuffer> item) {
+            triggerCleanup(new IOException("Data received while in pool"));
+        }
+
+        @Override
+        public void subscribe(Flow.Subscriber<? super List<ByteBuffer>> subscriber) {
+            subscriber.onSubscribe(this);
+        }
+
+        @Override
+        public String toString() {
+            return "CleanupTrigger(" + connection.getConnectionFlow() + ")";
+        }
+
+    }
+
 }
diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/CookieFilter.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/CookieFilter.java
index 204d5d03b7d..8ebf726f04a 100644
--- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/CookieFilter.java
+++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/CookieFilter.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2017, 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
@@ -26,7 +26,7 @@
 package jdk.incubator.http;
 
 import java.io.IOException;
-import java.net.CookieManager;
+import java.net.CookieHandler;
 import java.util.List;
 import java.util.Map;
 import java.util.Optional;
@@ -41,11 +41,11 @@ class CookieFilter implements HeaderFilter {
     @Override
     public void request(HttpRequestImpl r, MultiExchange<?,?> e) throws IOException {
         HttpClientImpl client = e.client();
-        Optional<CookieManager> cookieManOpt = client.cookieManager();
-        if (cookieManOpt.isPresent()) {
-            CookieManager cookieMan = cookieManOpt.get();
+        Optional<CookieHandler> cookieHandlerOpt = client.cookieHandler();
+        if (cookieHandlerOpt.isPresent()) {
+            CookieHandler cookieHandler = cookieHandlerOpt.get();
             Map<String,List<String>> userheaders = r.getUserHeaders().map();
-            Map<String,List<String>> cookies = cookieMan.get(r.uri(), userheaders);
+            Map<String,List<String>> cookies = cookieHandler.get(r.uri(), userheaders);
 
             // add the returned cookies
             HttpHeadersImpl systemHeaders = r.getSystemHeaders();
@@ -74,11 +74,11 @@ class CookieFilter implements HeaderFilter {
         HttpRequestImpl request = r.request();
         Exchange<?> e = r.exchange;
         Log.logTrace("Response: processing cookies for {0}", request.uri());
-        Optional<CookieManager> cookieManOpt = e.client().cookieManager();
-        if (cookieManOpt.isPresent()) {
-            CookieManager cookieMan = cookieManOpt.get();
+        Optional<CookieHandler> cookieHandlerOpt = e.client().cookieHandler();
+        if (cookieHandlerOpt.isPresent()) {
+            CookieHandler cookieHandler = cookieHandlerOpt.get();
             Log.logTrace("Response: parsing cookies from {0}", hdrs.map());
-            cookieMan.put(request.uri(), hdrs.map());
+            cookieHandler.put(request.uri(), hdrs.map());
         } else {
             Log.logTrace("Response: No cookie manager found for {0}",
                          request.uri());
diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/DefaultPublisher.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/DefaultPublisher.java
deleted file mode 100644
index ea0137b4bbe..00000000000
--- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/DefaultPublisher.java
+++ /dev/null
@@ -1,143 +0,0 @@
-/*
- * Copyright (c) 2016, 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.  Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * 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 jdk.incubator.http;
-
-import java.util.Objects;
-import java.util.Optional;
-import java.util.concurrent.Executor;
-import java.util.concurrent.Flow;
-import java.util.concurrent.RejectedExecutionException;
-import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.concurrent.atomic.AtomicLong;
-import java.util.concurrent.locks.Condition;
-import java.util.concurrent.locks.Lock;
-import java.util.concurrent.locks.ReentrantLock;
-import java.util.function.Supplier;
-
-class DefaultPublisher<T> implements Flow.Publisher<T> {
-
-    private final Supplier<Optional<T>> supplier;
-    // this executor will be wrapped in another executor
-    // which may override it and just run in the calling thread
-    // if it knows the user call is blocking
-    private final Executor executor;
-
-    /**
-     * Supplier returns non empty Optionals until final
-     */
-    DefaultPublisher(Supplier<Optional<T>> supplier, Executor executor) {
-        this.supplier = supplier;
-        this.executor = executor;
-    }
-
-    @Override
-    public void subscribe(Flow.Subscriber<? super T> subscriber) {
-        try {
-            subscriber.onSubscribe(new Subscription(subscriber));
-        } catch (RejectedExecutionException e) {
-            subscriber.onError(new IllegalStateException(e));
-        }
-    }
-
-    private class Subscription implements Flow.Subscription {
-
-        private final Flow.Subscriber<? super T> subscriber;
-        private final AtomicBoolean done = new AtomicBoolean();
-
-        private final AtomicLong demand = new AtomicLong();
-
-        private final Lock consumerLock = new ReentrantLock();
-        private final Condition consumerAlarm = consumerLock.newCondition();
-
-        Subscription(Flow.Subscriber<? super T> subscriber) {
-            this.subscriber = subscriber;
-
-            executor.execute(() -> {
-                try {
-                    while (!done.get()) {
-                        consumerLock.lock();
-                        try {
-                            while (!done.get() && demand.get() == 0) {
-                                consumerAlarm.await();
-                            }
-                        } finally {
-                            consumerLock.unlock();
-                        }
-
-                        long nbItemsDemanded = demand.getAndSet(0);
-                        for (long i = 0; i < nbItemsDemanded && !done.get(); i++) {
-                            try {
-                                Optional<T> item = Objects.requireNonNull(supplier.get());
-                                if (item.isPresent()) {
-                                    subscriber.onNext(item.get());
-                                } else {
-                                    if (done.compareAndSet(false, true)) {
-                                        subscriber.onComplete();
-                                    }
-                                }
-                            } catch (RuntimeException e) {
-                                if (done.compareAndSet(false, true)) {
-                                    subscriber.onError(e);
-                                }
-                            }
-                        }
-                    }
-                } catch (InterruptedException e) {
-                    Thread.currentThread().interrupt();
-                    if (done.compareAndSet(false, true)) {
-                        subscriber.onError(e);
-                    }
-                }
-            });
-        }
-
-        @Override
-        public void request(long n) {
-            if (!done.get() && n > 0) {
-                demand.updateAndGet(d -> (d + n > 0) ? d + n : Long.MAX_VALUE);
-                wakeConsumer();
-            } else if (done.compareAndSet(false, true)) {
-                subscriber.onError(new IllegalArgumentException("request(" + n + ")"));
-            }
-        }
-
-        @Override
-        public void cancel() {
-            done.set(true);
-            wakeConsumer();
-        }
-
-        private void wakeConsumer() {
-            consumerLock.lock();
-            try {
-                consumerAlarm.signal();
-            } finally {
-                consumerLock.unlock();
-            }
-        }
-
-    }
-}
diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/Exchange.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/Exchange.java
index 1d8abede2af..2cead021166 100644
--- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/Exchange.java
+++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/Exchange.java
@@ -26,26 +26,23 @@
 package jdk.incubator.http;
 
 import java.io.IOException;
-import java.io.UncheckedIOException;
+import java.lang.System.Logger.Level;
 import java.net.InetSocketAddress;
 import java.net.ProxySelector;
-import java.net.SocketPermission;
 import java.net.URI;
 import java.net.URISyntaxException;
 import java.net.URLPermission;
 import java.security.AccessControlContext;
-import java.security.AccessController;
-import java.security.PrivilegedAction;
-import java.security.PrivilegedActionException;
-import java.security.PrivilegedExceptionAction;
-import java.util.LinkedList;
 import java.util.List;
+import java.util.Map;
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.Executor;
 import jdk.incubator.http.internal.common.MinimalFuture;
 import jdk.incubator.http.internal.common.Utils;
 import jdk.incubator.http.internal.common.Log;
 
+import static jdk.incubator.http.internal.common.Utils.permissionForProxy;
+
 /**
  * One request/response exchange (handles 100/101 intermediate response also).
  * depth field used to track number of times a new request is being sent
@@ -61,19 +58,22 @@ import jdk.incubator.http.internal.common.Log;
  */
 final class Exchange<T> {
 
+    static final boolean DEBUG = Utils.DEBUG; // Revisit: temporary dev flag.
+    final System.Logger  debug = Utils.getDebugLogger(this::dbgString, DEBUG);
+
     final HttpRequestImpl request;
     final HttpClientImpl client;
     volatile ExchangeImpl<T> exchImpl;
+    volatile CompletableFuture<? extends ExchangeImpl<T>> exchangeCF;
     // used to record possible cancellation raised before the exchImpl
     // has been established.
     private volatile IOException failed;
-    final List<SocketPermission> permissions = new LinkedList<>();
     final AccessControlContext acc;
     final MultiExchange<?,T> multi;
     final Executor parentExecutor;
-    final HttpRequest.BodyProcessor requestProcessor;
     boolean upgrading; // to HTTP/2
     final PushGroup<?,T> pushGroup;
+    final String dbgTag;
 
     Exchange(HttpRequestImpl request, MultiExchange<?,T> multi) {
         this.request = request;
@@ -82,8 +82,8 @@ final class Exchange<T> {
         this.multi = multi;
         this.acc = multi.acc;
         this.parentExecutor = multi.executor;
-        this.requestProcessor = request.requestProcessor;
         this.pushGroup = multi.pushGroup;
+        this.dbgTag = "Exchange";
     }
 
     /* If different AccessControlContext to be used  */
@@ -97,8 +97,8 @@ final class Exchange<T> {
         this.client = multi.client();
         this.multi = multi;
         this.parentExecutor = multi.executor;
-        this.requestProcessor = request.requestProcessor;
         this.pushGroup = multi.pushGroup;
+        this.dbgTag = "Exchange";
     }
 
     PushGroup<?,T> getPushGroup() {
@@ -117,18 +117,35 @@ final class Exchange<T> {
         return client;
     }
 
-    public Response response() throws IOException, InterruptedException {
-        return responseImpl(null);
-    }
-
-    public T readBody(HttpResponse.BodyHandler<T> responseHandler) throws IOException {
-        // The connection will not be returned to the pool in the case of WebSocket
-        return exchImpl.readBody(responseHandler, !request.isWebSocket());
-    }
 
     public CompletableFuture<T> readBodyAsync(HttpResponse.BodyHandler<T> handler) {
         // The connection will not be returned to the pool in the case of WebSocket
-        return exchImpl.readBodyAsync(handler, !request.isWebSocket(), parentExecutor);
+        return exchImpl.readBodyAsync(handler, !request.isWebSocket(), parentExecutor)
+                .whenComplete((r,t) -> exchImpl.completed());
+    }
+
+    /**
+     * Called after a redirect or similar kind of retry where a body might
+     * be sent but we don't want it. Should send a RESET in h2. For http/1.1
+     * we can consume small quantity of data, or close the connection in
+     * other cases.
+     */
+    public CompletableFuture<Void> ignoreBody() {
+        return exchImpl.ignoreBody();
+    }
+
+    /**
+     * Called when a new exchange is created to replace this exchange.
+     * At this point it is guaranteed that readBody/readBodyAsync will
+     * not be called.
+     */
+    public void released() {
+        ExchangeImpl<?> impl = exchImpl;
+        if (impl != null) impl.released();
+        // Don't set exchImpl to null here. We need to keep
+        // it alive until it's replaced by a Stream in wrapForUpgrade.
+        // Setting it to null here might get it GC'ed too early, because
+        // the Http1Response is now only weakly referenced by the Selector.
     }
 
     public void cancel() {
@@ -153,19 +170,15 @@ final class Exchange<T> {
         ExchangeImpl<?> impl = exchImpl;
         if (impl != null) {
             // propagate the exception to the impl
+            debug.log(Level.DEBUG, "Cancelling exchImpl: %s", exchImpl);
             impl.cancel(cause);
         } else {
-            try {
-                // no impl yet. record the exception
-                failed = cause;
-                // now call checkCancelled to recheck the impl.
-                // if the failed state is set and the impl is not null, reset
-                // the failed state and propagate the exception to the impl.
-                checkCancelled(false);
-            } catch (IOException x) {
-                // should not happen - we passed 'false' above
-                throw new UncheckedIOException(x);
-            }
+            // no impl yet. record the exception
+            failed = cause;
+            // now call checkCancelled to recheck the impl.
+            // if the failed state is set and the impl is not null, reset
+            // the failed state and propagate the exception to the impl.
+            checkCancelled();
         }
     }
 
@@ -175,37 +188,34 @@ final class Exchange<T> {
     // will persist until the exception can be raised and the failed state
     // can be cleared.
     // Takes care of possible race conditions.
-    private void checkCancelled(boolean throwIfNoImpl) throws IOException {
+    private void checkCancelled() {
         ExchangeImpl<?> impl = null;
         IOException cause = null;
+        CompletableFuture<? extends ExchangeImpl<T>> cf = null;
         if (failed != null) {
             synchronized(this) {
                 cause = failed;
                 impl = exchImpl;
-                if (throwIfNoImpl || impl != null) {
-                    // The exception will be raised by one of the two methods
-                    // below: reset the failed state.
-                    failed = null;
-                }
+                cf = exchangeCF;
             }
         }
         if (cause == null) return;
         if (impl != null) {
             // The exception is raised by propagating it to the impl.
+            debug.log(Level.DEBUG, "Cancelling exchImpl: %s", impl);
             impl.cancel(cause);
-        } else if (throwIfNoImpl) {
-            // The exception is raised by throwing it immediately
-            throw cause;
+            failed = null;
         } else {
             Log.logTrace("Exchange: request [{0}/timeout={1}ms] no impl is set."
                          + "\n\tCan''t cancel yet with {2}",
                          request.uri(),
-                         request.duration() == null ? -1 :
+                         request.timeout().isPresent() ?
                          // calling duration.toMillis() can throw an exception.
                          // this is just debugging, we don't care if it overflows.
-                         (request.duration().getSeconds() * 1000
-                          + request.duration().getNano() / 1000000),
+                         (request.timeout().get().getSeconds() * 1000
+                          + request.timeout().get().getNano() / 1000000) : -1,
                          cause);
+            if (cf != null) cf.completeExceptionally(cause);
         }
     }
 
@@ -214,92 +224,51 @@ final class Exchange<T> {
         request.setH2Upgrade(client.client2());
     }
 
-    static final SocketPermission[] SOCKET_ARRAY = new SocketPermission[0];
-
-    Response responseImpl(HttpConnection connection)
-        throws IOException, InterruptedException
-    {
-        SecurityException e = securityCheck(acc);
-        if (e != null) {
-            throw e;
-        }
-
-        if (permissions.size() > 0) {
-            try {
-                return AccessController.doPrivileged(
-                        (PrivilegedExceptionAction<Response>)() ->
-                             responseImpl0(connection),
-                        null,
-                        permissions.toArray(SOCKET_ARRAY));
-            } catch (Throwable ee) {
-                if (ee instanceof PrivilegedActionException) {
-                    ee = ee.getCause();
-                }
-                if (ee instanceof IOException) {
-                    throw (IOException) ee;
-                } else {
-                    throw new RuntimeException(ee); // TODO: fix
-                }
-            }
-        } else {
-            return responseImpl0(connection);
-        }
+    synchronized IOException getCancelCause() {
+        return failed;
     }
 
     // get/set the exchange impl, solving race condition issues with
     // potential concurrent calls to cancel() or cancel(IOException)
-    private void establishExchange(HttpConnection connection)
-        throws IOException, InterruptedException
-    {
-        // check if we have been cancelled first.
-        checkCancelled(true);
-        // not yet cancelled: create/get a new impl
-        exchImpl = ExchangeImpl.get(this, connection);
-        // recheck for cancelled, in case of race conditions
-        checkCancelled(true);
-        // now we're good to go. because exchImpl is no longer null
-        // cancel() will be able to propagate directly to the impl
-        // after this point.
-    }
-
-    private Response responseImpl0(HttpConnection connection)
-        throws IOException, InterruptedException
-    {
-        establishExchange(connection);
-        if (request.expectContinue()) {
-            Log.logTrace("Sending Expect: 100-Continue");
-            request.addSystemHeader("Expect", "100-Continue");
-            exchImpl.sendHeadersOnly();
-
-            Log.logTrace("Waiting for 407-Expectation-Failed or 100-Continue");
-            Response resp = exchImpl.getResponse();
-            HttpResponseImpl.logResponse(resp);
-            int rcode = resp.statusCode();
-            if (rcode != 100) {
-                Log.logTrace("Expectation failed: Received {0}",
-                             rcode);
-                if (upgrading && rcode == 101) {
-                    throw new IOException(
-                        "Unable to handle 101 while waiting for 100-Continue");
-                }
-                return resp;
-            }
-
-            Log.logTrace("Received 100-Continue: sending body");
-            exchImpl.sendBody();
-
-            Log.logTrace("Body sent: waiting for response");
-            resp = exchImpl.getResponse();
-            HttpResponseImpl.logResponse(resp);
-
-            return checkForUpgrade(resp, exchImpl);
-        } else {
-            exchImpl.sendHeadersOnly();
-            exchImpl.sendBody();
-            Response resp = exchImpl.getResponse();
-            HttpResponseImpl.logResponse(resp);
-            return checkForUpgrade(resp, exchImpl);
+    private CompletableFuture<? extends ExchangeImpl<T>>
+    establishExchange(HttpConnection connection) {
+        if (debug.isLoggable(Level.DEBUG)) {
+            debug.log(Level.DEBUG,
+                    "establishing exchange for %s,%n\t proxy=%s",
+                    request,
+                    request.proxy());
         }
+        // check if we have been cancelled first.
+        Throwable t = getCancelCause();
+        checkCancelled();
+        if (t != null) {
+            return MinimalFuture.failedFuture(t);
+        }
+
+        CompletableFuture<? extends ExchangeImpl<T>> cf, res;
+        cf = ExchangeImpl.get(this, connection);
+        // We should probably use a VarHandle to get/set exchangeCF
+        // instead - as we need CAS semantics.
+        synchronized (this) { exchangeCF = cf; };
+        res = cf.whenComplete((r,x) -> {
+            synchronized(Exchange.this) {
+                if (exchangeCF == cf) exchangeCF = null;
+            }
+        });
+        checkCancelled();
+        return res.thenCompose((eimpl) -> {
+                    // recheck for cancelled, in case of race conditions
+                    exchImpl = eimpl;
+                    IOException tt = getCancelCause();
+                    checkCancelled();
+                    if (tt != null) {
+                        return MinimalFuture.failedFuture(tt);
+                    } else {
+                        // Now we're good to go. Because exchImpl is no longer
+                        // null cancel() will be able to propagate directly to
+                        // the impl after this point ( if needed ).
+                        return MinimalFuture.completedFuture(eimpl);
+                    } });
     }
 
     // Completed HttpResponse will be null if response succeeded
@@ -310,35 +279,23 @@ final class Exchange<T> {
     }
 
     CompletableFuture<Response> responseAsyncImpl(HttpConnection connection) {
-        SecurityException e = securityCheck(acc);
+        SecurityException e = checkPermissions();
         if (e != null) {
             return MinimalFuture.failedFuture(e);
-        }
-        if (permissions.size() > 0) {
-            return AccessController.doPrivileged(
-                    (PrivilegedAction<CompletableFuture<Response>>)() ->
-                        responseAsyncImpl0(connection),
-                    null,
-                    permissions.toArray(SOCKET_ARRAY));
         } else {
             return responseAsyncImpl0(connection);
         }
     }
 
     CompletableFuture<Response> responseAsyncImpl0(HttpConnection connection) {
-        try {
-            establishExchange(connection);
-        } catch (IOException | InterruptedException e) {
-            return MinimalFuture.failedFuture(e);
-        }
         if (request.expectContinue()) {
             request.addSystemHeader("Expect", "100-Continue");
             Log.logTrace("Sending Expect: 100-Continue");
-            return exchImpl
-                    .sendHeadersAsync()
+            return establishExchange(connection)
+                    .thenCompose((ex) -> ex.sendHeadersAsync())
                     .thenCompose(v -> exchImpl.getResponseAsync(parentExecutor))
                     .thenCompose((Response r1) -> {
-                        HttpResponseImpl.logResponse(r1);
+                        Log.logResponse(r1::toString);
                         int rcode = r1.statusCode();
                         if (rcode == 100) {
                             Log.logTrace("Received 100-Continue: sending body");
@@ -361,8 +318,8 @@ final class Exchange<T> {
                         }
                     });
         } else {
-            CompletableFuture<Response> cf = exchImpl
-                    .sendHeadersAsync()
+            CompletableFuture<Response> cf = establishExchange(connection)
+                    .thenCompose((ex) -> ex.sendHeadersAsync())
                     .thenCompose(ExchangeImpl::sendBodyAsync)
                     .thenCompose(exIm -> exIm.getResponseAsync(parentExecutor));
             cf = wrapForUpgrade(cf);
@@ -381,15 +338,15 @@ final class Exchange<T> {
     private CompletableFuture<Response> wrapForLog(CompletableFuture<Response> cf) {
         if (Log.requests()) {
             return cf.thenApply(response -> {
-                HttpResponseImpl.logResponse(response);
+                Log.logResponse(response::toString);
                 return response;
             });
         }
         return cf;
     }
 
-    HttpResponse.BodyProcessor<T> ignoreBody(int status, HttpHeaders hdrs) {
-        return HttpResponse.BodyProcessor.discard((T)null);
+    HttpResponse.BodySubscriber<T> ignoreBody(int status, HttpHeaders hdrs) {
+        return HttpResponse.BodySubscriber.discard((T)null);
     }
 
     // if this response was received in reply to an upgrade
@@ -406,50 +363,59 @@ final class Exchange<T> {
             // check for 101 switching protocols
             // 101 responses are not supposed to contain a body.
             //    => should we fail if there is one?
+            debug.log(Level.DEBUG, "Upgrading async %s", e.connection());
             return e.readBodyAsync(this::ignoreBody, false, parentExecutor)
-                .thenCompose((T v) -> // v is null
-                     Http2Connection.createAsync(e.connection(),
+                .thenCompose((T v) -> {// v is null
+                    debug.log(Level.DEBUG, "Ignored body");
+                    // we pass e::getBuffer to allow the ByteBuffers to accumulate
+                    // while we build the Http2Connection
+                    return Http2Connection.createAsync(e.connection(),
                                                  client.client2(),
-                                                 this, e.getBuffer())
+                                                 this, e::drainLeftOverBytes)
                         .thenCompose((Http2Connection c) -> {
                             c.putConnection();
                             Stream<T> s = c.getStream(1);
-                            exchImpl = s;
+                            if (s == null) {
+                                // s can be null if an exception occurred
+                                // asynchronously while sending the preface.
+                                Throwable t = c.getRecordedCause();
+                                if (t != null) {
+                                    return MinimalFuture.failedFuture(
+                                            new IOException("Can't get stream 1: " + t, t));
+                                }
+                            }
+                            exchImpl.released();
+                            Throwable t;
+                            // There's a race condition window where an external
+                            // thread (SelectorManager) might complete the
+                            // exchange in timeout at the same time where we're
+                            // trying to switch the exchange impl.
+                            // 'failed' will be reset to null after
+                            // exchImpl.cancel() has completed, so either we
+                            // will observe failed != null here, or we will
+                            // observe e.getCancelCause() != null, or the
+                            // timeout exception will be routed to 's'.
+                            // Either way, we need to relay it to s.
+                            synchronized (this) {
+                                exchImpl = s;
+                                t = failed;
+                            }
+                            // Check whether the HTTP/1.1 was cancelled.
+                            if (t == null) t = e.getCancelCause();
+                            // if HTTP/1.1 exchange was timed out, don't
+                            // try to go further.
+                            if (t instanceof HttpTimeoutException) {
+                                 s.cancelImpl(t);
+                                 return MinimalFuture.failedFuture(t);
+                            }
+                            debug.log(Level.DEBUG, "Getting response async %s", s);
                             return s.getResponseAsync(null);
-                        })
+                        });}
                 );
         }
         return MinimalFuture.completedFuture(resp);
     }
 
-    private Response checkForUpgrade(Response resp,
-                                             ExchangeImpl<T> ex)
-        throws IOException, InterruptedException
-    {
-        int rcode = resp.statusCode();
-        if (upgrading && (rcode == 101)) {
-            Http1Exchange<T> e = (Http1Exchange<T>) ex;
-
-            // 101 responses are not supposed to contain a body.
-            //    => should we fail if there is one?
-            //    => readBody called here by analogy with
-            //       checkForUpgradeAsync above
-            e.readBody(this::ignoreBody, false);
-
-            // must get connection from Http1Exchange
-            Http2Connection h2con = new Http2Connection(e.connection(),
-                                                        client.client2(),
-                                                        this, e.getBuffer());
-            h2con.putConnection();
-            Stream<T> s = h2con.getStream(1);
-            exchImpl = s;
-            Response xx = s.getResponse();
-            HttpResponseImpl.logResponse(xx);
-            return xx;
-        }
-        return resp;
-    }
-
     private URI getURIForSecurityCheck() {
         URI u;
         String method = request.method();
@@ -476,91 +442,65 @@ final class Exchange<T> {
     }
 
     /**
-     * Do the security check and return any exception.
-     * Return null if no check needed or passes.
-     *
-     * Also adds any generated permissions to the "permissions" list.
+     * Returns the security permission required for the given details.
+     * If method is CONNECT, then uri must be of form "scheme://host:port"
      */
-    private SecurityException securityCheck(AccessControlContext acc) {
+    private static URLPermission permissionForServer(URI uri,
+                                                     String method,
+                                                     Map<String, List<String>> headers) {
+        if (method.equals("CONNECT")) {
+            return new URLPermission(uri.toString(), "CONNECT");
+        } else {
+            return Utils.permissionForServer(uri, method, headers.keySet().stream());
+        }
+    }
+
+    /**
+     * Performs the necessary security permission checks required to retrieve
+     * the response. Returns a security exception representing the denied
+     * permission, or null if all checks pass or there is no security manager.
+     */
+    private SecurityException checkPermissions() {
+        String method = request.method();
         SecurityManager sm = System.getSecurityManager();
-        if (sm == null) {
+        if (sm == null || method.equals("CONNECT")) {
+            // tunneling will have a null acc, which is fine. The proxy
+            // permission check will have already been preformed.
             return null;
         }
 
-        String method = request.method();
         HttpHeaders userHeaders = request.getUserHeaders();
         URI u = getURIForSecurityCheck();
-        URLPermission p = Utils.getPermission(u, method, userHeaders.map());
+        URLPermission p = permissionForServer(u, method, userHeaders.map());
 
         try {
             assert acc != null;
             sm.checkPermission(p, acc);
-            permissions.add(getSocketPermissionFor(u));
         } catch (SecurityException e) {
             return e;
         }
-        ProxySelector ps = client.proxy().orElse(null);
+        ProxySelector ps = client.proxySelector();
         if (ps != null) {
-            InetSocketAddress proxy = (InetSocketAddress)
-                    ps.select(u).get(0).address(); // TODO: check this
-            // may need additional check
             if (!method.equals("CONNECT")) {
-                // a direct http proxy. Need to check access to proxy
-                try {
-                    u = new URI("socket", null, proxy.getHostString(),
-                        proxy.getPort(), null, null, null);
-                } catch (URISyntaxException e) {
-                    throw new InternalError(e); // shouldn't happen
+                // a non-tunneling HTTP proxy. Need to check access
+                URLPermission proxyPerm = permissionForProxy(request.proxy());
+                if (proxyPerm != null) {
+                    try {
+                        sm.checkPermission(proxyPerm, acc);
+                    } catch (SecurityException e) {
+                        return e;
+                    }
                 }
-                p = new URLPermission(u.toString(), "CONNECT");
-                try {
-                    sm.checkPermission(p, acc);
-                } catch (SecurityException e) {
-                    permissions.clear();
-                    return e;
-                }
-                String sockperm = proxy.getHostString() +
-                        ":" + Integer.toString(proxy.getPort());
-
-                permissions.add(new SocketPermission(sockperm, "connect,resolve"));
             }
         }
         return null;
     }
 
-    HttpClient.Redirect followRedirects() {
-        return client.followRedirects();
-    }
-
     HttpClient.Version version() {
         return multi.version();
     }
 
-    private static SocketPermission getSocketPermissionFor(URI url) {
-        if (System.getSecurityManager() == null) {
-            return null;
-        }
-
-        StringBuilder sb = new StringBuilder();
-        String host = url.getHost();
-        sb.append(host);
-        int port = url.getPort();
-        if (port == -1) {
-            String scheme = url.getScheme();
-            if ("http".equals(scheme)) {
-                sb.append(":80");
-            } else { // scheme must be https
-                sb.append(":443");
-            }
-        } else {
-            sb.append(':')
-              .append(Integer.toString(port));
-        }
-        String target = sb.toString();
-        return new SocketPermission(target, "connect");
-    }
-
-    AccessControlContext getAccessControlContext() {
-        return acc;
+    String dbgString() {
+        return dbgTag;
     }
 }
diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/ExchangeImpl.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/ExchangeImpl.java
index 3d41179d903..3b4b6fc6983 100644
--- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/ExchangeImpl.java
+++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/ExchangeImpl.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2017, 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
@@ -26,10 +26,13 @@
 package jdk.incubator.http;
 
 import java.io.IOException;
+import java.lang.System.Logger.Level;
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.Executor;
+import java.util.function.Function;
 import jdk.incubator.http.internal.common.MinimalFuture;
 import static jdk.incubator.http.HttpClient.Version.HTTP_1_1;
+import jdk.incubator.http.internal.common.Utils;
 
 /**
  * Splits request so that headers and body can be sent separately with optional
@@ -46,6 +49,10 @@ import static jdk.incubator.http.HttpClient.Version.HTTP_1_1;
  */
 abstract class ExchangeImpl<T> {
 
+    static final boolean DEBUG = Utils.DEBUG; // Revisit: temporary dev flag.
+    private static final System.Logger DEBUG_LOGGER =
+            Utils.getDebugLogger("ExchangeImpl"::toString, DEBUG);
+
     final Exchange<T> exchange;
 
     ExchangeImpl(Exchange<T> e) {
@@ -68,94 +75,129 @@ abstract class ExchangeImpl<T> {
      * Initiates a new exchange and assigns it to a connection if one exists
      * already. connection usually null.
      */
-    static <U> ExchangeImpl<U> get(Exchange<U> exchange, HttpConnection connection)
-        throws IOException, InterruptedException
+    static <U> CompletableFuture<? extends ExchangeImpl<U>>
+    get(Exchange<U> exchange, HttpConnection connection)
     {
-        HttpRequestImpl req = exchange.request();
         if (exchange.version() == HTTP_1_1) {
-            return new Http1Exchange<>(exchange, connection);
+            DEBUG_LOGGER.log(Level.DEBUG, "get: HTTP/1.1: new Http1Exchange");
+            return createHttp1Exchange(exchange, connection);
         } else {
             Http2ClientImpl c2 = exchange.client().client2(); // TODO: improve
             HttpRequestImpl request = exchange.request();
-            Http2Connection c;
-            try {
-                c = c2.getConnectionFor(request);
-            } catch (Http2Connection.ALPNException e) {
-                // failed to negotiate "h2"
-                AbstractAsyncSSLConnection as = e.getConnection();
-                as.stopAsyncReading();
-                HttpConnection sslc = as.downgrade();
-                ExchangeImpl<U> ex = new Http1Exchange<>(exchange, sslc);
+            CompletableFuture<Http2Connection> c2f = c2.getConnectionFor(request);
+            DEBUG_LOGGER.log(Level.DEBUG, "get: Trying to get HTTP/2 connection");
+            return c2f.handle((h2c, t) -> createExchangeImpl(h2c, t, exchange, connection))
+                    .thenCompose(Function.identity());
+        }
+    }
+
+    private static <U> CompletableFuture<? extends ExchangeImpl<U>>
+    createExchangeImpl(Http2Connection c,
+                       Throwable t,
+                       Exchange<U> exchange,
+                       HttpConnection connection)
+    {
+        DEBUG_LOGGER.log(Level.DEBUG, "handling HTTP/2 connection creation result");
+        if (t != null) {
+            DEBUG_LOGGER.log(Level.DEBUG,
+                             "handling HTTP/2 connection creation failed: %s",
+                             (Object)t);
+            t = Utils.getCompletionCause(t);
+            if (t instanceof Http2Connection.ALPNException) {
+                Http2Connection.ALPNException ee = (Http2Connection.ALPNException)t;
+                AbstractAsyncSSLConnection as = ee.getConnection();
+                DEBUG_LOGGER.log(Level.DEBUG, "downgrading to HTTP/1.1 with: %s", as);
+                CompletableFuture<? extends ExchangeImpl<U>> ex =
+                        createHttp1Exchange(exchange, as);
                 return ex;
+            } else {
+                DEBUG_LOGGER.log(Level.DEBUG, "HTTP/2 connection creation failed "
+                                  + "with unexpected exception: %s", (Object)t);
+                return CompletableFuture.failedFuture(t);
             }
-            if (c == null) {
-                // no existing connection. Send request with HTTP 1 and then
-                // upgrade if successful
-                ExchangeImpl<U> ex = new Http1Exchange<>(exchange, connection);
-                exchange.h2Upgrade();
-                return ex;
-            }
-            return c.createStream(exchange);
+        }
+        if (c == null) {
+            // no existing connection. Send request with HTTP 1 and then
+            // upgrade if successful
+            DEBUG_LOGGER.log(Level.DEBUG, "new Http1Exchange, try to upgrade");
+            return createHttp1Exchange(exchange, connection)
+                    .thenApply((e) -> {
+                        exchange.h2Upgrade();
+                        return e;
+                    });
+        } else {
+            DEBUG_LOGGER.log(Level.DEBUG, "creating HTTP/2 streams");
+            Stream<U> s = c.createStream(exchange);
+            CompletableFuture<? extends ExchangeImpl<U>> ex = MinimalFuture.completedFuture(s);
+            return ex;
+        }
+    }
+
+    private static <T> CompletableFuture<Http1Exchange<T>>
+    createHttp1Exchange(Exchange<T> ex, HttpConnection as)
+    {
+        try {
+            return MinimalFuture.completedFuture(new Http1Exchange<>(ex, as));
+        } catch (Throwable e) {
+            return MinimalFuture.failedFuture(e);
         }
     }
 
     /* The following methods have separate HTTP/1.1 and HTTP/2 implementations */
 
-    /**
-     * Sends the request headers only. May block until all sent.
-     */
-    abstract void sendHeadersOnly() throws IOException, InterruptedException;
+    abstract CompletableFuture<ExchangeImpl<T>> sendHeadersAsync();
 
-    // Blocking impl but in async style
-
-    CompletableFuture<ExchangeImpl<T>> sendHeadersAsync() {
-        // this is blocking. cf will already be completed.
-        return MinimalFuture.supply(() -> {
-            sendHeadersOnly();
-            return this;
-        });
-    }
-
-    /**
-     * Gets response by blocking if necessary. This may be an
-     * intermediate response (like 101) or a final response 200 etc. Returns
-     * before body is read.
-     */
-    abstract Response getResponse() throws IOException;
-
-    abstract T readBody(HttpResponse.BodyHandler<T> handler,
-                        boolean returnConnectionToPool) throws IOException;
+    /** Sends a request body, after request headers have been sent. */
+    abstract CompletableFuture<ExchangeImpl<T>> sendBodyAsync();
 
     abstract CompletableFuture<T> readBodyAsync(HttpResponse.BodyHandler<T> handler,
                                                 boolean returnConnectionToPool,
                                                 Executor executor);
 
     /**
-     * Async version of getResponse. Completes before body is read.
+     * Ignore/consume the body.
      */
+    abstract CompletableFuture<Void> ignoreBody();
+
+    /** Gets the response headers. Completes before body is read. */
     abstract CompletableFuture<Response> getResponseAsync(Executor executor);
 
-    /**
-     * Sends a request body after request headers.
-     */
-    abstract void sendBody() throws IOException, InterruptedException;
 
-    // Async version of sendBody(). This only used when body sent separately
-    // to headers (100 continue)
-    CompletableFuture<ExchangeImpl<T>> sendBodyAsync() {
-        return MinimalFuture.supply(() -> {
-            sendBody();
-            return this;
-        });
-    }
-
-    /**
-     * Cancels a request.  Not currently exposed through API.
-     */
+    /** Cancels a request.  Not currently exposed through API. */
     abstract void cancel();
 
     /**
      * Cancels a request with a cause.  Not currently exposed through API.
      */
     abstract void cancel(IOException cause);
+
+    /**
+     * Called when the exchange is released, so that cleanup actions may be
+     * performed - such as deregistering callbacks.
+     * Typically released is called during upgrade, when an HTTP/2 stream
+     * takes over from an Http1Exchange, or when a new exchange is created
+     * during a multi exchange before the final response body was received.
+     */
+    abstract void released();
+
+    /**
+     * Called when the exchange is completed, so that cleanup actions may be
+     * performed - such as deregistering callbacks.
+     * Typically, completed is called at the end of the exchange, when the
+     * final response body has been received (or an error has caused the
+     * completion of the exchange).
+     */
+    abstract void completed();
+
+    /**
+     * Returns true if this exchange was canceled.
+     * @return true if this exchange was canceled.
+     */
+    abstract boolean isCanceled();
+
+    /**
+     * Returns the cause for which this exchange was canceled, if available.
+     * @return the cause for which this exchange was canceled, if available.
+     */
+    abstract Throwable getCancelCause();
 }
diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/ExecutorWrapper.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/ExecutorWrapper.java
deleted file mode 100644
index 54b8cd242ff..00000000000
--- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/ExecutorWrapper.java
+++ /dev/null
@@ -1,105 +0,0 @@
-/*
- * Copyright (c) 2015, 2016, 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.  Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * 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 jdk.incubator.http;
-
-import java.net.SocketPermission;
-import java.security.AccessControlContext;
-import java.security.AccessController;
-import java.security.PrivilegedAction;
-import java.util.concurrent.Executor;
-import jdk.internal.misc.InnocuousThread;
-
-/**
- * Wraps the supplied user Executor
- *
- * when a Security manager set, the correct access control context is used to execute task
- *
- * The access control context is captured at creation time of this object
- */
-class ExecutorWrapper {
-
-    final Executor userExecutor; // the undeerlying executor provided by user
-    final Executor executor; // the executur which wraps the user's one
-    final AccessControlContext acc;
-    final ClassLoader ccl;
-
-    public ExecutorWrapper(Executor userExecutor, AccessControlContext acc) {
-        this.userExecutor = userExecutor;
-        this.acc = acc;
-        this.ccl = getCCL();
-        if (System.getSecurityManager() == null) {
-            this.executor = userExecutor;
-        } else {
-            this.executor = this::run;
-        }
-    }
-
-    private ClassLoader getCCL() {
-        return AccessController.doPrivileged(
-            (PrivilegedAction<ClassLoader>) () -> {
-                return Thread.currentThread().getContextClassLoader();
-            }
-        );
-    }
-
-    /**
-     * This is only used for the default HttpClient to deal with
-     * different application contexts that might be using it.
-     * The default client uses InnocuousThreads in its Executor.
-     */
-    private void prepareThread() {
-        final Thread me = Thread.currentThread();
-        if (!(me instanceof InnocuousThread))
-            return;
-        InnocuousThread innocuousMe = (InnocuousThread)me;
-
-        AccessController.doPrivileged(
-            (PrivilegedAction<Void>) () -> {
-                innocuousMe.setContextClassLoader(ccl);
-                innocuousMe.eraseThreadLocals();
-                return null;
-            }
-        );
-    }
-
-
-    void run(Runnable r) {
-        prepareThread();
-        try {
-            userExecutor.execute(r); // all throwables must be caught
-        } catch (Throwable t) {
-            t.printStackTrace();
-        }
-    }
-
-    public Executor userExecutor() {
-        return userExecutor;
-    }
-
-    public Executor executor() {
-        return executor;
-    }
-}
diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/FilterFactory.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/FilterFactory.java
index 9d0f0a447e2..0dac70da00a 100644
--- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/FilterFactory.java
+++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/FilterFactory.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2017, 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,8 +40,8 @@ class FilterFactory {
         List<HeaderFilter> l = new LinkedList<>();
         for (Class<? extends HeaderFilter> clazz : filterClasses) {
             try {
-                @SuppressWarnings("deprecation")
-                HeaderFilter headerFilter = clazz.newInstance();
+                // Requires a public no arg constructor.
+                HeaderFilter headerFilter = clazz.getConstructor().newInstance();
                 l.add(headerFilter);
             } catch (ReflectiveOperationException e) {
                 throw new InternalError(e);
diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/HeaderFilter.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/HeaderFilter.java
index 06f3843a056..c4baf8655ad 100644
--- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/HeaderFilter.java
+++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/HeaderFilter.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2017, 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
diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/HeaderParser.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/HeaderParser.java
index ec1ac485ed2..5235d625ce7 100644
--- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/HeaderParser.java
+++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/HeaderParser.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2017, 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
@@ -58,26 +58,26 @@ class HeaderParser {
         parse();
     }
 
-    private HeaderParser () { }
+//    private HeaderParser () { }
 
-    /**
-     * Creates a new HeaderParser from this, whose keys (and corresponding
-     * values) range from "start" to "end-1"
-     */
-    public HeaderParser subsequence(int start, int end) {
-        if (start == 0 && end == nkeys) {
-            return this;
-        }
-        if (start < 0 || start >= end || end > nkeys) {
-            throw new IllegalArgumentException("invalid start or end");
-        }
-        HeaderParser n = new HeaderParser();
-        n.tab = new String [asize][2];
-        n.asize = asize;
-        System.arraycopy (tab, start, n.tab, 0, (end-start));
-        n.nkeys= (end-start);
-        return n;
-    }
+//    /**
+//     * Creates a new HeaderParser from this, whose keys (and corresponding
+//     * values) range from "start" to "end-1"
+//     */
+//    public HeaderParser subsequence(int start, int end) {
+//        if (start == 0 && end == nkeys) {
+//            return this;
+//        }
+//        if (start < 0 || start >= end || end > nkeys) {
+//            throw new IllegalArgumentException("invalid start or end");
+//        }
+//        HeaderParser n = new HeaderParser();
+//        n.tab = new String [asize][2];
+//        n.asize = asize;
+//        System.arraycopy (tab, start, n.tab, 0, (end-start));
+//        n.nkeys= (end-start);
+//        return n;
+//    }
 
     private void parse() {
 
@@ -216,9 +216,9 @@ class HeaderParser {
         return new ParserIterator (false);
     }
 
-    public Iterator<String> values () {
-        return new ParserIterator (true);
-    }
+//    public Iterator<String> values () {
+//        return new ParserIterator (true);
+//    }
 
     @Override
     public String toString () {
@@ -242,11 +242,11 @@ class HeaderParser {
         return sb.toString();
     }
 
-    public int findInt(String k, int Default) {
-        try {
-            return Integer.parseInt(findValue(k, String.valueOf(Default)));
-        } catch (Throwable t) {
-            return Default;
-        }
-    }
+//    public int findInt(String k, int Default) {
+//        try {
+//            return Integer.parseInt(findValue(k, String.valueOf(Default)));
+//        } catch (Throwable t) {
+//            return Default;
+//        }
+//    }
 }
diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/Http1AsyncReceiver.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/Http1AsyncReceiver.java
new file mode 100644
index 00000000000..4f8fdf4da6a
--- /dev/null
+++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/Http1AsyncReceiver.java
@@ -0,0 +1,651 @@
+/*
+ * Copyright (c) 2017, 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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 jdk.incubator.http;
+
+import java.io.EOFException;
+import java.io.IOException;
+import java.lang.System.Logger.Level;
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.ConcurrentLinkedDeque;
+import java.util.concurrent.Executor;
+import java.util.concurrent.Flow;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.stream.Collectors;
+import jdk.incubator.http.internal.common.Demand;
+import jdk.incubator.http.internal.common.FlowTube.TubeSubscriber;
+import jdk.incubator.http.internal.common.SequentialScheduler;
+import jdk.incubator.http.internal.common.ConnectionExpiredException;
+import jdk.incubator.http.internal.common.Utils;
+
+
+/**
+ * A helper class that will queue up incoming data until the receiving
+ * side is ready to handle it.
+ */
+class Http1AsyncReceiver {
+
+    static final boolean DEBUG = Utils.DEBUG; // Revisit: temporary dev flag.
+    final System.Logger  debug = Utils.getDebugLogger(this::dbgString, DEBUG);
+
+    /**
+     * A delegate that can asynchronously receive data from an upstream flow,
+     * parse, it, then possibly transform it and either store it (response
+     * headers) or possibly pass it to a downstream subscriber (response body).
+     * Usually, there will be one Http1AsyncDelegate in charge of receiving
+     * and parsing headers, and another one in charge of receiving, parsing,
+     * and forwarding body. Each will sequentially subscribe with the
+     * Http1AsyncReceiver in turn. There may be additional delegates which
+     * subscribe to the Http1AsyncReceiver, mainly for the purpose of handling
+     * errors while the connection is busy transmitting the request body and the
+     * Http1Exchange::readBody method hasn't been called yet, and response
+     * delegates haven't subscribed yet.
+     */
+    static interface Http1AsyncDelegate {
+        /**
+         * Receives and handles a byte buffer reference.
+         * @param ref A byte buffer reference coming from upstream.
+         * @return false, if the byte buffer reference should be kept in the queue.
+         *         Usually, this means that either the byte buffer reference
+         *         was handled and parsing is finished, or that the receiver
+         *         didn't handle the byte reference at all.
+         *         There may or may not be any remaining data in the
+         *         byte buffer, and the byte buffer reference must not have
+         *         been cleared.
+         *         true, if the byte buffer reference was fully read and
+         *         more data can be received.
+         */
+        public boolean tryAsyncReceive(ByteBuffer ref);
+
+        /**
+         * Called when an exception is raised.
+         * @param ex The raised Throwable.
+         */
+        public void onReadError(Throwable ex);
+
+        /**
+         * Must be called before any other method on the delegate.
+         * The subscription can be either used directly by the delegate
+         * to request more data (e.g. if the delegate is a header parser),
+         * or can be forwarded to a downstream subscriber (if the delegate
+         * is a body parser that wraps a response BodySubscriber).
+         * In all cases, it is the responsibility of the delegate to ensure
+         * that request(n) and demand.tryDecrement() are called appropriately.
+         * No data will be sent to {@code tryAsyncReceive} unless
+         * the subscription has some demand.
+         *
+         * @param s A subscription that allows the delegate to control the
+         *          data flow.
+         */
+        public void onSubscribe(AbstractSubscription s);
+
+        /**
+         * Returns the subscription that was passed to {@code onSubscribe}
+         * @return the subscription that was passed to {@code onSubscribe}..
+         */
+        public AbstractSubscription subscription();
+
+    }
+
+    /**
+     * A simple subclass of AbstractSubscription that ensures the
+     * SequentialScheduler will be run when request() is called and demand
+     * becomes positive again.
+     */
+    private static final class Http1AsyncDelegateSubscription
+            extends AbstractSubscription
+    {
+        private final Runnable onCancel;
+        private final SequentialScheduler scheduler;
+        Http1AsyncDelegateSubscription(SequentialScheduler scheduler,
+                                       Runnable onCancel) {
+            this.scheduler = scheduler;
+            this.onCancel = onCancel;
+        }
+        @Override
+        public void request(long n) {
+            final Demand demand = demand();
+            if (demand.increase(n)) {
+                scheduler.runOrSchedule();
+            }
+        }
+        @Override
+        public void cancel() { onCancel.run();}
+    }
+
+    private final ConcurrentLinkedDeque<ByteBuffer> queue
+            = new ConcurrentLinkedDeque<>();
+    private final SequentialScheduler scheduler =
+            SequentialScheduler.synchronizedScheduler(this::flush);
+    private final Executor executor;
+    private final Http1TubeSubscriber subscriber = new Http1TubeSubscriber();
+    private final AtomicReference<Http1AsyncDelegate> pendingDelegateRef;
+    private final AtomicLong received = new AtomicLong();
+    final AtomicBoolean canRequestMore = new AtomicBoolean();
+
+    private volatile Throwable error;
+    private volatile Http1AsyncDelegate delegate;
+    // This reference is only used to prevent early GC of the exchange.
+    private volatile Http1Exchange<?>  owner;
+    // Only used for checking whether we run on the selector manager thread.
+    private final HttpClientImpl client;
+    private boolean retry;
+
+    public Http1AsyncReceiver(Executor executor, Http1Exchange<?> owner) {
+        this.pendingDelegateRef = new AtomicReference<>();
+        this.executor = executor;
+        this.owner = owner;
+        this.client = owner.client;
+    }
+
+    // This is the main loop called by the SequentialScheduler.
+    // It attempts to empty the queue until the scheduler is stopped,
+    // or the delegate is unregistered, or the delegate is unable to
+    // process the data (because it's not ready or already done), which
+    // it signals by returning 'true';
+    private void flush() {
+        ByteBuffer buf;
+        try {
+            assert !client.isSelectorThread() :
+                    "Http1AsyncReceiver::flush should not run in the selector: "
+                    + Thread.currentThread().getName();
+
+            // First check whether we have a pending delegate that has
+            // just subscribed, and if so, create a Subscription for it
+            // and call onSubscribe.
+            handlePendingDelegate();
+
+            // Then start emptying the queue, if possible.
+            while ((buf = queue.peek()) != null) {
+                Http1AsyncDelegate delegate = this.delegate;
+                debug.log(Level.DEBUG, "Got %s bytes for delegate %s",
+                                       buf.remaining(), delegate);
+                if (!hasDemand(delegate)) {
+                    // The scheduler will be invoked again later when the demand
+                    // becomes positive.
+                    return;
+                }
+
+                assert delegate != null;
+                debug.log(Level.DEBUG, "Forwarding %s bytes to delegate %s",
+                          buf.remaining(), delegate);
+                // The delegate has demand: feed it the next buffer.
+                if (!delegate.tryAsyncReceive(buf)) {
+                    final long remaining = buf.remaining();
+                    debug.log(Level.DEBUG, () -> {
+                        // If the scheduler is stopped, the queue may already
+                        // be empty and the reference may already be released.
+                        String remstr = scheduler.isStopped() ? "" :
+                                " remaining in ref: "
+                                + remaining;
+                        remstr =  remstr
+                                + " total remaining: " + remaining();
+                        return "Delegate done: " + remaining;
+                    });
+                    canRequestMore.set(false);
+                    // The last buffer parsed may have remaining unparsed bytes.
+                    // Don't take it out of the queue.
+                    return; // done.
+                }
+
+                // removed parsed buffer from queue, and continue with next
+                // if available
+                ByteBuffer parsed = queue.remove();
+                canRequestMore.set(queue.isEmpty());
+                assert parsed == buf;
+            }
+
+            // queue is empty: let's see if we should request more
+            checkRequestMore();
+
+        } catch (Throwable t) {
+            Throwable x = error;
+            if (x == null) error = t; // will be handled in the finally block
+            debug.log(Level.DEBUG, "Unexpected error caught in flush()", t);
+        } finally {
+            // Handles any pending error.
+            // The most recently subscribed delegate will get the error.
+            checkForErrors();
+        }
+    }
+
+    /**
+     * Must be called from within the scheduler main loop.
+     * Handles any pending errors by calling delegate.onReadError().
+     * If the error can be forwarded to the delegate, stops the scheduler.
+     */
+    private void checkForErrors() {
+        // Handles any pending error.
+        // The most recently subscribed delegate will get the error.
+        // If the delegate is null, the error will be handled by the next
+        // delegate that subscribes.
+        // If the queue is not empty, wait until it it is empty before
+        // handling the error.
+        Http1AsyncDelegate delegate = pendingDelegateRef.get();
+        if (delegate == null) delegate = this.delegate;
+        Throwable x = error;
+        if (delegate != null && x != null && queue.isEmpty()) {
+            // forward error only after emptying the queue.
+            final Object captured = delegate;
+            debug.log(Level.DEBUG, () -> "flushing " + x
+                    + "\n\t delegate: " + captured
+                    + "\t\t queue.isEmpty: " + queue.isEmpty());
+            scheduler.stop();
+            delegate.onReadError(x);
+        }
+    }
+
+    /**
+     * Must be called from within the scheduler main loop.
+     * Figure out whether more data should be requested from the
+     * Http1TubeSubscriber.
+     */
+    private void checkRequestMore() {
+        Http1AsyncDelegate delegate = this.delegate;
+        boolean more = this.canRequestMore.get();
+        boolean hasDemand = hasDemand(delegate);
+        debug.log(Level.DEBUG, () -> "checkRequestMore: "
+                  + "canRequestMore=" + more + ", hasDemand=" + hasDemand
+                  + (delegate == null ? ", delegate=null" : ""));
+        if (hasDemand) {
+            subscriber.requestMore();
+        }
+    }
+
+    /**
+     * Must be called from within the scheduler main loop.
+     * Return true if the delegate is not null and has some demand.
+     * @param delegate The Http1AsyncDelegate delegate
+     * @return true if the delegate is not null and has some demand
+     */
+    private boolean hasDemand(Http1AsyncDelegate delegate) {
+        if (delegate == null) return false;
+        AbstractSubscription subscription = delegate.subscription();
+        long demand = subscription.demand().get();
+        debug.log(Level.DEBUG, "downstream subscription demand is %s", demand);
+        return demand > 0;
+    }
+
+    /**
+     * Must be called from within the scheduler main loop.
+     * Handles pending delegate subscription.
+     * Return true if there was some pending delegate subscription and a new
+     * delegate was subscribed, false otherwise.
+     *
+     * @return true if there was some pending delegate subscription and a new
+     *         delegate was subscribed, false otherwise.
+     */
+    private boolean handlePendingDelegate() {
+        Http1AsyncDelegate pending = pendingDelegateRef.get();
+        if (pending != null && pendingDelegateRef.compareAndSet(pending, null)) {
+            Http1AsyncDelegate delegate = this.delegate;
+            if (delegate != null) unsubscribe(delegate);
+            Runnable cancel = () -> {
+                debug.log(Level.DEBUG, "Downstream subscription cancelled by %s", pending);
+                // The connection should be closed, as some data may
+                // be left over in the stream.
+                try {
+                    setRetryOnError(false);
+                    onReadError(new IOException("subscription cancelled"));
+                    unsubscribe(pending);
+                } finally {
+                    Http1Exchange<?> exchg = owner;
+                    stop();
+                    if (exchg != null) exchg.connection().close();
+                }
+            };
+            // The subscription created by a delegate is only loosely
+            // coupled with the upstream subscription. This is partly because
+            // the header/body parser work with a flow of ByteBuffer, whereas
+            // we have a flow List<ByteBuffer> upstream.
+            Http1AsyncDelegateSubscription subscription =
+                    new Http1AsyncDelegateSubscription(scheduler, cancel);
+            pending.onSubscribe(subscription);
+            this.delegate = delegate = pending;
+            final Object captured = delegate;
+            debug.log(Level.DEBUG, () -> "delegate is now " + captured
+                  + ", demand=" + subscription.demand().get()
+                  + ", canRequestMore=" + canRequestMore.get()
+                  + ", queue.isEmpty=" + queue.isEmpty());
+            return true;
+        }
+        return false;
+    }
+
+    synchronized void setRetryOnError(boolean retry) {
+        this.retry = retry;
+    }
+
+    void clear() {
+        debug.log(Level.DEBUG, "cleared");
+        this.pendingDelegateRef.set(null);
+        this.delegate = null;
+        this.owner = null;
+    }
+
+    void subscribe(Http1AsyncDelegate delegate) {
+        synchronized(this) {
+            pendingDelegateRef.set(delegate);
+        }
+        if (queue.isEmpty()) {
+            canRequestMore.set(true);
+        }
+        debug.log(Level.DEBUG, () ->
+                "Subscribed pending " + delegate + " queue.isEmpty: "
+                + queue.isEmpty());
+        // Everything may have been received already. Make sure
+        // we parse it.
+        if (client.isSelectorThread()) {
+            scheduler.deferOrSchedule(executor);
+        } else {
+            scheduler.runOrSchedule();
+        }
+    }
+
+    // Used for debugging only!
+    long remaining() {
+        return Utils.remaining(queue.toArray(Utils.EMPTY_BB_ARRAY));
+    }
+
+    void unsubscribe(Http1AsyncDelegate delegate) {
+        synchronized(this) {
+            if (this.delegate == delegate) {
+                debug.log(Level.DEBUG, "Unsubscribed %s", delegate);
+                this.delegate = null;
+            }
+        }
+    }
+
+    // Callback: Consumer of ByteBuffer
+    private void asyncReceive(ByteBuffer buf) {
+        debug.log(Level.DEBUG, "Putting %s bytes into the queue", buf.remaining());
+        received.addAndGet(buf.remaining());
+        queue.offer(buf);
+
+        // This callback is called from within the selector thread.
+        // Use an executor here to avoid doing the heavy lifting in the
+        // selector.
+        scheduler.deferOrSchedule(executor);
+    }
+
+    // Callback: Consumer of Throwable
+    void onReadError(Throwable ex) {
+        Http1AsyncDelegate delegate;
+        Throwable recorded;
+        debug.log(Level.DEBUG, "onError: %s", (Object) ex);
+        synchronized (this) {
+            delegate = this.delegate;
+            recorded = error;
+            if (recorded == null) {
+                // retry is set to true by HttpExchange when the connection is
+                // already connected, which means it's been retrieved from
+                // the pool.
+                if (retry && (ex instanceof IOException)) {
+                    // could be either EOFException, or
+                    // IOException("connection reset by peer), or
+                    // SSLHandshakeException resulting from the server having
+                    // closed the SSL session.
+                    if (received.get() == 0) {
+                        // If we receive such an exception before having
+                        // received any byte, then in this case, we will
+                        // throw ConnectionExpiredException
+                        // to try & force a retry of the request.
+                        retry = false;
+                        ex = new ConnectionExpiredException(
+                                "subscription is finished", ex);
+                    }
+                }
+                error = ex;
+            }
+            final Throwable t = (recorded == null ? ex : recorded);
+            debug.log(Level.DEBUG, () -> "recorded " + t
+                    + "\n\t delegate: " + delegate
+                    + "\t\t queue.isEmpty: " + queue.isEmpty(), ex);
+        }
+        if (queue.isEmpty() || pendingDelegateRef.get() != null) {
+            // This callback is called from within the selector thread.
+            // Use an executor here to avoid doing the heavy lifting in the
+            // selector.
+            scheduler.deferOrSchedule(executor);
+        }
+    }
+
+    void stop() {
+        debug.log(Level.DEBUG, "stopping");
+        scheduler.stop();
+        delegate = null;
+        owner  = null;
+    }
+
+    /**
+     * Returns the TubeSubscriber for reading from the connection flow.
+     * @return the TubeSubscriber for reading from the connection flow.
+     */
+    TubeSubscriber subscriber() {
+        return subscriber;
+    }
+
+    /**
+     * A simple tube subscriber for reading from the connection flow.
+     */
+    final class Http1TubeSubscriber implements TubeSubscriber {
+        volatile Flow.Subscription subscription;
+        volatile boolean completed;
+        volatile boolean dropped;
+
+        public void onSubscribe(Flow.Subscription subscription) {
+            // supports being called multiple time.
+            // doesn't cancel the previous subscription, since that is
+            // most probably the same as the new subscription.
+            assert this.subscription == null || dropped == false;
+            this.subscription = subscription;
+            dropped = false;
+            canRequestMore.set(true);
+            if (delegate != null) {
+                scheduler.deferOrSchedule(executor);
+            }
+        }
+
+        void requestMore() {
+            Flow.Subscription s = subscription;
+            if (s == null) return;
+            if (canRequestMore.compareAndSet(true, false)) {
+                if (!completed && !dropped) {
+                    debug.log(Level.DEBUG,
+                        "Http1TubeSubscriber: requesting one more from upstream");
+                    s.request(1);
+                    return;
+                }
+            }
+            debug.log(Level.DEBUG, "Http1TubeSubscriber: no need to request more");
+        }
+
+        @Override
+        public void onNext(List<ByteBuffer> item) {
+            canRequestMore.set(item.isEmpty());
+            for (ByteBuffer buffer : item) {
+                asyncReceive(buffer);
+            }
+        }
+
+        @Override
+        public void onError(Throwable throwable) {
+            onReadError(throwable);
+            completed = true;
+        }
+
+        @Override
+        public void onComplete() {
+            onReadError(new EOFException("EOF reached while reading"));
+            completed = true;
+        }
+
+        public void dropSubscription() {
+            debug.log(Level.DEBUG, "Http1TubeSubscriber: dropSubscription");
+            // we could probably set subscription to null here...
+            // then we might not need the 'dropped' boolean?
+            dropped = true;
+        }
+
+    }
+
+    // Drains the content of the queue into a single ByteBuffer.
+    // The scheduler must be permanently stopped before calling drain().
+    ByteBuffer drain(ByteBuffer initial) {
+        // Revisit: need to clean that up.
+        //
+        ByteBuffer b = initial = (initial == null ? Utils.EMPTY_BYTEBUFFER : initial);
+        assert scheduler.isStopped();
+
+        if (queue.isEmpty()) return b;
+
+        // sanity check: we shouldn't have queued the same
+        // buffer twice.
+        ByteBuffer[] qbb = queue.toArray(new ByteBuffer[queue.size()]);
+        assert java.util.stream.Stream.of(qbb)
+                .collect(Collectors.toSet())
+                .size() == qbb.length : debugQBB(qbb);
+
+        // compute the number of bytes in the queue, the number of bytes
+        // in the initial buffer
+        // TODO: will need revisiting - as it is not guaranteed that all
+        // data will fit in single BB!
+        int size = Utils.remaining(qbb, Integer.MAX_VALUE);
+        int remaining = b.remaining();
+        int free = b.capacity() - b.position() - remaining;
+        debug.log(Level.DEBUG,
+            "Flushing %s bytes from queue into initial buffer (remaining=%s, free=%s)",
+            size, remaining, free);
+
+        // check whether the initial buffer has enough space
+        if (size > free) {
+            debug.log(Level.DEBUG,
+                    "Allocating new buffer for initial: %s", (size + remaining));
+            // allocates a new buffer and copy initial to it
+            b = ByteBuffer.allocate(size + remaining);
+            Utils.copy(initial, b);
+            assert b.position() == remaining;
+            b.flip();
+            assert b.position() == 0;
+            assert b.limit() == remaining;
+            assert b.remaining() == remaining;
+        }
+
+        // store position and limit
+        int pos = b.position();
+        int limit = b.limit();
+        assert limit - pos == remaining;
+        assert b.capacity() >= remaining + size
+                : "capacity: " + b.capacity()
+                + ", remaining: " + b.remaining()
+                + ", size: " + size;
+
+        // prepare to copy the content of the queue
+        b.position(limit);
+        b.limit(pos + remaining + size);
+        assert b.remaining() >= size :
+                "remaining: " + b.remaining() + ", size: " + size;
+
+        // copy the content of the queue
+        int count = 0;
+        for (int i=0; i<qbb.length; i++) {
+            ByteBuffer b2 = qbb[i];
+            int r = b2.remaining();
+            assert b.remaining() >= r : "need at least " + r + " only "
+                    + b.remaining() + " available";
+            int copied = Utils.copy(b2, b);
+            assert copied == r : "copied="+copied+" available="+r;
+            assert b2.remaining() == 0;
+            count += copied;
+        }
+        assert count == size;
+        assert b.position() == pos + remaining + size :
+                "b.position="+b.position()+" != "+pos+"+"+remaining+"+"+size;
+
+        // reset limit and position
+        b.limit(limit+size);
+        b.position(pos);
+
+        // we can clear the refs
+        queue.clear();
+        final ByteBuffer bb = b;
+        debug.log(Level.DEBUG, () -> "Initial buffer now has " + bb.remaining()
+                + " pos=" + bb.position() + " limit=" + bb.limit());
+
+        return b;
+    }
+
+    private String debugQBB(ByteBuffer[] qbb) {
+        StringBuilder msg = new StringBuilder();
+        List<ByteBuffer> lbb = Arrays.asList(qbb);
+        Set<ByteBuffer> sbb = new HashSet<>(Arrays.asList(qbb));
+
+        int uniquebb = sbb.size();
+        msg.append("qbb: ").append(lbb.size())
+           .append(" (unique: ").append(uniquebb).append("), ")
+           .append("duplicates: ");
+        String sep = "";
+        for (ByteBuffer b : lbb) {
+            if (!sbb.remove(b)) {
+                msg.append(sep)
+                   .append(String.valueOf(b))
+                   .append("[remaining=")
+                   .append(b.remaining())
+                   .append(", position=")
+                   .append(b.position())
+                   .append(", capacity=")
+                   .append(b.capacity())
+                   .append("]");
+                sep = ", ";
+            }
+        }
+        return msg.toString();
+    }
+
+    volatile String dbgTag;
+    String dbgString() {
+        String tag = dbgTag;
+        if (tag == null) {
+            String flowTag = null;
+            Http1Exchange<?> exchg = owner;
+            Object flow = (exchg != null)
+                    ? exchg.connection().getConnectionFlow()
+                    : null;
+            flowTag = tag = flow == null ? null: (String.valueOf(flow));
+            if (flowTag != null) {
+                dbgTag = tag = flowTag + " Http1AsyncReceiver";
+            } else {
+                tag = "Http1AsyncReceiver";
+            }
+        }
+        return tag;
+    }
+}
diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/Http1Exchange.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/Http1Exchange.java
index faa3304e22b..3dc754eff29 100644
--- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/Http1Exchange.java
+++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/Http1Exchange.java
@@ -26,40 +26,126 @@
 package jdk.incubator.http;
 
 import java.io.IOException;
+import java.lang.System.Logger.Level;
 import java.net.InetSocketAddress;
 import jdk.incubator.http.HttpResponse.BodyHandler;
-import jdk.incubator.http.HttpResponse.BodyProcessor;
+import jdk.incubator.http.HttpResponse.BodySubscriber;
 import java.nio.ByteBuffer;
+import java.util.Objects;
 import java.util.concurrent.CompletableFuture;
-import java.util.Collections;
 import java.util.LinkedList;
 import java.util.List;
-import java.util.concurrent.CompletionException;
+import java.util.concurrent.ConcurrentLinkedDeque;
 import java.util.concurrent.Executor;
+import java.util.concurrent.Flow;
+import jdk.incubator.http.internal.common.Demand;
 import jdk.incubator.http.internal.common.Log;
+import jdk.incubator.http.internal.common.FlowTube;
+import jdk.incubator.http.internal.common.SequentialScheduler;
 import jdk.incubator.http.internal.common.MinimalFuture;
 import jdk.incubator.http.internal.common.Utils;
+import static jdk.incubator.http.HttpClient.Version.HTTP_1_1;
 
 /**
- * Encapsulates one HTTP/1.1 request/responseAsync exchange.
+ * Encapsulates one HTTP/1.1 request/response exchange.
  */
 class Http1Exchange<T> extends ExchangeImpl<T> {
 
-    final HttpRequestImpl request;        // main request
-    private final List<CompletableFuture<?>> operations; // used for cancel
+    static final boolean DEBUG = Utils.DEBUG; // Revisit: temporary dev flag.
+    final System.Logger  debug = Utils.getDebugLogger(this::dbgString, DEBUG);
+    private static final System.Logger DEBUG_LOGGER =
+            Utils.getDebugLogger("Http1Exchange"::toString, DEBUG);
+
+    final HttpRequestImpl request; // main request
     final Http1Request requestAction;
     private volatile Http1Response<T> response;
-    // use to record possible cancellation raised before any operation
-    // has been initiated.
-    private IOException failed;
     final HttpConnection connection;
     final HttpClientImpl client;
     final Executor executor;
-    volatile ByteBuffer buffer; // used for receiving
+    private final Http1AsyncReceiver asyncReceiver;
+
+    /** Records a possible cancellation raised before any operation
+     * has been initiated, or an error received while sending the request. */
+    private Throwable failed;
+    private final List<CompletableFuture<?>> operations; // used for cancel
+
+    /** Must be held when operating on any internal state or data. */
+    private final Object lock = new Object();
+
+    /** Holds the outgoing data, either the headers or a request body part. Or
+     * an error from the request body publisher. At most there can be ~2 pieces
+     * of outgoing data ( onComplete|onError can be invoked without demand ).*/
+    final ConcurrentLinkedDeque<DataPair> outgoing = new ConcurrentLinkedDeque<>();
+
+    /** The write publisher, responsible for writing the complete request ( both
+     * headers and body ( if any ). */
+    private final Http1Publisher writePublisher = new Http1Publisher();
+
+    /** Completed when the header have been published, or there is an error */
+    private volatile CompletableFuture<ExchangeImpl<T>> headersSentCF  = new MinimalFuture<>();
+     /** Completed when the body has been published, or there is an error */
+    private volatile CompletableFuture<ExchangeImpl<T>> bodySentCF = new MinimalFuture<>();
+
+    /** The subscriber to the request's body published. Maybe null. */
+    private volatile Http1BodySubscriber bodySubscriber;
+
+    enum State { INITIAL,
+                 HEADERS,
+                 BODY,
+                 ERROR,          // terminal state
+                 COMPLETING,
+                 COMPLETED }     // terminal state
+
+    private State state = State.INITIAL;
+
+    /** A carrier for either data or an error. Used to carry data, and communicate
+     * errors from the request ( both headers and body ) to the exchange. */
+    static class DataPair {
+        Throwable throwable;
+        List<ByteBuffer> data;
+        DataPair(List<ByteBuffer> data, Throwable throwable){
+            this.data = data;
+            this.throwable = throwable;
+        }
+        @Override
+        public String toString() {
+            return "DataPair [data=" + data + ", throwable=" + throwable + "]";
+        }
+    }
+
+    /** An abstract supertype for HTTP/1.1 body subscribers. There are two
+     * concrete implementations: {@link Http1Request.StreamSubscriber}, and
+     * {@link Http1Request.FixedContentSubscriber}, for receiving chunked and
+     * fixed length bodies, respectively. */
+    static abstract class Http1BodySubscriber implements Flow.Subscriber<ByteBuffer> {
+        protected volatile Flow.Subscription subscription;
+        protected volatile boolean complete;
+
+        /** Final sentinel in the stream of request body. */
+        static final List<ByteBuffer> COMPLETED = List.of(ByteBuffer.allocate(0));
+
+        void request(long n) {
+            DEBUG_LOGGER.log(Level.DEBUG, () ->
+                "Http1BodySubscriber requesting " + n + ", from " + subscription);
+            subscription.request(n);
+        }
+
+        static Http1BodySubscriber completeSubscriber() {
+            return new Http1BodySubscriber() {
+                @Override public void onSubscribe(Flow.Subscription subscription) { error(); }
+                @Override public void onNext(ByteBuffer item) { error(); }
+                @Override public void onError(Throwable throwable) { error(); }
+                @Override public void onComplete() { error(); }
+                private void error() {
+                    throw new InternalError("should not reach here");
+                }
+            };
+        }
+    }
 
     @Override
     public String toString() {
-        return request.toString();
+        return "HTTP/1.1 " + request.toString();
     }
 
     HttpRequestImpl request() {
@@ -74,44 +160,156 @@ class Http1Exchange<T> extends ExchangeImpl<T> {
         this.client = exchange.client();
         this.executor = exchange.executor();
         this.operations = new LinkedList<>();
-        this.buffer = Utils.EMPTY_BYTEBUFFER;
+        operations.add(headersSentCF);
+        operations.add(bodySentCF);
         if (connection != null) {
             this.connection = connection;
         } else {
-            InetSocketAddress addr = request.getAddress(client);
-            this.connection = HttpConnection.getConnection(addr, client, request);
+            InetSocketAddress addr = request.getAddress();
+            this.connection = HttpConnection.getConnection(addr, client, request, HTTP_1_1);
         }
-        this.requestAction = new Http1Request(request, client, this.connection);
+        this.requestAction = new Http1Request(request, this);
+        this.asyncReceiver = new Http1AsyncReceiver(executor, this);
+        asyncReceiver.subscribe(new InitialErrorReceiver());
     }
 
+    /** An initial receiver that handles no data, but cancels the request if
+     * it receives an error. Will be replaced when reading response body. */
+    final class InitialErrorReceiver implements Http1AsyncReceiver.Http1AsyncDelegate {
+        volatile AbstractSubscription s;
+        @Override
+        public boolean tryAsyncReceive(ByteBuffer ref) {
+            return false;  // no data has been processed, leave it in the queue
+        }
 
+        @Override
+        public void onReadError(Throwable ex) {
+            cancelImpl(ex);
+        }
+
+        @Override
+        public void onSubscribe(AbstractSubscription s) {
+            this.s = s;
+        }
+
+        public AbstractSubscription subscription() {
+            return s;
+        }
+    }
+
+    @Override
     HttpConnection connection() {
         return connection;
     }
 
+    private void connectFlows(HttpConnection connection) {
+        FlowTube tube =  connection.getConnectionFlow();
+        debug.log(Level.DEBUG, "%s connecting flows", tube);
+
+        // Connect the flow to our Http1TubeSubscriber:
+        //   asyncReceiver.subscriber().
+        tube.connectFlows(writePublisher,
+                          asyncReceiver.subscriber());
+    }
 
     @Override
-    T readBody(BodyHandler<T> handler, boolean returnConnectionToPool)
-        throws IOException
-    {
-        BodyProcessor<T> processor = handler.apply(response.responseCode(),
-                                                   response.responseHeaders());
-        CompletableFuture<T> bodyCF = response.readBody(processor,
-                                                        returnConnectionToPool,
-                                                        this::executeInline);
-        try {
-            return bodyCF.join();
-        } catch (CompletionException e) {
-            throw Utils.getIOException(e);
+    CompletableFuture<ExchangeImpl<T>> sendHeadersAsync() {
+        // create the response before sending the request headers, so that
+        // the response can set the appropriate receivers.
+        debug.log(Level.DEBUG, "Sending headers only");
+        if (response == null) {
+            response = new Http1Response<>(connection, this, asyncReceiver);
         }
+
+        debug.log(Level.DEBUG, "response created in advance");
+        // If the first attempt to read something triggers EOF, or
+        // IOException("channel reset by peer"), we're going to retry.
+        // Instruct the asyncReceiver to throw ConnectionExpiredException
+        // to force a retry.
+        asyncReceiver.setRetryOnError(true);
+
+        CompletableFuture<Void> connectCF;
+        if (!connection.connected()) {
+            debug.log(Level.DEBUG, "initiating connect async");
+            connectCF = connection.connectAsync();
+            synchronized (lock) {
+                operations.add(connectCF);
+            }
+        } else {
+            connectCF = new MinimalFuture<>();
+            connectCF.complete(null);
+        }
+
+        return connectCF
+                .thenCompose(unused -> {
+                    CompletableFuture<Void> cf = new MinimalFuture<>();
+                    try {
+                        connectFlows(connection);
+
+                        debug.log(Level.DEBUG, "requestAction.headers");
+                        List<ByteBuffer> data = requestAction.headers();
+                        synchronized (lock) {
+                            state = State.HEADERS;
+                        }
+                        debug.log(Level.DEBUG, "setting outgoing with headers");
+                        assert outgoing.isEmpty() : "Unexpected outgoing:" + outgoing;
+                        appendToOutgoing(data);
+                        cf.complete(null);
+                        return cf;
+                    } catch (Throwable t) {
+                        debug.log(Level.DEBUG, "Failed to send headers: %s", t);
+                        connection.close();
+                        cf.completeExceptionally(t);
+                        return cf;
+                    } })
+                .thenCompose(unused -> headersSentCF);
     }
 
-    private void executeInline(Runnable r) {
-        r.run();
+    @Override
+    CompletableFuture<ExchangeImpl<T>> sendBodyAsync() {
+        assert headersSentCF.isDone();
+        try {
+            bodySubscriber = requestAction.continueRequest();
+            if (bodySubscriber == null) {
+                bodySubscriber = Http1BodySubscriber.completeSubscriber();
+                appendToOutgoing(Http1BodySubscriber.COMPLETED);
+            } else {
+                bodySubscriber.request(1);  // start
+            }
+        } catch (Throwable t) {
+            connection.close();
+            bodySentCF.completeExceptionally(t);
+        }
+        return bodySentCF;
     }
 
-    synchronized ByteBuffer getBuffer() {
-        return buffer;
+    @Override
+    CompletableFuture<Response> getResponseAsync(Executor executor) {
+        CompletableFuture<Response> cf = response.readHeadersAsync(executor);
+        Throwable cause;
+        synchronized (lock) {
+            operations.add(cf);
+            cause = failed;
+            failed = null;
+        }
+
+        if (cause != null) {
+            Log.logTrace("Http1Exchange: request [{0}/timeout={1}ms]"
+                            + "\n\tCompleting exceptionally with {2}\n",
+                         request.uri(),
+                         request.timeout().isPresent() ?
+                            // calling duration.toMillis() can throw an exception.
+                            // this is just debugging, we don't care if it overflows.
+                            (request.timeout().get().getSeconds() * 1000
+                             + request.timeout().get().getNano() / 1000000) : -1,
+                         cause);
+            boolean acknowledged = cf.completeExceptionally(cause);
+            debug.log(Level.DEBUG,
+                      () -> acknowledged
+                            ? ("completed response with " + cause)
+                            : ("response already completed, ignoring " + cause));
+        }
+        return cf;
     }
 
     @Override
@@ -119,53 +317,35 @@ class Http1Exchange<T> extends ExchangeImpl<T> {
                                        boolean returnConnectionToPool,
                                        Executor executor)
     {
-        BodyProcessor<T> processor = handler.apply(response.responseCode(),
-                                                   response.responseHeaders());
-        CompletableFuture<T> bodyCF = response.readBody(processor,
+        BodySubscriber<T> bs = handler.apply(response.responseCode(),
+                                             response.responseHeaders());
+        CompletableFuture<T> bodyCF = response.readBody(bs,
                                                         returnConnectionToPool,
                                                         executor);
         return bodyCF;
     }
 
     @Override
-    void sendHeadersOnly() throws IOException, InterruptedException {
-        try {
-            if (!connection.connected()) {
-                connection.connect();
-            }
-            requestAction.sendHeadersOnly();
-        } catch (Throwable e) {
-            connection.close();
-            throw e;
+    CompletableFuture<Void> ignoreBody() {
+        return response.ignoreBody(executor);
+    }
+
+    ByteBuffer drainLeftOverBytes() {
+        synchronized (lock) {
+            asyncReceiver.stop();
+            return asyncReceiver.drain(Utils.EMPTY_BYTEBUFFER);
         }
     }
 
-    @Override
-    void sendBody() throws IOException {
-        try {
-            requestAction.continueRequest();
-        } catch (Throwable e) {
-            connection.close();
-            throw e;
-        }
+    void released() {
+        Http1Response<T> resp = this.response;
+        if (resp != null) resp.completed();
+        asyncReceiver.clear();
     }
 
-    @Override
-    Response getResponse() throws IOException {
-        try {
-            response = new Http1Response<>(connection, this);
-            response.readHeaders();
-            Response r = response.response();
-            buffer = response.getBuffer();
-            return r;
-        } catch (Throwable t) {
-            connection.close();
-            throw t;
-        }
-    }
-
-    private void closeConnection() {
-        connection.close();
+    void completed() {
+        Http1Response<T> resp = this.response;
+        if (resp != null) resp.completed();
     }
 
     /**
@@ -174,7 +354,7 @@ class Http1Exchange<T> extends ExchangeImpl<T> {
      */
     @Override
     void cancel() {
-        cancel(new IOException("Request cancelled"));
+        cancelImpl(new IOException("Request cancelled"));
     }
 
     /**
@@ -182,66 +362,255 @@ class Http1Exchange<T> extends ExchangeImpl<T> {
      * If not it closes the connection and completes all pending operations
      */
     @Override
-    synchronized void cancel(IOException cause) {
-        if (requestAction != null && requestAction.finished()
-                && response != null && response.finished()) {
-            return;
-        }
-        connection.close();
+    void cancel(IOException cause) {
+        cancelImpl(cause);
+    }
+
+    private void cancelImpl(Throwable cause) {
+        LinkedList<CompletableFuture<?>> toComplete = null;
         int count = 0;
-        if (operations.isEmpty()) {
-            failed = cause;
-            Log.logTrace("Http1Exchange: request [{0}/timeout={1}ms] no pending operation."
-                         + "\n\tCan''t cancel yet with {2}",
-                         request.uri(),
-                         request.duration() == null ? -1 :
-                         // calling duration.toMillis() can throw an exception.
-                         // this is just debugging, we don't care if it overflows.
-                         (request.duration().getSeconds() * 1000
-                          + request.duration().getNano() / 1000000),
-                         cause);
-        } else {
-            for (CompletableFuture<?> cf : operations) {
-                cf.completeExceptionally(cause);
-                count++;
+        synchronized (lock) {
+            if (failed == null)
+                failed = cause;
+            if (requestAction != null && requestAction.finished()
+                    && response != null && response.finished()) {
+                return;
+            }
+            connection.close();   // TODO: ensure non-blocking if holding the lock
+            writePublisher.writeScheduler.stop();
+            if (operations.isEmpty()) {
+                Log.logTrace("Http1Exchange: request [{0}/timeout={1}ms] no pending operation."
+                                + "\n\tCan''t cancel yet with {2}",
+                             request.uri(),
+                             request.timeout().isPresent() ?
+                                // calling duration.toMillis() can throw an exception.
+                                // this is just debugging, we don't care if it overflows.
+                                (request.timeout().get().getSeconds() * 1000
+                                 + request.timeout().get().getNano() / 1000000) : -1,
+                             cause);
+            } else {
+                for (CompletableFuture<?> cf : operations) {
+                    if (!cf.isDone()) {
+                        if (toComplete == null) toComplete = new LinkedList<>();
+                        toComplete.add(cf);
+                        count++;
+                    }
+                }
+                operations.clear();
             }
         }
         Log.logError("Http1Exchange.cancel: count=" + count);
+        if (toComplete != null) {
+            // We might be in the selector thread in case of timeout, when
+            // the SelectorManager calls purgeTimeoutsAndReturnNextDeadline()
+            // There may or may not be other places that reach here
+            // from the SelectorManager thread, so just make sure we
+            // don't complete any CF from within the selector manager
+            // thread.
+            Executor exec = client.isSelectorThread()
+                            ? executor
+                            : this::runInline;
+            while (!toComplete.isEmpty()) {
+                CompletableFuture<?> cf = toComplete.poll();
+                exec.execute(() -> {
+                    if (cf.completeExceptionally(cause)) {
+                        debug.log(Level.DEBUG, "completed cf with %s",
+                                 (Object) cause);
+                    }
+                });
+            }
+        }
     }
 
-    CompletableFuture<Response> getResponseAsyncImpl(Executor executor) {
-        return MinimalFuture.supply( () -> {
-            response = new Http1Response<>(connection, Http1Exchange.this);
-            response.readHeaders();
-            Response r = response.response();
-            buffer = response.getBuffer();
-            return r;
-        }, executor);
+    private void runInline(Runnable run) {
+        assert !client.isSelectorThread();
+        run.run();
     }
 
-    @Override
-    CompletableFuture<Response> getResponseAsync(Executor executor) {
-        CompletableFuture<Response> cf =
-            connection.whenReceivingResponse()
-                      .thenCompose((v) -> getResponseAsyncImpl(executor));
-        IOException cause;
-        synchronized(this) {
-            operations.add(cf);
-            cause = failed;
-            failed = null;
+    /** Returns true if this exchange was canceled. */
+    boolean isCanceled() {
+        synchronized (lock) {
+            return failed != null;
         }
-        if (cause != null) {
-            Log.logTrace("Http1Exchange: request [{0}/timeout={1}ms]"
-                         + "\n\tCompleting exceptionally with {2}\n",
-                         request.uri(),
-                         request.duration() == null ? -1 :
-                         // calling duration.toMillis() can throw an exception.
-                         // this is just debugging, we don't care if it overflows.
-                         (request.duration().getSeconds() * 1000
-                          + request.duration().getNano() / 1000000),
-                         cause);
-            cf.completeExceptionally(cause);
+    }
+
+    /** Returns the cause for which this exchange was canceled, if available. */
+    Throwable getCancelCause() {
+        synchronized (lock) {
+            return failed;
         }
-        return cf;
+    }
+
+    /** Convenience for {@link #appendToOutgoing(DataPair)}, with just a Throwable. */
+    void appendToOutgoing(Throwable throwable) {
+        appendToOutgoing(new DataPair(null, throwable));
+    }
+
+    /** Convenience for {@link #appendToOutgoing(DataPair)}, with just data. */
+    void appendToOutgoing(List<ByteBuffer> item) {
+        appendToOutgoing(new DataPair(item, null));
+    }
+
+    private void appendToOutgoing(DataPair dp) {
+        debug.log(Level.DEBUG, "appending to outgoing " + dp);
+        outgoing.add(dp);
+        writePublisher.writeScheduler.runOrSchedule();
+    }
+
+    /** Tells whether, or not, there is any outgoing data that can be published,
+     * or if there is an error. */
+    private boolean hasOutgoing() {
+        return !outgoing.isEmpty();
+    }
+
+    // Invoked only by the publisher
+    // ALL tasks should execute off the Selector-Manager thread
+    /** Returns the next portion of the HTTP request, or the error. */
+    private DataPair getOutgoing() {
+        final Executor exec = client.theExecutor();
+        final DataPair dp = outgoing.pollFirst();
+
+        if (dp == null)  // publisher has not published anything yet
+            return null;
+
+        synchronized (lock) {
+            if (dp.throwable != null) {
+                state = State.ERROR;
+                exec.execute(() -> {
+                    connection.close();
+                    headersSentCF.completeExceptionally(dp.throwable);
+                    bodySentCF.completeExceptionally(dp.throwable);
+                });
+                return dp;
+            }
+
+            switch (state) {
+                case HEADERS:
+                    state = State.BODY;
+                    // completeAsync, since dependent tasks should run in another thread
+                    debug.log(Level.DEBUG, "initiating completion of headersSentCF");
+                    headersSentCF.completeAsync(() -> this, exec);
+                    break;
+                case BODY:
+                    if (dp.data == Http1BodySubscriber.COMPLETED) {
+                        state = State.COMPLETING;
+                        debug.log(Level.DEBUG, "initiating completion of bodySentCF");
+                        bodySentCF.completeAsync(() -> this, exec);
+                    } else {
+                        debug.log(Level.DEBUG, "requesting more body from the subscriber");
+                        exec.execute(() -> bodySubscriber.request(1));
+                    }
+                    break;
+                case INITIAL:
+                case ERROR:
+                case COMPLETING:
+                case COMPLETED:
+                default:
+                    assert false : "Unexpected state:" + state;
+            }
+
+            return dp;
+        }
+    }
+
+    /** A Publisher of HTTP/1.1 headers and request body. */
+    final class Http1Publisher implements FlowTube.TubePublisher {
+
+        final System.Logger  debug = Utils.getDebugLogger(this::dbgString);
+        volatile Flow.Subscriber<? super List<ByteBuffer>> subscriber;
+        volatile boolean cancelled;
+        final Http1WriteSubscription subscription = new Http1WriteSubscription();
+        final Demand demand = new Demand();
+        final SequentialScheduler writeScheduler =
+                SequentialScheduler.synchronizedScheduler(new WriteTask());
+
+        @Override
+        public void subscribe(Flow.Subscriber<? super List<ByteBuffer>> s) {
+            assert state == State.INITIAL;
+            Objects.requireNonNull(s);
+            assert subscriber == null;
+
+            subscriber = s;
+            debug.log(Level.DEBUG, "got subscriber: %s", s);
+            s.onSubscribe(subscription);
+        }
+
+        volatile String dbgTag;
+        String dbgString() {
+            String tag = dbgTag;
+            Object flow = connection.getConnectionFlow();
+            if (tag == null && flow != null) {
+                dbgTag = tag = "Http1Publisher(" + flow + ")";
+            } else if (tag == null) {
+                tag = "Http1Publisher(?)";
+            }
+            return tag;
+        }
+
+        final class WriteTask implements Runnable {
+            @Override
+            public void run() {
+                assert state != State.COMPLETED : "Unexpected state:" + state;
+                debug.log(Level.DEBUG, "WriteTask");
+                if (subscriber == null) {
+                    debug.log(Level.DEBUG, "no subscriber yet");
+                    return;
+                }
+                debug.log(Level.DEBUG, () -> "hasOutgoing = " + hasOutgoing());
+                while (hasOutgoing() && demand.tryDecrement()) {
+                    DataPair dp = getOutgoing();
+
+                    if (dp.throwable != null) {
+                        debug.log(Level.DEBUG, "onError");
+                        // Do not call the subscriber's onError, it is not required.
+                        writeScheduler.stop();
+                    } else {
+                        List<ByteBuffer> data = dp.data;
+                        if (data == Http1BodySubscriber.COMPLETED) {
+                            synchronized (lock) {
+                                assert state == State.COMPLETING : "Unexpected state:" + state;
+                                state = State.COMPLETED;
+                            }
+                            debug.log(Level.DEBUG,
+                                     "completed, stopping %s", writeScheduler);
+                            writeScheduler.stop();
+                            // Do nothing more. Just do not publish anything further.
+                            // The next Subscriber will eventually take over.
+
+                        } else {
+                            debug.log(Level.DEBUG, () ->
+                                    "onNext with " + Utils.remaining(data) + " bytes");
+                            subscriber.onNext(data);
+                        }
+                    }
+                }
+            }
+        }
+
+        final class Http1WriteSubscription implements Flow.Subscription {
+
+            @Override
+            public void request(long n) {
+                if (cancelled)
+                    return;  //no-op
+                demand.increase(n);
+                debug.log(Level.DEBUG,
+                        "subscription request(%d), demand=%s", n, demand);
+                writeScheduler.deferOrSchedule(client.theExecutor());
+            }
+
+            @Override
+            public void cancel() {
+                debug.log(Level.DEBUG, "subscription cancelled");
+                if (cancelled)
+                    return;  //no-op
+                cancelled = true;
+                writeScheduler.stop();
+            }
+        }
+    }
+
+    String dbgString() {
+        return "Http1Exchange";
     }
 }
diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/Http1HeaderParser.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/Http1HeaderParser.java
new file mode 100644
index 00000000000..0777529573b
--- /dev/null
+++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/Http1HeaderParser.java
@@ -0,0 +1,257 @@
+/*
+ * Copyright (c) 2017, 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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 jdk.incubator.http;
+
+import java.net.ProtocolException;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import static java.lang.String.format;
+import static java.util.Objects.requireNonNull;
+
+class Http1HeaderParser {
+
+    private static final char CR = '\r';
+    private static final char LF = '\n';
+    private static final char HT = '\t';
+    private static final char SP = ' ';
+
+    private StringBuilder sb = new StringBuilder();
+    private String statusLine;
+    private int responseCode;
+    private HttpHeaders headers;
+    private Map<String,List<String>> privateMap = new HashMap<>();
+
+    enum State { STATUS_LINE,
+                 STATUS_LINE_FOUND_CR,
+                 STATUS_LINE_END,
+                 STATUS_LINE_END_CR,
+                 HEADER,
+                 HEADER_FOUND_CR,
+                 HEADER_FOUND_LF,
+                 HEADER_FOUND_CR_LF,
+                 HEADER_FOUND_CR_LF_CR,
+                 FINISHED }
+
+    private State state = State.STATUS_LINE;
+
+    /** Returns the status-line. */
+    String statusLine() { return statusLine; }
+
+    /** Returns the response code. */
+    int responseCode() { return responseCode; }
+
+    /** Returns the headers, possibly empty. */
+    HttpHeaders headers() { assert state == State.FINISHED; return headers; }
+
+    /**
+     * Parses HTTP/1.X status-line and headers from the given bytes. Must be
+     * called successive times, with additional data, until returns true.
+     *
+     * All given ByteBuffers will be consumed, until ( possibly ) the last one
+     * ( when true is returned ), which may not be fully consumed.
+     *
+     * @param input the ( partial ) header data
+     * @return true iff the end of the headers block has been reached
+     */
+    boolean parse(ByteBuffer input) throws ProtocolException {
+        requireNonNull(input, "null input");
+
+        while (input.hasRemaining() && state != State.FINISHED) {
+            switch (state) {
+                case STATUS_LINE:
+                    readResumeStatusLine(input);
+                    break;
+                case STATUS_LINE_FOUND_CR:
+                    readStatusLineFeed(input);
+                    break;
+                case STATUS_LINE_END:
+                    maybeStartHeaders(input);
+                    break;
+                case STATUS_LINE_END_CR:
+                    maybeEndHeaders(input);
+                    break;
+                case HEADER:
+                    readResumeHeader(input);
+                    break;
+                // fallthrough
+                case HEADER_FOUND_CR:
+                case HEADER_FOUND_LF:
+                    resumeOrLF(input);
+                    break;
+                case HEADER_FOUND_CR_LF:
+                    resumeOrSecondCR(input);
+                    break;
+                case HEADER_FOUND_CR_LF_CR:
+                    resumeOrEndHeaders(input);
+                    break;
+                default:
+                    throw new InternalError(
+                            "Unexpected state: " + String.valueOf(state));
+            }
+        }
+
+        return state == State.FINISHED;
+    }
+
+    private void readResumeStatusLine(ByteBuffer input) {
+        char c = 0;
+        while (input.hasRemaining() && (c =(char)input.get()) != CR) {
+            sb.append(c);
+        }
+
+        if (c == CR) {
+            state = State.STATUS_LINE_FOUND_CR;
+        }
+    }
+
+    private void readStatusLineFeed(ByteBuffer input) throws ProtocolException {
+        char c = (char)input.get();
+        if (c != LF) {
+            throw protocolException("Bad trailing char, \"%s\", when parsing status-line, \"%s\"",
+                                    c, sb.toString());
+        }
+
+        statusLine = sb.toString();
+        sb = new StringBuilder();
+        if (!statusLine.startsWith("HTTP/1.")) {
+            throw protocolException("Invalid status line: \"%s\"", statusLine);
+        }
+        if (statusLine.length() < 12) {
+            throw protocolException("Invalid status line: \"%s\"", statusLine);
+        }
+        responseCode = Integer.parseInt(statusLine.substring(9, 12));
+
+        state = State.STATUS_LINE_END;
+    }
+
+    private void maybeStartHeaders(ByteBuffer input) {
+        assert state == State.STATUS_LINE_END;
+        assert sb.length() == 0;
+        char c = (char)input.get();
+        if (c == CR) {
+            state = State.STATUS_LINE_END_CR;
+        } else {
+            sb.append(c);
+            state = State.HEADER;
+        }
+    }
+
+    private void maybeEndHeaders(ByteBuffer input) throws ProtocolException {
+        assert state == State.STATUS_LINE_END_CR;
+        assert sb.length() == 0;
+        char c = (char)input.get();
+        if (c == LF) {
+            headers = ImmutableHeaders.of(privateMap);
+            privateMap = null;
+            state = State.FINISHED;  // no headers
+        } else {
+            throw protocolException("Unexpected \"%s\", after status-line CR", c);
+        }
+    }
+
+    private void readResumeHeader(ByteBuffer input) {
+        assert state == State.HEADER;
+        assert input.hasRemaining();
+        while (input.hasRemaining()) {
+            char c = (char)input.get();
+            if (c == CR) {
+                state = State.HEADER_FOUND_CR;
+                break;
+            } else if (c == LF) {
+                state = State.HEADER_FOUND_LF;
+                break;
+            }
+
+            if (c == HT)
+                c = SP;
+            sb.append(c);
+        }
+    }
+
+    private void addHeaderFromString(String headerString) {
+        assert sb.length() == 0;
+        int idx = headerString.indexOf(':');
+        if (idx == -1)
+            return;
+        String name = headerString.substring(0, idx).trim();
+        if (name.isEmpty())
+            return;
+        String value = headerString.substring(idx + 1, headerString.length()).trim();
+
+        privateMap.computeIfAbsent(name.toLowerCase(Locale.US),
+                                   k -> new ArrayList<>()).add(value);
+    }
+
+    private void resumeOrLF(ByteBuffer input) {
+        assert state == State.HEADER_FOUND_CR || state == State.HEADER_FOUND_LF;
+        char c = (char)input.get();
+        if (c == LF && state == State.HEADER_FOUND_CR) {
+            String headerString = sb.toString();
+            sb = new StringBuilder();
+            addHeaderFromString(headerString);
+            state = State.HEADER_FOUND_CR_LF;
+        } else if (c == SP || c == HT) {
+            sb.append(SP); // parity with MessageHeaders
+            state = State.HEADER;
+        } else {
+            sb = new StringBuilder();
+            sb.append(c);
+            state = State.HEADER;
+        }
+    }
+
+    private void resumeOrSecondCR(ByteBuffer input) {
+        assert state == State.HEADER_FOUND_CR_LF;
+        assert sb.length() == 0;
+        char c = (char)input.get();
+        if (c == CR) {
+            state = State.HEADER_FOUND_CR_LF_CR;
+        } else {
+            sb.append(c);
+            state = State.HEADER;
+        }
+    }
+
+    private void resumeOrEndHeaders(ByteBuffer input) throws ProtocolException {
+        assert state == State.HEADER_FOUND_CR_LF_CR;
+        char c = (char)input.get();
+        if (c == LF) {
+            state = State.FINISHED;
+            headers = ImmutableHeaders.of(privateMap);
+            privateMap = null;
+        } else {
+            throw protocolException("Unexpected \"%s\", after CR LF CR", c);
+        }
+    }
+
+    private ProtocolException protocolException(String format, Object... args) {
+        return new ProtocolException(format(format, args));
+    }
+}
diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/Http1Request.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/Http1Request.java
index 5b58483686c..15e53be75a3 100644
--- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/Http1Request.java
+++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/Http1Request.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2017, 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
@@ -26,97 +26,73 @@
 package jdk.incubator.http;
 
 import java.io.IOException;
+import java.lang.System.Logger.Level;
 import java.net.URI;
 import java.nio.ByteBuffer;
+import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
-import java.util.Set;
 import java.net.InetSocketAddress;
-import jdk.incubator.http.HttpConnection.Mode;
-import java.nio.charset.StandardCharsets;
-import static java.nio.charset.StandardCharsets.US_ASCII;
-import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.CompletionException;
+import java.util.Objects;
 import java.util.concurrent.Flow;
 
+import jdk.incubator.http.Http1Exchange.Http1BodySubscriber;
 import jdk.incubator.http.internal.common.HttpHeadersImpl;
 import jdk.incubator.http.internal.common.Log;
-import jdk.incubator.http.internal.common.MinimalFuture;
 import jdk.incubator.http.internal.common.Utils;
 
+import static java.nio.charset.StandardCharsets.US_ASCII;
+
 /**
- *  A HTTP/1.1 request.
- *
- * send() -> Writes the request + body to the given channel, in one blocking
- * operation.
+ *  An HTTP/1.1 request.
  */
 class Http1Request {
-    final HttpClientImpl client;
-    final HttpRequestImpl request;
-    final HttpConnection chan;
-    // Multiple buffers are used to hold different parts of request
-    // See line 206 and below for description
-    final ByteBuffer[] buffers;
-    final HttpRequest.BodyProcessor requestProc;
-    final HttpHeaders userHeaders;
-    final HttpHeadersImpl systemHeaders;
-    boolean streaming;
-    long contentLength;
-    final CompletableFuture<Void> cf;
+    private final HttpRequestImpl request;
+    private final Http1Exchange<?> http1Exchange;
+    private final HttpConnection connection;
+    private final HttpRequest.BodyPublisher requestPublisher;
+    private final HttpHeaders userHeaders;
+    private final HttpHeadersImpl systemHeaders;
+    private volatile boolean streaming;
+    private volatile long contentLength;
 
-    Http1Request(HttpRequestImpl request, HttpClientImpl client, HttpConnection connection)
+    Http1Request(HttpRequestImpl request,
+                 Http1Exchange<?> http1Exchange)
         throws IOException
     {
-        this.client = client;
         this.request = request;
-        this.chan = connection;
-        buffers = new ByteBuffer[5]; // TODO: check
-        this.requestProc = request.requestProcessor;
+        this.http1Exchange = http1Exchange;
+        this.connection = http1Exchange.connection();
+        this.requestPublisher = request.requestPublisher;  // may be null
         this.userHeaders = request.getUserHeaders();
         this.systemHeaders = request.getSystemHeaders();
-        this.cf = new MinimalFuture<>();
     }
 
-    private void logHeaders() throws IOException {
-        StringBuilder sb = new StringBuilder(256);
-        sb.append("REQUEST HEADERS:\n");
-        Log.dumpHeaders(sb, "    ", systemHeaders);
-        Log.dumpHeaders(sb, "    ", userHeaders);
-        Log.logHeaders(sb.toString());
-    }
-
-    private void dummy(long x) {
-        // not used in this class
-    }
-
-    private void collectHeaders0() throws IOException {
+    private void logHeaders(String completeHeaders) {
         if (Log.headers()) {
-            logHeaders();
+            //StringBuilder sb = new StringBuilder(256);
+            //sb.append("REQUEST HEADERS:\n");
+            //Log.dumpHeaders(sb, "    ", systemHeaders);
+            //Log.dumpHeaders(sb, "    ", userHeaders);
+            //Log.logHeaders(sb.toString());
+
+            String s = completeHeaders.replaceAll("\r\n", "\n");
+            Log.logHeaders("REQUEST HEADERS:\n" + s);
         }
-        StringBuilder sb = new StringBuilder(256);
-        collectHeaders1(sb, request, systemHeaders);
-        collectHeaders1(sb, request, userHeaders);
-        sb.append("\r\n");
-        String headers = sb.toString();
-        buffers[1] = ByteBuffer.wrap(headers.getBytes(StandardCharsets.US_ASCII));
     }
 
-    private void collectHeaders1(StringBuilder sb,
-                                 HttpRequestImpl request,
-                                 HttpHeaders headers)
-        throws IOException
-    {
-        Map<String,List<String>> h = headers.map();
-        Set<Map.Entry<String,List<String>>> entries = h.entrySet();
+    private void collectHeaders0(StringBuilder sb) {
+        collectHeaders1(sb, systemHeaders);
+        collectHeaders1(sb, userHeaders);
+        sb.append("\r\n");
+    }
 
-        for (Map.Entry<String,List<String>> entry : entries) {
+    private void collectHeaders1(StringBuilder sb, HttpHeaders headers) {
+        for (Map.Entry<String,List<String>> entry : headers.map().entrySet()) {
             String key = entry.getKey();
             List<String> values = entry.getValue();
             for (String value : values) {
-                sb.append(key)
-                  .append(": ")
-                  .append(value)
-                  .append("\r\n");
+                sb.append(key).append(": ").append(value).append("\r\n");
             }
         }
     }
@@ -166,7 +142,7 @@ class Http1Request {
         URI uri = request.uri();
         String method = request.method();
 
-        if ((request.proxy(client) == null && !method.equals("CONNECT"))
+        if ((request.proxy() == null && !method.equals("CONNECT"))
                 || request.isWebSocket()) {
             return getPathAndQuery(uri);
         }
@@ -179,25 +155,12 @@ class Http1Request {
                 return getPathAndQuery(uri);
             }
         }
-        return uri == null? authorityString(request.authority()) : uri.toString();
-    }
-
-    void sendHeadersOnly() throws IOException {
-        collectHeaders();
-        chan.write(buffers, 0, 2);
-    }
-
-    void sendRequest() throws IOException {
-        collectHeaders();
-        chan.configureMode(Mode.BLOCKING);
-        if (contentLength == 0) {
-            chan.write(buffers, 0, 2);
-        } else if (contentLength > 0) {
-            writeFixedContent(true);
-        } else {
-            writeStreamedContent(true);
+        if (request.method().equals("CONNECT")) {
+            // use authority for connect itself
+            return authorityString(request.authority());
         }
-        setFinished();
+
+        return uri == null? authorityString(request.authority()) : uri.toString();
     }
 
     private boolean finished;
@@ -210,7 +173,7 @@ class Http1Request {
         finished = true;
     }
 
-    private void collectHeaders() throws IOException {
+    List<ByteBuffer> headers() {
         if (Log.requests() && request != null) {
             Log.logRequest(request.toString());
         }
@@ -220,250 +183,183 @@ class Http1Request {
           .append(' ')
           .append(uriString)
           .append(" HTTP/1.1\r\n");
-        String cmd = sb.toString();
 
-        buffers[0] = ByteBuffer.wrap(cmd.getBytes(StandardCharsets.US_ASCII));
         URI uri = request.uri();
         if (uri != null) {
             systemHeaders.setHeader("Host", hostString());
         }
-        if (request == null) {
-            // this is not a user request. No content
+        if (request == null || requestPublisher == null) {
+            // Not a user request, or maybe a method, e.g. GET, with no body.
             contentLength = 0;
         } else {
-            contentLength = requestProc.contentLength();
+            contentLength = requestPublisher.contentLength();
         }
 
         if (contentLength == 0) {
             systemHeaders.setHeader("Content-Length", "0");
-            collectHeaders0();
         } else if (contentLength > 0) {
-            /* [0] request line [1] headers [2] body  */
-            systemHeaders.setHeader("Content-Length",
-                                    Integer.toString((int) contentLength));
+            systemHeaders.setHeader("Content-Length", Long.toString(contentLength));
             streaming = false;
-            collectHeaders0();
-            buffers[2] = getBuffer();
         } else {
-            /* Chunked:
-             *
-             * [0] request line [1] headers [2] chunk header [3] chunk data [4]
-             * final chunk header and trailing CRLF of previous chunks
-             *
-             * 2,3,4 used repeatedly */
             streaming = true;
             systemHeaders.setHeader("Transfer-encoding", "chunked");
-            collectHeaders0();
-            buffers[3] = getBuffer();
         }
+        collectHeaders0(sb);
+        String hs = sb.toString();
+        logHeaders(hs);
+        ByteBuffer b = ByteBuffer.wrap(hs.getBytes(US_ASCII));
+        return List.of(b);
     }
 
-    private ByteBuffer getBuffer() {
-        return ByteBuffer.allocate(Utils.BUFSIZE);
-    }
-
-    // The following two methods used by Http1Exchange to handle expect continue
-
-    void continueRequest() throws IOException {
+    Http1BodySubscriber continueRequest()  {
+        Http1BodySubscriber subscriber;
         if (streaming) {
-            writeStreamedContent(false);
+            subscriber = new StreamSubscriber();
+            requestPublisher.subscribe(subscriber);
         } else {
-            writeFixedContent(false);
+            if (contentLength == 0)
+                return null;
+
+            subscriber = new FixedContentSubscriber();
+            requestPublisher.subscribe(subscriber);
         }
-        setFinished();
+        return subscriber;
     }
 
-    class StreamSubscriber implements Flow.Subscriber<ByteBuffer> {
-        volatile Flow.Subscription subscription;
-        volatile boolean includeHeaders;
-
-        StreamSubscriber(boolean includeHeaders) {
-            this.includeHeaders = includeHeaders;
-        }
+    class StreamSubscriber extends Http1BodySubscriber {
 
         @Override
         public void onSubscribe(Flow.Subscription subscription) {
             if (this.subscription != null) {
-                throw new IllegalStateException("already subscribed");
+                Throwable t = new IllegalStateException("already subscribed");
+                http1Exchange.appendToOutgoing(t);
+            } else {
+                this.subscription = subscription;
             }
-            this.subscription = subscription;
-            subscription.request(1);
         }
 
         @Override
         public void onNext(ByteBuffer item) {
-            int startbuf, nbufs;
-
-            if (cf.isDone()) {
-                throw new IllegalStateException("subscription already completed");
-            }
-
-            if (includeHeaders) {
-                startbuf = 0;
-                nbufs = 5;
+            Objects.requireNonNull(item);
+            if (complete) {
+                Throwable t = new IllegalStateException("subscription already completed");
+                http1Exchange.appendToOutgoing(t);
             } else {
-                startbuf = 2;
-                nbufs = 3;
+                int chunklen = item.remaining();
+                ArrayList<ByteBuffer> l = new ArrayList<>(3);
+                l.add(getHeader(chunklen));
+                l.add(item);
+                l.add(ByteBuffer.wrap(CRLF));
+                http1Exchange.appendToOutgoing(l);
             }
-            int chunklen = item.remaining();
-            buffers[3] = item;
-            buffers[2] = getHeader(chunklen);
-            buffers[4] = CRLF_BUFFER();
-            try {
-                chan.write(buffers, startbuf, nbufs);
-            } catch (IOException e) {
-                subscription.cancel();
-                cf.completeExceptionally(e);
-            }
-            includeHeaders = false;
-            subscription.request(1);
         }
 
         @Override
         public void onError(Throwable throwable) {
-            if (cf.isDone()) {
+            if (complete)
                 return;
-            }
+
             subscription.cancel();
-            cf.completeExceptionally(throwable);
+            http1Exchange.appendToOutgoing(throwable);
         }
 
         @Override
         public void onComplete() {
-            if (cf.isDone()) {
-                throw new IllegalStateException("subscription already completed");
+            if (complete) {
+                Throwable t = new IllegalStateException("subscription already completed");
+                http1Exchange.appendToOutgoing(t);
+            } else {
+                ArrayList<ByteBuffer> l = new ArrayList<>(2);
+                l.add(ByteBuffer.wrap(EMPTY_CHUNK_BYTES));
+                l.add(ByteBuffer.wrap(CRLF));
+                complete = true;
+                //setFinished();
+                http1Exchange.appendToOutgoing(l);
+                http1Exchange.appendToOutgoing(COMPLETED);
+                setFinished();  // TODO: before or after,? does it matter?
+
             }
-            buffers[3] = EMPTY_CHUNK_HEADER();
-            buffers[4] = CRLF_BUFFER();
-            try {
-                chan.write(buffers, 3, 2);
-            } catch (IOException ex) {
-                cf.completeExceptionally(ex);
-                return;
-            }
-            cf.complete(null);
         }
     }
 
-    private void waitForCompletion() throws IOException {
-        try {
-            cf.join();
-        } catch (CompletionException e) {
-            throw Utils.getIOException(e);
-        }
-    }
+    class FixedContentSubscriber extends Http1BodySubscriber {
 
-    /* Entire request is sent, or just body only  */
-    private void writeStreamedContent(boolean includeHeaders)
-        throws IOException
-    {
-        StreamSubscriber subscriber = new StreamSubscriber(includeHeaders);
-        requestProc.subscribe(subscriber);
-        waitForCompletion();
-    }
-
-    class FixedContentSubscriber implements Flow.Subscriber<ByteBuffer>
-    {
-        volatile Flow.Subscription subscription;
-        volatile boolean includeHeaders;
-        volatile long contentWritten = 0;
-
-        FixedContentSubscriber(boolean includeHeaders) {
-            this.includeHeaders = includeHeaders;
-        }
+        private volatile long contentWritten;
 
         @Override
         public void onSubscribe(Flow.Subscription subscription) {
             if (this.subscription != null) {
-                throw new IllegalStateException("already subscribed");
+                Throwable t = new IllegalStateException("already subscribed");
+                http1Exchange.appendToOutgoing(t);
+            } else {
+                this.subscription = subscription;
             }
-            this.subscription = subscription;
-            subscription.request(1);
         }
 
         @Override
         public void onNext(ByteBuffer item) {
-            int startbuf, nbufs;
-            long headersLength;
-
-            if (includeHeaders) {
-                startbuf = 0;
-                nbufs = 3;
-                headersLength = buffers[0].remaining() + buffers[1].remaining();
+            debug.log(Level.DEBUG, "onNext");
+            Objects.requireNonNull(item);
+            if (complete) {
+                Throwable t = new IllegalStateException("subscription already completed");
+                http1Exchange.appendToOutgoing(t);
             } else {
-                startbuf = 2;
-                nbufs = 1;
-                headersLength = 0;
-            }
-            buffers[2] = item;
-            try {
-                long writing = buffers[2].remaining() + headersLength;
-                contentWritten += buffers[2].remaining();
-                chan.checkWrite(writing, buffers, startbuf, nbufs);
+                long writing = item.remaining();
+                long written = (contentWritten += writing);
 
-                if (contentWritten > contentLength) {
-                    String msg = "Too many bytes in request body. Expected: " +
-                        Long.toString(contentLength) + " Sent: " +
-                        Long.toString(contentWritten);
-                    throw new IOException(msg);
+                if (written > contentLength) {
+                    subscription.cancel();
+                    String msg = connection.getConnectionFlow()
+                                  + " [" + Thread.currentThread().getName() +"] "
+                                  + "Too many bytes in request body. Expected: "
+                                  + contentLength + ", got: " + written;
+                    http1Exchange.appendToOutgoing(new IOException(msg));
+                } else {
+                    http1Exchange.appendToOutgoing(List.of(item));
                 }
-                subscription.request(1);
-            } catch (IOException e) {
-                subscription.cancel();
-                cf.completeExceptionally(e);
             }
         }
 
         @Override
         public void onError(Throwable throwable) {
-            if (cf.isDone()) {
+            debug.log(Level.DEBUG, "onError");
+            if (complete)  // TODO: error?
                 return;
-            }
+
             subscription.cancel();
-            cf.completeExceptionally(throwable);
+            http1Exchange.appendToOutgoing(throwable);
         }
 
         @Override
         public void onComplete() {
-            if (cf.isDone()) {
-                throw new IllegalStateException("subscription already completed");
-            }
-
-            if (contentLength > contentWritten) {
-                subscription.cancel();
-                Exception e = new IOException("Too few bytes returned by the processor");
-                cf.completeExceptionally(e);
+            debug.log(Level.DEBUG, "onComplete");
+            if (complete) {
+                Throwable t = new IllegalStateException("subscription already completed");
+                http1Exchange.appendToOutgoing(t);
             } else {
-                cf.complete(null);
+                complete = true;
+                long written = contentWritten;
+                if (contentLength > written) {
+                    subscription.cancel();
+                    Throwable t = new IOException(connection.getConnectionFlow()
+                                         + " [" + Thread.currentThread().getName() +"] "
+                                         + "Too few bytes returned by the publisher ("
+                                                  + written + "/"
+                                                  + contentLength + ")");
+                    http1Exchange.appendToOutgoing(t);
+                } else {
+                    http1Exchange.appendToOutgoing(COMPLETED);
+                }
             }
         }
     }
 
-    /* Entire request is sent, or just body only */
-    private void writeFixedContent(boolean includeHeaders)
-            throws IOException {
-        if (contentLength == 0) {
-            return;
-        }
-        FixedContentSubscriber subscriber = new FixedContentSubscriber(includeHeaders);
-        requestProc.subscribe(subscriber);
-        waitForCompletion();
-    }
-
     private static final byte[] CRLF = {'\r', '\n'};
     private static final byte[] EMPTY_CHUNK_BYTES = {'0', '\r', '\n'};
 
-    private ByteBuffer CRLF_BUFFER() {
-        return ByteBuffer.wrap(CRLF);
-    }
-
-    private ByteBuffer EMPTY_CHUNK_HEADER() {
-        return ByteBuffer.wrap(EMPTY_CHUNK_BYTES);
-    }
-
-    /* Returns a header for a particular chunk size */
-    private static ByteBuffer getHeader(int size){
-        String hexStr =  Integer.toHexString(size);
+    /** Returns a header for a particular chunk size */
+    private static ByteBuffer getHeader(int size) {
+        String hexStr = Integer.toHexString(size);
         byte[] hexBytes = hexStr.getBytes(US_ASCII);
         byte[] header = new byte[hexStr.length()+2];
         System.arraycopy(hexBytes, 0, header, 0, hexBytes.length);
@@ -471,4 +367,8 @@ class Http1Request {
         header[hexBytes.length+1] = CRLF[1];
         return ByteBuffer.wrap(header);
     }
+
+    static final boolean DEBUG = Utils.DEBUG; // Revisit: temporary dev flag.
+    final System.Logger  debug = Utils.getDebugLogger(this::toString, DEBUG);
+
 }
diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/Http1Response.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/Http1Response.java
index 92ce50c28bf..7e948649185 100644
--- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/Http1Response.java
+++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/Http1Response.java
@@ -25,16 +25,24 @@
 
 package jdk.incubator.http;
 
-import java.io.IOException;
+import java.io.EOFException;
+import java.lang.System.Logger.Level;
 import java.nio.ByteBuffer;
 import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.CompletionStage;
 import java.util.concurrent.Executor;
+import java.util.function.BiConsumer;
+import java.util.function.Consumer;
+import java.util.function.Function;
+import jdk.incubator.http.ResponseContent.BodyParser;
 import jdk.incubator.http.internal.common.Log;
 import static jdk.incubator.http.HttpClient.Version.HTTP_1_1;
+import jdk.incubator.http.internal.common.MinimalFuture;
+import jdk.incubator.http.internal.common.Utils;
 
 /**
- * Handles a HTTP/1.1 response in two blocking calls. readHeaders() and
- * readBody(). There can be more than one of these per Http exchange.
+ * Handles a HTTP/1.1 response (headers + body).
+ * There can be more than one of these per Http exchange.
  */
 class Http1Response<T> {
 
@@ -42,48 +50,71 @@ class Http1Response<T> {
     private final HttpRequestImpl request;
     private Response response;
     private final HttpConnection connection;
-    private ResponseHeaders headers;
+    private HttpHeaders headers;
     private int responseCode;
-    private ByteBuffer buffer;
     private final Http1Exchange<T> exchange;
-    private final boolean redirecting; // redirecting
     private boolean return2Cache; // return connection to cache when finished
+    private final HeadersReader headersReader; // used to read the headers
+    private final BodyReader bodyReader; // used to read the body
+    private final Http1AsyncReceiver asyncReceiver;
+    private volatile EOFException eof;
+    // max number of bytes of (fixed length) body to ignore on redirect
+    private final static int MAX_IGNORE = 1024;
 
-    Http1Response(HttpConnection conn, Http1Exchange<T> exchange) {
+    // Revisit: can we get rid of this?
+    static enum State {INITIAL, READING_HEADERS, READING_BODY, DONE}
+    private volatile State readProgress = State.INITIAL;
+    static final boolean DEBUG = Utils.DEBUG; // Revisit: temporary dev flag.
+    final System.Logger  debug = Utils.getDebugLogger(this.getClass()::getSimpleName, DEBUG);
+
+
+    Http1Response(HttpConnection conn,
+                  Http1Exchange<T> exchange,
+                  Http1AsyncReceiver asyncReceiver) {
+        this.readProgress = State.INITIAL;
         this.request = exchange.request();
         this.exchange = exchange;
         this.connection = conn;
-        this.redirecting = false;
-        buffer = exchange.getBuffer();
+        this.asyncReceiver = asyncReceiver;
+        headersReader = new HeadersReader(this::advance);
+        bodyReader = new BodyReader(this::advance);
     }
 
-    @SuppressWarnings("unchecked")
-    public void readHeaders() throws IOException {
-        String statusline = readStatusLine();
-        if (statusline == null) {
-            if (Log.errors()) {
-                Log.logError("Connection closed. Retry");
-            }
-            connection.close();
-            // connection was closed
-            throw new IOException("Connection closed");
-        }
-        if (!statusline.startsWith("HTTP/1.")) {
-            throw new IOException("Invalid status line: " + statusline);
-        }
-        if (Log.trace()) {
-            Log.logTrace("Statusline: {0}", statusline);
-        }
-        char c = statusline.charAt(7);
-        responseCode = Integer.parseInt(statusline.substring(9, 12));
+   public CompletableFuture<Response> readHeadersAsync(Executor executor) {
+        debug.log(Level.DEBUG, () -> "Reading Headers: (remaining: "
+                + asyncReceiver.remaining() +") "  + readProgress);
+        // with expect continue we will resume reading headers + body.
+        asyncReceiver.unsubscribe(bodyReader);
+        bodyReader.reset();
+        Http1HeaderParser hd = new Http1HeaderParser();
+        readProgress = State.READING_HEADERS;
+        headersReader.start(hd);
+        asyncReceiver.subscribe(headersReader);
+        CompletableFuture<State> cf = headersReader.completion();
+        assert cf != null : "parsing not started";
 
-        headers = new ResponseHeaders(connection, buffer);
-        if (Log.headers()) {
-            logHeaders(headers);
+        Function<State, Response> lambda = (State completed) -> {
+                assert completed == State.READING_HEADERS;
+                debug.log(Level.DEBUG, () ->
+                            "Reading Headers: creating Response object;"
+                            + " state is now " + readProgress);
+                asyncReceiver.unsubscribe(headersReader);
+                responseCode = hd.responseCode();
+                headers = hd.headers();
+
+                response = new Response(request,
+                                        exchange.getExchange(),
+                                        headers,
+                                        responseCode,
+                                        HTTP_1_1);
+                return response;
+            };
+
+        if (executor != null) {
+            return cf.thenApplyAsync(lambda, executor);
+        } else {
+            return cf.thenApply(lambda);
         }
-        response = new Response(
-                request, exchange.getExchange(),
-                headers, responseCode, HTTP_1_1);
     }
 
     private boolean finished;
@@ -96,10 +127,6 @@ class Http1Response<T> {
         return finished;
     }
 
-    ByteBuffer getBuffer() {
-        return buffer;
-    }
-
     int fixupContentLen(int clen) {
         if (request.method().equalsIgnoreCase("HEAD")) {
             return 0;
@@ -114,79 +141,123 @@ class Http1Response<T> {
         return clen;
     }
 
-    public CompletableFuture<T> readBody(
-            HttpResponse.BodyProcessor<T> p,
-            boolean return2Cache,
-            Executor executor) {
-        final BlockingPushPublisher<ByteBuffer> publisher = new BlockingPushPublisher<>();
-        return readBody(p, return2Cache, publisher, executor);
+    /**
+     * Read up to MAX_IGNORE bytes discarding
+     */
+    public CompletableFuture<Void> ignoreBody(Executor executor) {
+        int clen = (int)headers.firstValueAsLong("Content-Length").orElse(-1);
+        if (clen == -1 || clen > MAX_IGNORE) {
+            connection.close();
+            return MinimalFuture.completedFuture(null); // not treating as error
+        } else {
+            return readBody(HttpResponse.BodySubscriber.discard((Void)null), true, executor);
+        }
     }
 
-    private CompletableFuture<T> readBody(
-            HttpResponse.BodyProcessor<T> p,
-            boolean return2Cache,
-            AbstractPushPublisher<ByteBuffer> publisher,
-            Executor executor) {
+    public <U> CompletableFuture<U> readBody(HttpResponse.BodySubscriber<U> p,
+                                         boolean return2Cache,
+                                         Executor executor) {
         this.return2Cache = return2Cache;
-        final jdk.incubator.http.HttpResponse.BodyProcessor<T> pusher = p;
-        final CompletableFuture<T> cf = p.getBody().toCompletableFuture();
+        final HttpResponse.BodySubscriber<U> pusher = p;
+        final CompletionStage<U> bodyCF = p.getBody();
+        final CompletableFuture<U> cf = MinimalFuture.of(bodyCF);
+
+        int clen0 = (int)headers.firstValueAsLong("Content-Length").orElse(-1);
 
-        int clen0;
-        try {
-            clen0 = headers.getContentLength();
-        } catch (IOException ex) {
-            cf.completeExceptionally(ex);
-            return cf;
-        }
         final int clen = fixupContentLen(clen0);
 
+        // expect-continue reads headers and body twice.
+        // if we reach here, we must reset the headersReader state.
+        asyncReceiver.unsubscribe(headersReader);
+        headersReader.reset();
+
         executor.execute(() -> {
             try {
+                HttpClientImpl client = connection.client();
                 content = new ResponseContent(
                         connection, clen, headers, pusher,
-                        publisher.asDataConsumer(),
-                        (t -> {
-                            publisher.acceptError(t);
-                            connection.close();
-                            cf.completeExceptionally(t);
-                        }),
-                        () -> onFinished()
+                        this::onFinished
                 );
-                publisher.subscribe(p);
                 if (cf.isCompletedExceptionally()) {
                     // if an error occurs during subscription
                     connection.close();
                     return;
                 }
-                content.pushBody(buffer);
+                // increment the reference count on the HttpClientImpl
+                // to prevent the SelectorManager thread from exiting until
+                // the body is fully read.
+                client.reference();
+                bodyReader.start(content.getBodyParser(
+                    (t) -> {
+                        try {
+                            if (t != null) {
+                                pusher.onError(t);
+                                connection.close();
+                                if (!cf.isDone())
+                                    cf.completeExceptionally(t);
+                            }
+                        } finally {
+                            // decrement the reference count on the HttpClientImpl
+                            // to allow the SelectorManager thread to exit if no
+                            // other operation is pending and the facade is no
+                            // longer referenced.
+                            client.unreference();
+                            bodyReader.onComplete(t);
+                        }
+                    }));
+                CompletableFuture<State> bodyReaderCF = bodyReader.completion();
+                asyncReceiver.subscribe(bodyReader);
+                assert bodyReaderCF != null : "parsing not started";
+                // Make sure to keep a reference to asyncReceiver from
+                // within this
+                CompletableFuture<?> trailingOp = bodyReaderCF.whenComplete((s,t) ->  {
+                    t = Utils.getCompletionCause(t);
+                    try {
+                        if (t != null) {
+                            debug.log(Level.DEBUG, () ->
+                                    "Finished reading body: " + s);
+                            assert s == State.READING_BODY;
+                        }
+                        if (t != null && !cf.isDone()) {
+                            pusher.onError(t);
+                            cf.completeExceptionally(t);
+                        }
+                    } catch (Throwable x) {
+                        // not supposed to happen
+                        asyncReceiver.onReadError(x);
+                    }
+                });
+                connection.addTrailingOperation(trailingOp);
             } catch (Throwable t) {
-                cf.completeExceptionally(t);
+               debug.log(Level.DEBUG, () -> "Failed reading body: " + t);
+                try {
+                    if (!cf.isDone()) {
+                        pusher.onError(t);
+                        cf.completeExceptionally(t);
+                    }
+                } finally {
+                    asyncReceiver.onReadError(t);
+                }
             }
         });
         return cf;
     }
 
+
     private void onFinished() {
+        asyncReceiver.clear();
         if (return2Cache) {
-            Log.logTrace("Returning connection to the pool: {0}", connection);
-            connection.returnToCache(headers);
+            Log.logTrace("Attempting to return connection to the pool: {0}", connection);
+            // TODO: need to do something here?
+            // connection.setAsyncCallbacks(null, null, null);
+
+            // don't return the connection to the cache if EOF happened.
+            debug.log(Level.DEBUG, () -> connection.getConnectionFlow()
+                                   + ": return to HTTP/1.1 pool");
+            connection.closeOrReturnToCache(eof == null ? headers : null);
         }
     }
 
-    private void logHeaders(ResponseHeaders headers) {
-        StringBuilder sb = new StringBuilder("RESPONSE HEADERS:\n");
-        Log.dumpHeaders(sb, "    ", headers);
-        Log.logHeaders(sb.toString());
-    }
-
-    Response response() {
-        return response;
-    }
-
-    boolean redirecting() {
-        return redirecting;
-    }
-
     HttpHeaders responseHeaders() {
         return headers;
     }
@@ -195,50 +266,251 @@ class Http1Response<T> {
         return responseCode;
     }
 
-    static final char CR = '\r';
-    static final char LF = '\n';
+// ================ Support for plugging into Http1Receiver   =================
+// ============================================================================
 
-    private int obtainBuffer() throws IOException {
-        int n = buffer.remaining();
-
-        if (n == 0) {
-            buffer = connection.read();
-            if (buffer == null) {
-                return -1;
-            }
-            n = buffer.remaining();
+    // Callback: Error receiver: Consumer of Throwable.
+    void onReadError(Throwable t) {
+        Log.logError(t);
+        Receiver<?> receiver = receiver(readProgress);
+        if (t instanceof EOFException) {
+            debug.log(Level.DEBUG, "onReadError: received EOF");
+            eof = (EOFException) t;
         }
-        return n;
+        CompletableFuture<?> cf = receiver == null ? null : receiver.completion();
+        debug.log(Level.DEBUG, () -> "onReadError: cf is "
+                + (cf == null  ? "null"
+                : (cf.isDone() ? "already completed"
+                               : "not yet completed")));
+        if (cf != null && !cf.isDone()) cf.completeExceptionally(t);
+        else { debug.log(Level.DEBUG, "onReadError", t); }
+        debug.log(Level.DEBUG, () -> "closing connection: cause is " + t);
+        connection.close();
     }
 
-    String readStatusLine() throws IOException {
-        boolean cr = false;
-        StringBuilder statusLine = new StringBuilder(128);
-        while ((obtainBuffer()) != -1) {
-            byte[] buf = buffer.array();
-            int offset = buffer.position();
-            int len = buffer.limit() - offset;
+    // ========================================================================
 
-            for (int i = 0; i < len; i++) {
-                char c = (char) buf[i+offset];
+    private State advance(State previous) {
+        assert readProgress == previous;
+        switch(previous) {
+            case READING_HEADERS:
+                asyncReceiver.unsubscribe(headersReader);
+                return readProgress = State.READING_BODY;
+            case READING_BODY:
+                asyncReceiver.unsubscribe(bodyReader);
+                return readProgress = State.DONE;
+            default:
+                throw new InternalError("can't advance from " + previous);
+        }
+    }
 
-                if (cr) {
-                    if (c == LF) {
-                        buffer.position(i + 1 + offset);
-                        return statusLine.toString();
-                    } else {
-                        throw new IOException("invalid status line");
-                    }
+    Receiver<?> receiver(State state) {
+        switch(state) {
+            case READING_HEADERS: return headersReader;
+            case READING_BODY: return bodyReader;
+            default: return null;
+        }
+
+    }
+
+    static abstract class Receiver<T>
+            implements Http1AsyncReceiver.Http1AsyncDelegate {
+        abstract void start(T parser);
+        abstract CompletableFuture<State> completion();
+        // accepts a buffer from upstream.
+        // this should be implemented as a simple call to
+        // accept(ref, parser, cf)
+        public abstract boolean tryAsyncReceive(ByteBuffer buffer);
+        public abstract void onReadError(Throwable t);
+        // handle a byte buffer received from upstream.
+        // this method should set the value of Http1Response.buffer
+        // to ref.get() before beginning parsing.
+        abstract void handle(ByteBuffer buf, T parser,
+                             CompletableFuture<State> cf);
+        // resets this objects state so that it can be reused later on
+        // typically puts the reference to parser and completion to null
+        abstract void reset();
+
+        // accepts a byte buffer received from upstream
+        // returns true if the buffer is fully parsed and more data can
+        // be accepted, false otherwise.
+        final boolean accept(ByteBuffer buf, T parser,
+                CompletableFuture<State> cf) {
+            if (cf == null || parser == null || cf.isDone()) return false;
+            handle(buf, parser, cf);
+            return !cf.isDone();
+        }
+        public abstract void onSubscribe(AbstractSubscription s);
+        public abstract AbstractSubscription subscription();
+
+    }
+
+    // Invoked with each new ByteBuffer when reading headers...
+    final class HeadersReader extends Receiver<Http1HeaderParser> {
+        final Consumer<State> onComplete;
+        volatile Http1HeaderParser parser;
+        volatile CompletableFuture<State> cf;
+        volatile long count; // bytes parsed (for debug)
+        volatile AbstractSubscription subscription;
+
+        HeadersReader(Consumer<State> onComplete) {
+            this.onComplete = onComplete;
+        }
+
+        @Override
+        public AbstractSubscription subscription() {
+            return subscription;
+        }
+
+        @Override
+        public void onSubscribe(AbstractSubscription s) {
+            this.subscription = s;
+            s.request(1);
+        }
+
+        @Override
+        void reset() {
+            cf = null;
+            parser = null;
+            count = 0;
+            subscription = null;
+        }
+
+        // Revisit: do we need to support restarting?
+        @Override
+        final void start(Http1HeaderParser hp) {
+            count = 0;
+            cf = new MinimalFuture<>();
+            parser = hp;
+        }
+
+        @Override
+        CompletableFuture<State> completion() {
+            return cf;
+        }
+
+        @Override
+        public final boolean tryAsyncReceive(ByteBuffer ref) {
+            boolean hasDemand = subscription.demand().tryDecrement();
+            assert hasDemand;
+            boolean needsMore = accept(ref, parser, cf);
+            if (needsMore) subscription.request(1);
+            return needsMore;
+        }
+
+        @Override
+        public final void onReadError(Throwable t) {
+            Http1Response.this.onReadError(t);
+        }
+
+        @Override
+        final void handle(ByteBuffer b,
+                          Http1HeaderParser parser,
+                          CompletableFuture<State> cf) {
+            assert cf != null : "parsing not started";
+            assert parser != null : "no parser";
+            try {
+                count += b.remaining();
+                debug.log(Level.DEBUG, () -> "Sending " + b.remaining()
+                        + "/" + b.capacity() + " bytes to header parser");
+                if (parser.parse(b)) {
+                    count -= b.remaining();
+                    debug.log(Level.DEBUG, () ->
+                            "Parsing headers completed. bytes=" + count);
+                    onComplete.accept(State.READING_HEADERS);
+                    cf.complete(State.READING_HEADERS);
                 }
-                if (c == CR) {
-                    cr = true;
-                } else {
-                    statusLine.append(c);
+            } catch (Throwable t) {
+                debug.log(Level.DEBUG,
+                        () -> "Header parser failed to handle buffer: " + t);
+                cf.completeExceptionally(t);
+            }
+        }
+    }
+
+    // Invoked with each new ByteBuffer when reading bodies...
+    final class BodyReader extends Receiver<BodyParser> {
+        final Consumer<State> onComplete;
+        volatile BodyParser parser;
+        volatile CompletableFuture<State> cf;
+        volatile AbstractSubscription subscription;
+        BodyReader(Consumer<State> onComplete) {
+            this.onComplete = onComplete;
+        }
+
+        @Override
+        void reset() {
+            parser = null;
+            cf = null;
+            subscription = null;
+        }
+
+        // Revisit: do we need to support restarting?
+        @Override
+        final void start(BodyParser parser) {
+            cf = new MinimalFuture<>();
+            this.parser = parser;
+        }
+
+        @Override
+        CompletableFuture<State> completion() {
+            return cf;
+        }
+
+        @Override
+        public final boolean tryAsyncReceive(ByteBuffer b) {
+            return accept(b, parser, cf);
+        }
+
+        @Override
+        public final void onReadError(Throwable t) {
+            Http1Response.this.onReadError(t);
+        }
+
+        @Override
+        public AbstractSubscription subscription() {
+            return subscription;
+        }
+
+        @Override
+        public void onSubscribe(AbstractSubscription s) {
+            this.subscription = s;
+            parser.onSubscribe(s);
+        }
+
+        @Override
+        final void handle(ByteBuffer b,
+                          BodyParser parser,
+                          CompletableFuture<State> cf) {
+            assert cf != null : "parsing not started";
+            assert parser != null : "no parser";
+            try {
+                debug.log(Level.DEBUG, () -> "Sending " + b.remaining()
+                        + "/" + b.capacity() + " bytes to body parser");
+                parser.accept(b);
+            } catch (Throwable t) {
+                debug.log(Level.DEBUG,
+                        () -> "Body parser failed to handle buffer: " + t);
+                if (!cf.isDone()) {
+                    cf.completeExceptionally(t);
                 }
             }
-            // unlikely, but possible, that multiple reads required
-            buffer.position(buffer.limit());
         }
-        return null;
+
+        final void onComplete(Throwable closedExceptionally) {
+            if (cf.isDone()) return;
+            if (closedExceptionally != null) {
+                cf.completeExceptionally(closedExceptionally);
+            } else {
+                onComplete.accept(State.READING_BODY);
+                cf.complete(State.READING_BODY);
+            }
+        }
+
+        @Override
+        public String toString() {
+            return super.toString() + "/parser=" + String.valueOf(parser);
+        }
+
     }
 }
diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/Http2ClientImpl.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/Http2ClientImpl.java
index 7b8205cfe5a..21a8a4a4fd2 100644
--- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/Http2ClientImpl.java
+++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/Http2ClientImpl.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2017, 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
@@ -25,17 +25,18 @@
 
 package jdk.incubator.http;
 
-import java.io.IOException;
+import java.lang.System.Logger.Level;
 import java.net.InetSocketAddress;
 import java.net.URI;
 import java.util.Base64;
 import java.util.Collections;
-import java.util.HashMap;
 import java.util.HashSet;
+import java.util.HashMap;
 import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
-
+import java.util.concurrent.CompletableFuture;
+import jdk.incubator.http.internal.common.MinimalFuture;
 import jdk.incubator.http.internal.common.Utils;
 import jdk.incubator.http.internal.frame.SettingsFrame;
 import static jdk.incubator.http.internal.frame.SettingsFrame.INITIAL_WINDOW_SIZE;
@@ -49,6 +50,10 @@ import static jdk.incubator.http.internal.frame.SettingsFrame.MAX_FRAME_SIZE;
  */
 class Http2ClientImpl {
 
+    static final boolean DEBUG = Utils.DEBUG; // Revisit: temporary dev flag.
+    final static System.Logger debug =
+            Utils.getDebugLogger("Http2ClientImpl"::toString, DEBUG);
+
     private final HttpClientImpl client;
 
     Http2ClientImpl(HttpClientImpl client) {
@@ -59,13 +64,26 @@ class Http2ClientImpl {
     private final Map<String,Http2Connection> connections = new ConcurrentHashMap<>();
 
     private final Set<String> opening = Collections.synchronizedSet(new HashSet<>());
+    private final Map<String,Set<CompletableFuture<Http2Connection>>> waiting =
+    Collections.synchronizedMap(new HashMap<>());
 
-    boolean haveConnectionFor(URI uri, InetSocketAddress proxy) {
-        return connections.containsKey(Http2Connection.keyFor(uri,proxy));
+    private void addToWaiting(String key, CompletableFuture<Http2Connection> cf) {
+        synchronized (waiting) {
+            Set<CompletableFuture<Http2Connection>> waiters = waiting.get(key);
+            if (waiters == null) {
+                waiters = new HashSet<>();
+                waiting.put(key, waiters);
+            }
+            waiters.add(cf);
+        }
     }
 
+//    boolean haveConnectionFor(URI uri, InetSocketAddress proxy) {
+//        return connections.containsKey(Http2Connection.keyFor(uri,proxy));
+//    }
+
     /**
-     * If a https request then blocks and waits until a connection is opened.
+     * If a https request then async waits until a connection is opened.
      * Returns null if the request is 'http' as a different (upgrade)
      * mechanism is used.
      *
@@ -78,49 +96,60 @@ class Http2ClientImpl {
      * In latter case, when the Http2Connection is connected, putConnection() must
      * be called to store it.
      */
-    Http2Connection getConnectionFor(HttpRequestImpl req)
-            throws IOException, InterruptedException {
+    CompletableFuture<Http2Connection> getConnectionFor(HttpRequestImpl req) {
         URI uri = req.uri();
-        InetSocketAddress proxy = req.proxy(client);
+        InetSocketAddress proxy = req.proxy();
         String key = Http2Connection.keyFor(uri, proxy);
-        Http2Connection connection = connections.get(key);
-        if (connection != null) { // fast path if connection already exists
-            return connection;
-        }
-        synchronized (opening) {
-            while ((connection = connections.get(key)) == null) {
-                if (!req.secure()) {
-                    return null;
-                }
-                if (!opening.contains(key)) {
-                    opening.add(key);
-                    break;
-                } else {
-                    opening.wait();
-                }
-            }
-        }
-        if (connection != null) {
-            return connection;
-        }
-        // we are opening the connection here blocking until it is done.
-        try {
-            connection = new Http2Connection(req, this);
-        } catch (Throwable t) {
-            synchronized (opening) {
-                opening.remove(key);
-                opening.notifyAll();
-            }
-            throw t;
-        }
-        synchronized (opening) {
-            connections.put(key, connection);
-            opening.remove(key);
-            opening.notifyAll();
-        }
-        return connection;
-    }
 
+        synchronized (opening) {
+            Http2Connection connection = connections.get(key);
+            if (connection != null) { // fast path if connection already exists
+                return CompletableFuture.completedFuture(connection);
+            }
+
+            if (!req.secure()) {
+                return MinimalFuture.completedFuture(null);
+            }
+
+            if (!opening.contains(key)) {
+                debug.log(Level.DEBUG, "Opening: %s", key);
+                opening.add(key);
+            } else {
+                CompletableFuture<Http2Connection> cf = new MinimalFuture<>();
+                addToWaiting(key, cf);
+                return cf;
+            }
+        }
+        return Http2Connection
+                .createAsync(req, this)
+                .whenComplete((conn, t) -> {
+                    debug.log(Level.DEBUG,
+                            "waking up dependents with created connection");
+                    synchronized (opening) {
+                        Set<CompletableFuture<Http2Connection>> waiters = waiting.remove(key);
+                        debug.log(Level.DEBUG, "Opening completed: %s", key);
+                        opening.remove(key);
+                        if (t == null && conn != null)
+                            putConnection(conn);
+                        final Throwable cause = Utils.getCompletionCause(t);
+                        if (waiters == null) {
+                            debug.log(Level.DEBUG, "no dependent to wake up");
+                            return;
+                        } else if (cause instanceof Http2Connection.ALPNException) {
+                            waiters.forEach((cf1) -> cf1.completeAsync(() -> null,
+                                    client.theExecutor()));
+                        } else if (cause != null) {
+                            debug.log(Level.DEBUG,
+                                    () -> "waking up dependants: failed: " + cause);
+                            waiters.forEach((cf1) -> cf1.completeExceptionally(cause));
+                        } else  {
+                            debug.log(Level.DEBUG, "waking up dependants: succeeded");
+                            waiters.forEach((cf1) -> cf1.completeAsync(() -> conn,
+                                    client.theExecutor()));
+                        }
+                    }
+                });
+    }
 
     /*
      * TODO: If there isn't a connection to the same destination, then
@@ -134,6 +163,16 @@ class Http2ClientImpl {
         connections.remove(c.key());
     }
 
+    void stop() {
+        debug.log(Level.DEBUG, "stopping");
+        connections.values().forEach(this::close);
+        connections.clear();
+    }
+
+    private void close(Http2Connection h2c) {
+        try { h2c.close(); } catch (Throwable t) {}
+    }
+
     HttpClientImpl client() {
         return client;
     }
diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/Http2Connection.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/Http2Connection.java
index 8e42022c286..e1461d8b5ea 100644
--- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/Http2Connection.java
+++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/Http2Connection.java
@@ -25,27 +25,50 @@
 
 package jdk.incubator.http;
 
+import java.io.EOFException;
 import java.io.IOException;
+import java.lang.System.Logger.Level;
 import java.net.InetSocketAddress;
 import java.net.URI;
-import jdk.incubator.http.HttpConnection.Mode;
 import java.nio.ByteBuffer;
 import java.nio.charset.StandardCharsets;
-import java.util.HashMap;
 import java.util.Iterator;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
 import java.util.concurrent.CompletableFuture;
 import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Formatter;
+import java.util.Objects;
 import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.CountDownLatch;
-import java.util.stream.Collectors;
+import java.util.concurrent.ConcurrentLinkedQueue;
+import java.util.concurrent.Flow;
+import java.util.function.Function;
+import java.util.function.Supplier;
 import javax.net.ssl.SSLEngine;
-import jdk.incubator.http.internal.common.*;
-import jdk.incubator.http.internal.frame.*;
+import jdk.incubator.http.HttpConnection.HttpPublisher;
+import jdk.incubator.http.internal.common.FlowTube;
+import jdk.incubator.http.internal.common.FlowTube.TubeSubscriber;
+import jdk.incubator.http.internal.common.HttpHeadersImpl;
+import jdk.incubator.http.internal.common.Log;
+import jdk.incubator.http.internal.common.MinimalFuture;
+import jdk.incubator.http.internal.common.SequentialScheduler;
+import jdk.incubator.http.internal.common.Utils;
+import jdk.incubator.http.internal.frame.ContinuationFrame;
+import jdk.incubator.http.internal.frame.DataFrame;
+import jdk.incubator.http.internal.frame.ErrorFrame;
+import jdk.incubator.http.internal.frame.FramesDecoder;
+import jdk.incubator.http.internal.frame.FramesEncoder;
+import jdk.incubator.http.internal.frame.GoAwayFrame;
+import jdk.incubator.http.internal.frame.HeaderFrame;
+import jdk.incubator.http.internal.frame.HeadersFrame;
+import jdk.incubator.http.internal.frame.Http2Frame;
+import jdk.incubator.http.internal.frame.MalformedFrame;
+import jdk.incubator.http.internal.frame.OutgoingHeaders;
+import jdk.incubator.http.internal.frame.PingFrame;
+import jdk.incubator.http.internal.frame.PushPromiseFrame;
+import jdk.incubator.http.internal.frame.ResetFrame;
+import jdk.incubator.http.internal.frame.SettingsFrame;
+import jdk.incubator.http.internal.frame.WindowUpdateFrame;
 import jdk.incubator.http.internal.hpack.Encoder;
 import jdk.incubator.http.internal.hpack.Decoder;
 import jdk.incubator.http.internal.hpack.DecodingCallback;
@@ -83,11 +106,21 @@ import static jdk.incubator.http.internal.frame.SettingsFrame.*;
  * stream are provided by calling Stream.incoming().
  */
 class Http2Connection  {
+
+    static final boolean DEBUG = Utils.DEBUG; // Revisit: temporary dev flag.
+    static final boolean DEBUG_HPACK = Utils.DEBUG_HPACK; // Revisit: temporary dev flag.
+    final System.Logger  debug = Utils.getDebugLogger(this::dbgString, DEBUG);
+    final static System.Logger  DEBUG_LOGGER =
+            Utils.getDebugLogger("Http2Connection"::toString, DEBUG);
+    private final System.Logger debugHpack =
+                  Utils.getHpackLogger(this::dbgString, DEBUG_HPACK);
+    static final ByteBuffer EMPTY_TRIGGER = ByteBuffer.allocate(0);
+
     /*
      *  ByteBuffer pooling strategy for HTTP/2 protocol:
      *
      * In general there are 4 points where ByteBuffers are used:
-     *  - incoming/outgoing frames from/to ByteBufers plus incoming/outgoing encrypted data
+     *  - incoming/outgoing frames from/to ByteBuffers plus incoming/outgoing encrypted data
      *    in case of SSL connection.
      *
      * 1. Outgoing frames encoded to ByteBuffers.
@@ -116,40 +149,49 @@ class Http2Connection  {
     // preface is sent will be buffered.
     private final class FramesController {
         volatile boolean prefaceSent;
-        volatile List<ByteBufferReference> pending;
+        volatile List<ByteBuffer> pending;
 
-        boolean processReceivedData(FramesDecoder decoder, ByteBufferReference buf)
+        boolean processReceivedData(FramesDecoder decoder, ByteBuffer buf)
                 throws IOException
         {
             // if preface is not sent, buffers data in the pending list
             if (!prefaceSent) {
+                debug.log(Level.DEBUG, "Preface is not sent: buffering %d",
+                          buf.remaining());
                 synchronized (this) {
                     if (!prefaceSent) {
                         if (pending == null) pending = new ArrayList<>();
                         pending.add(buf);
+                        debug.log(Level.DEBUG, () -> "there are now "
+                              + Utils.remaining(pending)
+                              + " bytes buffered waiting for preface to be sent");
                         return false;
                     }
                 }
             }
 
             // Preface is sent. Checks for pending data and flush it.
-            // We rely on this method being called from within the readlock,
-            // so we know that no other thread could execute this method
+            // We rely on this method being called from within the Http2TubeSubscriber
+            // scheduler, so we know that no other thread could execute this method
             // concurrently while we're here.
             // This ensures that later incoming buffers will not
             // be processed before we have flushed the pending queue.
             // No additional synchronization is therefore necessary here.
-            List<ByteBufferReference> pending = this.pending;
+            List<ByteBuffer> pending = this.pending;
             this.pending = null;
             if (pending != null) {
                 // flush pending data
-                for (ByteBufferReference b : pending) {
+                debug.log(Level.DEBUG, () -> "Processing buffered data: "
+                      + Utils.remaining(pending));
+                for (ByteBuffer b : pending) {
                     decoder.decode(b);
                 }
             }
-
             // push the received buffer to the frames decoder.
-            decoder.decode(buf);
+            if (buf != EMPTY_TRIGGER) {
+                debug.log(Level.DEBUG, "Processing %d", buf.remaining());
+                decoder.decode(buf);
+            }
             return true;
         }
 
@@ -167,7 +209,6 @@ class Http2Connection  {
 
     //-------------------------------------
     final HttpConnection connection;
-    private final HttpClientImpl client;
     private final Http2ClientImpl client2;
     private final Map<Integer,Stream<?>> streams = new ConcurrentHashMap<>();
     private int nextstreamid;
@@ -186,7 +227,10 @@ class Http2Connection  {
      */
     private final WindowController windowController = new WindowController();
     private final FramesController framesController = new FramesController();
+    private final Http2TubeSubscriber subscriber = new Http2TubeSubscriber();
     final WindowUpdateSender windowUpdater;
+    private volatile Throwable cause;
+    private volatile Supplier<ByteBuffer> initial;
 
     static final int DEFAULT_FRAME_SIZE = 16 * 1024;
 
@@ -199,7 +243,6 @@ class Http2Connection  {
                             int nextstreamid,
                             String key) {
         this.connection = connection;
-        this.client = client2.client();
         this.client2 = client2;
         this.nextstreamid = nextstreamid;
         this.key = key;
@@ -209,102 +252,147 @@ class Http2Connection  {
         this.serverSettings = SettingsFrame.getDefaultSettings();
         this.hpackOut = new Encoder(serverSettings.getParameter(HEADER_TABLE_SIZE));
         this.hpackIn = new Decoder(clientSettings.getParameter(HEADER_TABLE_SIZE));
-        this.windowUpdater = new ConnectionWindowUpdateSender(this, client.getReceiveBufferSize());
+        debugHpack.log(Level.DEBUG, () -> "For the record:" + super.toString());
+        debugHpack.log(Level.DEBUG, "Decoder created: %s", hpackIn);
+        debugHpack.log(Level.DEBUG, "Encoder created: %s", hpackOut);
+        this.windowUpdater = new ConnectionWindowUpdateSender(this, client().getReceiveBufferSize());
     }
 
     /**
      * Case 1) Create from upgraded HTTP/1.1 connection.
-     * Is ready to use. Will not be SSL. exchange is the Exchange
+     * Is ready to use. Can be SSL. exchange is the Exchange
      * that initiated the connection, whose response will be delivered
      * on a Stream.
      */
-    Http2Connection(HttpConnection connection,
+    private Http2Connection(HttpConnection connection,
                     Http2ClientImpl client2,
                     Exchange<?> exchange,
-                    ByteBuffer initial)
+                    Supplier<ByteBuffer> initial)
         throws IOException, InterruptedException
     {
         this(connection,
                 client2,
                 3, // stream 1 is registered during the upgrade
                 keyFor(connection));
-        assert !(connection instanceof SSLConnection);
         Log.logTrace("Connection send window size {0} ", windowController.connectionWindowSize());
 
         Stream<?> initialStream = createStream(exchange);
         initialStream.registerStream(1);
         windowController.registerStream(1, getInitialSendWindowSize());
         initialStream.requestSent();
+        // Upgrading:
+        //    set callbacks before sending preface - makes sure anything that
+        //    might be sent by the server will come our way.
+        this.initial = initial;
+        connectFlows(connection);
         sendConnectionPreface();
-        // start reading and writing
-        // start reading
-        AsyncConnection asyncConn = (AsyncConnection)connection;
-        asyncConn.setAsyncCallbacks(this::asyncReceive, this::shutdown, this::getReadBuffer);
-        connection.configureMode(Mode.ASYNC); // set mode only AFTER setAsyncCallbacks to provide visibility.
-        asyncReceive(ByteBufferReference.of(initial));
-        asyncConn.startReading();
     }
 
-    // async style but completes immediately
+    // Used when upgrading an HTTP/1.1 connection to HTTP/2 after receiving
+    // agreement from the server. Async style but completes immediately, because
+    // the connection is already connected.
     static CompletableFuture<Http2Connection> createAsync(HttpConnection connection,
                                                           Http2ClientImpl client2,
                                                           Exchange<?> exchange,
-                                                          ByteBuffer initial) {
+                                                          Supplier<ByteBuffer> initial)
+    {
         return MinimalFuture.supply(() -> new Http2Connection(connection, client2, exchange, initial));
     }
 
+    // Requires TLS handshake. So, is really async
+    static CompletableFuture<Http2Connection> createAsync(HttpRequestImpl request,
+                                                          Http2ClientImpl h2client) {
+        assert request.secure();
+        AbstractAsyncSSLConnection connection = (AbstractAsyncSSLConnection)
+        HttpConnection.getConnection(request.getAddress(),
+                                     h2client.client(),
+                                     request,
+                                     HttpClient.Version.HTTP_2);
+
+        return connection.connectAsync()
+                  .thenCompose(unused -> checkSSLConfig(connection))
+                  .thenCompose(notused-> {
+                      CompletableFuture<Http2Connection> cf = new MinimalFuture<>();
+                      try {
+                          Http2Connection hc = new Http2Connection(request, h2client, connection);
+                          cf.complete(hc);
+                      } catch (IOException e) {
+                          cf.completeExceptionally(e);
+                      }
+                      return cf; } );
+    }
+
     /**
      * Cases 2) 3)
      *
      * request is request to be sent.
      */
-    Http2Connection(HttpRequestImpl request, Http2ClientImpl h2client)
-        throws IOException, InterruptedException
+    private Http2Connection(HttpRequestImpl request,
+                            Http2ClientImpl h2client,
+                            HttpConnection connection)
+        throws IOException
     {
-        this(HttpConnection.getConnection(request.getAddress(h2client.client()), h2client.client(), request, true),
-                h2client,
-                1,
-                keyFor(request.uri(), request.proxy(h2client.client())));
+        this(connection,
+             h2client,
+             1,
+             keyFor(request.uri(), request.proxy()));
+
         Log.logTrace("Connection send window size {0} ", windowController.connectionWindowSize());
 
-        // start reading
-        AsyncConnection asyncConn = (AsyncConnection)connection;
-        asyncConn.setAsyncCallbacks(this::asyncReceive, this::shutdown, this::getReadBuffer);
-        connection.connect();
-        checkSSLConfig();
         // safe to resume async reading now.
-        asyncConn.enableCallback();
+        connectFlows(connection);
         sendConnectionPreface();
     }
 
+    private void connectFlows(HttpConnection connection) {
+        FlowTube tube =  connection.getConnectionFlow();
+        // Connect the flow to our Http2TubeSubscriber:
+        tube.connectFlows(connection.publisher(), subscriber);
+    }
+
+    final HttpClientImpl client() {
+        return client2.client();
+    }
+
     /**
      * Throws an IOException if h2 was not negotiated
      */
-    private void checkSSLConfig() throws IOException {
-        AbstractAsyncSSLConnection aconn = (AbstractAsyncSSLConnection)connection;
-        SSLEngine engine = aconn.getEngine();
-        String alpn = engine.getApplicationProtocol();
-        if (alpn == null || !alpn.equals("h2")) {
-            String msg;
-            if (alpn == null) {
-                Log.logSSL("ALPN not supported");
-                msg = "ALPN not supported";
-            } else switch (alpn) {
-              case "":
-                Log.logSSL("No ALPN returned");
-                msg = "No ALPN negotiated";
-                break;
-              case "http/1.1":
-                Log.logSSL("HTTP/1.1 ALPN returned");
-                msg = "HTTP/1.1 ALPN returned";
-                break;
-              default:
-                Log.logSSL("unknown ALPN returned");
-                msg = "Unexpected ALPN: " + alpn;
-                throw new IOException(msg);
+    private static CompletableFuture<?> checkSSLConfig(AbstractAsyncSSLConnection aconn) {
+        assert aconn.isSecure();
+
+        Function<String, CompletableFuture<Void>> checkAlpnCF = (alpn) -> {
+            CompletableFuture<Void> cf = new MinimalFuture<>();
+            SSLEngine engine = aconn.getEngine();
+            assert Objects.equals(alpn, engine.getApplicationProtocol());
+
+            DEBUG_LOGGER.log(Level.DEBUG, "checkSSLConfig: alpn: %s", alpn );
+
+            if (alpn == null || !alpn.equals("h2")) {
+                String msg;
+                if (alpn == null) {
+                    Log.logSSL("ALPN not supported");
+                    msg = "ALPN not supported";
+                } else {
+                    switch (alpn) {
+                        case "":
+                            Log.logSSL(msg = "No ALPN negotiated");
+                            break;
+                        case "http/1.1":
+                            Log.logSSL( msg = "HTTP/1.1 ALPN returned");
+                            break;
+                        default:
+                            Log.logSSL(msg = "Unexpected ALPN: " + alpn);
+                            cf.completeExceptionally(new IOException(msg));
+                    }
+                }
+                cf.completeExceptionally(new ALPNException(msg, aconn));
+                return cf;
             }
-            throw new ALPNException(msg, aconn);
-        }
+            cf.complete(null);
+            return cf;
+        };
+
+        return aconn.getALPN().thenCompose(checkAlpnCF);
     }
 
     static String keyFor(HttpConnection connection) {
@@ -322,7 +410,7 @@ class Http2Connection  {
         String host;
         int port;
 
-        if (isProxy) {
+        if (proxy != null) {
             host = proxy.getHostString();
             port = proxy.getPort();
         } else {
@@ -350,47 +438,26 @@ class Http2Connection  {
         client2.putConnection(this);
     }
 
-    private static String toHexdump1(ByteBuffer bb) {
-        bb.mark();
-        StringBuilder sb = new StringBuilder(512);
-        Formatter f = new Formatter(sb);
-
-        while (bb.hasRemaining()) {
-            int i =  Byte.toUnsignedInt(bb.get());
-            f.format("%02x:", i);
-        }
-        sb.deleteCharAt(sb.length()-1);
-        bb.reset();
-        return sb.toString();
+    private HttpPublisher publisher() {
+        return connection.publisher();
     }
 
-    private static String toHexdump(ByteBuffer bb) {
-        List<String> words = new ArrayList<>();
-        int i = 0;
-        bb.mark();
-        while (bb.hasRemaining()) {
-            if (i % 2 == 0) {
-                words.add("");
-            }
-            byte b = bb.get();
-            String hex = Integer.toHexString(256 + Byte.toUnsignedInt(b)).substring(1);
-            words.set(i / 2, words.get(i / 2) + hex);
-            i++;
-        }
-        bb.reset();
-        return words.stream().collect(Collectors.joining(" "));
-    }
+    private void decodeHeaders(HeaderFrame frame, DecodingCallback decoder)
+            throws IOException
+    {
+        debugHpack.log(Level.DEBUG, "decodeHeaders(%s)", decoder);
 
-    private void decodeHeaders(HeaderFrame frame, DecodingCallback decoder) {
         boolean endOfHeaders = frame.getFlag(HeaderFrame.END_HEADERS);
 
-        ByteBufferReference[] buffers = frame.getHeaderBlock();
-        for (int i = 0; i < buffers.length; i++) {
-            hpackIn.decode(buffers[i].get(), endOfHeaders && (i == buffers.length - 1), decoder);
+        List<ByteBuffer> buffers = frame.getHeaderBlock();
+        int len = buffers.size();
+        for (int i = 0; i < len; i++) {
+            ByteBuffer b = buffers.get(i);
+            hpackIn.decode(b, endOfHeaders && (i == len - 1), decoder);
         }
     }
 
-    int getInitialSendWindowSize() {
+    final int getInitialSendWindowSize() {
         return serverSettings.getParameter(INITIAL_WINDOW_SIZE);
     }
 
@@ -400,16 +467,8 @@ class Http2Connection  {
         sendFrame(f);
     }
 
-    private ByteBufferPool readBufferPool = new ByteBufferPool();
-
-    // provides buffer to read data (default size)
-    public ByteBufferReference getReadBuffer() {
-        return readBufferPool.get(getMaxReceiveFrameSize() + Http2Frame.FRAME_HEADER_SIZE);
-    }
-
-    private final Object readlock = new Object();
-
-    public void asyncReceive(ByteBufferReference buffer) {
+    long count;
+    final void asyncReceive(ByteBuffer buffer) {
         // We don't need to read anything and
         // we don't want to send anything back to the server
         // until the connection preface has been sent.
@@ -419,23 +478,61 @@ class Http2Connection  {
         // SettingsFrame sent by the server) before the connection
         // preface is fully sent might result in the server
         // sending a GOAWAY frame with 'invalid_preface'.
-        synchronized (readlock) {
-            try {
-                // the readlock ensures that the order of incoming buffers
-                // is preserved.
-                framesController.processReceivedData(framesDecoder, buffer);
-            } catch (Throwable e) {
-                String msg = Utils.stackTrace(e);
-                Log.logTrace(msg);
-                shutdown(e);
+        //
+        // Note: asyncReceive is only called from the Http2TubeSubscriber
+        //       sequential scheduler.
+        try {
+            Supplier<ByteBuffer> bs = initial;
+            // ensure that we always handle the initial buffer first,
+            // if any.
+            if (bs != null) {
+                initial = null;
+                ByteBuffer b = bs.get();
+                if (b.hasRemaining()) {
+                    long c = ++count;
+                    debug.log(Level.DEBUG, () -> "H2 Receiving Initial("
+                        + c +"): " + b.remaining());
+                    framesController.processReceivedData(framesDecoder, b);
+                }
             }
+            ByteBuffer b = buffer;
+            // the Http2TubeSubscriber scheduler ensures that the order of incoming
+            // buffers is preserved.
+            if (b == EMPTY_TRIGGER) {
+                debug.log(Level.DEBUG, "H2 Received EMPTY_TRIGGER");
+                boolean prefaceSent = framesController.prefaceSent;
+                assert prefaceSent;
+                // call framesController.processReceivedData to potentially
+                // trigger the processing of all the data buffered there.
+                framesController.processReceivedData(framesDecoder, buffer);
+                debug.log(Level.DEBUG, "H2 processed buffered data");
+            } else {
+                long c = ++count;
+                debug.log(Level.DEBUG, "H2 Receiving(%d): %d", c, b.remaining());
+                framesController.processReceivedData(framesDecoder, buffer);
+                debug.log(Level.DEBUG, "H2 processed(%d)", c);
+            }
+        } catch (Throwable e) {
+            String msg = Utils.stackTrace(e);
+            Log.logTrace(msg);
+            shutdown(e);
         }
     }
 
+    Throwable getRecordedCause() {
+        return cause;
+    }
 
     void shutdown(Throwable t) {
+        debug.log(Level.DEBUG, () -> "Shutting down h2c (closed="+closed+"): " + t);
+        if (closed == true) return;
+        synchronized (this) {
+            if (closed == true) return;
+            closed = true;
+        }
         Log.logError(t);
-        closed = true;
+        Throwable initialCause = this.cause;
+        if (initialCause == null) this.cause = t;
         client2.deleteConnection(this);
         List<Stream<?>> c = new LinkedList<>(streams.values());
         for (Stream<?> s : c) {
@@ -457,8 +554,12 @@ class Http2Connection  {
         if (frame instanceof MalformedFrame) {
             Log.logError(((MalformedFrame) frame).getMessage());
             if (streamid == 0) {
-                protocolError(((MalformedFrame) frame).getErrorCode());
+                framesDecoder.close("Malformed frame on stream 0");
+                protocolError(((MalformedFrame) frame).getErrorCode(),
+                        ((MalformedFrame) frame).getMessage());
             } else {
+                debug.log(Level.DEBUG, () -> "Reset stream: "
+                          + ((MalformedFrame) frame).getMessage());
                 resetStream(streamid, ((MalformedFrame) frame).getErrorCode());
             }
             return;
@@ -468,6 +569,8 @@ class Http2Connection  {
         } else {
             if (frame instanceof SettingsFrame) {
                 // The stream identifier for a SETTINGS frame MUST be zero
+                framesDecoder.close(
+                        "The stream identifier for a SETTINGS frame MUST be zero");
                 protocolError(GoAwayFrame.PROTOCOL_ERROR);
                 return;
             }
@@ -476,9 +579,16 @@ class Http2Connection  {
             if (stream == null) {
                 // Should never receive a frame with unknown stream id
 
-                // To avoid looping, an endpoint MUST NOT send a RST_STREAM in
-                // response to a RST_STREAM frame.
-                if (!(frame instanceof ResetFrame)) {
+                if (frame instanceof HeaderFrame) {
+                    // always decode the headers as they may affect
+                    // connection-level HPACK decoding state
+                    HeaderDecoder decoder = new LoggingHeaderDecoder(new HeaderDecoder());
+                    decodeHeaders((HeaderFrame) frame, decoder);
+                }
+
+                int sid = frame.streamid();
+                if (sid >= nextstreamid && !(frame instanceof ResetFrame)) {
+                    // otherwise the stream has already been reset/closed
                     resetStream(streamid, ResetFrame.PROTOCOL_ERROR);
                 }
                 return;
@@ -499,6 +609,11 @@ class Http2Connection  {
     private <T> void handlePushPromise(Stream<T> parent, PushPromiseFrame pp)
         throws IOException
     {
+        // always decode the headers as they may affect connection-level HPACK
+        // decoding state
+        HeaderDecoder decoder = new LoggingHeaderDecoder(new HeaderDecoder());
+        decodeHeaders(pp, decoder);
+
         HttpRequestImpl parentReq = parent.request;
         int promisedStreamid = pp.getPromisedStream();
         if (promisedStreamid != nextPushStream) {
@@ -507,8 +622,7 @@ class Http2Connection  {
         } else {
             nextPushStream += 2;
         }
-        HeaderDecoder decoder = new HeaderDecoder();
-        decodeHeaders(pp, decoder);
+
         HttpHeadersImpl headers = decoder.headers();
         HttpRequestImpl pushReq = HttpRequestImpl.createPushRequest(parentReq, headers);
         Exchange<T> pushExch = new Exchange<>(pushReq, parent.exchange.multi);
@@ -549,7 +663,15 @@ class Http2Connection  {
     }
 
     void closeStream(int streamid) {
+        debug.log(Level.DEBUG, "Closed stream %d", streamid);
         Stream<?> s = streams.remove(streamid);
+        if (s != null) {
+            // decrement the reference count on the HttpClientImpl
+            // to allow the SelectorManager thread to exit if no
+            // other operation is pending and the facade is no
+            // longer referenced.
+            client().unreference();
+        }
         // ## Remove s != null. It is a hack for delayed cancellation,reset
         if (s != null && !(s instanceof Stream.PushedStream)) {
             // Since PushStreams have no request body, then they have no
@@ -578,10 +700,16 @@ class Http2Connection  {
 
     private void protocolError(int errorCode)
         throws IOException
+    {
+        protocolError(errorCode, null);
+    }
+
+    private void protocolError(int errorCode, String msg)
+        throws IOException
     {
         GoAwayFrame frame = new GoAwayFrame(0, errorCode);
         sendFrame(frame);
-        shutdown(new IOException("protocol error"));
+        shutdown(new IOException("protocol error" + (msg == null?"":(": " + msg))));
     }
 
     private void handleSettings(SettingsFrame frame)
@@ -633,11 +761,6 @@ class Http2Connection  {
         return clientSettings.getParameter(MAX_FRAME_SIZE);
     }
 
-    // Not sure how useful this is.
-    public int getMaxHeadersSize() {
-        return serverSettings.getParameter(MAX_HEADER_LIST_SIZE);
-    }
-
     private static final String CLIENT_PREFACE = "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n";
 
     private static final byte[] PREFACE_BYTES =
@@ -652,10 +775,12 @@ class Http2Connection  {
                      connection.channel().getLocalAddress(),
                      connection.address());
         SettingsFrame sf = client2.getClientSettings();
-        ByteBufferReference ref = framesEncoder.encodeConnectionPreface(PREFACE_BYTES, sf);
+        ByteBuffer buf = framesEncoder.encodeConnectionPreface(PREFACE_BYTES, sf);
         Log.logFrames(sf, "OUT");
         // send preface bytes and SettingsFrame together
-        connection.write(ref.get());
+        HttpPublisher publisher = publisher();
+        publisher.enqueue(List.of(buf));
+        publisher.signalEnqueued();
         // mark preface sent.
         framesController.markPrefaceSent();
         Log.logTrace("PREFACE_BYTES sent");
@@ -669,6 +794,9 @@ class Http2Connection  {
         // cause any pending data stored before the preface was sent to be
         // flushed (see PrefaceController).
         Log.logTrace("finished sending connection preface");
+        debug.log(Level.DEBUG, "Triggering processing of buffered data"
+                  + " after sending connection preface");
+        subscriber.onNext(List.of(EMPTY_TRIGGER));
     }
 
     /**
@@ -682,38 +810,37 @@ class Http2Connection  {
     /**
      * Creates Stream with given id.
      */
-    <T> Stream<T> createStream(Exchange<T> exchange) {
-        Stream<T> stream = new Stream<>(client, this, exchange, windowController);
+    final <T> Stream<T> createStream(Exchange<T> exchange) {
+        Stream<T> stream = new Stream<>(this, exchange, windowController);
         return stream;
     }
 
     <T> Stream.PushedStream<?,T> createPushStream(Stream<T> parent, Exchange<T> pushEx) {
         PushGroup<?,T> pg = parent.exchange.getPushGroup();
-        return new Stream.PushedStream<>(pg, client, this, parent, pushEx);
+        return new Stream.PushedStream<>(pg, this, pushEx);
     }
 
     <T> void putStream(Stream<T> stream, int streamid) {
+        // increment the reference count on the HttpClientImpl
+        // to prevent the SelectorManager thread from exiting until
+        // the stream is closed.
+        client().reference();
         streams.put(streamid, stream);
     }
 
-    void deleteStream(int streamid) {
-        streams.remove(streamid);
-        windowController.removeStream(streamid);
-    }
-
     /**
      * Encode the headers into a List<ByteBuffer> and then create HEADERS
      * and CONTINUATION frames from the list and return the List<Http2Frame>.
      */
     private List<HeaderFrame> encodeHeaders(OutgoingHeaders<Stream<?>> frame) {
-        List<ByteBufferReference> buffers = encodeHeadersImpl(
+        List<ByteBuffer> buffers = encodeHeadersImpl(
                 getMaxSendFrameSize(),
                 frame.getAttachment().getRequestPseudoHeaders(),
                 frame.getUserHeaders(),
                 frame.getSystemHeaders());
 
         List<HeaderFrame> frames = new ArrayList<>(buffers.size());
-        Iterator<ByteBufferReference> bufIterator = buffers.iterator();
+        Iterator<ByteBuffer> bufIterator = buffers.iterator();
         HeaderFrame oframe = new HeadersFrame(frame.streamid(), frame.getFlags(), bufIterator.next());
         frames.add(oframe);
         while(bufIterator.hasNext()) {
@@ -728,12 +855,12 @@ class Http2Connection  {
     // There can be no concurrent access to this  buffer as all access to this buffer
     // and its content happen within a single critical code block section protected
     // by the sendLock. / (see sendFrame())
-    private ByteBufferPool headerEncodingPool = new ByteBufferPool();
+    // private final ByteBufferPool headerEncodingPool = new ByteBufferPool();
 
-    private ByteBufferReference getHeaderBuffer(int maxFrameSize) {
-        ByteBufferReference ref = headerEncodingPool.get(maxFrameSize);
-        ref.get().limit(maxFrameSize);
-        return ref;
+    private ByteBuffer getHeaderBuffer(int maxFrameSize) {
+        ByteBuffer buf = ByteBuffer.allocate(maxFrameSize);
+        buf.limit(maxFrameSize);
+        return buf;
     }
 
     /*
@@ -747,29 +874,29 @@ class Http2Connection  {
      *     header field names MUST be converted to lowercase prior to their
      *     encoding in HTTP/2...
      */
-    private List<ByteBufferReference> encodeHeadersImpl(int maxFrameSize, HttpHeaders... headers) {
-        ByteBufferReference buffer = getHeaderBuffer(maxFrameSize);
-        List<ByteBufferReference> buffers = new ArrayList<>();
+    private List<ByteBuffer> encodeHeadersImpl(int maxFrameSize, HttpHeaders... headers) {
+        ByteBuffer buffer = getHeaderBuffer(maxFrameSize);
+        List<ByteBuffer> buffers = new ArrayList<>();
         for(HttpHeaders header : headers) {
             for (Map.Entry<String, List<String>> e : header.map().entrySet()) {
                 String lKey = e.getKey().toLowerCase();
                 List<String> values = e.getValue();
                 for (String value : values) {
                     hpackOut.header(lKey, value);
-                    while (!hpackOut.encode(buffer.get())) {
-                        buffer.get().flip();
+                    while (!hpackOut.encode(buffer)) {
+                        buffer.flip();
                         buffers.add(buffer);
                         buffer =  getHeaderBuffer(maxFrameSize);
                     }
                 }
             }
         }
-        buffer.get().flip();
+        buffer.flip();
         buffers.add(buffer);
         return buffers;
     }
 
-    private ByteBufferReference[] encodeHeaders(OutgoingHeaders<Stream<?>> oh, Stream<?> stream) {
+    private List<ByteBuffer> encodeHeaders(OutgoingHeaders<Stream<?>> oh, Stream<?> stream) {
         oh.streamid(stream.streamid);
         if (Log.headers()) {
             StringBuilder sb = new StringBuilder("HEADERS FRAME (stream=");
@@ -783,26 +910,13 @@ class Http2Connection  {
         return encodeFrames(frames);
     }
 
-    private ByteBufferReference[] encodeFrames(List<HeaderFrame> frames) {
+    private List<ByteBuffer> encodeFrames(List<HeaderFrame> frames) {
         if (Log.frames()) {
             frames.forEach(f -> Log.logFrames(f, "OUT"));
         }
         return framesEncoder.encodeFrames(frames);
     }
 
-    static Throwable getExceptionFrom(CompletableFuture<?> cf) {
-        try {
-            cf.get();
-            return null;
-        } catch (Throwable e) {
-            if (e.getCause() != null) {
-                return e.getCause();
-            } else {
-                return e;
-            }
-        }
-    }
-
     private Stream<?> registerNewStream(OutgoingHeaders<Stream<?>> oh) {
         Stream<?> stream = oh.getAttachment();
         int streamid = nextstreamid;
@@ -818,18 +932,19 @@ class Http2Connection  {
 
     void sendFrame(Http2Frame frame) {
         try {
+            HttpPublisher publisher = publisher();
             synchronized (sendlock) {
                 if (frame instanceof OutgoingHeaders) {
                     @SuppressWarnings("unchecked")
                     OutgoingHeaders<Stream<?>> oh = (OutgoingHeaders<Stream<?>>) frame;
                     Stream<?> stream = registerNewStream(oh);
                     // provide protection from inserting unordered frames between Headers and Continuation
-                    connection.writeAsync(encodeHeaders(oh, stream));
+                    publisher.enqueue(encodeHeaders(oh, stream));
                 } else {
-                    connection.writeAsync(encodeFrame(frame));
+                    publisher.enqueue(encodeFrame(frame));
                 }
             }
-            connection.flushAsync();
+            publisher.signalEnqueued();
         } catch (IOException e) {
             if (!closed) {
                 Log.logError(e);
@@ -838,15 +953,16 @@ class Http2Connection  {
         }
     }
 
-    private ByteBufferReference[] encodeFrame(Http2Frame frame) {
+    private List<ByteBuffer> encodeFrame(Http2Frame frame) {
         Log.logFrames(frame, "OUT");
         return framesEncoder.encodeFrame(frame);
     }
 
     void sendDataFrame(DataFrame frame) {
         try {
-            connection.writeAsync(encodeFrame(frame));
-            connection.flushAsync();
+            HttpPublisher publisher = publisher();
+            publisher.enqueue(encodeFrame(frame));
+            publisher.signalEnqueued();
         } catch (IOException e) {
             if (!closed) {
                 Log.logError(e);
@@ -862,8 +978,9 @@ class Http2Connection  {
      */
     void sendUnorderedFrame(Http2Frame frame) {
         try {
-            connection.writeAsyncUnordered(encodeFrame(frame));
-            connection.flushAsync();
+            HttpPublisher publisher = publisher();
+            publisher.enqueueUnordered(encodeFrame(frame));
+            publisher.signalEnqueued();
         } catch (IOException e) {
             if (!closed) {
                 Log.logError(e);
@@ -872,6 +989,200 @@ class Http2Connection  {
         }
     }
 
+    /**
+     * A simple tube subscriber for reading from the connection flow.
+     */
+    final class Http2TubeSubscriber implements TubeSubscriber {
+        volatile Flow.Subscription subscription;
+        volatile boolean completed;
+        volatile boolean dropped;
+        volatile Throwable error;
+        final ConcurrentLinkedQueue<ByteBuffer> queue
+                = new ConcurrentLinkedQueue<>();
+        final SequentialScheduler scheduler =
+                SequentialScheduler.synchronizedScheduler(this::processQueue);
+
+        final void processQueue() {
+            try {
+                while (!queue.isEmpty() && !scheduler.isStopped()) {
+                    ByteBuffer buffer = queue.poll();
+                    debug.log(Level.DEBUG,
+                              "sending %d to Http2Connection.asyncReceive",
+                              buffer.remaining());
+                    asyncReceive(buffer);
+                }
+            } catch (Throwable t) {
+                Throwable x = error;
+                if (x == null) error = t;
+            } finally {
+                Throwable x = error;
+                if (x != null) {
+                    debug.log(Level.DEBUG, "Stopping scheduler", x);
+                    scheduler.stop();
+                    Http2Connection.this.shutdown(x);
+                }
+            }
+        }
+
+
+        public void onSubscribe(Flow.Subscription subscription) {
+            // supports being called multiple time.
+            // doesn't cancel the previous subscription, since that is
+            // most probably the same as the new subscription.
+            assert this.subscription == null || dropped == false;
+            this.subscription = subscription;
+            dropped = false;
+            // TODO FIXME: request(1) should be done by the delegate.
+            if (!completed) {
+                debug.log(Level.DEBUG, "onSubscribe: requesting Long.MAX_VALUE for reading");
+                subscription.request(Long.MAX_VALUE);
+            } else {
+                debug.log(Level.DEBUG, "onSubscribe: already completed");
+            }
+        }
+
+        @Override
+        public void onNext(List<ByteBuffer> item) {
+            debug.log(Level.DEBUG, () -> "onNext: got " + Utils.remaining(item)
+                    + " bytes in " + item.size() + " buffers");
+            queue.addAll(item);
+            scheduler.deferOrSchedule(client().theExecutor());
+        }
+
+        @Override
+        public void onError(Throwable throwable) {
+            debug.log(Level.DEBUG, () -> "onError: " + throwable);
+            error = throwable;
+            completed = true;
+            scheduler.deferOrSchedule(client().theExecutor());
+        }
+
+        @Override
+        public void onComplete() {
+            debug.log(Level.DEBUG, "EOF");
+            error = new EOFException("EOF reached while reading");
+            completed = true;
+            scheduler.deferOrSchedule(client().theExecutor());
+        }
+
+        public void dropSubscription() {
+            debug.log(Level.DEBUG, "dropSubscription");
+            // we could probably set subscription to null here...
+            // then we might not need the 'dropped' boolean?
+            dropped = true;
+        }
+    }
+
+    @Override
+    public final String toString() {
+        return dbgString();
+    }
+
+    final String dbgString() {
+        return "Http2Connection("
+                    + connection.getConnectionFlow() + ")";
+    }
+
+    final class LoggingHeaderDecoder extends HeaderDecoder {
+
+        private final HeaderDecoder delegate;
+        private final System.Logger debugHpack =
+                Utils.getHpackLogger(this::dbgString, DEBUG_HPACK);
+
+        LoggingHeaderDecoder(HeaderDecoder delegate) {
+            this.delegate = delegate;
+        }
+
+        String dbgString() {
+            return Http2Connection.this.dbgString() + "/LoggingHeaderDecoder";
+        }
+
+        @Override
+        public void onDecoded(CharSequence name, CharSequence value) {
+            delegate.onDecoded(name, value);
+        }
+
+        @Override
+        public void onIndexed(int index,
+                              CharSequence name,
+                              CharSequence value) {
+            debugHpack.log(Level.DEBUG, "onIndexed(%s, %s, %s)%n",
+                           index, name, value);
+            delegate.onIndexed(index, name, value);
+        }
+
+        @Override
+        public void onLiteral(int index,
+                              CharSequence name,
+                              CharSequence value,
+                              boolean valueHuffman) {
+            debugHpack.log(Level.DEBUG, "onLiteral(%s, %s, %s, %s)%n",
+                              index, name, value, valueHuffman);
+            delegate.onLiteral(index, name, value, valueHuffman);
+        }
+
+        @Override
+        public void onLiteral(CharSequence name,
+                              boolean nameHuffman,
+                              CharSequence value,
+                              boolean valueHuffman) {
+            debugHpack.log(Level.DEBUG, "onLiteral(%s, %s, %s, %s)%n",
+                           name, nameHuffman, value, valueHuffman);
+            delegate.onLiteral(name, nameHuffman, value, valueHuffman);
+        }
+
+        @Override
+        public void onLiteralNeverIndexed(int index,
+                                          CharSequence name,
+                                          CharSequence value,
+                                          boolean valueHuffman) {
+            debugHpack.log(Level.DEBUG, "onLiteralNeverIndexed(%s, %s, %s, %s)%n",
+                           index, name, value, valueHuffman);
+            delegate.onLiteralNeverIndexed(index, name, value, valueHuffman);
+        }
+
+        @Override
+        public void onLiteralNeverIndexed(CharSequence name,
+                                          boolean nameHuffman,
+                                          CharSequence value,
+                                          boolean valueHuffman) {
+            debugHpack.log(Level.DEBUG, "onLiteralNeverIndexed(%s, %s, %s, %s)%n",
+                           name, nameHuffman, value, valueHuffman);
+            delegate.onLiteralNeverIndexed(name, nameHuffman, value, valueHuffman);
+        }
+
+        @Override
+        public void onLiteralWithIndexing(int index,
+                                          CharSequence name,
+                                          CharSequence value,
+                                          boolean valueHuffman) {
+            debugHpack.log(Level.DEBUG, "onLiteralWithIndexing(%s, %s, %s, %s)%n",
+                           index, name, value, valueHuffman);
+            delegate.onLiteralWithIndexing(index, name, value, valueHuffman);
+        }
+
+        @Override
+        public void onLiteralWithIndexing(CharSequence name,
+                                          boolean nameHuffman,
+                                          CharSequence value,
+                                          boolean valueHuffman) {
+            debugHpack.log(Level.DEBUG, "onLiteralWithIndexing(%s, %s, %s, %s)%n",
+                              name, nameHuffman, value, valueHuffman);
+            delegate.onLiteralWithIndexing(name, nameHuffman, value, valueHuffman);
+        }
+
+        @Override
+        public void onSizeUpdate(int capacity) {
+            debugHpack.log(Level.DEBUG, "onSizeUpdate(%s)%n", capacity);
+            delegate.onSizeUpdate(capacity);
+        }
+
+        @Override
+        HttpHeadersImpl headers() {
+            return delegate.headers();
+        }
+    }
+
     static class HeaderDecoder implements DecodingCallback {
         HttpHeadersImpl headers;
 
diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/HttpClient.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/HttpClient.java
index 89e138e69b7..d537c805d89 100644
--- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/HttpClient.java
+++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/HttpClient.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2017, 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
@@ -27,13 +27,16 @@ package jdk.incubator.http;
 
 import java.io.IOException;
 import java.net.Authenticator;
-import java.net.CookieManager;
+import java.net.CookieHandler;
 import java.net.InetSocketAddress;
+import java.net.Proxy;
 import java.net.ProxySelector;
 import java.net.URI;
 import java.util.Optional;
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.Executor;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ThreadFactory;
 import javax.net.ssl.SSLContext;
 import javax.net.ssl.SSLParameters;
 
@@ -60,10 +63,25 @@ public abstract class HttpClient {
     /**
      * Returns a new HttpClient with default settings.
      *
+     * <p> Equivalent to {@code newBuilder().build()}.
+     *
+     * <p> The default settings include: the "GET" request method, a preference
+     * of {@linkplain HttpClient.Version#HTTP_2 HTTP/2}, a redirection policy of
+     * {@linkplain Redirect#NEVER NEVER}, the {@linkplain
+     * ProxySelector#getDefault() default proxy selector}, and the {@linkplain
+     * SSLContext#getDefault() default SSL context}.
+     *
+     * @implNote The system-wide default values are retrieved at the time the
+     * {@code HttpClient} instance is constructed. Changing the system-wide
+     * values after an {@code HttpClient} instance has been built, for
+     * instance, by calling {@link ProxySelector#setDefault(ProxySelector)}
+     * or {@link SSLContext#setDefault(SSLContext)}, has no effect on already
+     * built instances.
+     *
      * @return a new HttpClient
      */
     public static HttpClient newHttpClient() {
-        return new HttpClientBuilderImpl().build();
+        return newBuilder().build();
     }
 
     /**
@@ -76,55 +94,65 @@ public abstract class HttpClient {
     }
 
     /**
-     * A builder of immutable {@link HttpClient}s. {@code HttpClient.Builder}s
-     * are created by calling {@link HttpClient#newBuilder()}.
+     * A builder of immutable {@link HttpClient}s.
      * {@Incubating}
      *
-     * <p> Each of the setter methods in this class modifies the state of the
-     * builder and returns <i>this</i> (ie. the same instance). The methods are
-     * not synchronized and should not be called from multiple threads without
-     * external synchronization.
-     *
-     * <p> {@link #build()} returns a new {@code HttpClient} each time it is
-     * called.
+     * <p> Builders are created by invoking {@linkplain HttpClient#newBuilder()
+     * newBuilder}. Each of the setter methods modifies the state of the builder
+     * and returns the same instance. Builders are not thread-safe and should not be
+     * used concurrently from multiple threads without external synchronization.
      *
      * @since 9
      */
     public abstract static class Builder {
 
+        /**
+         * A proxy selector that always return {@link Proxy#NO_PROXY} implying
+         * a direct connection.
+         * This is a convenience object that can be passed to {@link #proxy(ProxySelector)}
+         * in order to build an instance of {@link HttpClient} that uses no
+         * proxy.
+         */
+        public static final ProxySelector NO_PROXY = ProxySelector.of(null);
+
+        /**
+         * Creates a Builder.
+         */
         protected Builder() {}
 
         /**
-         * Sets a cookie manager.
+         * Sets a cookie handler.
          *
-         * @param cookieManager the cookie manager
+         * @param cookieHandler the cookie handler
          * @return this builder
          */
-        public abstract Builder cookieManager(CookieManager cookieManager);
+        public abstract Builder cookieHandler(CookieHandler cookieHandler);
 
         /**
-         * Sets an {@code SSLContext}. If a security manager is set, then the caller
-         * must have the {@link java.net.NetPermission NetPermission}
-         * ({@code "setSSLContext"})
+         * Sets an {@code SSLContext}.
          *
-         * <p> The effect of not calling this method, is that a default {@link
-         * javax.net.ssl.SSLContext} is used, which is normally adequate for
-         * client applications that do not need to specify protocols, or require
-         * client authentication.
+         * <p> If this method is not invoked prior to {@linkplain #build()
+         * building}, then newly built clients will use the {@linkplain
+         * SSLContext#getDefault() default context}, which is normally adequate
+         * for client applications that do not need to specify protocols, or
+         * require client authentication.
          *
          * @param sslContext the SSLContext
          * @return this builder
-         * @throws SecurityException if a security manager is set and the
-         *                           caller does not have any required permission
          */
         public abstract Builder sslContext(SSLContext sslContext);
 
         /**
-         * Sets an {@code SSLParameters}. If this method is not called, then a default
-         * set of parameters are used. The contents of the given object are
-         * copied. Some parameters which are used internally by the HTTP protocol
-         * implementation (such as application protocol list) should not be set
-         * by callers, as they are ignored.
+         * Sets an {@code SSLParameters}.
+         *
+         * <p> If this method is not invoked prior to {@linkplain #build()
+         * building}, then newly built clients will use a default,
+         * implementation specific, set of parameters.
+         *
+         * <p> Some parameters which are used internally by the HTTP Client
+         * implementation (such as the application protocol list) should not be
+         * set by callers, as they may be ignored. The contents of the given
+         * object are copied.
          *
          * @param sslParameters the SSLParameters
          * @return this builder
@@ -132,10 +160,17 @@ public abstract class HttpClient {
         public abstract Builder sslParameters(SSLParameters sslParameters);
 
         /**
-         * Sets the executor to be used for asynchronous tasks. If this method is
-         * not called, a default executor is set, which is the one returned from {@link
-         * java.util.concurrent.Executors#newCachedThreadPool()
-         * Executors.newCachedThreadPool}.
+         * Sets the executor to be used for asynchronous and dependent tasks.
+         *
+         * <p> If this method is not invoked prior to {@linkplain #build()
+         * building}, a default executor is created for each newly built {@code
+         * HttpClient}. The default executor uses a {@linkplain
+         * Executors#newCachedThreadPool(ThreadFactory) cached thread pool},
+         * with a custom thread factory.
+         *
+         * @implNote If a security manager has been installed, the thread
+         * factory creates threads that run with an access control context that
+         * has no permissions.
          *
          * @param executor the Executor
          * @return this builder
@@ -144,8 +179,11 @@ public abstract class HttpClient {
 
         /**
          * Specifies whether requests will automatically follow redirects issued
-         * by the server. This setting can be overridden on each request. The
-         * default value for this setting is {@link Redirect#NEVER NEVER}
+         * by the server.
+         *
+         * <p> If this method is not invoked prior to {@linkplain #build()
+         * building}, then newly built clients will use a default redirection
+         * policy of {@link Redirect#NEVER NEVER}.
          *
          * @param policy the redirection policy
          * @return this builder
@@ -153,10 +191,14 @@ public abstract class HttpClient {
         public abstract Builder followRedirects(Redirect policy);
 
         /**
-         * Requests a specific HTTP protocol version where possible. If not set,
-         * the version defaults to {@link HttpClient.Version#HTTP_2}. If
-         * {@link HttpClient.Version#HTTP_2} is set, then each request will
-         * attempt to upgrade to HTTP/2. If the upgrade succeeds, then the
+         * Requests a specific HTTP protocol version where possible.
+         *
+         * <p> If this method is not invoked prior to {@linkplain #build()
+         * building}, then newly built clients will prefer {@linkplain
+         * Version#HTTP_2 HTTP/2}.
+         *
+         * <p> If set to {@linkplain Version#HTTP_2 HTTP/2}, then each request
+         * will attempt to upgrade to HTTP/2. If the upgrade succeeds, then the
          * response to this request will use HTTP/2 and all subsequent requests
          * and responses to the same
          * <a href="https://tools.ietf.org/html/rfc6454#section-4">origin server</a>
@@ -180,12 +222,22 @@ public abstract class HttpClient {
         public abstract Builder priority(int priority);
 
         /**
-         * Sets a {@link java.net.ProxySelector} for this client. If no selector
-         * is set, then no proxies are used. If a {@code null} parameter is
-         * given then the system wide default proxy selector is used.
+         * Sets a {@link java.net.ProxySelector}.
          *
-         * @implNote {@link java.net.ProxySelector#of(InetSocketAddress)}
-         * provides a {@code ProxySelector} which uses one proxy for all requests.
+         * @apiNote {@link ProxySelector#of(InetSocketAddress)}
+         * provides a {@code ProxySelector} which uses a single proxy for all
+         * requests. The system-wide proxy selector can be retrieved by
+         * {@link ProxySelector#getDefault()}.
+         *
+         * @implNote
+         * If this method is not invoked prior to {@linkplain #build()
+         * building}, then newly built clients will use the {@linkplain
+         * ProxySelector#getDefault() default proxy selector}, which
+         * is normally adequate for client applications. This default
+         * behavior can be turned off by supplying an explicit proxy
+         * selector to this method, such as {@link #NO_PROXY} or one
+         * returned by {@link ProxySelector#of(InetSocketAddress)},
+         * before calling {@link #build()}.
          *
          * @param selector the ProxySelector
          * @return this builder
@@ -201,7 +253,7 @@ public abstract class HttpClient {
         public abstract Builder authenticator(Authenticator a);
 
         /**
-         * Returns a {@link HttpClient} built from the current state of this
+         * Returns a new {@link HttpClient} built from the current state of this
          * builder.
          *
          * @return this builder
@@ -211,50 +263,58 @@ public abstract class HttpClient {
 
 
     /**
-     * Returns an {@code Optional} which contains this client's {@link
-     * CookieManager}. If no {@code CookieManager} was set in this client's builder,
-     * then the {@code Optional} is empty.
+     * Returns an {@code Optional} containing this client's {@linkplain
+     * CookieHandler}. If no {@code CookieHandler} was set in this client's
+     * builder, then the {@code Optional} is empty.
      *
-     * @return an {@code Optional} containing this client's {@code CookieManager}
+     * @return an {@code Optional} containing this client's {@code CookieHandler}
      */
-    public abstract Optional<CookieManager> cookieManager();
+    public abstract Optional<CookieHandler> cookieHandler();
 
     /**
-     * Returns the follow-redirects setting for this client. The default value
-     * for this setting is {@link HttpClient.Redirect#NEVER}
+     * Returns the follow redirects policy for this client. The default value
+     * for client's built by builders that do not specify a redirect policy is
+     * {@link HttpClient.Redirect#NEVER NEVER}.
      *
      * @return this client's follow redirects setting
      */
     public abstract Redirect followRedirects();
 
     /**
-     * Returns an {@code Optional} containing the {@code ProxySelector} for this client.
-     * If no proxy is set then the {@code Optional} is empty.
+     * Returns an {@code Optional} containing the {@code ProxySelector}
+     * supplied to this client. If no proxy selector was set in this client's
+     * builder, then the {@code Optional} is empty.
      *
-     * @return an {@code Optional} containing this client's proxy selector
+     * <p> Even though this method may return an empty optional, the {@code
+     * HttpClient} may still have an non-exposed {@linkplain
+     * Builder#proxy(ProxySelector) default proxy selector} that is
+     * used for sending HTTP requests.
+     *
+     * @return an {@code Optional} containing the proxy selector supplied
+     *        to this client.
      */
     public abstract Optional<ProxySelector> proxy();
 
     /**
-     * Returns the {@code SSLContext}, if one was set on this client. If a security
-     * manager is set, then the caller must have the
-     * {@link java.net.NetPermission NetPermission}("getSSLContext") permission.
-     * If no {@code SSLContext} was set, then the default context is returned.
+     * Returns this client's {@code SSLContext}.
+     *
+     * <p> If no {@code SSLContext} was set in this client's builder, then the
+     * {@linkplain SSLContext#getDefault() default context} is returned.
      *
      * @return this client's SSLContext
-     * @throws SecurityException if the caller does not have permission to get
-     *         the SSLContext
      */
     public abstract SSLContext sslContext();
 
     /**
-     * Returns an {@code Optional} containing the {@link SSLParameters} set on
-     * this client. If no {@code SSLParameters} were set in the client's builder,
-     * then the {@code Optional} is empty.
+     * Returns a copy of this client's {@link SSLParameters}.
      *
-     * @return an {@code Optional} containing this client's {@code SSLParameters}
+     * <p> If no {@code SSLParameters} were set in the client's builder, then an
+     * implementation specific default set of parameters, that the client will
+     * use, is returned.
+     *
+     * @return this client's {@code SSLParameters}
      */
-    public abstract Optional<SSLParameters> sslParameters();
+    public abstract SSLParameters sslParameters();
 
     /**
      * Returns an {@code Optional} containing the {@link Authenticator} set on
@@ -274,14 +334,18 @@ public abstract class HttpClient {
     public abstract HttpClient.Version version();
 
     /**
-     * Returns the {@code Executor} set on this client. If an {@code
-     * Executor} was not set on the client's builder, then a default
-     * object is returned. The default {@code Executor} is created independently
-     * for each client.
+     * Returns an {@code Optional} containing this client's {@linkplain
+     * Executor}. If no {@code Executor} was set in the client's builder,
+     * then the {@code Optional} is empty.
      *
-     * @return this client's Executor
+     * <p> Even though this method may return an empty optional, the {@code
+     * HttpClient} may still have an non-exposed {@linkplain
+     * HttpClient.Builder#executor(Executor) default executor} that is used for
+     * executing asynchronous and dependent tasks.
+     *
+     * @return an {@code Optional} containing this client's {@code Executor}
      */
-    public abstract Executor executor();
+    public abstract Optional<Executor> executor();
 
     /**
      * The HTTP protocol version.
@@ -349,8 +413,14 @@ public abstract class HttpClient {
      * @param req the request
      * @param responseBodyHandler the response body handler
      * @return the response body
-     * @throws java.io.IOException if an I/O error occurs when sending or receiving
-     * @throws java.lang.InterruptedException if the operation is interrupted
+     * @throws IOException if an I/O error occurs when sending or receiving
+     * @throws InterruptedException if the operation is interrupted
+     * @throws IllegalArgumentException if the request method is not supported
+     * @throws SecurityException If a security manager has been installed
+     *          and it denies {@link java.net.URLPermission access} to the
+     *          URL in the given request, or proxy if one is configured.
+     *          See HttpRequest for further information about
+     *          <a href="HttpRequest.html#securitychecks">security checks</a>.
      */
     public abstract <T> HttpResponse<T>
     send(HttpRequest req, HttpResponse.BodyHandler<T> responseBodyHandler)
@@ -360,6 +430,17 @@ public abstract class HttpClient {
      * Sends the given request asynchronously using this client and the given
      * response handler.
      *
+     * <p> The returned completable future completes exceptionally with:
+     * <ul>
+     * <li>{@link IOException} - if an I/O error occurs when sending or receiving</li>
+     * <li>{@link IllegalArgumentException} - if the request method is not supported</li>
+     * <li>{@link SecurityException} - If a security manager has been installed
+     *          and it denies {@link java.net.URLPermission access} to the
+     *          URL in the given request, or proxy if one is configured.
+     *          See HttpRequest for further information about
+     *          <a href="HttpRequest.html#securitychecks">security checks</a>.</li>
+     * </ul>
+     *
      * @param <T> the response body type
      * @param req the request
      * @param responseBodyHandler the response body handler
@@ -372,25 +453,34 @@ public abstract class HttpClient {
      * Sends the given request asynchronously using this client and the given
      * multi response handler.
      *
+     * <p> The returned completable future completes exceptionally with:
+     * <ul>
+     * <li>{@link IOException} - if an I/O error occurs when sending or receiving</li>
+     * <li>{@link IllegalArgumentException} - if the request method is not supported</li>
+     * <li>{@link SecurityException} - If a security manager has been installed
+     *          and it denies {@link java.net.URLPermission access} to the
+     *          URL in the given request, or proxy if one is configured.
+     *          See HttpRequest for further information about
+     *          <a href="HttpRequest.html#securitychecks">security checks</a>.</li>
+     * </ul>
+     *
      * @param <U> a type representing the aggregated results
      * @param <T> a type representing all of the response bodies
      * @param req the request
-     * @param multiProcessor the MultiProcessor for the request
+     * @param multiSubscriber the multiSubscriber for the request
      * @return a {@code CompletableFuture<U>}
      */
     public abstract <U, T> CompletableFuture<U>
-    sendAsync(HttpRequest req, HttpResponse.MultiProcessor<U, T> multiProcessor);
+    sendAsync(HttpRequest req, HttpResponse.MultiSubscriber<U, T> multiSubscriber);
 
     /**
-     * Creates a builder of {@link WebSocket} instances connected to the given
-     * URI and receiving events and messages with the given {@code Listener}.
+     * Creates a new {@code WebSocket} builder (optional operation).
      *
      * <p> <b>Example</b>
      * <pre>{@code
      *     HttpClient client = HttpClient.newHttpClient();
-     *     WebSocket.Builder builder = client.newWebSocketBuilder(
-     *             URI.create("ws://websocket.example.com"),
-     *             listener);
+     *     CompletableFuture<WebSocket> ws = client.newWebSocketBuilder()
+     *             .buildAsync(URI.create("ws://websocket.example.com"), listener);
      * }</pre>
      *
      * <p> Finer control over the WebSocket Opening Handshake can be achieved
@@ -402,28 +492,33 @@ public abstract class HttpClient {
      *     HttpClient client = HttpClient.newBuilder()
      *             .proxy(ProxySelector.of(addr))
      *             .build();
-     *     WebSocket.Builder builder = client.newWebSocketBuilder(
-     *             URI.create("ws://websocket.example.com"),
-     *             listener);
+     *     CompletableFuture<WebSocket> ws = client.newWebSocketBuilder()
+     *             .buildAsync(URI.create("ws://websocket.example.com"), listener);
      * }</pre>
      *
-     * @implSpec The default implementation of this method throws {@code
-     * UnsupportedOperationException}. However, clients obtained through
+     * <p> A {@code WebSocket.Builder} returned from this method is not safe for
+     * use by multiple threads without external synchronization.
+     *
+     * @implSpec The default implementation of this method throws
+     * {@code UnsupportedOperationException}. Clients obtained through
      * {@link HttpClient#newHttpClient()} or {@link HttpClient#newBuilder()}
-     * provide WebSocket capability.
+     * return a {@code WebSocket} builder.
      *
-     * @param uri
-     *         the WebSocket URI
-     * @param listener
-     *         the listener
+     * @implNote Both builder and {@code WebSocket}s created with it operate in
+     * a non-blocking fashion. That is, their methods do not block before
+     * returning a {@code CompletableFuture}. Asynchronous tasks are executed in
+     * this {@code HttpClient}'s executor.
      *
-     * @return a builder of {@code WebSocket} instances
+     * <p> When a {@code CompletionStage} returned from
+     * {@link WebSocket.Listener#onClose Listener.onClose} completes,
+     * the {@code WebSocket} will send a Close message that has the same code
+     * the received message has and an empty reason.
+     *
+     * @return a {@code WebSocket.Builder}
      * @throws UnsupportedOperationException
      *         if this {@code HttpClient} does not provide WebSocket support
      */
-    public WebSocket.Builder newWebSocketBuilder(URI uri,
-                                                 WebSocket.Listener listener)
-    {
+    public WebSocket.Builder newWebSocketBuilder() {
         throw new UnsupportedOperationException();
     }
 }
diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/HttpClientBuilderImpl.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/HttpClientBuilderImpl.java
index f2351cffa52..98a838a58ed 100644
--- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/HttpClientBuilderImpl.java
+++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/HttpClientBuilderImpl.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2017, 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
@@ -26,7 +26,7 @@
 package jdk.incubator.http;
 
 import java.net.Authenticator;
-import java.net.CookieManager;
+import java.net.CookieHandler;
 import java.net.ProxySelector;
 import java.util.concurrent.Executor;
 import javax.net.ssl.SSLContext;
@@ -36,7 +36,7 @@ import static java.util.Objects.requireNonNull;
 
 class HttpClientBuilderImpl extends HttpClient.Builder {
 
-    CookieManager cookieManager;
+    CookieHandler cookieHandler;
     HttpClient.Redirect followRedirects;
     ProxySelector proxy;
     Authenticator authenticator;
@@ -48,9 +48,9 @@ class HttpClientBuilderImpl extends HttpClient.Builder {
     int priority = -1;
 
     @Override
-    public HttpClientBuilderImpl cookieManager(CookieManager cookieManager) {
-        requireNonNull(cookieManager);
-        this.cookieManager = cookieManager;
+    public HttpClientBuilderImpl cookieHandler(CookieHandler cookieHandler) {
+        requireNonNull(cookieHandler);
+        this.cookieHandler = cookieHandler;
         return this;
     }
 
@@ -58,7 +58,6 @@ class HttpClientBuilderImpl extends HttpClient.Builder {
     @Override
     public HttpClientBuilderImpl sslContext(SSLContext sslContext) {
         requireNonNull(sslContext);
-        Utils.checkNetPermission("setSSLContext");
         this.sslContext = sslContext;
         return this;
     }
@@ -67,7 +66,7 @@ class HttpClientBuilderImpl extends HttpClient.Builder {
     @Override
     public HttpClientBuilderImpl sslParameters(SSLParameters sslParameters) {
         requireNonNull(sslParameters);
-        this.sslParams = sslParameters;
+        this.sslParams = Utils.copySSLParameters(sslParameters);
         return this;
     }
 
diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/HttpClientFacade.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/HttpClientFacade.java
new file mode 100644
index 00000000000..449cc9830df
--- /dev/null
+++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/HttpClientFacade.java
@@ -0,0 +1,141 @@
+/*
+ * Copyright (c) 2017, 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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 jdk.incubator.http;
+
+import java.io.IOException;
+import java.lang.ref.Reference;
+import java.net.Authenticator;
+import java.net.CookieHandler;
+import java.net.ProxySelector;
+import java.net.URI;
+import java.util.Optional;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.Executor;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLParameters;
+
+/**
+ * An HttpClientFacade is a simple class that wraps an HttpClient implementation
+ * and delegates everything to its implementation delegate.
+ */
+final class HttpClientFacade extends HttpClient {
+
+    final HttpClientImpl impl;
+
+    /**
+     * Creates an HttpClientFacade.
+     */
+    HttpClientFacade(HttpClientImpl impl) {
+        this.impl = impl;
+    }
+
+    @Override
+    public Optional<CookieHandler> cookieHandler() {
+        return impl.cookieHandler();
+    }
+
+    @Override
+    public Redirect followRedirects() {
+        return impl.followRedirects();
+    }
+
+    @Override
+    public Optional<ProxySelector> proxy() {
+        return impl.proxy();
+    }
+
+    @Override
+    public SSLContext sslContext() {
+        return impl.sslContext();
+    }
+
+    @Override
+    public SSLParameters sslParameters() {
+        return impl.sslParameters();
+    }
+
+    @Override
+    public Optional<Authenticator> authenticator() {
+        return impl.authenticator();
+    }
+
+    @Override
+    public HttpClient.Version version() {
+        return impl.version();
+    }
+
+    @Override
+    public Optional<Executor> executor() {
+        return impl.executor();
+    }
+
+    @Override
+    public <T> HttpResponse<T>
+    send(HttpRequest req, HttpResponse.BodyHandler<T> responseBodyHandler)
+        throws IOException, InterruptedException
+    {
+        try {
+            return impl.send(req, responseBodyHandler);
+        } finally {
+            Reference.reachabilityFence(this);
+        }
+    }
+
+    @Override
+    public <T> CompletableFuture<HttpResponse<T>>
+    sendAsync(HttpRequest req, HttpResponse.BodyHandler<T> responseBodyHandler) {
+        try {
+            return impl.sendAsync(req, responseBodyHandler);
+        } finally {
+            Reference.reachabilityFence(this);
+        }
+    }
+
+    @Override
+    public <U, T> CompletableFuture<U>
+    sendAsync(HttpRequest req, HttpResponse.MultiSubscriber<U, T> multiSubscriber) {
+        try {
+            return impl.sendAsync(req, multiSubscriber);
+        } finally {
+            Reference.reachabilityFence(this);
+        }
+    }
+
+    @Override
+    public WebSocket.Builder newWebSocketBuilder() {
+        try {
+            return impl.newWebSocketBuilder();
+        } finally {
+            Reference.reachabilityFence(this);
+        }
+    }
+
+    @Override
+    public String toString() {
+        // Used by tests to get the client's id.
+        return impl.toString();
+    }
+}
diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/HttpClientImpl.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/HttpClientImpl.java
index c4087b0a4ec..d5753361686 100644
--- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/HttpClientImpl.java
+++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/HttpClientImpl.java
@@ -28,33 +28,45 @@ package jdk.incubator.http;
 import javax.net.ssl.SSLContext;
 import javax.net.ssl.SSLParameters;
 import java.io.IOException;
+import java.lang.System.Logger.Level;
 import java.lang.ref.WeakReference;
 import java.net.Authenticator;
-import java.net.CookieManager;
+import java.net.CookieHandler;
 import java.net.ProxySelector;
-import java.net.URI;
+import java.nio.channels.CancelledKeyException;
 import java.nio.channels.ClosedChannelException;
 import java.nio.channels.SelectableChannel;
 import java.nio.channels.SelectionKey;
 import java.nio.channels.Selector;
 import java.nio.channels.SocketChannel;
+import java.security.AccessControlContext;
+import java.security.AccessController;
 import java.security.NoSuchAlgorithmException;
+import java.security.PrivilegedAction;
 import java.time.Instant;
 import java.time.temporal.ChronoUnit;
 import java.util.ArrayList;
+import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Optional;
 import java.util.Set;
 import java.util.TreeSet;
 import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutionException;
 import java.util.concurrent.Executor;
 import java.util.concurrent.Executors;
 import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicLong;
 import java.util.stream.Stream;
+import jdk.incubator.http.HttpResponse.BodyHandler;
+import jdk.incubator.http.HttpResponse.MultiSubscriber;
 import jdk.incubator.http.internal.common.Log;
+import jdk.incubator.http.internal.common.Pair;
 import jdk.incubator.http.internal.common.Utils;
 import jdk.incubator.http.internal.websocket.BuilderImpl;
+import jdk.internal.misc.InnocuousThread;
 
 /**
  * Client implementation. Contains all configuration information and also
@@ -63,46 +75,138 @@ import jdk.incubator.http.internal.websocket.BuilderImpl;
  */
 class HttpClientImpl extends HttpClient {
 
+    static final boolean DEBUG = Utils.DEBUG;  // Revisit: temporary dev flag.
+    static final boolean DEBUGELAPSED = Utils.TESTING || DEBUG;  // Revisit: temporary dev flag.
+    static final boolean DEBUGTIMEOUT = false; // Revisit: temporary dev flag.
+    final System.Logger  debug = Utils.getDebugLogger(this::dbgString, DEBUG);
+    final System.Logger  debugelapsed = Utils.getDebugLogger(this::dbgString, DEBUGELAPSED);
+    final System.Logger  debugtimeout = Utils.getDebugLogger(this::dbgString, DEBUGTIMEOUT);
+    static final AtomicLong CLIENT_IDS = new AtomicLong();
+
     // Define the default factory as a static inner class
     // that embeds all the necessary logic to avoid
     // the risk of using a lambda that might keep a reference on the
     // HttpClient instance from which it was created (helps with
     // heapdump analysis).
     private static final class DefaultThreadFactory implements ThreadFactory {
-        private DefaultThreadFactory() {}
+        private final String namePrefix;
+        private final AtomicInteger nextId = new AtomicInteger();
+
+        DefaultThreadFactory(long clientID) {
+            namePrefix = "HttpClient-" + clientID + "-Worker-";
+        }
+
         @Override
         public Thread newThread(Runnable r) {
-            Thread t = new Thread(null, r, "HttpClient_worker", 0, true);
+            String name = namePrefix + nextId.getAndIncrement();
+            Thread t;
+            if (System.getSecurityManager() == null) {
+                t = new Thread(null, r, name, 0, false);
+            } else {
+                t = InnocuousThread.newThread(name, r);
+            }
             t.setDaemon(true);
             return t;
         }
-        static final ThreadFactory INSTANCE = new DefaultThreadFactory();
     }
 
-    private final CookieManager cookieManager;
+    private final CookieHandler cookieHandler;
     private final Redirect followRedirects;
+    private final Optional<ProxySelector> userProxySelector;
     private final ProxySelector proxySelector;
     private final Authenticator authenticator;
     private final Version version;
     private final ConnectionPool connections;
     private final Executor executor;
+    private final boolean isDefaultExecutor;
     // Security parameters
     private final SSLContext sslContext;
     private final SSLParameters sslParams;
     private final SelectorManager selmgr;
     private final FilterFactory filters;
     private final Http2ClientImpl client2;
+    private final long id;
+    private final String dbgTag;
+
+    // This reference is used to keep track of the facade HttpClient
+    // that was returned to the application code.
+    // It makes it possible to know when the application no longer
+    // holds any reference to the HttpClient.
+    // Unfortunately, this information is not enough to know when
+    // to exit the SelectorManager thread. Because of the asynchronous
+    // nature of the API, we also need to wait until all pending operations
+    // have completed.
+    private final WeakReference<HttpClientFacade> facadeRef;
+
+    // This counter keeps track of the number of operations pending
+    // on the HttpClient. The SelectorManager thread will wait
+    // until there are no longer any pending operations and the
+    // facadeRef is cleared before exiting.
+    //
+    // The pendingOperationCount is incremented every time a send/sendAsync
+    // operation is invoked on the HttpClient, and is decremented when
+    // the HttpResponse<T> object is returned to the user.
+    // However, at this point, the body may not have been fully read yet.
+    // This is the case when the response T is implemented as a streaming
+    // subscriber (such as an InputStream).
+    //
+    // To take care of this issue the pendingOperationCount will additionally
+    // be incremented/decremented in the following cases:
+    //
+    // 1. For HTTP/2  it is incremented when a stream is added to the
+    //    Http2Connection streams map, and decreased when the stream is removed
+    //    from the map. This should also take care of push promises.
+    // 2. For WebSocket the count is increased when creating a
+    //    DetachedConnectionChannel for the socket, and decreased
+    //    when the the channel is closed.
+    //    In addition, the HttpClient facade is passed to the WebSocket builder,
+    //    (instead of the client implementation delegate).
+    // 3. For HTTP/1.1 the count is incremented before starting to parse the body
+    //    response, and decremented when the parser has reached the end of the
+    //    response body flow.
+    //
+    // This should ensure that the selector manager thread remains alive until
+    // the response has been fully received or the web socket is closed.
+    private final AtomicLong pendingOperationCount = new AtomicLong();
+    private final AtomicLong pendingWebSocketCount = new AtomicLong();
+    private final AtomicLong pendingHttpRequestCount = new AtomicLong();
 
     /** A Set of, deadline first, ordered timeout events. */
     private final TreeSet<TimeoutEvent> timeouts;
 
-    public static HttpClientImpl create(HttpClientBuilderImpl builder) {
-        HttpClientImpl impl = new HttpClientImpl(builder);
-        impl.start();
-        return impl;
+    /**
+     * This is a bit tricky:
+     * 1. an HttpClientFacade has a final HttpClientImpl field.
+     * 2. an HttpClientImpl has a final WeakReference<HttpClientFacade> field,
+     *    where the referent is the facade created for that instance.
+     * 3. We cannot just create the HttpClientFacade in the HttpClientImpl
+     *    constructor, because it would be only weakly referenced and could
+     *    be GC'ed before we can return it.
+     * The solution is to use an instance of SingleFacadeFactory which will
+     * allow the caller of new HttpClientImpl(...) to retrieve the facade
+     * after the HttpClientImpl has been created.
+     */
+    private static final class SingleFacadeFactory {
+        HttpClientFacade facade;
+        HttpClientFacade createFacade(HttpClientImpl impl) {
+            assert facade == null;
+            return (facade = new HttpClientFacade(impl));
+        }
     }
 
-    private HttpClientImpl(HttpClientBuilderImpl builder) {
+    static HttpClientFacade create(HttpClientBuilderImpl builder) {
+        SingleFacadeFactory facadeFactory = new SingleFacadeFactory();
+        HttpClientImpl impl = new HttpClientImpl(builder, facadeFactory);
+        impl.start();
+        assert facadeFactory.facade != null;
+        assert impl.facadeRef.get() == facadeFactory.facade;
+        return facadeFactory.facade;
+    }
+
+    private HttpClientImpl(HttpClientBuilderImpl builder,
+                           SingleFacadeFactory facadeFactory) {
+        id = CLIENT_IDS.incrementAndGet();
+        dbgTag = "HttpClientImpl(" + id +")";
         if (builder.sslContext == null) {
             try {
                 sslContext = SSLContext.getDefault();
@@ -114,16 +218,23 @@ class HttpClientImpl extends HttpClient {
         }
         Executor ex = builder.executor;
         if (ex == null) {
-            ex = Executors.newCachedThreadPool(DefaultThreadFactory.INSTANCE);
+            ex = Executors.newCachedThreadPool(new DefaultThreadFactory(id));
+            isDefaultExecutor = true;
         } else {
             ex = builder.executor;
+            isDefaultExecutor = false;
         }
+        facadeRef = new WeakReference<>(facadeFactory.createFacade(this));
         client2 = new Http2ClientImpl(this);
         executor = ex;
-        cookieManager = builder.cookieManager;
+        cookieHandler = builder.cookieHandler;
         followRedirects = builder.followRedirects == null ?
                 Redirect.NEVER : builder.followRedirects;
-        this.proxySelector = builder.proxy;
+        this.userProxySelector = Optional.ofNullable(builder.proxy);
+        this.proxySelector = userProxySelector
+                .orElseGet(HttpClientImpl::getDefaultProxySelector);
+        debug.log(Level.DEBUG, "proxySelector is %s (user-supplied=%s)",
+                this.proxySelector, userProxySelector.isPresent());
         authenticator = builder.authenticator;
         if (builder.version == null) {
             version = HttpClient.Version.HTTP_2;
@@ -135,7 +246,7 @@ class HttpClientImpl extends HttpClient {
         } else {
             sslParams = builder.sslParams;
         }
-        connections = new ConnectionPool();
+        connections = new ConnectionPool(id);
         connections.start();
         timeouts = new TreeSet<>();
         try {
@@ -147,30 +258,102 @@ class HttpClientImpl extends HttpClient {
         selmgr.setDaemon(true);
         filters = new FilterFactory();
         initFilters();
+        assert facadeRef.get() != null;
     }
 
     private void start() {
         selmgr.start();
     }
 
+    // Called from the SelectorManager thread, just before exiting.
+    // Clears the HTTP/1.1 and HTTP/2 cache, ensuring that the connections
+    // that may be still lingering there are properly closed (and their
+    // possibly still opened SocketChannel released).
+    private void stop() {
+        // Clears HTTP/1.1 cache and close its connections
+        connections.stop();
+        // Clears HTTP/2 cache and close its connections.
+        client2.stop();
+    }
+
     private static SSLParameters getDefaultParams(SSLContext ctx) {
         SSLParameters params = ctx.getSupportedSSLParameters();
         params.setProtocols(new String[]{"TLSv1.2"});
         return params;
     }
 
+    private static ProxySelector getDefaultProxySelector() {
+        PrivilegedAction<ProxySelector> action = ProxySelector::getDefault;
+        return AccessController.doPrivileged(action);
+    }
+
+    // Returns the facade that was returned to the application code.
+    // May be null if that facade is no longer referenced.
+    final HttpClientFacade facade() {
+        return facadeRef.get();
+    }
+
+    // Increments the pendingOperationCount.
+    final long reference() {
+        pendingHttpRequestCount.incrementAndGet();
+        return pendingOperationCount.incrementAndGet();
+    }
+
+    // Decrements the pendingOperationCount.
+    final long unreference() {
+        final long count = pendingOperationCount.decrementAndGet();
+        final long httpCount = pendingHttpRequestCount.decrementAndGet();
+        final long webSocketCount = pendingWebSocketCount.get();
+        if (count == 0 && facade() == null) {
+            selmgr.wakeupSelector();
+        }
+        assert httpCount >= 0 : "count of HTTP operations < 0";
+        assert webSocketCount >= 0 : "count of WS operations < 0";
+        assert count >= 0 : "count of pending operations < 0";
+        return count;
+    }
+
+    // Increments the pendingOperationCount.
+    final long webSocketOpen() {
+        pendingWebSocketCount.incrementAndGet();
+        return pendingOperationCount.incrementAndGet();
+    }
+
+    // Decrements the pendingOperationCount.
+    final long webSocketClose() {
+        final long count = pendingOperationCount.decrementAndGet();
+        final long webSocketCount = pendingWebSocketCount.decrementAndGet();
+        final long httpCount = pendingHttpRequestCount.get();
+        if (count == 0 && facade() == null) {
+            selmgr.wakeupSelector();
+        }
+        assert httpCount >= 0 : "count of HTTP operations < 0";
+        assert webSocketCount >= 0 : "count of WS operations < 0";
+        assert count >= 0 : "count of pending operations < 0";
+        return count;
+    }
+
+    // Returns the pendingOperationCount.
+    final long referenceCount() {
+        return pendingOperationCount.get();
+    }
+
+    // Called by the SelectorManager thread to figure out whether it's time
+    // to terminate.
+    final boolean isReferenced() {
+        HttpClient facade = facade();
+        return facade != null || referenceCount() > 0;
+    }
+
     /**
-     * Wait for activity on given exchange (assuming blocking = false).
-     * It's a no-op if blocking = true. In particular, the following occurs
-     * in the SelectorManager thread.
+     * Wait for activity on given exchange.
+     * The following occurs in the SelectorManager thread.
      *
-     *  1) mark the connection non-blocking
-     *  2) add to selector
-     *  3) If selector fires for this exchange then
-     *  4)   - mark connection as blocking
-     *  5)   - call AsyncEvent.handle()
+     *  1) add to selector
+     *  2) If selector fires for this exchange then
+     *     call AsyncEvent.handle()
      *
-     * If exchange needs to block again, then call registerEvent() again
+     * If exchange needs to change interest ops, then call registerEvent() again.
      */
     void registerEvent(AsyncEvent exchange) throws IOException {
         selmgr.register(exchange);
@@ -184,131 +367,196 @@ class HttpClientImpl extends HttpClient {
         selmgr.cancel(s);
     }
 
+    /**
+     * Allows an AsyncEvent to modify its interestOps.
+     * @param event The modified event.
+     */
+    void eventUpdated(AsyncEvent event) throws ClosedChannelException {
+        assert !(event instanceof AsyncTriggerEvent);
+        selmgr.eventUpdated(event);
+    }
+
+    boolean isSelectorThread() {
+        return Thread.currentThread() == selmgr;
+    }
 
     Http2ClientImpl client2() {
         return client2;
     }
 
-    /*
-    @Override
-    public ByteBuffer getBuffer() {
-        return pool.getBuffer();
+    private void debugCompleted(String tag, long startNanos, HttpRequest req) {
+        if (debugelapsed.isLoggable(Level.DEBUG)) {
+            debugelapsed.log(Level.DEBUG, () -> tag + " elapsed "
+                    + (System.nanoTime() - startNanos)/1000_000L
+                    + " millis for " + req.method()
+                    + " to " + req.uri());
+        }
     }
 
-    // SSL buffers are larger. Manage separately
-
-    int size = 16 * 1024;
-
-    ByteBuffer getSSLBuffer() {
-        return ByteBuffer.allocate(size);
-    }
-
-    /**
-     * Return a new buffer that's a bit bigger than the given one
-     *
-     * @param buf
-     * @return
-     *
-    ByteBuffer reallocSSLBuffer(ByteBuffer buf) {
-        size = buf.capacity() * 12 / 10; // 20% bigger
-        return ByteBuffer.allocate(size);
-    }
-
-    synchronized void returnSSLBuffer(ByteBuffer buf) {
-        if (buf.capacity() >= size)
-           sslBuffers.add(0, buf);
-    }
-
-    @Override
-    public void returnBuffer(ByteBuffer buffer) {
-        pool.returnBuffer(buffer);
-    }
-    */
-
     @Override
     public <T> HttpResponse<T>
-    send(HttpRequest req, HttpResponse.BodyHandler<T> responseHandler)
+    send(HttpRequest req, BodyHandler<T> responseHandler)
         throws IOException, InterruptedException
     {
-        MultiExchange<Void,T> mex = new MultiExchange<>(req, this, responseHandler);
-        return mex.response();
+        try {
+            return sendAsync(req, responseHandler).get();
+        } catch (ExecutionException e) {
+            Throwable t = e.getCause();
+            if (t instanceof Error)
+                throw (Error)t;
+            if (t instanceof RuntimeException)
+                throw (RuntimeException)t;
+            else if (t instanceof IOException)
+                throw Utils.getIOException(t);
+            else
+                throw new InternalError("Unexpected exception", t);
+        }
     }
 
     @Override
     public <T> CompletableFuture<HttpResponse<T>>
-    sendAsync(HttpRequest req, HttpResponse.BodyHandler<T> responseHandler)
+    sendAsync(HttpRequest userRequest, BodyHandler<T> responseHandler)
     {
-        MultiExchange<Void,T> mex = new MultiExchange<>(req, this, responseHandler);
-        return mex.responseAsync()
-                  .thenApply((HttpResponseImpl<T> b) -> (HttpResponse<T>) b);
+        AccessControlContext acc = null;
+        if (System.getSecurityManager() != null)
+            acc = AccessController.getContext();
+
+        // Clone the, possibly untrusted, HttpRequest
+        HttpRequestImpl requestImpl = new HttpRequestImpl(userRequest, proxySelector, acc);
+        if (requestImpl.method().equals("CONNECT"))
+            throw new IllegalArgumentException("Unsupported method CONNECT");
+
+        long start = DEBUGELAPSED ? System.nanoTime() : 0;
+        reference();
+        try {
+            debugelapsed.log(Level.DEBUG, "ClientImpl (async) send %s", userRequest);
+
+            MultiExchange<Void,T> mex = new MultiExchange<>(userRequest,
+                                                            requestImpl,
+                                                            this,
+                                                            responseHandler,
+                                                            acc);
+            CompletableFuture<HttpResponse<T>> res =
+                    mex.responseAsync().whenComplete((b,t) -> unreference());
+            if (DEBUGELAPSED) {
+                res = res.whenComplete(
+                        (b,t) -> debugCompleted("ClientImpl (async)", start, userRequest));
+            }
+            // makes sure that any dependent actions happen in the executor
+            if (acc != null) {
+                res.whenCompleteAsync((r, t) -> { /* do nothing */},
+                                      new PrivilegedExecutor(executor, acc));
+            }
+
+            return res;
+        } catch(Throwable t) {
+            unreference();
+            debugCompleted("ClientImpl (async)", start, userRequest);
+            throw t;
+        }
     }
 
     @Override
     public <U, T> CompletableFuture<U>
-    sendAsync(HttpRequest req, HttpResponse.MultiProcessor<U, T> responseHandler) {
-        MultiExchange<U,T> mex = new MultiExchange<>(req, this, responseHandler);
-        return mex.multiResponseAsync();
-    }
+    sendAsync(HttpRequest userRequest, MultiSubscriber<U, T> responseHandler) {
+        AccessControlContext acc = null;
+        if (System.getSecurityManager() != null)
+            acc = AccessController.getContext();
 
-    // new impl. Should get rid of above
-    /*
-    static class BufferPool implements BufferHandler {
+        // Clone the, possibly untrusted, HttpRequest
+        HttpRequestImpl requestImpl = new HttpRequestImpl(userRequest, proxySelector, acc);
+        if (requestImpl.method().equals("CONNECT"))
+            throw new IllegalArgumentException("Unsupported method CONNECT");
 
-        final LinkedList<ByteBuffer> freelist = new LinkedList<>();
+        long start = DEBUGELAPSED ? System.nanoTime() : 0;
+        reference();
+        try {
+            debugelapsed.log(Level.DEBUG, "ClientImpl (async) send multi %s", userRequest);
 
-        @Override
-        public synchronized ByteBuffer getBuffer() {
-            ByteBuffer buf;
-
-            while (!freelist.isEmpty()) {
-                buf = freelist.removeFirst();
-                buf.clear();
-                return buf;
+            MultiExchange<U,T> mex = new MultiExchange<>(userRequest,
+                                                         requestImpl,
+                                                         this,
+                                                         responseHandler,
+                                                         acc);
+            CompletableFuture<U> res = mex.multiResponseAsync()
+                      .whenComplete((b,t) -> unreference());
+            if (DEBUGELAPSED) {
+                res = res.whenComplete(
+                        (b,t) -> debugCompleted("ClientImpl (async)", start, userRequest));
+            }
+            // makes sure that any dependent actions happen in the executor
+            if (acc != null) {
+                res.whenCompleteAsync((r, t) -> { /* do nothing */},
+                                      new PrivilegedExecutor(executor, acc));
             }
-            return ByteBuffer.allocate(BUFSIZE);
-        }
 
-        @Override
-        public synchronized void returnBuffer(ByteBuffer buffer) {
-            assert buffer.capacity() > 0;
-            freelist.add(buffer);
+            return res;
+        } catch(Throwable t) {
+            unreference();
+            debugCompleted("ClientImpl (async)", start, userRequest);
+            throw t;
         }
     }
 
-    static BufferPool pool = new BufferPool();
-
-    static BufferHandler pool() {
-        return pool;
-    }
-*/
     // Main loop for this client's selector
     private final static class SelectorManager extends Thread {
 
-        private static final long NODEADLINE = 3000L;
+        // For testing purposes we have an internal System property that
+        // can control the frequency at which the selector manager will wake
+        // up when there are no pending operations.
+        // Increasing the frequency (shorter delays) might allow the selector
+        // to observe that the facade is no longer referenced and might allow
+        // the selector thread to terminate more timely - for when nothing is
+        // ongoing it will only check for that condition every NODEADLINE ms.
+        // To avoid misuse of the property, the delay that can be specified
+        // is comprised between [MIN_NODEADLINE, MAX_NODEADLINE], and its default
+        // value if unspecified (or <= 0) is DEF_NODEADLINE = 3000ms
+        // The property is -Djdk.httpclient.internal.selector.timeout=<millis>
+        private static final int MIN_NODEADLINE = 1000; // ms
+        private static final int MAX_NODEADLINE = 1000 * 1200; // ms
+        private static final int DEF_NODEADLINE = 3000; // ms
+        private static final long NODEADLINE; // default is DEF_NODEADLINE ms
+        static {
+            // ensure NODEADLINE is initialized with some valid value.
+            long deadline =  Utils.getIntegerNetProperty(
+                "jdk.httpclient.internal.selector.timeout",
+                DEF_NODEADLINE); // millis
+            if (deadline <= 0) deadline = DEF_NODEADLINE;
+            deadline = Math.max(deadline, MIN_NODEADLINE);
+            NODEADLINE = Math.min(deadline, MAX_NODEADLINE);
+        }
+
         private final Selector selector;
         private volatile boolean closed;
-        private final List<AsyncEvent> readyList;
         private final List<AsyncEvent> registrations;
-
-        // Uses a weak reference to the HttpClient owning this
-        // selector: a strong reference prevents its garbage
-        // collection while the thread is running.
-        // We want the thread to exit gracefully when the
-        // HttpClient that owns it gets GC'ed.
-        WeakReference<HttpClientImpl> ownerRef;
+        private final System.Logger debug;
+        private final System.Logger debugtimeout;
+        HttpClientImpl owner;
+        ConnectionPool pool;
 
         SelectorManager(HttpClientImpl ref) throws IOException {
-            super(null, null, "SelectorManager", 0, false);
-            ownerRef = new WeakReference<>(ref);
-            readyList = new ArrayList<>();
+            super(null, null, "HttpClient-" + ref.id + "-SelectorManager", 0, false);
+            owner = ref;
+            debug = ref.debug;
+            debugtimeout = ref.debugtimeout;
+            pool = ref.connectionPool();
             registrations = new ArrayList<>();
             selector = Selector.open();
         }
 
+        void eventUpdated(AsyncEvent e) throws ClosedChannelException {
+            if (Thread.currentThread() == this) {
+                SelectionKey key = e.channel().keyFor(selector);
+                SelectorAttachment sa = (SelectorAttachment) key.attachment();
+                if (sa != null) sa.register(e);
+            } else {
+                register(e);
+            }
+        }
+
         // This returns immediately. So caller not allowed to send/receive
         // on connection.
-
-        synchronized void register(AsyncEvent e) throws IOException {
+        synchronized void register(AsyncEvent e) {
             registrations.add(e);
             selector.wakeup();
         }
@@ -326,23 +574,34 @@ class HttpClientImpl extends HttpClient {
         }
 
         synchronized void shutdown() {
+            debug.log(Level.DEBUG, "SelectorManager shutting down");
             closed = true;
             try {
                 selector.close();
-            } catch (IOException ignored) { }
+            } catch (IOException ignored) {
+            } finally {
+                owner.stop();
+            }
         }
 
         @Override
         public void run() {
+            List<Pair<AsyncEvent,IOException>> errorList = new ArrayList<>();
+            List<AsyncEvent> readyList = new ArrayList<>();
             try {
                 while (!Thread.currentThread().isInterrupted()) {
-                    HttpClientImpl client;
                     synchronized (this) {
-                        for (AsyncEvent exchange : registrations) {
-                            SelectableChannel c = exchange.channel();
+                        assert errorList.isEmpty();
+                        assert readyList.isEmpty();
+                        for (AsyncEvent event : registrations) {
+                            if (event instanceof AsyncTriggerEvent) {
+                                readyList.add(event);
+                                continue;
+                            }
+                            SelectableChannel chan = event.channel();
+                            SelectionKey key = null;
                             try {
-                                c.configureBlocking(false);
-                                SelectionKey key = c.keyFor(selector);
+                                key = chan.keyFor(selector);
                                 SelectorAttachment sa;
                                 if (key == null || !key.isValid()) {
                                     if (key != null) {
@@ -351,91 +610,163 @@ class HttpClientImpl extends HttpClient {
                                         // before registering the new event.
                                         selector.selectNow();
                                     }
-                                    sa = new SelectorAttachment(c, selector);
+                                    sa = new SelectorAttachment(chan, selector);
                                 } else {
                                     sa = (SelectorAttachment) key.attachment();
                                 }
-                                sa.register(exchange);
+                                // may throw IOE if channel closed: that's OK
+                                sa.register(event);
+                                if (!chan.isOpen()) {
+                                    throw new IOException("Channel closed");
+                                }
                             } catch (IOException e) {
-                                Log.logError("HttpClientImpl: " + e);
-                                c.close();
-                                // let the exchange deal with it
-                                handleEvent(exchange);
+                                Log.logTrace("HttpClientImpl: " + e);
+                                debug.log(Level.DEBUG, () ->
+                                        "Got " + e.getClass().getName()
+                                                 + " while handling"
+                                                 + " registration events");
+                                chan.close();
+                                // let the event abort deal with it
+                                errorList.add(new Pair<>(event, e));
+                                if (key != null) {
+                                    key.cancel();
+                                    selector.selectNow();
+                                }
                             }
                         }
                         registrations.clear();
+                        selector.selectedKeys().clear();
                     }
 
+                    for (AsyncEvent event : readyList) {
+                        assert event instanceof AsyncTriggerEvent;
+                        event.handle();
+                    }
+                    readyList.clear();
+
+                    for (Pair<AsyncEvent,IOException> error : errorList) {
+                        // an IOException was raised and the channel closed.
+                        handleEvent(error.first, error.second);
+                    }
+                    errorList.clear();
+
                     // Check whether client is still alive, and if not,
                     // gracefully stop this thread
-                    if ((client = ownerRef.get()) == null) {
+                    if (!owner.isReferenced()) {
                         Log.logTrace("HttpClient no longer referenced. Exiting...");
                         return;
                     }
-                    long millis = client.purgeTimeoutsAndReturnNextDeadline();
-                    client = null; // don't hold onto the client ref
 
-                    //debugPrint(selector);
+                    // Timeouts will have milliseconds granularity. It is important
+                    // to handle them in a timely fashion.
+                    long nextTimeout = owner.purgeTimeoutsAndReturnNextDeadline();
+                    debugtimeout.log(Level.DEBUG, "next timeout: %d", nextTimeout);
+
+                    // Keep-alive have seconds granularity. It's not really an
+                    // issue if we keep connections linger a bit more in the keep
+                    // alive cache.
+                    long nextExpiry = pool.purgeExpiredConnectionsAndReturnNextDeadline();
+                    debugtimeout.log(Level.DEBUG, "next expired: %d", nextExpiry);
+
+                    assert nextTimeout >= 0;
+                    assert nextExpiry >= 0;
+
                     // Don't wait for ever as it might prevent the thread to
                     // stop gracefully. millis will be 0 if no deadline was found.
+                    if (nextTimeout <= 0) nextTimeout = NODEADLINE;
+
+                    // Clip nextExpiry at NODEADLINE limit. The default
+                    // keep alive is 1200 seconds (half an hour) - we don't
+                    // want to wait that long.
+                    if (nextExpiry <= 0) nextExpiry = NODEADLINE;
+                    else nextExpiry = Math.min(NODEADLINE, nextExpiry);
+
+                    // takes the least of the two.
+                    long millis = Math.min(nextExpiry, nextTimeout);
+
+                    debugtimeout.log(Level.DEBUG, "Next deadline is %d",
+                                     (millis == 0 ? NODEADLINE : millis));
+                    //debugPrint(selector);
                     int n = selector.select(millis == 0 ? NODEADLINE : millis);
                     if (n == 0) {
                         // Check whether client is still alive, and if not,
                         // gracefully stop this thread
-                        if ((client = ownerRef.get()) == null) {
+                        if (!owner.isReferenced()) {
                             Log.logTrace("HttpClient no longer referenced. Exiting...");
                             return;
                         }
-                        client.purgeTimeoutsAndReturnNextDeadline();
-                        client = null; // don't hold onto the client ref
+                        owner.purgeTimeoutsAndReturnNextDeadline();
                         continue;
                     }
                     Set<SelectionKey> keys = selector.selectedKeys();
 
+                    assert errorList.isEmpty();
                     for (SelectionKey key : keys) {
                         SelectorAttachment sa = (SelectorAttachment) key.attachment();
-                        int eventsOccurred = key.readyOps();
+                        if (!key.isValid()) {
+                            IOException ex = sa.chan.isOpen()
+                                    ? new IOException("Invalid key")
+                                    : new ClosedChannelException();
+                            sa.pending.forEach(e -> errorList.add(new Pair<>(e,ex)));
+                            sa.pending.clear();
+                            continue;
+                        }
+
+                        int eventsOccurred;
+                        try {
+                            eventsOccurred = key.readyOps();
+                        } catch (CancelledKeyException ex) {
+                            IOException io = Utils.getIOException(ex);
+                            sa.pending.forEach(e -> errorList.add(new Pair<>(e,io)));
+                            sa.pending.clear();
+                            continue;
+                        }
                         sa.events(eventsOccurred).forEach(readyList::add);
                         sa.resetInterestOps(eventsOccurred);
                     }
                     selector.selectNow(); // complete cancellation
                     selector.selectedKeys().clear();
 
-                    for (AsyncEvent exchange : readyList) {
-                        if (exchange.blocking()) {
-                            exchange.channel().configureBlocking(true);
-                        }
-                        handleEvent(exchange); // will be delegated to executor
+                    for (AsyncEvent event : readyList) {
+                        handleEvent(event, null); // will be delegated to executor
                     }
                     readyList.clear();
+                    errorList.forEach((p) -> handleEvent(p.first, p.second));
+                    errorList.clear();
                 }
             } catch (Throwable e) {
+                //e.printStackTrace();
                 if (!closed) {
                     // This terminates thread. So, better just print stack trace
                     String err = Utils.stackTrace(e);
                     Log.logError("HttpClientImpl: fatal error: " + err);
                 }
+                debug.log(Level.DEBUG, "shutting down", e);
+                if (Utils.ASSERTIONSENABLED && !debug.isLoggable(Level.DEBUG)) {
+                    e.printStackTrace(System.err); // always print the stack
+                }
             } finally {
                 shutdown();
             }
         }
 
-        void debugPrint(Selector selector) {
-            System.err.println("Selector: debugprint start");
-            Set<SelectionKey> keys = selector.keys();
-            for (SelectionKey key : keys) {
-                SelectableChannel c = key.channel();
-                int ops = key.interestOps();
-                System.err.printf("selector chan:%s ops:%d\n", c, ops);
-            }
-            System.err.println("Selector: debugprint end");
-        }
+//        void debugPrint(Selector selector) {
+//            System.err.println("Selector: debugprint start");
+//            Set<SelectionKey> keys = selector.keys();
+//            for (SelectionKey key : keys) {
+//                SelectableChannel c = key.channel();
+//                int ops = key.interestOps();
+//                System.err.printf("selector chan:%s ops:%d\n", c, ops);
+//            }
+//            System.err.println("Selector: debugprint end");
+//        }
 
-        void handleEvent(AsyncEvent e) {
-            if (closed) {
-                e.abort();
+        /** Handles the given event. The given ioe may be null. */
+        void handleEvent(AsyncEvent event, IOException ioe) {
+            if (closed || ioe != null) {
+                event.abort(ioe);
             } else {
-                e.handle();
+                event.handle();
             }
         }
     }
@@ -453,11 +784,13 @@ class HttpClientImpl extends HttpClient {
     private static class SelectorAttachment {
         private final SelectableChannel chan;
         private final Selector selector;
-        private final ArrayList<AsyncEvent> pending;
+        private final Set<AsyncEvent> pending;
+        private final static System.Logger debug =
+                Utils.getDebugLogger("SelectorAttachment"::toString, DEBUG);
         private int interestOps;
 
         SelectorAttachment(SelectableChannel chan, Selector selector) {
-            this.pending = new ArrayList<>();
+            this.pending = new HashSet<>();
             this.chan = chan;
             this.selector = selector;
         }
@@ -506,23 +839,43 @@ class HttpClientImpl extends HttpClient {
 
             this.interestOps = newOps;
             SelectionKey key = chan.keyFor(selector);
-            if (newOps == 0) {
+            if (newOps == 0 && pending.isEmpty()) {
                 key.cancel();
             } else {
-                key.interestOps(newOps);
+                try {
+                    key.interestOps(newOps);
+                } catch (CancelledKeyException x) {
+                    // channel may have been closed
+                    debug.log(Level.DEBUG, "key cancelled for " + chan);
+                    abortPending(x);
+                }
+            }
+        }
+
+        void abortPending(Throwable x) {
+            if (!pending.isEmpty()) {
+                AsyncEvent[] evts = pending.toArray(new AsyncEvent[0]);
+                pending.clear();
+                IOException io = Utils.getIOException(x);
+                for (AsyncEvent event : evts) {
+                    event.abort(io);
+                }
             }
         }
     }
 
-    @Override
-    public SSLContext sslContext() {
-        Utils.checkNetPermission("getSSLContext");
+    /*package-private*/ SSLContext theSSLContext() {
         return sslContext;
     }
 
     @Override
-    public Optional<SSLParameters> sslParameters() {
-        return Optional.ofNullable(sslParams);
+    public SSLContext sslContext() {
+        return sslContext;
+    }
+
+    @Override
+    public SSLParameters sslParameters() {
+        return Utils.copySSLParameters(sslParams);
     }
 
     @Override
@@ -530,11 +883,15 @@ class HttpClientImpl extends HttpClient {
         return Optional.ofNullable(authenticator);
     }
 
-    @Override
-    public Executor executor() {
+    /*package-private*/ final Executor theExecutor() {
         return executor;
     }
 
+    @Override
+    public final Optional<Executor> executor() {
+        return isDefaultExecutor ? Optional.empty() : Optional.of(executor);
+    }
+
     ConnectionPool connectionPool() {
         return connections;
     }
@@ -546,19 +903,28 @@ class HttpClientImpl extends HttpClient {
 
 
     @Override
-    public Optional<CookieManager> cookieManager() {
-        return Optional.ofNullable(cookieManager);
+    public Optional<CookieHandler> cookieHandler() {
+        return Optional.ofNullable(cookieHandler);
     }
 
     @Override
     public Optional<ProxySelector> proxy() {
-        return Optional.ofNullable(this.proxySelector);
+        return this.userProxySelector;
+    }
+
+    // Return the effective proxy that this client uses.
+    ProxySelector proxySelector() {
+        return proxySelector;
     }
 
     @Override
-    public WebSocket.Builder newWebSocketBuilder(URI uri,
-                                                 WebSocket.Listener listener) {
-        return new BuilderImpl(this, uri, listener);
+    public WebSocket.Builder newWebSocketBuilder() {
+        // Make sure to pass the HttpClientFacade to the WebSocket builder.
+        // This will ensure that the facade is not released before the
+        // WebSocket has been created, at which point the pendingOperationCount
+        // will have been incremented by the DetachedConnectionChannel
+        // (see PlainHttpConnection.detachChannel())
+        return new BuilderImpl(this.facade(), proxySelector);
     }
 
     @Override
@@ -566,16 +932,21 @@ class HttpClientImpl extends HttpClient {
         return version;
     }
 
-    //private final HashMap<String, Boolean> http2NotSupported = new HashMap<>();
+    String dbgString() {
+        return dbgTag;
+    }
 
-    boolean getHttp2Allowed() {
-        return version.equals(Version.HTTP_2);
+    @Override
+    public String toString() {
+        // Used by tests to get the client's id and compute the
+        // name of the SelectorManager thread.
+        return super.toString() + ("(" + id + ")");
     }
 
     private void initFilters() {
         addFilter(AuthenticationFilter.class);
         addFilter(RedirectFilter.class);
-        if (this.cookieManager != null) {
+        if (this.cookieHandler != null) {
             addFilter(CookieFilter.class);
         }
     }
diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/HttpConnection.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/HttpConnection.java
index d81d3950374..66542f57587 100644
--- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/HttpConnection.java
+++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/HttpConnection.java
@@ -25,15 +25,27 @@
 
 package jdk.incubator.http;
 
-import javax.net.ssl.SSLParameters;
 import java.io.Closeable;
 import java.io.IOException;
+import java.lang.System.Logger.Level;
 import java.net.InetSocketAddress;
 import java.nio.ByteBuffer;
 import java.nio.channels.SocketChannel;
+import java.util.IdentityHashMap;
+import java.util.List;
+import java.util.Map;
 import java.util.concurrent.CompletableFuture;
-
-import jdk.incubator.http.internal.common.ByteBufferReference;
+import java.util.concurrent.CompletionStage;
+import java.util.concurrent.ConcurrentLinkedDeque;
+import java.util.concurrent.Flow;
+import jdk.incubator.http.HttpClient.Version;
+import jdk.incubator.http.internal.common.Demand;
+import jdk.incubator.http.internal.common.FlowTube;
+import jdk.incubator.http.internal.common.SequentialScheduler;
+import jdk.incubator.http.internal.common.SequentialScheduler.DeferredCompleter;
+import jdk.incubator.http.internal.common.Log;
+import jdk.incubator.http.internal.common.Utils;
+import static jdk.incubator.http.HttpClient.Version.HTTP_2;
 
 /**
  * Wraps socket channel layer and takes care of SSL also.
@@ -42,75 +54,151 @@ import jdk.incubator.http.internal.common.ByteBufferReference;
  *      PlainHttpConnection: regular direct TCP connection to server
  *      PlainProxyConnection: plain text proxy connection
  *      PlainTunnelingConnection: opens plain text (CONNECT) tunnel to server
- *      SSLConnection: TLS channel direct to server
- *      SSLTunnelConnection: TLS channel via (CONNECT) proxy tunnel
+ *      AsyncSSLConnection: TLS channel direct to server
+ *      AsyncSSLTunnelConnection: TLS channel via (CONNECT) proxy tunnel
  */
 abstract class HttpConnection implements Closeable {
 
-    enum Mode {
-        BLOCKING,
-        NON_BLOCKING,
-        ASYNC
-    }
+    static final boolean DEBUG = Utils.DEBUG; // Revisit: temporary dev flag.
+    final System.Logger  debug = Utils.getDebugLogger(this::dbgString, DEBUG);
+    final static System.Logger DEBUG_LOGGER = Utils.getDebugLogger(
+            () -> "HttpConnection(SocketTube(?))", DEBUG);
 
-    protected Mode mode;
-
-    // address we are connected to. Could be a server or a proxy
+    /** The address this connection is connected to. Could be a server or a proxy. */
     final InetSocketAddress address;
-    final HttpClientImpl client;
+    private final HttpClientImpl client;
+    private final TrailingOperations trailingOperations;
 
     HttpConnection(InetSocketAddress address, HttpClientImpl client) {
         this.address = address;
         this.client = client;
+        trailingOperations = new TrailingOperations();
     }
 
-    /**
-     * Public API to this class. addr is the ultimate destination. Any proxies
-     * etc are figured out from the request. Returns an instance of one of the
-     * following
-     *      PlainHttpConnection
-     *      PlainTunnelingConnection
-     *      SSLConnection
-     *      SSLTunnelConnection
-     *
-     * When object returned, connect() or connectAsync() must be called, which
-     * when it returns/completes, the connection is usable for requests.
-     */
-    public static HttpConnection getConnection(
-            InetSocketAddress addr, HttpClientImpl client, HttpRequestImpl request)
-    {
-        return getConnectionImpl(addr, client, request, false);
+    private static final class TrailingOperations {
+        private final Map<CompletionStage<?>, Boolean> operations =
+                new IdentityHashMap<>();
+        void add(CompletionStage<?> cf) {
+            synchronized(operations) {
+                cf.whenComplete((r,t)-> remove(cf));
+                operations.put(cf, Boolean.TRUE);
+            }
+        }
+        boolean remove(CompletionStage<?> cf) {
+            synchronized(operations) {
+                return operations.remove(cf);
+            }
+        }
     }
 
-    /**
-     * Called specifically to get an async connection for HTTP/2 over SSL.
-     */
-    public static HttpConnection getConnection(InetSocketAddress addr,
-        HttpClientImpl client, HttpRequestImpl request, boolean isHttp2) {
-
-        return getConnectionImpl(addr, client, request, isHttp2);
+    final void addTrailingOperation(CompletionStage<?> cf) {
+        trailingOperations.add(cf);
     }
 
-    public abstract void connect() throws IOException, InterruptedException;
+//    final void removeTrailingOperation(CompletableFuture<?> cf) {
+//        trailingOperations.remove(cf);
+//    }
+
+    final HttpClientImpl client() {
+        return client;
+    }
+
+    //public abstract void connect() throws IOException, InterruptedException;
 
     public abstract CompletableFuture<Void> connectAsync();
 
-    /**
-     * Returns whether this connection is connected to its destination
-     */
+    /** Tells whether, or not, this connection is connected to its destination. */
     abstract boolean connected();
 
+    /** Tells whether, or not, this connection is secure ( over SSL ) */
     abstract boolean isSecure();
 
+    /** Tells whether, or not, this connection is proxied. */
     abstract boolean isProxied();
 
-    /**
-     * Completes when the first byte of the response is available to be read.
-     */
-    abstract CompletableFuture<Void> whenReceivingResponse();
-
+    /** Tells whether, or not, this connection is open. */
     final boolean isOpen() {
-        return channel().isOpen();
+        return channel().isOpen() &&
+                (connected() ? !getConnectionFlow().isFinished() : true);
+    }
+
+    interface HttpPublisher extends FlowTube.TubePublisher {
+        void enqueue(List<ByteBuffer> buffers) throws IOException;
+        void enqueueUnordered(List<ByteBuffer> buffers) throws IOException;
+        void signalEnqueued() throws IOException;
+    }
+
+    /**
+     * Returns the HTTP publisher associated with this connection.  May be null
+     * if invoked before connecting.
+     */
+    abstract HttpPublisher publisher();
+
+    /**
+     * Factory for retrieving HttpConnections. A connection can be retrieved
+     * from the connection pool, or a new one created if none available.
+     *
+     * The given {@code addr} is the ultimate destination. Any proxies,
+     * etc, are determined from the request. Returns a concrete instance which
+     * is one of the following:
+     *      {@link PlainHttpConnection}
+     *      {@link PlainTunnelingConnection}
+     *
+     * The returned connection, if not from the connection pool, must have its,
+     * connect() or connectAsync() method invoked, which ( when it completes
+     * successfully ) renders the connection usable for requests.
+     */
+    public static HttpConnection getConnection(InetSocketAddress addr,
+                                               HttpClientImpl client,
+                                               HttpRequestImpl request,
+                                               Version version) {
+        HttpConnection c = null;
+        InetSocketAddress proxy = request.proxy();
+        if (proxy != null && proxy.isUnresolved()) {
+            // The default proxy selector may select a proxy whose  address is
+            // unresolved. We must resolve the address before connecting to it.
+            proxy = new InetSocketAddress(proxy.getHostString(), proxy.getPort());
+        }
+        boolean secure = request.secure();
+        ConnectionPool pool = client.connectionPool();
+
+        if (!secure) {
+            c = pool.getConnection(false, addr, proxy);
+            if (c != null && c.isOpen() /* may have been eof/closed when in the pool */) {
+                final HttpConnection conn = c;
+                DEBUG_LOGGER.log(Level.DEBUG, () -> conn.getConnectionFlow()
+                            + ": plain connection retrieved from HTTP/1.1 pool");
+                return c;
+            } else {
+                return getPlainConnection(addr, proxy, request, client);
+            }
+        } else {  // secure
+            if (version != HTTP_2) { // only HTTP/1.1 connections are in the pool
+                c = pool.getConnection(true, addr, proxy);
+            }
+            if (c != null && c.isOpen()) {
+                final HttpConnection conn = c;
+                DEBUG_LOGGER.log(Level.DEBUG, () -> conn.getConnectionFlow()
+                            + ": SSL connection retrieved from HTTP/1.1 pool");
+                return c;
+            } else {
+                String[] alpn = null;
+                if (version == HTTP_2) {
+                    alpn = new String[] { "h2", "http/1.1" };
+                }
+                return getSSLConnection(addr, proxy, alpn, client);
+            }
+        }
+    }
+
+    private static HttpConnection getSSLConnection(InetSocketAddress addr,
+                                                   InetSocketAddress proxy,
+                                                   String[] alpn,
+                                                   HttpClientImpl client) {
+        if (proxy != null)
+            return new AsyncSSLTunnelConnection(addr, client, alpn, proxy);
+        else
+            return new AsyncSSLConnection(addr, client, alpn);
     }
 
     /* Returns either a plain HTTP connection or a plain tunnelling connection
@@ -119,192 +207,54 @@ abstract class HttpConnection implements Closeable {
                                                      InetSocketAddress proxy,
                                                      HttpRequestImpl request,
                                                      HttpClientImpl client) {
-        if (request.isWebSocket() && proxy != null) {
+        if (request.isWebSocket() && proxy != null)
             return new PlainTunnelingConnection(addr, proxy, client);
-        } else {
-            if (proxy == null) {
-                return new PlainHttpConnection(addr, client);
-            } else {
-                return new PlainProxyConnection(proxy, client);
-            }
-        }
+
+        if (proxy == null)
+            return new PlainHttpConnection(addr, client);
+        else
+            return new PlainProxyConnection(proxy, client);
     }
 
-    private static HttpConnection getSSLConnection(InetSocketAddress addr,
-            InetSocketAddress proxy, HttpRequestImpl request,
-            String[] alpn, boolean isHttp2, HttpClientImpl client)
-    {
-        if (proxy != null) {
-            if (!isHttp2) {
-                return new SSLTunnelConnection(addr, client, proxy);
-            } else {
-                return new AsyncSSLTunnelConnection(addr, client, alpn, proxy);
-            }
-        } else if (!isHttp2) {
-            return new SSLConnection(addr, client, alpn);
-        } else {
-            return new AsyncSSLConnection(addr, client, alpn);
-        }
-    }
-
-    /**
-     * Main factory method.   Gets a HttpConnection, either cached or new if
-     * none available.
-     */
-    private static HttpConnection getConnectionImpl(InetSocketAddress addr,
-            HttpClientImpl client,
-            HttpRequestImpl request, boolean isHttp2)
-    {
-        HttpConnection c = null;
-        InetSocketAddress proxy = request.proxy(client);
-        if (proxy != null && proxy.isUnresolved()) {
-            // The default proxy selector may select a proxy whose
-            // address is unresolved. We must resolve the address
-            // before using it to connect.
-            proxy = new InetSocketAddress(proxy.getHostString(), proxy.getPort());
-        }
-        boolean secure = request.secure();
-        ConnectionPool pool = client.connectionPool();
-        String[] alpn =  null;
-
-        if (secure && isHttp2) {
-            alpn = new String[2];
-            alpn[0] = "h2";
-            alpn[1] = "http/1.1";
-        }
-
-        if (!secure) {
-            c = pool.getConnection(false, addr, proxy);
-            if (c != null) {
-                return c;
-            } else {
-                return getPlainConnection(addr, proxy, request, client);
-            }
-        } else {
-            if (!isHttp2) { // if http2 we don't cache connections
-                c = pool.getConnection(true, addr, proxy);
-            }
-            if (c != null) {
-                return c;
-            } else {
-                return getSSLConnection(addr, proxy, request, alpn, isHttp2, client);
-            }
-        }
-    }
-
-    void returnToCache(HttpHeaders hdrs) {
+    void closeOrReturnToCache(HttpHeaders hdrs) {
         if (hdrs == null) {
-            // the connection was closed by server
+            // the connection was closed by server, eof
             close();
             return;
         }
         if (!isOpen()) {
             return;
         }
+        HttpClientImpl client = client();
+        if (client == null) {
+            close();
+            return;
+        }
         ConnectionPool pool = client.connectionPool();
         boolean keepAlive = hdrs.firstValue("Connection")
                 .map((s) -> !s.equalsIgnoreCase("close"))
                 .orElse(true);
 
         if (keepAlive) {
+            Log.logTrace("Returning connection to the pool: {0}", this);
             pool.returnToPool(this);
         } else {
             close();
         }
     }
 
-    /**
-     * Also check that the number of bytes written is what was expected. This
-     * could be different if the buffer is user-supplied and its internal
-     * pointers were manipulated in a race condition.
-     */
-    final void checkWrite(long expected, ByteBuffer buffer) throws IOException {
-        long written = write(buffer);
-        if (written != expected) {
-            throw new IOException("incorrect number of bytes written");
-        }
-    }
-
-    final void checkWrite(long expected,
-                          ByteBuffer[] buffers,
-                          int start,
-                          int length)
-        throws IOException
-    {
-        long written = write(buffers, start, length);
-        if (written != expected) {
-            throw new IOException("incorrect number of bytes written");
-        }
-    }
-
     abstract SocketChannel channel();
 
     final InetSocketAddress address() {
         return address;
     }
 
-    synchronized void configureMode(Mode mode) throws IOException {
-        this.mode = mode;
-        if (mode == Mode.BLOCKING) {
-            channel().configureBlocking(true);
-        } else {
-            channel().configureBlocking(false);
-        }
-    }
-
-    synchronized Mode getMode() {
-        return mode;
-    }
-
     abstract ConnectionPool.CacheKey cacheKey();
 
-    // overridden in SSL only
-    SSLParameters sslParameters() {
-        return null;
-    }
-
-    // Methods to be implemented for Plain TCP and SSL
-
-    abstract long write(ByteBuffer[] buffers, int start, int number)
-        throws IOException;
-
-    abstract long write(ByteBuffer buffer) throws IOException;
-
-    // Methods to be implemented for Plain TCP (async mode) and AsyncSSL
-
-    /**
-     * In {@linkplain Mode#ASYNC async mode}, this method puts buffers at the
-     * end of the send queue; Otherwise, it is equivalent to {@link
-     * #write(ByteBuffer[], int, int) write(buffers, 0, buffers.length)}.
-     * When in async mode, calling this method should later be followed by
-     * subsequent flushAsync invocation.
-     * That allows multiple threads to put buffers into the queue while some other
-     * thread is writing.
-     */
-    abstract void writeAsync(ByteBufferReference[] buffers) throws IOException;
-
-    /**
-     * In {@linkplain Mode#ASYNC async mode}, this method may put
-     * buffers at the beginning of send queue, breaking frames sequence and
-     * allowing to write these buffers before other buffers in the queue;
-     * Otherwise, it is equivalent to {@link
-     * #write(ByteBuffer[], int, int) write(buffers, 0, buffers.length)}.
-     * When in async mode, calling this method should later be followed by
-     * subsequent flushAsync invocation.
-     * That allows multiple threads to put buffers into the queue while some other
-     * thread is writing.
-     */
-    abstract void writeAsyncUnordered(ByteBufferReference[] buffers) throws IOException;
-
-    /**
-     * This method should be called after  any writeAsync/writeAsyncUnordered
-     * invocation.
-     * If there is a race to flushAsync from several threads one thread
-     * (race winner) capture flush operation and write the whole queue content.
-     * Other threads (race losers) exits from the method (not blocking)
-     * and continue execution.
-     */
-    abstract void flushAsync() throws IOException;
+//    // overridden in SSL only
+//    SSLParameters sslParameters() {
+//        return null;
+//    }
 
     /**
      * Closes this connection, by returning the socket to its connection pool.
@@ -316,32 +266,142 @@ abstract class HttpConnection implements Closeable {
 
     abstract void shutdownOutput() throws IOException;
 
-    /**
-     * Puts position to limit and limit to capacity so we can resume reading
-     * into this buffer, but if required > 0 then limit may be reduced so that
-     * no more than required bytes are read next time.
-     */
-    static void resumeChannelRead(ByteBuffer buf, int required) {
-        int limit = buf.limit();
-        buf.position(limit);
-        int capacity = buf.capacity() - limit;
-        if (required > 0 && required < capacity) {
-            buf.limit(limit + required);
-        } else {
-            buf.limit(buf.capacity());
+    // Support for WebSocket/RawChannelImpl which unfortunately
+    // still depends on synchronous read/writes.
+    // It should be removed when RawChannelImpl moves to using asynchronous APIs.
+    abstract static class DetachedConnectionChannel implements Closeable {
+        DetachedConnectionChannel() {}
+        abstract SocketChannel channel();
+        abstract long write(ByteBuffer[] buffers, int start, int number)
+                throws IOException;
+        abstract void shutdownInput() throws IOException;
+        abstract void shutdownOutput() throws IOException;
+        abstract ByteBuffer read() throws IOException;
+        @Override
+        public abstract void close();
+        @Override
+        public String toString() {
+            return this.getClass().getSimpleName() + ": " + channel().toString();
         }
     }
 
-    final ByteBuffer read() throws IOException {
-        ByteBuffer b = readImpl();
-        return b;
+    // Support for WebSocket/RawChannelImpl which unfortunately
+    // still depends on synchronous read/writes.
+    // It should be removed when RawChannelImpl moves to using asynchronous APIs.
+    abstract DetachedConnectionChannel detachChannel();
+
+    abstract FlowTube getConnectionFlow();
+
+    /**
+     * A publisher that makes it possible to publish (write)
+     * ordered (normal priority) and unordered (high priority)
+     * buffers downstream.
+     */
+    final class PlainHttpPublisher implements HttpPublisher {
+        final Object reading;
+        PlainHttpPublisher() {
+            this(new Object());
+        }
+        PlainHttpPublisher(Object readingLock) {
+            this.reading = readingLock;
+        }
+        final ConcurrentLinkedDeque<List<ByteBuffer>> queue = new ConcurrentLinkedDeque<>();
+        volatile Flow.Subscriber<? super List<ByteBuffer>> subscriber;
+        volatile HttpWriteSubscription subscription;
+        final SequentialScheduler writeScheduler =
+                    new SequentialScheduler(this::flushTask);
+        @Override
+        public void subscribe(Flow.Subscriber<? super List<ByteBuffer>> subscriber) {
+            synchronized (reading) {
+                //assert this.subscription == null;
+                //assert this.subscriber == null;
+                if (subscription == null) {
+                    subscription = new HttpWriteSubscription();
+                }
+                this.subscriber = subscriber;
+            }
+            // TODO: should we do this in the flow?
+            subscriber.onSubscribe(subscription);
+            signal();
+        }
+
+        void flushTask(DeferredCompleter completer) {
+            try {
+                HttpWriteSubscription sub = subscription;
+                if (sub != null) sub.flush();
+            } finally {
+                completer.complete();
+            }
+        }
+
+        void signal() {
+            writeScheduler.runOrSchedule();
+        }
+
+        final class HttpWriteSubscription implements Flow.Subscription {
+            final Demand demand = new Demand();
+
+            @Override
+            public void request(long n) {
+                if (n <= 0) throw new IllegalArgumentException("non-positive request");
+                demand.increase(n);
+                debug.log(Level.DEBUG, () -> "HttpPublisher: got request of "
+                            + n + " from "
+                            + getConnectionFlow());
+                writeScheduler.runOrSchedule();
+            }
+
+            @Override
+            public void cancel() {
+                debug.log(Level.DEBUG, () -> "HttpPublisher: cancelled by "
+                          + getConnectionFlow());
+            }
+
+            void flush() {
+                while (!queue.isEmpty() && demand.tryDecrement()) {
+                    List<ByteBuffer> elem = queue.poll();
+                    debug.log(Level.DEBUG, () -> "HttpPublisher: sending "
+                                + Utils.remaining(elem) + " bytes ("
+                                + elem.size() + " buffers) to "
+                                + getConnectionFlow());
+                    subscriber.onNext(elem);
+                }
+            }
+        }
+
+        @Override
+        public void enqueue(List<ByteBuffer> buffers) throws IOException {
+            queue.add(buffers);
+            int bytes = buffers.stream().mapToInt(ByteBuffer::remaining).sum();
+            debug.log(Level.DEBUG, "added %d bytes to the write queue", bytes);
+        }
+
+        @Override
+        public void enqueueUnordered(List<ByteBuffer> buffers) throws IOException {
+            // Unordered frames are sent before existing frames.
+            int bytes = buffers.stream().mapToInt(ByteBuffer::remaining).sum();
+            queue.addFirst(buffers);
+            debug.log(Level.DEBUG, "inserted %d bytes in the write queue", bytes);
+        }
+
+        @Override
+        public void signalEnqueued() throws IOException {
+            debug.log(Level.DEBUG, "signalling the publisher of the write queue");
+            signal();
+        }
     }
 
-    /*
-     * Returns a ByteBuffer with the data available at the moment, or null if
-     * reached EOF.
-     */
-    protected abstract ByteBuffer readImpl() throws IOException;
+    String dbgTag = null;
+    final String dbgString() {
+        FlowTube flow = getConnectionFlow();
+        String tag = dbgTag;
+        if (tag == null && flow != null) {
+            dbgTag = tag = this.getClass().getSimpleName() + "(" + flow + ")";
+        } else if (tag == null) {
+            tag = this.getClass().getSimpleName() + "(?)";
+        }
+        return tag;
+    }
 
     @Override
     public String toString() {
diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/HttpHeaders.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/HttpHeaders.java
index b443dd07aa8..a66908dcc1c 100644
--- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/HttpHeaders.java
+++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/HttpHeaders.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2017, 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
@@ -29,55 +29,136 @@ import java.util.List;
 import java.util.Map;
 import java.util.Optional;
 import java.util.OptionalLong;
+import static java.util.Collections.emptyList;
+import static java.util.Collections.unmodifiableList;
+import static java.util.Objects.requireNonNull;
 
 /**
  * A read-only view of a set of HTTP headers.
  * {@Incubating}
  *
+ * <p> The methods of this class ( that accept a String header name ), and the
+ * Map returned by the {@linkplain #map() map} method, operate without regard to
+ * case when retrieving the header value.
+ *
+ * <p> HttpHeaders instances are immutable.
+ *
  * @since 9
  */
-public interface HttpHeaders {
+public abstract class HttpHeaders {
 
     /**
-     * Returns an {@link java.util.Optional} containing the first value of the
-     * given named (and possibly multi-valued) header. If the header is not
-     * present, then the returned {@code Optional} is empty.
+     * Creates an HttpHeaders.
+     */
+    protected HttpHeaders() {}
+
+    /**
+     * Returns an {@link Optional} containing the first value of the given named
+     * (and possibly multi-valued) header. If the header is not present, then
+     * the returned {@code Optional} is empty.
+     *
+     * @implSpec
+     * The default implementation invokes
+     * {@code allValues(name).stream().findFirst()}
      *
      * @param name the header name
      * @return an {@code Optional<String>} for the first named value
      */
-    public Optional<String> firstValue(String name);
+    public Optional<String> firstValue(String name) {
+        return allValues(name).stream().findFirst();
+    }
 
     /**
-     * Returns an {@link java.util.OptionalLong} containing the first value of the
-     * named header field. If the header is not
-     * present, then the Optional is empty. If the header is present but
-     * contains a value that does not parse as a {@code Long} value, then an
-     * exception is thrown.
+     * Returns an {@link OptionalLong} containing the first value of the
+     * named header field. If the header is not present, then the Optional is
+     * empty. If the header is present but contains a value that does not parse
+     * as a {@code Long} value, then an exception is thrown.
+     *
+     * @implSpec
+     * The default implementation invokes
+     * {@code allValues(name).stream().mapToLong(Long::valueOf).findFirst()}
      *
      * @param name the header name
      * @return  an {@code OptionalLong}
      * @throws NumberFormatException if a value is found, but does not parse as
      *                               a Long
      */
-    public OptionalLong firstValueAsLong(String name);
+    public OptionalLong firstValueAsLong(String name) {
+        return allValues(name).stream().mapToLong(Long::valueOf).findFirst();
+    }
 
     /**
      * Returns an unmodifiable List of all of the values of the given named
      * header. Always returns a List, which may be empty if the header is not
      * present.
      *
+     * @implSpec
+     * The default implementation invokes, among other things, the
+     * {@code map().get(name)} to retrieve the list of header values.
+     *
      * @param name the header name
      * @return a List of String values
      */
-    public List<String> allValues(String name);
+    public List<String> allValues(String name) {
+        requireNonNull(name);
+        List<String> values = map().get(name);
+        // Making unmodifiable list out of empty in order to make a list which
+        // throws UOE unconditionally
+        return values != null ? values : unmodifiableList(emptyList());
+    }
 
     /**
-     * Returns an unmodifiable multi Map view of this HttpHeaders. This
-     * interface should only be used when it is required to iterate over the
-     * entire set of headers.
+     * Returns an unmodifiable multi Map view of this HttpHeaders.
      *
      * @return the Map
      */
-    public Map<String,List<String>> map();
+    public abstract Map<String, List<String>> map();
+
+    /**
+     * Tests this HTTP headers instance for equality with the given object.
+     *
+     * <p> If the given object is not an {@code HttpHeaders} then this
+     * method returns {@code false}. Two HTTP headers are equal if each
+     * of their corresponding {@linkplain #map() maps} are equal.
+     *
+     * <p> This method satisfies the general contract of the {@link
+     * Object#equals(Object) Object.equals} method.
+     *
+     * @param obj the object to which this object is to be compared
+     * @return {@code true} if, and only if, the given object is an {@code
+     *         HttpHeaders} that is equal to this HTTP headers
+     */
+    public final boolean equals(Object obj) {
+        if (!(obj instanceof HttpHeaders))
+            return false;
+        HttpHeaders that = (HttpHeaders)obj;
+        return this.map().equals(that.map());
+    }
+
+    /**
+     * Computes a hash code for this HTTP headers instance.
+     *
+     * <p> The hash code is based upon the components of the HTTP headers
+     * {@linkplain #map() map}, and satisfies the general contract of the
+     * {@link Object#hashCode Object.hashCode} method.
+     *
+     * @return the hash-code value for this HTTP headers
+     */
+    public final int hashCode() {
+        return map().hashCode();
+    }
+
+    /**
+     * Returns this HTTP headers as a string.
+     *
+     * @return a string describing the HTTP headers
+     */
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+        sb.append(super.toString()).append(" ");
+        sb.append(map());
+        sb.append(" }");
+        return sb.toString();
+    }
 }
diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/HttpRequest.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/HttpRequest.java
index 8226cc2ac69..c1636d9f8f8 100644
--- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/HttpRequest.java
+++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/HttpRequest.java
@@ -28,34 +28,41 @@ package jdk.incubator.http;
 import java.io.FileNotFoundException;
 import java.io.InputStream;
 import java.net.URI;
+import java.net.URLPermission;
 import java.nio.ByteBuffer;
-import java.nio.charset.*;
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
 import java.nio.file.Path;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
 import java.time.Duration;
 import java.util.Iterator;
+import java.util.Objects;
 import java.util.Optional;
 import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.Executor;
 import java.util.concurrent.Flow;
 import java.util.function.Supplier;
+import static java.nio.charset.StandardCharsets.UTF_8;
 
 /**
  * Represents one HTTP request which can be sent to a server.
  * {@Incubating }
  *
- * <p> {@code HttpRequest}s are built from {@code HttpRequest}
- * {@link HttpRequest.Builder builder}s. {@code HttpRequest} builders are
- * obtained by calling {@link HttpRequest#newBuilder(java.net.URI)
- * HttpRequest.newBuilder}.
- * A request's {@link java.net.URI}, headers and body can be set. Request bodies
- * are provided through a {@link BodyProcessor} object supplied to the
- * {@link Builder#DELETE(jdk.incubator.http.HttpRequest.BodyProcessor) DELETE},
- * {@link Builder#POST(jdk.incubator.http.HttpRequest.BodyProcessor) POST} or
- * {@link Builder#PUT(jdk.incubator.http.HttpRequest.BodyProcessor) PUT} methods.
+ * <p> {@code HttpRequest} instances are built from {@code HttpRequest}
+ * {@linkplain HttpRequest.Builder builders}. {@code HttpRequest} builders
+ * are obtained by calling {@link HttpRequest#newBuilder(URI) HttpRequest.newBuilder}.
+ * A request's {@linkplain URI}, headers and body can be set. Request bodies are
+ * provided through a {@link BodyPublisher} object supplied to the
+ * {@link Builder#DELETE(BodyPublisher) DELETE},
+ * {@link Builder#POST(BodyPublisher) POST} or
+ * {@link Builder#PUT(BodyPublisher) PUT} methods.
  * {@link Builder#GET() GET} does not take a body. Once all required
  * parameters have been set in the builder, {@link Builder#build() } is called
- * to return the {@code HttpRequest}. Builders can also be copied
- * and modified multiple times in order to build multiple related requests that
- * differ in some parameters.
+ * to return the {@code HttpRequest}. Builders can also be copied and modified
+ * multiple times in order to build multiple related requests that differ in
+ * some parameters.
  *
  * <p> Two simple, example HTTP interactions are shown below:
  * <pre>
@@ -79,7 +86,7 @@ import java.util.function.Supplier;
  *          HttpRequest
  *              .newBuilder(new URI("http://www.foo.com/"))
  *              .headers("Foo", "foovalue", "Bar", "barvalue")
- *              .POST(BodyProcessor.fromString("Hello world"))
+ *              .POST(BodyPublisher.fromString("Hello world"))
  *              .build(),
  *          BodyHandler.asFile(Paths.get("/path"))
  *      );
@@ -87,6 +94,7 @@ import java.util.function.Supplier;
  *      Path body = response.body(); // should be "/path"
  * }
  * </pre>
+ *
  * <p> The request is sent and the response obtained by calling one of the
  * following methods in {@link HttpClient}.
  * <ul><li>{@link HttpClient#send(HttpRequest, HttpResponse.BodyHandler)} blocks
@@ -95,7 +103,7 @@ import java.util.function.Supplier;
  * request and receives the response asynchronously. Returns immediately with a
  * {@link java.util.concurrent.CompletableFuture CompletableFuture}&lt;{@link
  * HttpResponse}&gt;.</li>
- * <li>{@link HttpClient#sendAsync(HttpRequest,HttpResponse.MultiProcessor) }
+ * <li>{@link HttpClient#sendAsync(HttpRequest, HttpResponse.MultiSubscriber) }
  * sends the request asynchronously, expecting multiple responses. This
  * capability is of most relevance to HTTP/2 server push, but can be used for
  * single responses (HTTP/1.1 or HTTP/2) also.</li>
@@ -109,34 +117,36 @@ import java.util.function.Supplier;
  *
  * <p> <b>Request bodies</b>
  *
- * <p> Request bodies are sent using one of the request processor implementations
- * below provided in {@link HttpRequest.BodyProcessor}, or else a custom implementation can be
- * used.
+ * <p> Request bodies can be sent using one of the convenience request publisher
+ * implementations below, provided in {@link BodyPublisher}. Alternatively, a
+ * custom Publisher implementation can be used.
  * <ul>
- * <li>{@link BodyProcessor#fromByteArray(byte[]) fromByteArray(byte[])} from byte array</li>
- * <li>{@link BodyProcessor#fromByteArrays(Iterable) fromByteArrays(Iterable)}
+ * <li>{@link BodyPublisher#fromByteArray(byte[]) fromByteArray(byte[])} from byte array</li>
+ * <li>{@link BodyPublisher#fromByteArrays(Iterable) fromByteArrays(Iterable)}
  *      from an Iterable of byte arrays</li>
- * <li>{@link BodyProcessor#fromFile(java.nio.file.Path) fromFile(Path)} from the file located
+ * <li>{@link BodyPublisher#fromFile(java.nio.file.Path) fromFile(Path)} from the file located
  *     at the given Path</li>
- * <li>{@link BodyProcessor#fromString(java.lang.String) fromString(String)} from a String </li>
- * <li>{@link BodyProcessor#fromInputStream(Supplier) fromInputStream}({@link Supplier}&lt;
+ * <li>{@link BodyPublisher#fromString(java.lang.String) fromString(String)} from a String </li>
+ * <li>{@link BodyPublisher#fromInputStream(Supplier) fromInputStream}({@link Supplier}&lt;
  *      {@link InputStream}&gt;) from an InputStream obtained from a Supplier</li>
- * <li>{@link BodyProcessor#noBody() } no request body is sent</li>
+ * <li>{@link BodyPublisher#noBody() } no request body is sent</li>
  * </ul>
  *
  * <p> <b>Response bodies</b>
  *
- * <p>Responses bodies are handled at two levels. When sending the request,
- * a response body handler is specified. This is a function ({@link HttpResponse.BodyHandler})
- * which will be called with the response status code and headers, once these are received. This
- * function is then expected to return a {@link HttpResponse.BodyProcessor}
- * {@code <T>} which is then used to read the response body converting it
- * into an instance of T. After this occurs, the response becomes
- * available in a {@link HttpResponse} and {@link HttpResponse#body()} can then
- * be called to obtain the body. Some implementations and examples of usage of both {@link
- * HttpResponse.BodyProcessor} and {@link HttpResponse.BodyHandler}
- * are provided in {@link HttpResponse}:
- * <p><b>Some of the pre-defined body handlers</b><br>
+ * <p> Responses bodies are handled at two levels. When sending the request,
+ * a response body handler is specified. This is a function ({@linkplain
+ * HttpResponse.BodyHandler}) which will be called with the response status code
+ * and headers, once they are received. This function is then expected to return
+ * a {@link HttpResponse.BodySubscriber}{@code <T>} which is then used to read
+ * the response body, converting it into an instance of T. After this occurs,
+ * the response becomes available in a {@link HttpResponse}, and {@link
+ * HttpResponse#body()} can then be called to obtain the actual body. Some
+ * implementations and examples of usage of both {@link
+ * HttpResponse.BodySubscriber} and {@link HttpResponse.BodyHandler} are
+ * provided in {@link HttpResponse}:
+ *
+ * <p> <b>Some of the pre-defined body handlers</b><br>
  * <ul>
  * <li>{@link HttpResponse.BodyHandler#asByteArray() BodyHandler.asByteArray()}
  * stores the body in a byte array</li>
@@ -152,8 +162,8 @@ import java.util.function.Supplier;
  *
  * <p> With HTTP/2 it is possible for a server to return a main response and zero
  * or more additional responses (known as server pushes) to a client-initiated
- * request. These are handled using a special response processor called {@link
- * HttpResponse.MultiProcessor}.
+ * request. These are handled using a special response subscriber called {@link
+ * HttpResponse.MultiSubscriber}.
  *
  * <p> <b>Blocking/asynchronous behavior and thread usage</b>
  *
@@ -161,29 +171,38 @@ import java.util.function.Supplier;
  * <i>asynchronous</i>. {@link HttpClient#send(HttpRequest, HttpResponse.BodyHandler) }
  * blocks the calling thread until the request has been sent and the response received.
  *
- * <p> {@link HttpClient#sendAsync(HttpRequest, HttpResponse.BodyHandler)}  is asynchronous and returns
- * immediately with a {@link java.util.concurrent.CompletableFuture}&lt;{@link
- * HttpResponse}&gt; and when this object completes (in a background thread) the
- * response has been received.
+ * <p> {@link HttpClient#sendAsync(HttpRequest, HttpResponse.BodyHandler)} is
+ * asynchronous and returns immediately with a {@link CompletableFuture}&lt;{@link
+ * HttpResponse}&gt; and when this object completes (possibly in a different
+ * thread) the response has been received.
  *
- * <p> {@link HttpClient#sendAsync(HttpRequest,HttpResponse.MultiProcessor)}
+ * <p> {@link HttpClient#sendAsync(HttpRequest, HttpResponse.MultiSubscriber)}
  * is the variant for multi responses and is also asynchronous.
  *
- * <p> {@code CompletableFuture}s can be combined in different ways to declare the
- * dependencies among several asynchronous tasks, while allowing for the maximum
- * level of parallelism to be utilized.
+ * <p> Instances of {@code CompletableFuture} can be combined in different ways
+ * to declare the dependencies among several asynchronous tasks, while allowing
+ * for the maximum level of parallelism to be utilized.
  *
- * <p> <b>Security checks</b>
+ * <p> <a id="securitychecks"></a><b>Security checks</b></a>
  *
  * <p> If a security manager is present then security checks are performed by
- * the sending methods. A {@link java.net.URLPermission} or {@link java.net.SocketPermission} is required to
- * access any destination origin server and proxy server utilised. {@code URLPermission}s
- * should be preferred in policy files over {@code SocketPermission}s given the more
- * limited scope of {@code URLPermission}. Permission is always implicitly granted to a
- * system's default proxies. The {@code URLPermission} form used to access proxies uses
- * a method parameter of {@code "CONNECT"} (for all kinds of proxying) and a url string
- * of the form {@code "socket://host:port"} where host and port specify the proxy's
- * address.
+ * the HTTP Client's sending methods. An appropriate {@link URLPermission} is
+ * required to access the destination server, and proxy server if one has
+ * been configured. The {@code URLPermission} form used to access proxies uses a
+ * method parameter of {@code "CONNECT"} (for all kinds of proxying) and a URL
+ * string  of the form {@code "socket://host:port"} where host and port specify
+ * the proxy's address.
+ *
+ * <p> In this implementation, if an explicit {@linkplain
+ * HttpClient.Builder#executor(Executor) executor} has not been set for an
+ * {@code HttpClient}, and a security manager has been installed, then the
+ * default executor will execute asynchronous and dependent tasks in a context
+ * that is granted no permissions. Custom {@linkplain HttpRequest.BodyPublisher
+ * request body publishers}, {@linkplain HttpResponse.BodyHandler response body
+ * handlers}, {@linkplain HttpResponse.BodySubscriber response body subscribers},
+ * and {@linkplain WebSocket.Listener WebSocket Listeners}, if executing
+ * operations that require privileges, should do so  within an appropriate
+ * {@linkplain AccessController#doPrivileged(PrivilegedAction) privileged context}.
  *
  * <p> <b>Examples</b>
  * <pre>{@code
@@ -193,7 +212,7 @@ import java.util.function.Supplier;
  *
  *      HttpRequest request = HttpRequest
  *              .newBuilder(new URI("http://www.foo.com/"))
- *              .POST(BodyProcessor.fromString("Hello world"))
+ *              .POST(BodyPublisher.fromString("Hello world"))
  *              .build();
  *
  *      HttpResponse<Path> response =
@@ -239,8 +258,8 @@ import java.util.function.Supplier;
  *      // Use File.exists() to check whether file was successfully downloaded
  * }
  * </pre>
- * <p>
- * Unless otherwise stated, {@code null} parameter values will cause methods
+ *
+ * <p> Unless otherwise stated, {@code null} parameter values will cause methods
  * of this class to throw {@code NullPointerException}.
  *
  * @since 9
@@ -253,17 +272,18 @@ public abstract class HttpRequest {
     protected HttpRequest() {}
 
     /**
-     * A builder of {@link HttpRequest}s.
+     * A builder of {@linkplain HttpRequest HTTP Requests}.
      * {@Incubating}
      *
-     * <p> {@code HttpRequest.Builder}s are created by calling {@link
+     * <p> Instances of {@code HttpRequest.Builder} are created by calling {@link
      * HttpRequest#newBuilder(URI)} or {@link HttpRequest#newBuilder()}.
      *
      * <p> Each of the setter methods in this class modifies the state of the
      * builder and returns <i>this</i> (ie. the same instance). The methods are
      * not synchronized and should not be called from multiple threads without
      * external synchronization.
-     * <p>Note, that not all request headers may be set by user code. Some are
+     *
+     * <p> Note, that not all request headers may be set by user code. Some are
      * restricted for security reasons and others such as the headers relating
      * to authentication, redirection and cookie management are managed by
      * specific APIs rather than through directly user set headers.
@@ -286,16 +306,17 @@ public abstract class HttpRequest {
          * @param uri the request URI
          * @return this request builder
          * @throws IllegalArgumentException if the {@code URI} scheme is not
-         *         supported.
+         *         supported
          */
         public abstract Builder uri(URI uri);
 
         /**
-         * Request server to acknowledge request before sending request
-         * body. This is disabled by default. If enabled, the server is requested
-         * to send an error response or a {@code 100 Continue} response before the client
-         * sends the request body. This means the request processor for the
-         * request will not be invoked until this interim response is received.
+         * Requests the server to acknowledge the request before sending the
+         * body. This is disabled by default. If enabled, the server is
+         * requested to send an error response or a {@code 100 Continue}
+         * response before the client sends the request body. This means the
+         * request publisher for the request will not be invoked until this
+         * interim response is received.
          *
          * @param enable {@code true} if Expect continue to be sent
          * @return this request builder
@@ -303,11 +324,12 @@ public abstract class HttpRequest {
         public abstract Builder expectContinue(boolean enable);
 
         /**
-         * Sets the preferred {@link HttpClient.Version} for this
-         * request. The corresponding {@link HttpResponse} should be checked
-         * for the version that was used. If the version is not set
-         * in a request, then the version requested will be that of the
-         * sending {@link HttpClient}.
+         * Sets the preferred {@link HttpClient.Version} for this request.
+         *
+         * <p> The corresponding {@link HttpResponse} should be checked for the
+         * version that was actually used. If the version is not set in a
+         * request, then the version requested will be that of the sending
+         * {@link HttpClient}.
          *
          * @param version the HTTP protocol version requested
          * @return this request builder
@@ -316,33 +338,31 @@ public abstract class HttpRequest {
 
         /**
          * Adds the given name value pair to the set of headers for this request.
+         * The given value is added to the list of values for that name.
          *
          * @param name the header name
          * @param value the header value
          * @return this request builder
+         * @throws IllegalArgumentException if the header name or value is not
+         *         valid, see <a href="https://tools.ietf.org/html/rfc7230#section-3.2">
+         *         RFC 7230 section-3.2</a>
          */
         public abstract Builder header(String name, String value);
 
-//        /**
-//         * Overrides the {@code ProxySelector} set on the request's client for this
-//         * request.
-//         *
-//         * @param proxy the ProxySelector to use
-//         * @return this request builder
-//         */
-//        public abstract Builder proxy(ProxySelector proxy);
-
         /**
          * Adds the given name value pairs to the set of headers for this
-         * request. The supplied {@code String}s must alternate as names and values.
+         * request. The supplied {@code String} instances must alternate as
+         * header names and header values.
+         * To add several values to the same name then the same name must
+         * be supplied with each new value.
          *
-         * @param headers the list of String name value pairs
+         * @param headers the list of name value pairs
          * @return this request builder
-         * @throws IllegalArgumentException if there is an odd number of
-         *                                  parameters
+         * @throws IllegalArgumentException if there are an odd number of
+         *         parameters, or if a header name or value is not valid, see
+         *         <a href="https://tools.ietf.org/html/rfc7230#section-3.2">
+         *         RFC 7230 section-3.2</a>
          */
-        // TODO (spec): consider signature change
-        // public abstract Builder headers(java.util.Map.Entry<String,String>... headers);
         public abstract Builder headers(String... headers);
 
         /**
@@ -358,6 +378,7 @@ public abstract class HttpRequest {
          *
          * @param duration the timeout duration
          * @return this request builder
+         * @throws IllegalArgumentException if the duration is non-positive
          */
         public abstract Builder timeout(Duration duration);
 
@@ -368,11 +389,15 @@ public abstract class HttpRequest {
          * @param name the header name
          * @param value the header value
          * @return this request builder
+         * @throws IllegalArgumentException if the header name or value is not valid,
+         *         see <a href="https://tools.ietf.org/html/rfc7230#section-3.2">
+         *         RFC 7230 section-3.2</a>
          */
         public abstract Builder setHeader(String name, String value);
 
         /**
          * Sets the request method of this builder to GET.
+         * This is the default.
          *
          * @return a {@code HttpRequest}
          */
@@ -380,57 +405,62 @@ public abstract class HttpRequest {
 
         /**
          * Sets the request method of this builder to POST and sets its
-         * request body processor to the given value.
+         * request body publisher to the given value.
          *
-         * @param body the body processor
+         * @param bodyPublisher the body publisher
          *
          * @return a {@code HttpRequest}
          */
-        public abstract Builder POST(BodyProcessor body);
+        public abstract Builder POST(BodyPublisher bodyPublisher);
 
         /**
          * Sets the request method of this builder to PUT and sets its
-         * request body processor to the given value.
+         * request body publisher to the given value.
          *
-         * @param body the body processor
+         * @param bodyPublisher the body publisher
          *
          * @return a {@code HttpRequest}
          */
-        public abstract Builder PUT(BodyProcessor body);
+        public abstract Builder PUT(BodyPublisher bodyPublisher);
 
         /**
          * Sets the request method of this builder to DELETE and sets its
-         * request body processor to the given value.
+         * request body publisher to the given value.
          *
-         * @param body the body processor
+         * @param bodyPublisher the body publisher
          *
          * @return a {@code HttpRequest}
          */
 
-        public abstract Builder DELETE(BodyProcessor body);
+        public abstract Builder DELETE(BodyPublisher bodyPublisher);
 
         /**
          * Sets the request method and request body of this builder to the
          * given values.
          *
-         * @param body the body processor
+         * @apiNote The {@linkplain BodyPublisher#noBody() noBody} request
+         * body publisher can be used where no request body is required or
+         * appropriate.
+         *
          * @param method the method to use
+         * @param bodyPublisher the body publisher
          * @return a {@code HttpRequest}
-         * @throws IllegalArgumentException if an unrecognized method is used
+         * @throws IllegalArgumentException if the method is unrecognised
          */
-        public abstract Builder method(String method, BodyProcessor body);
+        public abstract Builder method(String method, BodyPublisher bodyPublisher);
 
         /**
          * Builds and returns a {@link HttpRequest}.
          *
          * @return the request
+         * @throws IllegalStateException if a URI has not been set
          */
         public abstract HttpRequest build();
 
         /**
-         * Returns an exact duplicate copy of this {@code Builder} based on current
-         * state. The new builder can then be modified independently of this
-         * builder.
+         * Returns an exact duplicate copy of this {@code Builder} based on
+         * current state. The new builder can then be modified independently of
+         * this builder.
          *
          * @return an exact copy of this Builder
          */
@@ -458,14 +488,13 @@ public abstract class HttpRequest {
     }
 
     /**
-     * Returns an {@code Optional} containing the {@link BodyProcessor}
-     * set on this request. If no {@code BodyProcessor} was set in the
-     * requests's builder, then the {@code Optional} is empty.
+     * Returns an {@code Optional} containing the {@link BodyPublisher} set on
+     * this request. If no {@code BodyPublisher} was set in the requests's
+     * builder, then the {@code Optional} is empty.
      *
-     * @return an {@code Optional} containing this request's
-     *         {@code BodyProcessor}
+     * @return an {@code Optional} containing this request's {@code BodyPublisher}
      */
-    public abstract Optional<BodyProcessor> bodyProcessor();
+    public abstract Optional<BodyPublisher> bodyPublisher();
 
     /**
      * Returns the request method for this request. If not set explicitly,
@@ -476,11 +505,13 @@ public abstract class HttpRequest {
     public abstract String method();
 
     /**
-     * Returns the duration for this request.
+     * Returns an {@code Optional} containing this request's timeout duration.
+     * If the timeout duration was not set in the request's builder, then the
+     * {@code Optional} is empty.
      *
-     * @return this requests duration
+     * @return an {@code Optional} containing this request's timeout duration
      */
-    public abstract Duration duration();
+    public abstract Optional<Duration> timeout();
 
     /**
      * Returns this request's {@link HttpRequest.Builder#expectContinue(boolean)
@@ -517,140 +548,203 @@ public abstract class HttpRequest {
      */
     public abstract HttpHeaders headers();
 
-
     /**
-     * A request body handler which sends no request body.
+     * Tests this HTTP request instance for equality with the given object.
      *
-     * @return a BodyProcessor
+     * <p> If the given object is not an {@code HttpRequest} then this
+     * method returns {@code false}. Two HTTP requests are equal if their URI,
+     * method, and headers fields are all equal.
+     *
+     * <p> This method satisfies the general contract of the {@link
+     * Object#equals(Object) Object.equals} method.
+     *
+     * @param obj the object to which this object is to be compared
+     * @return {@code true} if, and only if, the given object is an {@code
+     *         HttpRequest} that is equal to this HTTP request
      */
-    public static BodyProcessor noBody() {
-        return new RequestProcessors.EmptyProcessor();
+    @Override
+    public final boolean equals(Object obj) {
+       if (! (obj instanceof HttpRequest))
+           return false;
+       HttpRequest that = (HttpRequest)obj;
+       if (!that.method().equals(this.method()))
+           return false;
+       if (!that.uri().equals(this.uri()))
+           return false;
+       if (!that.headers().equals(this.headers()))
+           return false;
+       return true;
     }
 
     /**
-     * A processor which converts high level Java objects into flows of
-     * {@link java.nio.ByteBuffer}s suitable for sending as request bodies.
+     * Computes a hash code for this HTTP request instance.
+     *
+     * <p> The hash code is based upon the HTTP request's URI, method, and
+     * header components, and satisfies the general contract of the
+     * {@link Object#hashCode Object.hashCode} method.
+     *
+     * @return the hash-code value for this HTTP request
+     */
+    public final int hashCode() {
+        return method().hashCode()
+                + uri().hashCode()
+                + headers().hashCode();
+    }
+
+    /**
+     * A Publisher which converts high level Java objects into flows of
+     * byte buffers suitable for sending as request bodies.
      * {@Incubating}
-     * <p>
-     * {@code BodyProcessor}s implement {@link Flow.Publisher} which means they
-     * act as a publisher of byte buffers.
-     * <p>
-     * The HTTP client implementation subscribes to the processor in
-     * order to receive the flow of outgoing data buffers. The normal semantics
-     * of {@link Flow.Subscriber} and {@link Flow.Publisher} are implemented
-     * by the library and expected from processor implementations.
-     * Each outgoing request results in one {@code Subscriber} subscribing to the
-     * {@code Publisher} in order to provide the sequence of {@code ByteBuffer}s containing
-     * the request body. {@code ByteBuffer}s must be allocated by the processor,
-     * and must not be accessed after being handed over to the library.
-     * These subscriptions complete normally when the request is fully
-     * sent, and can be canceled or terminated early through error. If a request
+     *
+     * <p> The {@code BodyPublisher} class implements {@link Flow.Publisher
+     * Flow.Publisher&lt;ByteBuffer&gt;} which means that a {@code BodyPublisher}
+     * acts as a publisher of {@linkplain ByteBuffer byte buffers}.
+     *
+     * <p> The HTTP client implementation subscribes to the publisher in order
+     * to receive the flow of outgoing data buffers. The normal semantics of
+     * {@link Flow.Subscriber} and {@link Flow.Publisher} are implemented by the
+     * library and are expected from publisher implementations. Each outgoing
+     * request results in one {@code Subscriber} subscribing to the {@code
+     * BodyPublisher} in order to provide the sequence of byte buffers
+     * containing the request body.
+     * Instances of {@code ByteBuffer} published  by the publisher must be
+     * allocated by the publisher, and must not be accessed after being handed
+     * over to the library.
+     * These subscriptions complete normally when the request is fully sent,
+     * and can be canceled or terminated early through error. If a request
      * needs to be resent for any reason, then a new subscription is created
      * which is expected to generate the same data as before.
+     *
+     * <p> A publisher that reports a {@linkplain #contentLength() content
+     * length} of {@code 0} may not be subscribed to by the HTTP client
+     * implementation, as it has effectively no data to publish.
      */
-    public interface BodyProcessor extends Flow.Publisher<ByteBuffer> {
+    public interface BodyPublisher extends Flow.Publisher<ByteBuffer> {
 
         /**
-         * Returns a request body processor whose body is the given {@code String},
-         * converted using the {@link java.nio.charset.StandardCharsets#UTF_8 UTF_8}
+         * Returns a request body publisher whose body is the given {@code
+         * String}, converted using the {@link StandardCharsets#UTF_8 UTF_8}
          * character set.
          *
          * @param body the String containing the body
-         * @return a BodyProcessor
+         * @return a BodyPublisher
          */
-        static BodyProcessor fromString(String body) {
-            return fromString(body, StandardCharsets.UTF_8);
+        static BodyPublisher fromString(String body) {
+            return fromString(body, UTF_8);
         }
 
         /**
-         * Returns a request body processor whose body is the given {@code String}, converted
-         * using the given character set.
+         * Returns a request body publisher whose body is the given {@code
+         * String}, converted using the given character set.
          *
          * @param s the String containing the body
          * @param charset the character set to convert the string to bytes
-         * @return a BodyProcessor
+         * @return a BodyPublisher
          */
-        static BodyProcessor fromString(String s, Charset charset) {
-            return new RequestProcessors.StringProcessor(s, charset);
+        static BodyPublisher fromString(String s, Charset charset) {
+            return new RequestPublishers.StringPublisher(s, charset);
         }
 
         /**
-         * A request body processor that reads its data from an {@link java.io.InputStream}.
-         * A {@link Supplier} of {@code InputStream} is used in case the request needs
-         * to be sent again as the content is not buffered. The {@code Supplier} may return
-         * {@code null} on subsequent attempts in which case, the request fails.
+         * A request body publisher that reads its data from an {@link
+         * InputStream}. A {@link Supplier} of {@code InputStream} is used in
+         * case the request needs to be repeated, as the content is not buffered.
+         * The {@code Supplier} may return {@code null} on subsequent attempts,
+         * in which case the request fails.
          *
          * @param streamSupplier a Supplier of open InputStreams
-         * @return a BodyProcessor
+         * @return a BodyPublisher
          */
         // TODO (spec): specify that the stream will be closed
-        static BodyProcessor fromInputStream(Supplier<? extends InputStream> streamSupplier) {
-            return new RequestProcessors.InputStreamProcessor(streamSupplier);
+        static BodyPublisher fromInputStream(Supplier<? extends InputStream> streamSupplier) {
+            return new RequestPublishers.InputStreamPublisher(streamSupplier);
         }
 
         /**
-         * Returns a request body processor whose body is the given byte array.
+         * Returns a request body publisher whose body is the given byte array.
          *
          * @param buf the byte array containing the body
-         * @return a BodyProcessor
+         * @return a BodyPublisher
          */
-        static BodyProcessor fromByteArray(byte[] buf) {
-            return new RequestProcessors.ByteArrayProcessor(buf);
+        static BodyPublisher fromByteArray(byte[] buf) {
+            return new RequestPublishers.ByteArrayPublisher(buf);
         }
 
         /**
-         * Returns a request body processor whose body is the content of the given byte
-         * array of {@code length} bytes starting from the specified
+         * Returns a request body publisher whose body is the content of the
+         * given byte array of {@code length} bytes starting from the specified
          * {@code offset}.
          *
          * @param buf the byte array containing the body
          * @param offset the offset of the first byte
          * @param length the number of bytes to use
-         * @return a BodyProcessor
+         * @return a BodyPublisher
+         * @throws IndexOutOfBoundsException if the sub-range is defined to be
+         *                                   out-of-bounds
          */
-        static BodyProcessor fromByteArray(byte[] buf, int offset, int length) {
-            return new RequestProcessors.ByteArrayProcessor(buf, offset, length);
+        static BodyPublisher fromByteArray(byte[] buf, int offset, int length) {
+            Objects.checkFromIndexSize(offset, length, buf.length);
+            return new RequestPublishers.ByteArrayPublisher(buf, offset, length);
+        }
+
+        private static String pathForSecurityCheck(Path path) {
+            return path.toFile().getPath();
         }
 
         /**
-         * A request body processor that takes data from the contents of a File.
+         * A request body publisher that takes data from the contents of a File.
          *
          * @param path the path to the file containing the body
-         * @return a BodyProcessor
-         * @throws java.io.FileNotFoundException if path not found
+         * @return a BodyPublisher
+         * @throws java.io.FileNotFoundException if the path is not found
+         * @throws SecurityException if a security manager has been installed
+         *          and it denies {@link SecurityManager#checkRead(String)
+         *          read access} to the given file
          */
-        static BodyProcessor fromFile(Path path) throws FileNotFoundException {
-            return new RequestProcessors.FileProcessor(path);
+        static BodyPublisher fromFile(Path path) throws FileNotFoundException {
+            Objects.requireNonNull(path);
+            SecurityManager sm = System.getSecurityManager();
+            if (sm != null)
+                sm.checkRead(pathForSecurityCheck(path));
+            if (Files.notExists(path))
+                throw new FileNotFoundException(path + " not found");
+            return new RequestPublishers.FilePublisher(path);
         }
 
         /**
-         * A request body processor that takes data from an {@code Iterable} of byte arrays.
-         * An {@link Iterable} is provided which supplies {@link Iterator} instances.
-         * Each attempt to send the request results in one invocation of the
-         * {@code Iterable}
+         * A request body publisher that takes data from an {@code Iterable}
+         * of byte arrays. An {@link Iterable} is provided which supplies
+         * {@link Iterator} instances. Each attempt to send the request results
+         * in one invocation of the {@code Iterable}.
          *
          * @param iter an Iterable of byte arrays
-         * @return a BodyProcessor
+         * @return a BodyPublisher
          */
-        static BodyProcessor fromByteArrays(Iterable<byte[]> iter) {
-            return new RequestProcessors.IterableProcessor(iter);
+        static BodyPublisher fromByteArrays(Iterable<byte[]> iter) {
+            return new RequestPublishers.IterablePublisher(iter);
         }
+
+        /**
+         * A request body publisher which sends no request body.
+         *
+         * @return a BodyPublisher which completes immediately and sends
+         *         no request body.
+         */
+        static BodyPublisher noBody() {
+            return new RequestPublishers.EmptyPublisher();
+        }
+
         /**
          * Returns the content length for this request body. May be zero
-         * if no request content being sent, greater than zero for a fixed
-         * length content, and less than zero for an unknown content length.
+         * if no request body being sent, greater than zero for a fixed
+         * length content, or less than zero for an unknown content length.
          *
-         * @return the content length for this request body if known
+         * This method may be invoked before the publisher is subscribed to.
+         * This method may be invoked more than once by the HTTP client
+         * implementation, and MUST return the same constant value each time.
+         *
+         * @return the content length for this request body, if known
          */
         long contentLength();
-
-//        /**
-//         * Returns a used {@code ByteBuffer} to this request processor. When the
-//         * HTTP implementation has finished sending the contents of a buffer,
-//         * this method is called to return it to the processor for re-use.
-//         *
-//         * @param buffer a used ByteBuffer
-//         */
-        //void returnBuffer(ByteBuffer buffer);
     }
 }
diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/HttpRequestBuilderImpl.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/HttpRequestBuilderImpl.java
index ed0426943b2..44d47beb672 100644
--- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/HttpRequestBuilderImpl.java
+++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/HttpRequestBuilderImpl.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2017, 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
@@ -26,11 +26,12 @@
 package jdk.incubator.http;
 
 import java.net.URI;
-import jdk.incubator.http.HttpRequest.BodyProcessor;
 import java.time.Duration;
 import java.util.Optional;
-import static java.util.Objects.requireNonNull;
+import jdk.incubator.http.HttpRequest.BodyPublisher;
 import jdk.incubator.http.internal.common.HttpHeadersImpl;
+import static java.lang.String.format;
+import static java.util.Objects.requireNonNull;
 import static jdk.incubator.http.internal.common.Utils.isValidName;
 import static jdk.incubator.http.internal.common.Utils.isValidValue;
 
@@ -39,16 +40,13 @@ class HttpRequestBuilderImpl extends HttpRequest.Builder {
     private HttpHeadersImpl userHeaders;
     private URI uri;
     private String method;
-    //private HttpClient.Redirect followRedirects;
     private boolean expectContinue;
-    private HttpRequest.BodyProcessor body;
+    private BodyPublisher bodyPublisher;
     private volatile Optional<HttpClient.Version> version;
-    //private final HttpClientImpl client;
-    //private ProxySelector proxy;
     private Duration duration;
 
     public HttpRequestBuilderImpl(URI uri) {
-        //this.client = client;
+        requireNonNull(uri, "uri must be non-null");
         checkURI(uri);
         this.uri = uri;
         this.userHeaders = new HttpHeadersImpl();
@@ -58,31 +56,66 @@ class HttpRequestBuilderImpl extends HttpRequest.Builder {
 
     public HttpRequestBuilderImpl() {
         this.userHeaders = new HttpHeadersImpl();
+        this.method = "GET"; // default, as per spec
         this.version = Optional.empty();
     }
 
     @Override
     public HttpRequestBuilderImpl uri(URI uri) {
-        requireNonNull(uri);
+        requireNonNull(uri, "uri must be non-null");
         checkURI(uri);
         this.uri = uri;
         return this;
     }
 
+    private static IllegalArgumentException newIAE(String message, Object... args) {
+        return new IllegalArgumentException(format(message, args));
+    }
+
     private static void checkURI(URI uri) {
-        String scheme = uri.getScheme().toLowerCase();
-        if (!scheme.equals("https") && !scheme.equals("http")) {
-            throw new IllegalArgumentException("invalid URI scheme");
+        String scheme = uri.getScheme();
+        if (scheme == null)
+            throw newIAE("URI with undefined scheme");
+        scheme = scheme.toLowerCase();
+        if (!(scheme.equals("https") || scheme.equals("http"))) {
+            throw newIAE("invalid URI scheme %s", scheme);
+        }
+        if (uri.getHost() == null) {
+            throw newIAE("unsupported URI %s", uri);
         }
     }
-/*
+
     @Override
-    public HttpRequestBuilderImpl followRedirects(HttpClient.Redirect follow) {
-        requireNonNull(follow);
-        this.followRedirects = follow;
+    public HttpRequestBuilderImpl copy() {
+        HttpRequestBuilderImpl b = new HttpRequestBuilderImpl(this.uri);
+        b.userHeaders = this.userHeaders.deepCopy();
+        b.method = this.method;
+        b.expectContinue = this.expectContinue;
+        b.bodyPublisher = bodyPublisher;
+        b.uri = uri;
+        b.duration = duration;
+        b.version = version;
+        return b;
+    }
+
+    private void checkNameAndValue(String name, String value) {
+        requireNonNull(name, "name");
+        requireNonNull(value, "value");
+        if (!isValidName(name)) {
+            throw newIAE("invalid header name:", name);
+        }
+        if (!isValidValue(value)) {
+            throw newIAE("invalid header value:%s", value);
+        }
+    }
+
+    @Override
+    public HttpRequestBuilderImpl setHeader(String name, String value) {
+        checkNameAndValue(name, value);
+        userHeaders.setHeader(name, value);
         return this;
     }
-*/
+
     @Override
     public HttpRequestBuilderImpl header(String name, String value) {
         checkNameAndValue(name, value);
@@ -93,8 +126,8 @@ class HttpRequestBuilderImpl extends HttpRequest.Builder {
     @Override
     public HttpRequestBuilderImpl headers(String... params) {
         requireNonNull(params);
-        if (params.length % 2 != 0) {
-            throw new IllegalArgumentException("wrong number of parameters");
+        if (params.length == 0 || params.length % 2 != 0) {
+            throw newIAE("wrong number, %d, of parameters", params.length);
         }
         for (int i = 0; i < params.length; i += 2) {
             String name  = params[i];
@@ -104,45 +137,6 @@ class HttpRequestBuilderImpl extends HttpRequest.Builder {
         return this;
     }
 
-    /*
-    @Override
-    public HttpRequestBuilderImpl proxy(ProxySelector proxy) {
-        requireNonNull(proxy);
-        this.proxy = proxy;
-        return this;
-    }
-*/
-    @Override
-    public HttpRequestBuilderImpl copy() {
-        HttpRequestBuilderImpl b = new HttpRequestBuilderImpl(this.uri);
-        b.userHeaders = this.userHeaders.deepCopy();
-        b.method = this.method;
-        //b.followRedirects = this.followRedirects;
-        b.expectContinue = this.expectContinue;
-        b.body = body;
-        b.uri = uri;
-        //b.proxy = proxy;
-        return b;
-    }
-
-    @Override
-    public HttpRequestBuilderImpl setHeader(String name, String value) {
-        checkNameAndValue(name, value);
-        userHeaders.setHeader(name, value);
-        return this;
-    }
-
-    private void checkNameAndValue(String name, String value) {
-        requireNonNull(name, "name");
-        requireNonNull(value, "value");
-        if (!isValidName(name)) {
-            throw new IllegalArgumentException("invalid header name");
-        }
-        if (!isValidValue(value)) {
-            throw new IllegalArgumentException("invalid header value");
-        }
-    }
-
     @Override
     public HttpRequestBuilderImpl expectContinue(boolean enable) {
         expectContinue = enable;
@@ -158,49 +152,60 @@ class HttpRequestBuilderImpl extends HttpRequest.Builder {
 
     HttpHeadersImpl headers() {  return userHeaders; }
 
-    //HttpClientImpl client() {return client;}
-
     URI uri() { return uri; }
 
     String method() { return method; }
 
-    //HttpClient.Redirect followRedirects() { return followRedirects; }
-
-    //ProxySelector proxy() { return proxy; }
-
     boolean expectContinue() { return expectContinue; }
 
-    public HttpRequest.BodyProcessor body() { return body; }
+    BodyPublisher bodyPublisher() { return bodyPublisher; }
 
     Optional<HttpClient.Version> version() { return version; }
 
     @Override
-    public HttpRequest.Builder GET() { return method("GET", null); }
-
-    @Override
-    public HttpRequest.Builder POST(BodyProcessor body) {
-        return method("POST", body);
+    public HttpRequest.Builder GET() {
+        return method0("GET", null);
     }
 
     @Override
-    public HttpRequest.Builder DELETE(BodyProcessor body) {
-        return method("DELETE", body);
+    public HttpRequest.Builder POST(BodyPublisher body) {
+        return method0("POST", requireNonNull(body));
     }
 
     @Override
-    public HttpRequest.Builder PUT(BodyProcessor body) {
-        return method("PUT", body);
+    public HttpRequest.Builder DELETE(BodyPublisher body) {
+        return method0("DELETE", requireNonNull(body));
     }
 
     @Override
-    public HttpRequest.Builder method(String method, BodyProcessor body) {
-        this.method = requireNonNull(method);
-        this.body = body;
+    public HttpRequest.Builder PUT(BodyPublisher body) {
+        return method0("PUT", requireNonNull(body));
+    }
+
+    @Override
+    public HttpRequest.Builder method(String method, BodyPublisher body) {
+        requireNonNull(method);
+        if (method.equals(""))
+            throw newIAE("illegal method <empty string>");
+        if (method.equals("CONNECT"))
+            throw newIAE("method CONNECT is not supported");
+        return method0(method, requireNonNull(body));
+    }
+
+    private HttpRequest.Builder method0(String method, BodyPublisher body) {
+        assert method != null;
+        assert !method.equals("GET") ? body != null : true;
+        assert !method.equals("");
+        this.method = method;
+        this.bodyPublisher = body;
         return this;
     }
 
     @Override
     public HttpRequest build() {
+        if (uri == null)
+            throw new IllegalStateException("uri is null");
+        assert method != null;
         return new HttpRequestImpl(this);
     }
 
@@ -213,6 +218,6 @@ class HttpRequestBuilderImpl extends HttpRequest.Builder {
         return this;
     }
 
-    Duration duration() { return duration; }
+    Duration timeout() { return duration; }
 
 }
diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/HttpRequestImpl.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/HttpRequestImpl.java
index eb6a1cdfd4e..9ebb29c5e24 100644
--- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/HttpRequestImpl.java
+++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/HttpRequestImpl.java
@@ -30,10 +30,14 @@ import jdk.incubator.http.internal.websocket.WebSocketRequest;
 
 import java.io.IOException;
 import java.net.InetSocketAddress;
+import java.net.Proxy;
 import java.net.ProxySelector;
 import java.net.URI;
 import java.security.AccessControlContext;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
 import java.time.Duration;
+import java.util.List;
 import java.util.Locale;
 import java.util.Optional;
 
@@ -44,42 +48,50 @@ class HttpRequestImpl extends HttpRequest implements WebSocketRequest {
     private final HttpHeaders userHeaders;
     private final HttpHeadersImpl systemHeaders;
     private final URI uri;
+    private Proxy proxy;
     private InetSocketAddress authority; // only used when URI not specified
     private final String method;
-    final BodyProcessor requestProcessor;
+    final BodyPublisher requestPublisher;
     final boolean secure;
     final boolean expectContinue;
     private boolean isWebSocket;
     private AccessControlContext acc;
-    private final Duration duration;
+    private final Duration timeout;  // may be null
     private final Optional<HttpClient.Version> version;
 
+    private static String userAgent() {
+        PrivilegedAction<String> pa = () -> System.getProperty("java.version");
+        String version = AccessController.doPrivileged(pa);
+        return "Java-http-client/" + version;
+    }
+
+    /** The value of the User-Agent header for all requests sent by the client. */
+    public static final String USER_AGENT = userAgent();
+
     /**
      * Creates an HttpRequestImpl from the given builder.
      */
     public HttpRequestImpl(HttpRequestBuilderImpl builder) {
         String method = builder.method();
-        this.method = method == null? "GET" : method;
+        this.method = method == null ? "GET" : method;
         this.userHeaders = ImmutableHeaders.of(builder.headers().map(), ALLOWED_HEADERS);
         this.systemHeaders = new HttpHeadersImpl();
         this.uri = builder.uri();
+        assert uri != null;
+        this.proxy = null;
         this.expectContinue = builder.expectContinue();
         this.secure = uri.getScheme().toLowerCase(Locale.US).equals("https");
-        if (builder.body() == null) {
-            this.requestProcessor = HttpRequest.noBody();
-        } else {
-            this.requestProcessor = builder.body();
-        }
-        this.duration = builder.duration();
+        this.requestPublisher = builder.bodyPublisher();  // may be null
+        this.timeout = builder.timeout();
         this.version = builder.version();
     }
 
     /**
      * Creates an HttpRequestImpl from the given request.
      */
-    public HttpRequestImpl(HttpRequest request) {
+    public HttpRequestImpl(HttpRequest request, ProxySelector ps, AccessControlContext acc) {
         String method = request.method();
-        this.method = method == null? "GET" : method;
+        this.method = method == null ? "GET" : method;
         this.userHeaders = request.headers();
         if (request instanceof HttpRequestImpl) {
             this.systemHeaders = ((HttpRequestImpl) request).systemHeaders;
@@ -87,15 +99,25 @@ class HttpRequestImpl extends HttpRequest implements WebSocketRequest {
         } else {
             this.systemHeaders = new HttpHeadersImpl();
         }
+        this.systemHeaders.setHeader("User-Agent", USER_AGENT);
         this.uri = request.uri();
+        if (isWebSocket) {
+            // WebSocket determines and sets the proxy itself
+            this.proxy = ((HttpRequestImpl) request).proxy;
+        } else {
+            if (ps != null)
+                this.proxy = retrieveProxy(ps, uri);
+            else
+                this.proxy = null;
+        }
         this.expectContinue = request.expectContinue();
         this.secure = uri.getScheme().toLowerCase(Locale.US).equals("https");
-        if (!request.bodyProcessor().isPresent()) {
-            this.requestProcessor = HttpRequest.noBody();
-        } else {
-            this.requestProcessor = request.bodyProcessor().get();
+        this.requestPublisher = request.bodyPublisher().orElse(null);
+        if (acc != null && requestPublisher instanceof RequestPublishers.FilePublisher) {
+            // Restricts the file publisher with the senders ACC, if any
+            ((RequestPublishers.FilePublisher)requestPublisher).setAccessControlContext(acc);
         }
-        this.duration = request.duration();
+        this.timeout = request.timeout().orElse(null);
         this.version = request.version();
     }
 
@@ -108,30 +130,38 @@ class HttpRequestImpl extends HttpRequest implements WebSocketRequest {
         this.isWebSocket = other.isWebSocket;
         this.systemHeaders = other.systemHeaders;
         this.uri = uri;
+        this.proxy = other.proxy;
         this.expectContinue = other.expectContinue;
         this.secure = uri.getScheme().toLowerCase(Locale.US).equals("https");
-        this.requestProcessor = other.requestProcessor;
+        this.requestPublisher = other.requestPublisher;  // may be null
         this.acc = other.acc;
-        this.duration = other.duration;
+        this.timeout = other.timeout;
         this.version = other.version();
     }
 
     /* used for creating CONNECT requests  */
-    HttpRequestImpl(String method, HttpClientImpl client,
-                    InetSocketAddress authority) {
+    HttpRequestImpl(String method, InetSocketAddress authority) {
         // TODO: isWebSocket flag is not specified, but the assumption is that
         // such a request will never be made on a connection that will be returned
         // to the connection pool (we might need to revisit this constructor later)
         this.method = method;
         this.systemHeaders = new HttpHeadersImpl();
         this.userHeaders = ImmutableHeaders.empty();
-        this.uri = URI.create("socket://" + authority.getHostString() + ":" + Integer.toString(authority.getPort()) + "/");
-        this.requestProcessor = HttpRequest.noBody();
+        this.uri = URI.create("socket://" + authority.getHostString() + ":"
+                              + Integer.toString(authority.getPort()) + "/");
+        this.proxy = null;
+        this.requestPublisher = null;
         this.authority = authority;
         this.secure = false;
         this.expectContinue = false;
-        this.duration = null;
-        this.version = Optional.of(client.version());
+        this.timeout = null;
+        // The CONNECT request sent for tunneling is only used in two cases:
+        //   1. websocket, which only supports HTTP/1.1
+        //   2. SSL tunneling through a HTTP/1.1 proxy
+        // In either case we do not want to upgrade the connection to the proxy.
+        // What we want to possibly upgrade is the tunneled connection to the
+        // target server (so not the CONNECT request itself)
+        this.version = Optional.of(HttpClient.Version.HTTP_1_1);
     }
 
     /**
@@ -161,14 +191,14 @@ class HttpRequestImpl extends HttpRequest implements WebSocketRequest {
         StringBuilder sb = new StringBuilder();
         sb.append(scheme).append("://").append(authority).append(path);
         this.uri = URI.create(sb.toString());
-
+        this.proxy = null;
         this.userHeaders = ImmutableHeaders.of(headers.map(), ALLOWED_HEADERS);
         this.systemHeaders = parent.systemHeaders;
         this.expectContinue = parent.expectContinue;
         this.secure = parent.secure;
-        this.requestProcessor = parent.requestProcessor;
+        this.requestPublisher = parent.requestPublisher;
         this.acc = parent.acc;
-        this.duration = parent.duration;
+        this.timeout = parent.timeout;
         this.version = parent.version;
     }
 
@@ -193,19 +223,34 @@ class HttpRequestImpl extends HttpRequest implements WebSocketRequest {
     @Override
     public boolean expectContinue() { return expectContinue; }
 
-    InetSocketAddress proxy(HttpClientImpl client) {
-        ProxySelector ps = client.proxy().orElse(null);
-        if (ps == null) {
-            ps = client.proxy().orElse(null);
+    /** Retrieves the proxy, from the given ProxySelector, if there is one. */
+    private static Proxy retrieveProxy(ProxySelector ps, URI uri) {
+        Proxy proxy = null;
+        List<Proxy> pl = ps.select(uri);
+        if (!pl.isEmpty()) {
+            Proxy p = pl.get(0);
+            if (p.type() == Proxy.Type.HTTP)
+                proxy = p;
         }
-        if (ps == null || method.equalsIgnoreCase("CONNECT")) {
+        return proxy;
+    }
+
+    InetSocketAddress proxy() {
+        if (proxy == null || proxy.type() != Proxy.Type.HTTP
+                || method.equalsIgnoreCase("CONNECT")) {
             return null;
         }
-        return (InetSocketAddress)ps.select(uri).get(0).address();
+        return (InetSocketAddress)proxy.address();
     }
 
     boolean secure() { return secure; }
 
+    @Override
+    public void setProxy(Proxy proxy) {
+        assert isWebSocket;
+        this.proxy = proxy;
+    }
+
     @Override
     public void isWebSocket(boolean is) {
         isWebSocket = is;
@@ -215,15 +260,10 @@ class HttpRequestImpl extends HttpRequest implements WebSocketRequest {
         return isWebSocket;
     }
 
-//    /** Returns the follow-redirects setting for this request. */
-//    @Override
-//    public jdk.incubator.http.HttpClient.Redirect followRedirects() {
-//        return followRedirects;
-//    }
-
     @Override
-    public Optional<BodyProcessor> bodyProcessor() {
-        return Optional.of(requestProcessor);
+    public Optional<BodyPublisher> bodyPublisher() {
+        return requestPublisher == null ? Optional.empty()
+                                        : Optional.of(requestPublisher);
     }
 
     /**
@@ -237,14 +277,10 @@ class HttpRequestImpl extends HttpRequest implements WebSocketRequest {
     public URI uri() { return uri; }
 
     @Override
-    public Duration duration() {
-        return duration;
+    public Optional<Duration> timeout() {
+        return timeout == null ? Optional.empty() : Optional.of(timeout);
     }
 
-//    HttpClientImpl client() {
-//        return client;
-//    }
-
     HttpHeaders getUserHeaders() { return userHeaders; }
 
     HttpHeadersImpl getSystemHeaders() { return systemHeaders; }
@@ -261,57 +297,24 @@ class HttpRequestImpl extends HttpRequest implements WebSocketRequest {
         systemHeaders.setHeader(name, value);
     }
 
-//    @Override
-//    public <T> HttpResponse<T>
-//    response(HttpResponse.BodyHandler<T> responseHandler)
-//        throws IOException, InterruptedException
-//    {
-//        if (!sent.compareAndSet(false, true)) {
-//            throw new IllegalStateException("request already sent");
-//        }
-//        MultiExchange<Void,T> mex = new MultiExchange<>(this, responseHandler);
-//        return mex.response();
-//    }
-//
-//    @Override
-//    public <T> CompletableFuture<HttpResponse<T>>
-//    responseAsync(HttpResponse.BodyHandler<T> responseHandler)
-//    {
-//        if (!sent.compareAndSet(false, true)) {
-//            throw new IllegalStateException("request already sent");
-//        }
-//        MultiExchange<Void,T> mex = new MultiExchange<>(this, responseHandler);
-//        return mex.responseAsync(null)
-//                  .thenApply((HttpResponseImpl<T> b) -> (HttpResponse<T>) b);
-//    }
-//
-//    @Override
-//    public <U, T> CompletableFuture<U>
-//    multiResponseAsync(HttpResponse.MultiProcessor<U, T> responseHandler)
-//    {
-//        if (!sent.compareAndSet(false, true)) {
-//            throw new IllegalStateException("request already sent");
-//        }
-//        MultiExchange<U,T> mex = new MultiExchange<>(this, responseHandler);
-//        return mex.multiResponseAsync();
-//    }
-
-    public InetSocketAddress getAddress(HttpClientImpl client) {
+    InetSocketAddress getAddress() {
         URI uri = uri();
         if (uri == null) {
             return authority();
         }
-        int port = uri.getPort();
-        if (port == -1) {
+        int p = uri.getPort();
+        if (p == -1) {
             if (uri.getScheme().equalsIgnoreCase("https")) {
-                port = 443;
+                p = 443;
             } else {
-                port = 80;
+                p = 80;
             }
         }
-        String host = uri.getHost();
-        if (proxy(client) == null) {
-            return new InetSocketAddress(host, port);
+        final String host = uri.getHost();
+        final int port = p;
+        if (proxy() == null) {
+            PrivilegedAction<InetSocketAddress> pa = () -> new InetSocketAddress(host, port);
+            return AccessController.doPrivileged(pa);
         } else {
             return InetSocketAddress.createUnresolved(host, port);
         }
diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/HttpResponse.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/HttpResponse.java
index f8aa46cef0d..70fa5d25273 100644
--- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/HttpResponse.java
+++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/HttpResponse.java
@@ -26,20 +26,22 @@
 package jdk.incubator.http;
 
 import java.io.IOException;
-import java.io.UncheckedIOException;
+import java.io.InputStream;
 import java.net.URI;
-import jdk.incubator.http.ResponseProcessors.MultiFile;
-import jdk.incubator.http.ResponseProcessors.MultiProcessorImpl;
+import jdk.incubator.http.ResponseSubscribers.MultiSubscriberImpl;
 import static jdk.incubator.http.internal.common.Utils.unchecked;
 import static jdk.incubator.http.internal.common.Utils.charsetFrom;
 import java.nio.ByteBuffer;
 import java.nio.charset.Charset;
-import java.nio.charset.StandardCharsets;
+import java.nio.channels.FileChannel;
 import java.nio.file.OpenOption;
 import java.nio.file.Path;
 import java.nio.file.Paths;
 import java.nio.file.StandardOpenOption;
-import java.util.Map;
+import java.security.AccessControlContext;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Objects;
 import java.util.Optional;
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.CompletionStage;
@@ -52,7 +54,7 @@ import javax.net.ssl.SSLParameters;
  * Represents a response to a {@link HttpRequest}.
  * {@Incubating}
  *
- * <p>A {@code HttpResponse} is available when the response status code and
+ * <p> A {@code HttpResponse} is available when the response status code and
  * headers have been received, and typically after the response body has also
  * been received. This depends on the response body handler provided when
  * sending the request. In all cases, the response body handler is invoked
@@ -61,23 +63,24 @@ import javax.net.ssl.SSLParameters;
  *
  * <p> Methods are provided in this class for accessing the response headers,
  * and response body.
- * <p>
- * <b>Response handlers and processors</b>
- * <p>
- * Response bodies are handled at two levels. Application code supplies a response
- * handler ({@link BodyHandler}) which may examine the response status code
- * and headers, and which then returns a {@link BodyProcessor} to actually read
- * (or discard) the body and convert it into some useful Java object type. The handler
- * can return one of the pre-defined processor types, or a custom processor, or
- * if the body is to be discarded, it can call {@link BodyProcessor#discard(Object)
- * BodyProcessor.discard()} and return a processor which discards the response body.
- * Static implementations of both handlers and processors are provided in
- * {@link BodyHandler BodyHandler} and {@link BodyProcessor BodyProcessor} respectively.
- * In all cases, the handler functions provided are convenience implementations
- * which ignore the supplied status code and
- * headers and return the relevant pre-defined {@code BodyProcessor}.
- * <p>
- * See {@link BodyHandler} for example usage.
+ *
+ * <p><b>Response handlers and subscribers</b>
+ *
+ * <p> Response bodies are handled at two levels. Application code supplies a
+ * response handler ({@link BodyHandler}) which may examine the response status
+ * code and headers, and which then returns a {@link BodySubscriber} to actually
+ * read (or discard) the body and convert it into some useful Java object type.
+ * The handler can return one of the pre-defined subscriber types, or a custom
+ * subscriber, or if the body is to be discarded it can call {@link
+ * BodySubscriber#discard(Object) discard} and return a subscriber which
+ * discards the response body. Static implementations of both handlers and
+ * subscribers are provided in {@linkplain BodyHandler BodyHandler} and
+ * {@linkplain BodySubscriber BodySubscriber} respectively. In all cases, the
+ * handler functions provided are convenience implementations which ignore the
+ * supplied status code and headers and return the relevant pre-defined {@code
+ * BodySubscriber}.
+ *
+ * <p> See {@link BodyHandler} for example usage.
  *
  * @param <T> the response body type
  * @since 9
@@ -97,19 +100,26 @@ public abstract class HttpResponse<T> {
     public abstract int statusCode();
 
     /**
-     * Returns the initial {@link HttpRequest} that initiated the exchange.
+     * Returns the {@link HttpRequest} corresponding to this response.
+     *
+     * <p> This may not be the original request provided by the caller,
+     * for example, if that request was redirected.
+     *
+     * @see #previousResponse()
      *
      * @return the request
      */
     public abstract HttpRequest request();
 
     /**
-     * Returns the final {@link HttpRequest} that was sent on the wire for the
-     * exchange ( may, or may not, be the same as the initial request ).
+     * Returns an {@code Optional} containing the previous intermediate response
+     * if one was received. An intermediate response is one that is received
+     * as a result of redirection or authentication. If no previous response
+     * was received then an empty {@code Optional} is returned.
      *
-     * @return the request
+     * @return an Optional containing the HttpResponse, if any.
      */
-    public abstract HttpRequest finalRequest();
+    public abstract Optional<HttpResponse<T>> previousResponse();
 
     /**
      * Returns the received response headers.
@@ -119,21 +129,14 @@ public abstract class HttpResponse<T> {
     public abstract HttpHeaders headers();
 
     /**
-     * Returns the received response trailers, if there are any, when they
-     * become available. For many response processor types this will be at the same
-     * time as the {@code HttpResponse} itself is available. In such cases, the
-     * returned {@code CompletableFuture} will be already completed.
-     *
-     * @return a CompletableFuture of the response trailers (may be empty)
-     */
-    public abstract CompletableFuture<HttpHeaders> trailers();
-
-    /**
-     * Returns the body. Depending on the type of {@code T}, the returned body may
-     * represent the body after it was read (such as {@code byte[]}, or
+     * Returns the body. Depending on the type of {@code T}, the returned body
+     * may represent the body after it was read (such as {@code byte[]}, or
      * {@code String}, or {@code Path}) or it may represent an object with
      * which the body is read, such as an {@link java.io.InputStream}.
      *
+     * <p> If this {@code HttpResponse} was returned from an invocation of
+     * {@link #previousResponse()} then this method returns {@code null}
+     *
      * @return the body
      */
     public abstract T body();
@@ -161,36 +164,124 @@ public abstract class HttpResponse<T> {
      */
     public abstract HttpClient.Version version();
 
+
+    private static String pathForSecurityCheck(Path path) {
+        return path.toFile().getPath();
+    }
+
+    /** A body handler that is further restricted by a given ACC. */
+    interface UntrustedBodyHandler<T> extends BodyHandler<T> {
+        void setAccessControlContext(AccessControlContext acc);
+    }
+
+    /**
+     * A Path body handler.
+     *
+     * Note: Exists mainly too allow setting of the senders ACC post creation of
+     * the handler.
+     */
+    static class PathBodyHandler implements UntrustedBodyHandler<Path> {
+        private final Path file;
+        private final OpenOption[]openOptions;
+        private volatile AccessControlContext acc;
+
+        PathBodyHandler(Path file, OpenOption... openOptions) {
+            this.file = file;
+            this.openOptions = openOptions;
+        }
+
+        @Override
+        public void setAccessControlContext(AccessControlContext acc) {
+            this.acc = acc;
+        }
+
+        @Override
+        public BodySubscriber<Path> apply(int statusCode, HttpHeaders headers) {
+            ResponseSubscribers.PathSubscriber bs = (ResponseSubscribers.PathSubscriber)
+                    BodySubscriber.asFileImpl(file, openOptions);
+            bs.setAccessControlContext(acc);
+            return bs;
+        }
+    }
+
+    // Similar to Path body handler, but for file download. Supports setting ACC.
+    static class FileDownloadBodyHandler implements UntrustedBodyHandler<Path> {
+        private final Path directory;
+        private final OpenOption[]openOptions;
+        private volatile AccessControlContext acc;
+
+        FileDownloadBodyHandler(Path directory, OpenOption... openOptions) {
+            this.directory = directory;
+            this.openOptions = openOptions;
+        }
+
+        @Override
+        public void setAccessControlContext(AccessControlContext acc) {
+            this.acc = acc;
+        }
+
+        @Override
+        public BodySubscriber<Path> apply(int statusCode, HttpHeaders headers) {
+            String dispoHeader = headers.firstValue("Content-Disposition")
+                    .orElseThrow(() -> unchecked(new IOException("No Content-Disposition")));
+            if (!dispoHeader.startsWith("attachment;")) {
+                throw unchecked(new IOException("Unknown Content-Disposition type"));
+            }
+            int n = dispoHeader.indexOf("filename=");
+            if (n == -1) {
+                throw unchecked(new IOException("Bad Content-Disposition type"));
+            }
+            int lastsemi = dispoHeader.lastIndexOf(';');
+            String disposition;
+            if (lastsemi < n) {
+                disposition = dispoHeader.substring(n + 9);
+            } else {
+                disposition = dispoHeader.substring(n + 9, lastsemi);
+            }
+            Path file = Paths.get(directory.toString(), disposition);
+
+            ResponseSubscribers.PathSubscriber bs = (ResponseSubscribers.PathSubscriber)
+                    BodySubscriber.asFileImpl(file, openOptions);
+            bs.setAccessControlContext(acc);
+            return bs;
+        }
+    }
+
     /**
      * A handler for response bodies.
      * {@Incubating}
-     * <p>
-     * This is a function that takes two parameters: the response status code,
-     * and the response headers, and which returns a {@link BodyProcessor}.
+     *
+     * <p> This is a function that takes two parameters: the response status code,
+     * and the response headers, and which returns a {@linkplain BodySubscriber}.
      * The function is always called just before the response body is read. Its
      * implementation may examine the status code or headers and must decide,
      * whether to accept the response body or discard it, and if accepting it,
      * exactly how to handle it.
-     * <p>
-     * Some pre-defined implementations which do not utilize the status code
+     *
+     * <p> Some pre-defined implementations which do not utilize the status code
      * or headers (meaning the body is always accepted) are defined:
      * <ul><li>{@link #asByteArray() }</li>
      * <li>{@link #asByteArrayConsumer(java.util.function.Consumer)
      * asByteArrayConsumer(Consumer)}</li>
+     * <li>{@link #asString(java.nio.charset.Charset) asString(Charset)}</li>
+     * <li>{@link #asFile(Path, OpenOption...)
+     * asFile(Path,OpenOption...)}</li>
      * <li>{@link #asFileDownload(java.nio.file.Path,OpenOption...)
      * asFileDownload(Path,OpenOption...)}</li>
+     * <li>{@link #asInputStream() asInputStream()}</li>
      * <li>{@link #discard(Object) }</li>
-     * <li>{@link #asString(java.nio.charset.Charset)
-     * asString(Charset)}</li></ul>
-     * <p>
-     * These implementations return the equivalent {@link BodyProcessor}.
+     * <li>{@link #buffering(BodyHandler, int)
+     * buffering(BodyHandler,int)}</li>
+     * </ul>
+     *
+     * <p> These implementations return the equivalent {@link BodySubscriber}.
      * Alternatively, the handler can be used to examine the status code
-     * or headers and return different body processors as appropriate.
-     * <p>
-     * <b>Examples of handler usage</b>
-     * <p>
-     * The first example uses one of the predefined handler functions which
-     * ignore the response headers and status, and always process the response
+     * or headers and return different body subscribers as appropriate.
+     *
+     * <p><b>Examples of handler usage</b>
+     *
+     * <p> The first example uses one of the predefined handler functions which
+     * ignores the response headers and status, and always process the response
      * body in the same way.
      * <pre>
      * {@code
@@ -201,11 +292,11 @@ public abstract class HttpResponse<T> {
      * }
      * </pre>
      * Note, that even though these pre-defined handlers ignore the status code
-     * and headers, this information is still accessible from the {@code HttpResponse}
-     * when it is returned.
-     * <p>
-     * In the second example, the function returns a different processor depending
-     * on the status code.
+     * and headers, this information is still accessible from the
+     * {@code HttpResponse} when it is returned.
+     *
+     * <p> In the second example, the function returns a different subscriber
+     * depending on the status code.
      * <pre>
      * {@code
      *      HttpResponse<Path> resp1 = HttpRequest
@@ -213,93 +304,134 @@ public abstract class HttpResponse<T> {
      *              .GET()
      *              .response(
      *                  (status, headers) -> status == 200
-     *                      ? BodyProcessor.asFile(Paths.get("/tmp/f"))
-     *                      : BodyProcessor.discard(Paths.get("/NULL")));
+     *                      ? BodySubscriber.asFile(Paths.get("/tmp/f"))
+     *                      : BodySubscriber.discard(Paths.get("/NULL")));
      * }
      * </pre>
      *
-     * @param <T> the response body type.
+     * @param <T> the response body type
      */
     @FunctionalInterface
     public interface BodyHandler<T> {
 
         /**
-         * Returns a {@link BodyProcessor BodyProcessor} considering the given response status
-         * code and headers. This method is always called before the body is read
-         * and its implementation can decide to keep the body and store it somewhere
-         * or else discard it, by  returning the {@code BodyProcessor} returned
-         * from {@link BodyProcessor#discard(java.lang.Object) discard()}.
+         * Returns a {@link BodySubscriber BodySubscriber} considering the given
+         * response status code and headers. This method is always called before
+         * the body is read and its implementation can decide to keep the body
+         * and store it somewhere, or else discard it by returning the {@code
+         * BodySubscriber} returned from {@link BodySubscriber#discard(Object)
+         * discard}.
          *
          * @param statusCode the HTTP status code received
          * @param responseHeaders the response headers received
-         * @return a response body handler
+         * @return a body subscriber
          */
-        public BodyProcessor<T> apply(int statusCode, HttpHeaders responseHeaders);
+        public BodySubscriber<T> apply(int statusCode, HttpHeaders responseHeaders);
 
         /**
          * Returns a response body handler which discards the response body and
          * uses the given value as a replacement for it.
          *
          * @param <U> the response body type
-         * @param value the value of U to return as the body
+         * @param value the value of U to return as the body, may be {@code null}
          * @return a response body handler
          */
         public static <U> BodyHandler<U> discard(U value) {
-            return (status, headers) -> BodyProcessor.discard(value);
+            return (status, headers) -> BodySubscriber.discard(value);
         }
 
         /**
          * Returns a {@code BodyHandler<String>} that returns a
-         * {@link BodyProcessor BodyProcessor}{@code <String>} obtained from
-         * {@link BodyProcessor#asString(java.nio.charset.Charset)
-         * BodyProcessor.asString(Charset)}. If a charset is provided, the
-         * body is decoded using it. If charset is {@code null} then the processor
-         * tries to determine the character set from the {@code Content-encoding}
-         * header. If that charset is not supported then
-         * {@link java.nio.charset.StandardCharsets#UTF_8 UTF_8} is used.
+         * {@link BodySubscriber BodySubscriber}{@code <String>} obtained from
+         * {@link BodySubscriber#asString(Charset) BodySubscriber.asString(Charset)}.
+         * If a charset is provided, the body is decoded using it. If charset is
+         * {@code null} then the handler tries to determine the character set
+         * from the {@code Content-encoding} header. If that charset is not
+         * supported then {@link java.nio.charset.StandardCharsets#UTF_8 UTF_8}
+         * is used.
          *
-         * @param charset the name of the charset to interpret the body as. If
-         * {@code null} then charset determined from Content-encoding header
+         * @param charset The name of the charset to interpret the body as. If
+         *                {@code null} then the charset is determined from the
+         *                <i>Content-encoding</i> header.
          * @return a response body handler
          */
         public static BodyHandler<String> asString(Charset charset) {
             return (status, headers) -> {
                 if (charset != null) {
-                    return BodyProcessor.asString(charset);
+                    return BodySubscriber.asString(charset);
                 }
-                return BodyProcessor.asString(charsetFrom(headers));
+                return BodySubscriber.asString(charsetFrom(headers));
             };
         }
 
-
         /**
          * Returns a {@code BodyHandler<Path>} that returns a
-         * {@link BodyProcessor BodyProcessor}{@code <Path>} obtained from
-         * {@link BodyProcessor#asFile(Path) BodyProcessor.asFile(Path)}.
-         * <p>
-         * When the {@code HttpResponse} object is returned, the body has been completely
-         * written to the file, and {@link #body()} returns a reference to its
-         * {@link Path}.
+         * {@link BodySubscriber BodySubscriber}{@code <Path>} obtained from
+         * {@link BodySubscriber#asFile(Path, OpenOption...)
+         * BodySubscriber.asFile(Path,OpenOption...)}.
          *
-         * @param file the file to store the body in
+         * <p> When the {@code HttpResponse} object is returned, the body has
+         * been completely written to the file, and {@link #body()} returns a
+         * reference to its {@link Path}.
+         *
+         * @param file the filename to store the body in
+         * @param openOptions any options to use when opening/creating the file
          * @return a response body handler
+         * @throws SecurityException If a security manager has been installed
+         *          and it denies {@link SecurityManager#checkWrite(String)
+         *          write access} to the file. The {@link
+         *          SecurityManager#checkDelete(String) checkDelete} method is
+         *          invoked to check delete access if the file is opened with
+         *          the {@code DELETE_ON_CLOSE} option.
          */
-        public static BodyHandler<Path> asFile(Path file) {
-            return (status, headers) -> BodyProcessor.asFile(file);
+        public static BodyHandler<Path> asFile(Path file, OpenOption... openOptions) {
+            Objects.requireNonNull(file);
+            SecurityManager sm = System.getSecurityManager();
+            if (sm != null) {
+                String fn = pathForSecurityCheck(file);
+                sm.checkWrite(fn);
+                List<OpenOption> opts = Arrays.asList(openOptions);
+                if (opts.contains(StandardOpenOption.DELETE_ON_CLOSE))
+                    sm.checkDelete(fn);
+                if (opts.contains(StandardOpenOption.READ))
+                    sm.checkRead(fn);
+            }
+            return new PathBodyHandler(file, openOptions);
         }
 
         /**
          * Returns a {@code BodyHandler<Path>} that returns a
-         * {@link BodyProcessor BodyProcessor}&lt;{@link Path}&gt;
+         * {@link BodySubscriber BodySubscriber}{@code <Path>} obtained from
+         * {@link BodySubscriber#asFile(Path) BodySubscriber.asFile(Path)}.
+         *
+         * <p> When the {@code HttpResponse} object is returned, the body has
+         * been completely written to the file, and {@link #body()} returns a
+         * reference to its {@link Path}.
+         *
+         * @param file the file to store the body in
+         * @return a response body handler
+         * @throws SecurityException if a security manager has been installed
+         *          and it denies {@link SecurityManager#checkWrite(String)
+         *          write access} to the file
+         */
+        public static BodyHandler<Path> asFile(Path file) {
+            return BodyHandler.asFile(file, StandardOpenOption.CREATE,
+                                            StandardOpenOption.WRITE);
+        }
+
+        /**
+         * Returns a {@code BodyHandler<Path>} that returns a
+         * {@link BodySubscriber BodySubscriber}&lt;{@link Path}&gt;
          * where the download directory is specified, but the filename is
          * obtained from the {@code Content-Disposition} response header. The
-         * {@code Content-Disposition} header must specify the <i>attachment</i> type
-         * and must also contain a
-         * <i>filename</i> parameter. If the filename specifies multiple path
-         * components only the final component is used as the filename (with the
-         * given directory name). When the {@code HttpResponse} object is
-         * returned, the body has been completely written to the file and {@link
-         * #body()} returns a {@code Path} object for the file. The returned {@code Path} is the
+         * {@code Content-Disposition} header must specify the <i>attachment</i>
+         * type and must also contain a <i>filename</i> parameter. If the
+         * filename specifies multiple path components only the final component
+         * is used as the filename (with the given directory name).
+         *
+         * <p> When the {@code HttpResponse} object is returned, the body has
+         * been completely written to the file and {@link #body()} returns a
+         * {@code Path} object for the file. The returned {@code Path} is the
          * combination of the supplied directory name and the file name supplied
          * by the server. If the destination directory does not exist or cannot
          * be written to, then the response will fail with an {@link IOException}.
@@ -307,245 +439,355 @@ public abstract class HttpResponse<T> {
          * @param directory the directory to store the file in
          * @param openOptions open options
          * @return a response body handler
+         * @throws SecurityException If a security manager has been installed
+         *          and it denies {@link SecurityManager#checkWrite(String)
+         *          write access} to the file. The {@link
+         *          SecurityManager#checkDelete(String) checkDelete} method is
+         *          invoked to check delete access if the file is opened with
+         *          the {@code DELETE_ON_CLOSE} option.
          */
-        public static BodyHandler<Path> asFileDownload(Path directory, OpenOption... openOptions) {
-            return (status, headers) -> {
-                String dispoHeader = headers.firstValue("Content-Disposition")
-                        .orElseThrow(() -> unchecked(new IOException("No Content-Disposition")));
-                if (!dispoHeader.startsWith("attachment;")) {
-                    throw unchecked(new IOException("Unknown Content-Disposition type"));
-                }
-                int n = dispoHeader.indexOf("filename=");
-                if (n == -1) {
-                    throw unchecked(new IOException("Bad Content-Disposition type"));
-                }
-                int lastsemi = dispoHeader.lastIndexOf(';');
-                String disposition;
-                if (lastsemi < n) {
-                    disposition = dispoHeader.substring(n + 9);
-                } else {
-                    disposition = dispoHeader.substring(n + 9, lastsemi);
-                }
-                Path file = Paths.get(directory.toString(), disposition);
-                return BodyProcessor.asFile(file, openOptions);
-            };
+         //####: check if the dir exists and is writable??
+        public static BodyHandler<Path> asFileDownload(Path directory,
+                                                       OpenOption... openOptions) {
+            Objects.requireNonNull(directory);
+            SecurityManager sm = System.getSecurityManager();
+            if (sm != null) {
+                String fn = pathForSecurityCheck(directory);
+                sm.checkWrite(fn);
+                List<OpenOption> opts = Arrays.asList(openOptions);
+                if (opts.contains(StandardOpenOption.DELETE_ON_CLOSE))
+                    sm.checkDelete(fn);
+                if (opts.contains(StandardOpenOption.READ))
+                    sm.checkRead(fn);
+            }
+            return new FileDownloadBodyHandler(directory, openOptions);
         }
 
         /**
-         * Returns a {@code BodyHandler<Path>} that returns a
-         * {@link BodyProcessor BodyProcessor}{@code <Path>} obtained from
-         * {@link BodyProcessor#asFile(java.nio.file.Path, java.nio.file.OpenOption...)
-         * BodyProcessor.asFile(Path,OpenOption...)}.
-         * <p>
-         * When the {@code HttpResponse} object is returned, the body has been completely
-         * written to the file, and {@link #body()} returns a reference to its
-         * {@link Path}.
+         * Returns a {@code BodyHandler<InputStream>} that returns a
+         * {@link BodySubscriber BodySubscriber}{@code <InputStream>} obtained
+         * from {@link BodySubscriber#asInputStream() BodySubscriber.asInputStream}.
+         *
+         * <p> When the {@code HttpResponse} object is returned, the response
+         * headers will have been completely read, but the body may not have
+         * been fully received yet. The {@link #body()} method returns an
+         * {@link InputStream} from which the body can be read as it is received.
+         *
+         * @apiNote See {@link BodySubscriber#asInputStream()} for more information.
          *
-         * @param file the filename to store the body in
-         * @param openOptions any options to use when opening/creating the file
          * @return a response body handler
          */
-        public static BodyHandler<Path> asFile(Path file, OpenOption... openOptions) {
-            return (status, headers) -> BodyProcessor.asFile(file, openOptions);
+        public static BodyHandler<InputStream> asInputStream() {
+            return (status, headers) -> BodySubscriber.asInputStream();
         }
 
         /**
          * Returns a {@code BodyHandler<Void>} that returns a
-         * {@link BodyProcessor BodyProcessor}{@code <Void>} obtained from
-         * {@link BodyProcessor#asByteArrayConsumer(java.util.function.Consumer)
-         * BodyProcessor.asByteArrayConsumer(Consumer)}.
-         * <p>
-         * When the {@code HttpResponse} object is returned, the body has been completely
-         * written to the consumer.
+         * {@link BodySubscriber BodySubscriber}{@code <Void>} obtained from
+         * {@link BodySubscriber#asByteArrayConsumer(Consumer)
+         * BodySubscriber.asByteArrayConsumer(Consumer)}.
+         *
+         * <p> When the {@code HttpResponse} object is returned, the body has
+         * been completely written to the consumer.
          *
          * @param consumer a Consumer to accept the response body
          * @return a response body handler
          */
         public static BodyHandler<Void> asByteArrayConsumer(Consumer<Optional<byte[]>> consumer) {
-            return (status, headers) -> BodyProcessor.asByteArrayConsumer(consumer);
+            return (status, headers) -> BodySubscriber.asByteArrayConsumer(consumer);
         }
 
         /**
          * Returns a {@code BodyHandler<byte[]>} that returns a
-         * {@link BodyProcessor BodyProcessor}&lt;{@code byte[]}&gt; obtained
-         * from {@link BodyProcessor#asByteArray() BodyProcessor.asByteArray()}.
-         * <p>
-         * When the {@code HttpResponse} object is returned, the body has been completely
-         * written to the byte array.
+         * {@link BodySubscriber BodySubscriber}&lt;{@code byte[]}&gt; obtained
+         * from {@link BodySubscriber#asByteArray() BodySubscriber.asByteArray()}.
+         *
+         * <p> When the {@code HttpResponse} object is returned, the body has
+         * been completely written to the byte array.
          *
          * @return a response body handler
          */
         public static BodyHandler<byte[]> asByteArray() {
-            return (status, headers) -> BodyProcessor.asByteArray();
+            return (status, headers) -> BodySubscriber.asByteArray();
         }
 
         /**
          * Returns a {@code BodyHandler<String>} that returns a
-         * {@link BodyProcessor BodyProcessor}{@code <String>} obtained from
-         * {@link BodyProcessor#asString(java.nio.charset.Charset)
-         * BodyProcessor.asString(Charset)}. The body is
+         * {@link BodySubscriber BodySubscriber}{@code <String>} obtained from
+         * {@link BodySubscriber#asString(java.nio.charset.Charset)
+         * BodySubscriber.asString(Charset)}. The body is
          * decoded using the character set specified in
          * the {@code Content-encoding} response header. If there is no such
          * header, or the character set is not supported, then
          * {@link java.nio.charset.StandardCharsets#UTF_8 UTF_8} is used.
-         * <p>
-         * When the {@code HttpResponse} object is returned, the body has been completely
-         * written to the string.
+         *
+         * <p> When the {@code HttpResponse} object is returned, the body has
+         * been completely written to the string.
          *
          * @return a response body handler
          */
         public static BodyHandler<String> asString() {
-            return (status, headers) -> BodyProcessor.asString(charsetFrom(headers));
+            return (status, headers) -> BodySubscriber.asString(charsetFrom(headers));
         }
+
+        /**
+         * Returns a {@code BodyHandler} which, when invoked, returns a {@linkplain
+         * BodySubscriber#buffering(BodySubscriber,int) buffering BodySubscriber}
+         * that buffers data before delivering it to the downstream subscriber.
+         * These {@code BodySubscriber} instances are created by calling
+         * {@linkplain BodySubscriber#buffering(BodySubscriber,int)
+         * BodySubscriber.buffering} with a subscriber obtained from the given
+         * downstream handler and the {@code bufferSize} parameter.
+         *
+         * @param downstreamHandler the downstream handler
+         * @param bufferSize the buffer size parameter passed to {@linkplain
+         *        BodySubscriber#buffering(BodySubscriber,int) BodySubscriber.buffering}
+         * @return a body handler
+         * @throws IllegalArgumentException if {@code bufferSize <= 0}
+         */
+         public static <T> BodyHandler<T> buffering(BodyHandler<T> downstreamHandler,
+                                                    int bufferSize) {
+             if (bufferSize <= 0)
+                 throw new IllegalArgumentException("must be greater than 0");
+             return (status, headers) -> BodySubscriber
+                     .buffering(downstreamHandler.apply(status, headers),
+                                bufferSize);
+         }
     }
 
     /**
-     * A processor for response bodies.
+     * A subscriber for response bodies.
      * {@Incubating}
-     * <p>
-     * The object acts as a {@link Flow.Subscriber}&lt;{@link ByteBuffer}&gt; to
-     * the HTTP client implementation which publishes ByteBuffers containing the
-     * response body. The processor converts the incoming buffers of data to
-     * some user-defined object type {@code T}.
-     * <p>
-     * The {@link #getBody()} method returns a {@link CompletionStage}{@code <T>}
-     * that provides the response body object. The {@code CompletionStage} must
-     * be obtainable at any time. When it completes depends on the nature
-     * of type {@code T}. In many cases, when {@code T} represents the entire body after being
-     * read then it completes after the body has been read. If {@code T} is a streaming
-     * type such as {@link java.io.InputStream} then it completes before the
-     * body has been read, because the calling code uses it to consume the data.
+     *
+     * <p> The object acts as a {@link Flow.Subscriber}&lt;{@link List}&lt;{@link
+     * ByteBuffer}&gt;&gt; to the HTTP client implementation, which publishes
+     * unmodifiable lists of ByteBuffers containing the response body. The Flow
+     * of data, as well as the order of ByteBuffers in the Flow lists, is a
+     * strictly ordered representation of the response body. Both the Lists and
+     * the ByteBuffers, once passed to the subscriber, are no longer used by the
+     * HTTP client. The subscriber converts the incoming buffers of data to some
+     * user-defined object type {@code T}.
+     *
+     * <p> The {@link #getBody()} method returns a {@link CompletionStage}{@code
+     * <T>} that provides the response body object. The {@code CompletionStage}
+     * must be obtainable at any time. When it completes depends on the nature
+     * of type {@code T}. In many cases, when {@code T} represents the entire
+     * body after being read then it completes after the body has been read. If
+     * {@code T} is a streaming type such as {@link java.io.InputStream} then it
+     * completes before the body has been read, because the calling code uses it
+     * to consume the data.
+     *
+     * @apiNote To ensure that all resources associated with the
+     * corresponding exchange are properly released, an implementation
+     * of {@code BodySubscriber} must ensure to {@linkplain
+     * Flow.Subscription#request request} more data until {@link
+     * #onComplete() onComplete} or {@link #onError(Throwable) onError}
+     * are signalled, or {@linkplain Flow.Subscription#request cancel} its
+     * {@linkplain #onSubscribe(Flow.Subscription) subscription}
+     * if unable or unwilling to do so.
+     * Calling {@code cancel} before exhausting the data may cause
+     * the underlying HTTP connection to be closed and prevent it
+     * from being reused for subsequent operations.
      *
      * @param <T> the response body type
      */
-    public interface BodyProcessor<T>
-            extends Flow.Subscriber<ByteBuffer> {
+    public interface BodySubscriber<T>
+            extends Flow.Subscriber<List<ByteBuffer>> {
 
         /**
-         * Returns a {@code CompletionStage} which when completed will return the
-         * response body object.
+         * Returns a {@code CompletionStage} which when completed will return
+         * the response body object.
          *
          * @return a CompletionStage for the response body
          */
         public CompletionStage<T> getBody();
 
         /**
-         * Returns a body processor which stores the response body as a {@code
+         * Returns a body subscriber which stores the response body as a {@code
          * String} converted using the given {@code Charset}.
-         * <p>
-         * The {@link HttpResponse} using this processor is available after the
-         * entire response has been read.
+         *
+         * <p> The {@link HttpResponse} using this subscriber is available after
+         * the entire response has been read.
          *
          * @param charset the character set to convert the String with
-         * @return a body processor
+         * @return a body subscriber
          */
-        public static BodyProcessor<String> asString(Charset charset) {
-            return new ResponseProcessors.ByteArrayProcessor<>(
+        public static BodySubscriber<String> asString(Charset charset) {
+            return new ResponseSubscribers.ByteArraySubscriber<>(
                     bytes -> new String(bytes, charset)
             );
         }
 
         /**
-         * Returns a {@code BodyProcessor} which stores the response body as a
+         * Returns a {@code BodySubscriber} which stores the response body as a
          * byte array.
-         * <p>
-         * The {@link HttpResponse} using this processor is available after the
-         * entire response has been read.
          *
-         * @return a body processor
+         * <p> The {@link HttpResponse} using this subscriber is available after
+         * the entire response has been read.
+         *
+         * @return a body subscriber
          */
-        public static BodyProcessor<byte[]> asByteArray() {
-            return new ResponseProcessors.ByteArrayProcessor<>(
+        public static BodySubscriber<byte[]> asByteArray() {
+            return new ResponseSubscribers.ByteArraySubscriber<>(
                     Function.identity() // no conversion
             );
         }
 
+        // no security check
+        private static BodySubscriber<Path> asFileImpl(Path file, OpenOption... openOptions) {
+            return new ResponseSubscribers.PathSubscriber(file, openOptions);
+        }
+
         /**
-         * Returns a {@code BodyProcessor} which stores the response body in a
+         * Returns a {@code BodySubscriber} which stores the response body in a
          * file opened with the given options and name. The file will be opened
-         * with the given options using
-         * {@link java.nio.channels.FileChannel#open(java.nio.file.Path,java.nio.file.OpenOption...)
-         * FileChannel.open} just before the body is read. Any exception thrown will be returned
-         * or thrown from {@link HttpClient#send(jdk.incubator.http.HttpRequest,
-         * jdk.incubator.http.HttpResponse.BodyHandler) HttpClient::send}
-         * or {@link HttpClient#sendAsync(jdk.incubator.http.HttpRequest,
-         * jdk.incubator.http.HttpResponse.BodyHandler) HttpClient::sendAsync}
-         * as appropriate.
-         * <p>
-         * The {@link HttpResponse} using this processor is available after the
-         * entire response has been read.
+         * with the given options using {@link FileChannel#open(Path,OpenOption...)
+         * FileChannel.open} just before the body is read. Any exception thrown
+         * will be returned or thrown from {@link HttpClient#send(HttpRequest,
+         * BodyHandler) HttpClient::send} or {@link HttpClient#sendAsync(HttpRequest,
+         * BodyHandler) HttpClient::sendAsync} as appropriate.
+         *
+         * <p> The {@link HttpResponse} using this subscriber is available after
+         * the entire response has been read.
          *
          * @param file the file to store the body in
          * @param openOptions the list of options to open the file with
-         * @return a body processor
+         * @return a body subscriber
+         * @throws SecurityException If a security manager has been installed
+         *          and it denies {@link SecurityManager#checkWrite(String)
+         *          write access} to the file. The {@link
+         *          SecurityManager#checkDelete(String) checkDelete} method is
+         *          invoked to check delete access if the file is opened with the
+         *          {@code DELETE_ON_CLOSE} option.
          */
-        public static BodyProcessor<Path> asFile(Path file, OpenOption... openOptions) {
-            return new ResponseProcessors.PathProcessor(file, openOptions);
+        public static BodySubscriber<Path> asFile(Path file, OpenOption... openOptions) {
+            Objects.requireNonNull(file);
+            SecurityManager sm = System.getSecurityManager();
+            if (sm != null) {
+                String fn = pathForSecurityCheck(file);
+                sm.checkWrite(fn);
+                List<OpenOption> opts = Arrays.asList(openOptions);
+                if (opts.contains(StandardOpenOption.DELETE_ON_CLOSE))
+                    sm.checkDelete(fn);
+                if (opts.contains(StandardOpenOption.READ))
+                    sm.checkRead(fn);
+            }
+            return asFileImpl(file, openOptions);
         }
 
         /**
-         * Returns a {@code BodyProcessor} which provides the incoming body
-         * data to the provided Consumer of {@code Optional<byte[]>}. Each
-         * call to {@link Consumer#accept(java.lang.Object) Consumer.accept()}
-         * will contain a non empty {@code Optional}, except for the final invocation after
-         * all body data has been read, when the {@code Optional} will be empty.
-         * <p>
-         * The {@link HttpResponse} using this processor is available after the
-         * entire response has been read.
-         *
-         * @param consumer a Consumer of byte arrays
-         * @return a BodyProcessor
-         */
-        public static BodyProcessor<Void> asByteArrayConsumer(Consumer<Optional<byte[]>> consumer) {
-            return new ResponseProcessors.ConsumerProcessor(consumer);
-        }
-
-        /**
-         * Returns a {@code BodyProcessor} which stores the response body in a
+         * Returns a {@code BodySubscriber} which stores the response body in a
          * file opened with the given name. Has the same effect as calling
-         * {@link #asFile(java.nio.file.Path, java.nio.file.OpenOption...) asFile}
-         * with the standard open options {@code CREATE} and {@code WRITE}
-         * <p>
-         * The {@link HttpResponse} using this processor is available after the
-         * entire response has been read.
+         * {@link #asFile(Path, OpenOption...) asFile} with the standard open
+         * options {@code CREATE} and {@code WRITE}
+         *
+         * <p> The {@link HttpResponse} using this subscriber is available after
+         * the entire response has been read.
          *
          * @param file the file to store the body in
-         * @return a body processor
+         * @return a body subscriber
+         * @throws SecurityException if a security manager has been installed
+         *          and it denies {@link SecurityManager#checkWrite(String)
+         *          write access} to the file
          */
-        public static BodyProcessor<Path> asFile(Path file) {
-            return new ResponseProcessors.PathProcessor(
-                    file,
-                    StandardOpenOption.CREATE, StandardOpenOption.WRITE);
+        public static BodySubscriber<Path> asFile(Path file) {
+            return asFile(file, StandardOpenOption.CREATE, StandardOpenOption.WRITE);
         }
 
         /**
-         * Returns a response processor which discards the response body. The
+         * Returns a {@code BodySubscriber} which provides the incoming body
+         * data to the provided Consumer of {@code Optional<byte[]>}. Each
+         * call to {@link Consumer#accept(java.lang.Object) Consumer.accept()}
+         * will contain a non empty {@code Optional}, except for the final
+         * invocation after all body data has been read, when the {@code
+         * Optional} will be empty.
+         *
+         * <p> The {@link HttpResponse} using this subscriber is available after
+         * the entire response has been read.
+         *
+         * @param consumer a Consumer of byte arrays
+         * @return a BodySubscriber
+         */
+        public static BodySubscriber<Void> asByteArrayConsumer(Consumer<Optional<byte[]>> consumer) {
+            return new ResponseSubscribers.ConsumerSubscriber(consumer);
+        }
+
+        /**
+         * Returns a {@code BodySubscriber} which streams the response body as
+         * an {@link InputStream}.
+         *
+         * <p> The {@link HttpResponse} using this subscriber is available
+         * immediately after the response headers have been read, without
+         * requiring to wait for the entire body to be processed. The response
+         * body can then be read directly from the {@link InputStream}.
+         *
+         * @apiNote To ensure that all resources associated with the
+         * corresponding exchange are properly released the caller must
+         * ensure to either read all bytes until EOF is reached, or call
+         * {@link InputStream#close} if it is unable or unwilling to do so.
+         * Calling {@code close} before exhausting the stream may cause
+         * the underlying HTTP connection to be closed and prevent it
+         * from being reused for subsequent operations.
+         *
+         * @return a body subscriber that streams the response body as an
+         *         {@link InputStream}.
+         */
+        public static BodySubscriber<InputStream> asInputStream() {
+            return new ResponseSubscribers.HttpResponseInputStream();
+        }
+
+        /**
+         * Returns a response subscriber which discards the response body. The
          * supplied value is the value that will be returned from
          * {@link HttpResponse#body()}.
          *
          * @param <U> The type of the response body
-         * @param value the value to return from HttpResponse.body()
-         * @return a {@code BodyProcessor}
+         * @param value the value to return from HttpResponse.body(), may be {@code null}
+         * @return a {@code BodySubscriber}
          */
-        public static <U> BodyProcessor<U> discard(U value) {
-            return new ResponseProcessors.NullProcessor<>(Optional.ofNullable(value));
+        public static <U> BodySubscriber<U> discard(U value) {
+            return new ResponseSubscribers.NullSubscriber<>(Optional.ofNullable(value));
         }
+
+        /**
+         * Returns a {@code BodySubscriber} which buffers data before delivering
+         * it to the given downstream subscriber. The subscriber guarantees to
+         * deliver {@code buffersize} bytes of data to each invocation of the
+         * downstream's {@linkplain #onNext(Object) onNext} method, except for
+         * the final invocation, just before {@linkplain #onComplete() onComplete}
+         * is invoked. The final invocation of {@code onNext} may contain fewer
+         * than {@code buffersize} bytes.
+         *
+         * <p> The returned subscriber delegates its {@link #getBody()} method
+         * to the downstream subscriber.
+         *
+         * @param downstream the downstream subscriber
+         * @param bufferSize the buffer size
+         * @return a buffering body subscriber
+         * @throws IllegalArgumentException if {@code bufferSize <= 0}
+         */
+         public static <T> BodySubscriber<T> buffering(BodySubscriber<T> downstream,
+                                                       int bufferSize) {
+             if (bufferSize <= 0)
+                 throw new IllegalArgumentException("must be greater than 0");
+             return new BufferingSubscriber<T>(downstream, bufferSize);
+         }
     }
 
     /**
-     * A response processor for a HTTP/2 multi response.
+     * A response subscriber for a HTTP/2 multi response.
      * {@Incubating}
-     * <p>
-     * A multi response comprises a main response, and zero or more additional
+     *
+     * <p> A multi response comprises a main response, and zero or more additional
      * responses. Each additional response is sent by the server in response to
-     * requests that the server also generates. Additional responses are
+     * requests (PUSH_PROMISEs) that the server also generates. Additional responses are
      * typically resources that the server expects the client will need which
      * are related to the initial request.
      * <p>
      * Note. Instead of implementing this interface, applications should consider
      * first using the mechanism (built on this interface) provided by
-     * {@link MultiProcessor#asMap(java.util.function.Function, boolean)
-     * MultiProcessor.asMap()} which is a slightly simplified, but
+     * {@link MultiSubscriber#asMap(java.util.function.Function, boolean)
+     * MultiSubscriber.asMap()} which is a slightly simplified, but also
      * general purpose interface.
      * <p>
      * The server generated requests are also known as <i>push promises</i>.
@@ -556,7 +798,7 @@ public abstract class HttpResponse<T> {
      * the server does not wait for any acknowledgment before sending the
      * response, this must be done quickly to avoid unnecessary data transmission.
      *
-     * <p> {@code MultiProcessor}s are parameterized with a type {@code U} which
+     * <p> {@code MultiSubscriber}s are parameterized with a type {@code U} which
      * represents some meaningful aggregate of the responses received. This
      * would typically be a collection of response or response body objects.
      *
@@ -565,29 +807,43 @@ public abstract class HttpResponse<T> {
      *
      * @since 9
      */
-    public interface MultiProcessor<U,T> {
+    public interface MultiSubscriber<U,T> {
         /**
-         * Called for the main request and each push promise that is received.
-         * The first call will always be for the main request that was sent
-         * by the caller. This {@link HttpRequest} parameter
-         * represents the initial request or subsequent PUSH_PROMISE. The
-         * implementation must return an {@code Optional} of {@link BodyHandler} for
-         * the response body. Different handlers (of the same type) can be returned
-         * for different pushes within the same multi send. If no handler
-         * (an empty {@code Optional}) is returned, then the push will be canceled. It is
-         * an error to not return a valid {@code BodyHandler} for the initial (main) request.
+         * Called for the main request from the user. This {@link HttpRequest}
+         * parameter is the request that was supplied to {@link
+         * HttpClient#sendAsync(HttpRequest, MultiSubscriber)}. The
+         * implementation must return an {@link BodyHandler} for the response
+         * body.
          *
-         * @param request the main request or subsequent push promise
+         * @param request the request
          *
          * @return an optional body handler
          */
-        Optional<BodyHandler<T>> onRequest(HttpRequest request);
+        BodyHandler<T> onRequest(HttpRequest request);
+
+        /**
+         * Called for each push promise that is received. The {@link HttpRequest}
+         * parameter represents the PUSH_PROMISE. The implementation must return
+         * an {@code Optional} of {@link BodyHandler} for the response body.
+         * Different handlers (of the same type) can be returned for different
+         * pushes within the same multi send. If no handler (an empty {@code
+         * Optional}) is returned, then the push will be canceled. If required,
+         * the {@code CompletableFuture<Void>} supplied to the {@code
+         * onFinalPushPromise} parameter of {@link
+         * #completion(CompletableFuture, CompletableFuture)} can be used to
+         * determine when the final PUSH_PROMISE is received.
+         *
+         * @param pushPromise the push promise
+         *
+         * @return an optional body handler
+         */
+        Optional<BodyHandler<T>> onPushPromise(HttpRequest pushPromise);
 
         /**
          * Called for each response received. For each request either one of
          * onResponse() or onError() is guaranteed to be called, but not both.
          *
-         * [Note] The reason for switching to this callback interface rather
+         * <p> Note: The reason for switching to this callback interface rather
          * than using CompletableFutures supplied to onRequest() is that there
          * is a subtle interaction between those CFs and the CF returned from
          * completion() (or when onComplete() was called formerly). The completion()
@@ -615,9 +871,11 @@ public abstract class HttpResponse<T> {
          * Returns a {@link java.util.concurrent.CompletableFuture}{@code <U>}
          * which completes when the aggregate result object itself is available.
          * It is expected that the returned {@code CompletableFuture} will depend
-         * on one of the given {@code CompletableFuture<Void}s which themselves complete
-         * after all individual responses associated with the multi response
-         * have completed, or after all push promises have been received.
+         * on one of the given {@code CompletableFuture<Void}s which themselves
+         * complete after all individual responses associated with the multi
+         * response have completed, or after all push promises have been received.
+         * This method is called after {@link #onRequest(HttpRequest)} but
+         * before any other methods.
          *
          * @implNote Implementations might follow the pattern shown below
          * <pre>
@@ -653,47 +911,50 @@ public abstract class HttpResponse<T> {
          * generated push promise) is returned as a key of the map. The value
          * corresponding to each key is a
          * {@code CompletableFuture<HttpResponse<V>>}.
-         * <p>
-         * There are two ways to use these handlers, depending on the value of
-         * the <i>completion</I> parameter. If completion is true, then the
+         *
+         * <p> There are two ways to use these handlers, depending on the value
+         * of the <i>completion</I> parameter. If completion is true, then the
          * aggregated result will be available after all responses have
          * themselves completed. If <i>completion</i> is false, then the
          * aggregated result will be available immediately after the last push
          * promise was received. In the former case, this implies that all the
          * CompletableFutures in the map values will have completed. In the
          * latter case, they may or may not have completed yet.
-         * <p>
-         * The simplest way to use these handlers is to set completion to
+         *
+         * <p> The simplest way to use these handlers is to set completion to
          * {@code true}, and then all (results) values in the Map will be
          * accessible without blocking.
          * <p>
-         * See {@link #asMap(java.util.function.Function, boolean)
-         * }
+         * See {@link #asMap(java.util.function.Function, boolean)}
          * for a code sample of using this interface.
          *
-         * @param <V> the body type used for all responses
-         * @param pushHandler a function invoked for each request or push
-         * promise
-         * @param completion {@code true} if the aggregate CompletableFuture
-         * completes after all responses have been received, or {@code false}
-         * after all push promises received.
+         * <p> See {@link #asMap(Function, boolean)} for a code sample of using
+         * this interface.
          *
-         * @return a MultiProcessor
+         * @param <V> the body type used for all responses
+         * @param reqHandler a function invoked for the user's request and each
+         *                   push promise
+         * @param completion {@code true} if the aggregate CompletableFuture
+         *                   completes after all responses have been received,
+         *                   or {@code false} after all push promises received
+         *
+         * @return a MultiSubscriber
          */
-        public static <V> MultiProcessor<MultiMapResult<V>,V> asMap(
-            Function<HttpRequest, Optional<HttpResponse.BodyHandler<V>>> pushHandler,
+        public static <V> MultiSubscriber<MultiMapResult<V>,V> asMap(
+                Function<HttpRequest, Optional<HttpResponse.BodyHandler<V>>> reqHandler,
                 boolean completion) {
-
-            return new MultiProcessorImpl<V>(pushHandler, completion);
+            return new MultiSubscriberImpl<V>(reqHandler.andThen(optv -> optv.get()),
+                                              reqHandler,
+                                              completion);
         }
 
         /**
          * Returns a general purpose handler for multi responses. This is a
-         * convenience method which invokes {@link #asMap(java.util.function.Function,boolean)
+         * convenience method which invokes {@link #asMap(Function,boolean)
          * asMap(Function, true)} meaning that the aggregate result
          * object completes after all responses have been received.
-         * <p>
-         * <b>Example usage:</b>
+         *
+         * <p><b>Example usage:</b>
          * <br>
          * <pre>
          * {@code
@@ -705,26 +966,26 @@ public abstract class HttpResponse<T> {
          *          HttpClient client = HttpClient.newHttpClient();
          *
          *          Map<HttpRequest,CompletableFuture<HttpResponse<String>>> results = client
-         *              .sendAsync(request, MultiProcessor.asMap(
+         *              .sendAsync(request, MultiSubscriber.asMap(
          *                  (req) -> Optional.of(HttpResponse.BodyHandler.asString())))
          *              .join();
          * }</pre>
-         * <p>
-         * The lambda in this example is the simplest possible implementation,
+         *
+         * <p> The lambda in this example is the simplest possible implementation,
          * where neither the incoming requests are examined, nor the response
          * headers, and every push that the server sends is accepted. When the
          * join() call returns, all {@code HttpResponse}s and their associated
          * body objects are available.
          *
          * @param <V> the body type used for all responses
-         * @param pushHandler a function invoked for each request or push
-         * promise
-         * @return a MultiProcessor
+         * @param reqHandler a function invoked for each push promise and the
+         *                   main request
+         * @return a MultiSubscriber
          */
-        public static <V> MultiProcessor<MultiMapResult<V>,V> asMap(
-            Function<HttpRequest, Optional<HttpResponse.BodyHandler<V>>> pushHandler) {
+        public static <V> MultiSubscriber<MultiMapResult<V>,V> asMap(
+                Function<HttpRequest, Optional<HttpResponse.BodyHandler<V>>> reqHandler) {
 
-            return asMap(pushHandler, true);
+            return asMap(reqHandler, true);
         }
 
     }
diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/HttpResponseImpl.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/HttpResponseImpl.java
index c2a3c2ac8a5..589eec2f533 100644
--- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/HttpResponseImpl.java
+++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/HttpResponseImpl.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2017, 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,9 +28,10 @@ package jdk.incubator.http;
 import java.io.IOException;
 import java.net.URI;
 import java.nio.ByteBuffer;
+import java.util.Optional;
 import java.util.concurrent.CompletableFuture;
+import java.util.function.Supplier;
 import javax.net.ssl.SSLParameters;
-import jdk.incubator.http.internal.common.Log;
 import jdk.incubator.http.internal.websocket.RawChannel;
 
 /**
@@ -41,13 +42,11 @@ class HttpResponseImpl<T> extends HttpResponse<T> implements RawChannel.Provider
     final int responseCode;
     final Exchange<T> exchange;
     final HttpRequest initialRequest;
-    final HttpRequestImpl finalRequest;
+    final Optional<HttpResponse<T>> previousResponse;
     final HttpHeaders headers;
-    //final HttpHeaders trailers;
     final SSLParameters sslParameters;
     final URI uri;
     final HttpClient.Version version;
-    //final AccessControlContext acc;
     RawChannel rawchan;
     final HttpConnection connection;
     final Stream<T> stream;
@@ -55,41 +54,43 @@ class HttpResponseImpl<T> extends HttpResponse<T> implements RawChannel.Provider
 
     public HttpResponseImpl(HttpRequest initialRequest,
                             Response response,
-                            T body, Exchange<T> exch) {
+                            HttpResponse<T> previousResponse,
+                            T body,
+                            Exchange<T> exch) {
         this.responseCode = response.statusCode();
         this.exchange = exch;
         this.initialRequest = initialRequest;
-        this.finalRequest = exchange.request();
+        this.previousResponse = Optional.ofNullable(previousResponse);
         this.headers = response.headers();
         //this.trailers = trailers;
-        this.sslParameters = exch.client().sslParameters().orElse(null);
-        this.uri = finalRequest.uri();
+        this.sslParameters = exch.client().sslParameters();
+        this.uri = response.request().uri();
         this.version = response.version();
         this.connection = exch.exchImpl.connection();
         this.stream = null;
         this.body = body;
     }
 
-    // A response to a PUSH_PROMISE
-    public HttpResponseImpl(Response response,
-                            HttpRequestImpl pushRequest,
-                            ImmutableHeaders headers,
-                            Stream<T> stream,
-                            SSLParameters sslParameters,
-                            T body) {
-        this.responseCode = response.statusCode();
-        this.exchange = null;
-        this.initialRequest = null; // ## fix this
-        this.finalRequest = pushRequest;
-        this.headers = headers;
-        //this.trailers = null;
-        this.sslParameters = sslParameters;
-        this.uri = finalRequest.uri(); // TODO: take from headers
-        this.version = HttpClient.Version.HTTP_2;
-        this.connection = stream.connection();
-        this.stream = stream;
-        this.body = body;
-    }
+//    // A response to a PUSH_PROMISE
+//    public HttpResponseImpl(Response response,
+//                            HttpRequestImpl pushRequest,
+//                            ImmutableHeaders headers,
+//                            Stream<T> stream,
+//                            SSLParameters sslParameters,
+//                            T body) {
+//        this.responseCode = response.statusCode();
+//        this.exchange = null;
+//        this.initialRequest = null; // ## fix this
+//        this.finalRequest = pushRequest;
+//        this.headers = headers;
+//        //this.trailers = null;
+//        this.sslParameters = sslParameters;
+//        this.uri = finalRequest.uri(); // TODO: take from headers
+//        this.version = HttpClient.Version.HTTP_2;
+//        this.connection = stream.connection();
+//        this.stream = stream;
+//        this.body = body;
+//    }
 
     private ExchangeImpl<?> exchangeImpl() {
         return exchange != null ? exchange.exchImpl : stream;
@@ -106,8 +107,8 @@ class HttpResponseImpl<T> extends HttpResponse<T> implements RawChannel.Provider
     }
 
     @Override
-    public HttpRequest finalRequest() {
-        return finalRequest;
+    public Optional<HttpResponse<T>> previousResponse() {
+        return previousResponse;
     }
 
     @Override
@@ -162,31 +163,24 @@ class HttpResponseImpl<T> extends HttpResponse<T> implements RawChannel.Provider
             }
             // Http1Exchange may have some remaining bytes in its
             // internal buffer.
-            final ByteBuffer remaining =((Http1Exchange<?>)exchImpl).getBuffer();
-            rawchan = new RawChannelImpl(exchange.client(), connection, remaining);
+            Supplier<ByteBuffer> initial = ((Http1Exchange<?>)exchImpl)::drainLeftOverBytes;
+            rawchan = new RawChannelImpl(exchange.client(), connection, initial);
         }
         return rawchan;
     }
 
     @Override
-    public CompletableFuture<HttpHeaders> trailers() {
-        throw new UnsupportedOperationException("Not supported yet.");
-    }
-
-    static void logResponse(Response r) {
-        if (!Log.requests()) {
-            return;
-        }
+    public String toString() {
         StringBuilder sb = new StringBuilder();
-        String method = r.request().method();
-        URI uri = r.request().uri();
+        String method = request().method();
+        URI uri = request().uri();
         String uristring = uri == null ? "" : uri.toString();
         sb.append('(')
-                .append(method)
-                .append(" ")
-                .append(uristring)
-                .append(") ")
-                .append(r.statusCode());
-        Log.logResponse(sb.toString());
+          .append(method)
+          .append(" ")
+          .append(uristring)
+          .append(") ")
+          .append(statusCode());
+        return sb.toString();
     }
 }
diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/HttpTimeoutException.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/HttpTimeoutException.java
index eaeb0233676..84160fe8f13 100644
--- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/HttpTimeoutException.java
+++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/HttpTimeoutException.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2017, 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
diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/ImmutableHeaders.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/ImmutableHeaders.java
index 20644d0f9ff..0c23f8d4ab8 100644
--- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/ImmutableHeaders.java
+++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/ImmutableHeaders.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2017, 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,14 @@ package jdk.incubator.http;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
-import java.util.Optional;
-import java.util.OptionalLong;
 import java.util.TreeMap;
 import java.util.function.Predicate;
-import static java.util.Collections.emptyList;
 import static java.util.Collections.emptyMap;
 import static java.util.Collections.unmodifiableList;
 import static java.util.Collections.unmodifiableMap;
 import static java.util.Objects.requireNonNull;
 
-final class ImmutableHeaders implements HttpHeaders {
+final class ImmutableHeaders extends HttpHeaders {
 
     private final Map<String, List<String>> map;
 
@@ -71,25 +68,6 @@ final class ImmutableHeaders implements HttpHeaders {
         this.map = unmodifiableMap(m);
     }
 
-    @Override
-    public Optional<String> firstValue(String name) {
-        return allValues(name).stream().findFirst();
-    }
-
-    @Override
-    public OptionalLong firstValueAsLong(String name) {
-        return allValues(name).stream().mapToLong(Long::valueOf).findFirst();
-    }
-
-    @Override
-    public List<String> allValues(String name) {
-        requireNonNull(name);
-        List<String> values = map.get(name);
-        // Making unmodifiable list out of empty in order to make a list which
-        // throws UOE unconditionally
-        return values != null ? values : unmodifiableList(emptyList());
-    }
-
     @Override
     public Map<String, List<String>> map() {
         return map;
diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/MultiExchange.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/MultiExchange.java
index 44e34eef5b9..4c5da8ccf4a 100644
--- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/MultiExchange.java
+++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/MultiExchange.java
@@ -26,22 +26,23 @@
 package jdk.incubator.http;
 
 import java.io.IOException;
+import java.lang.System.Logger.Level;
 import java.time.Duration;
 import java.util.List;
 import java.security.AccessControlContext;
-import java.security.AccessController;
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.CompletionException;
 import java.util.concurrent.ExecutionException;
-import java.util.function.BiFunction;
 import java.util.concurrent.Executor;
-import java.util.function.UnaryOperator;
-
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.function.Function;
+import jdk.incubator.http.HttpResponse.UntrustedBodyHandler;
 import jdk.incubator.http.internal.common.Log;
 import jdk.incubator.http.internal.common.MinimalFuture;
-import jdk.incubator.http.internal.common.Pair;
+import jdk.incubator.http.internal.common.ConnectionExpiredException;
 import jdk.incubator.http.internal.common.Utils;
-import static jdk.incubator.http.internal.common.Pair.pair;
+import static jdk.incubator.http.internal.common.MinimalFuture.completedFuture;
+import static jdk.incubator.http.internal.common.MinimalFuture.failedFuture;
 
 /**
  * Encapsulates multiple Exchanges belonging to one HttpRequestImpl.
@@ -53,18 +54,25 @@ import static jdk.incubator.http.internal.common.Pair.pair;
  */
 class MultiExchange<U,T> {
 
+    static final boolean DEBUG = Utils.DEBUG; // Revisit: temporary dev flag.
+    static final System.Logger DEBUG_LOGGER =
+            Utils.getDebugLogger("MultiExchange"::toString, DEBUG);
+
     private final HttpRequest userRequest; // the user request
     private final HttpRequestImpl request; // a copy of the user request
     final AccessControlContext acc;
     final HttpClientImpl client;
     final HttpResponse.BodyHandler<T> responseHandler;
-    final ExecutorWrapper execWrapper;
     final Executor executor;
-    final HttpResponse.MultiProcessor<U,T> multiResponseHandler;
+    final HttpResponse.MultiSubscriber<U,T> multiResponseSubscriber;
+    final AtomicInteger attempts = new AtomicInteger();
     HttpRequestImpl currentreq; // used for async only
     Exchange<T> exchange; // the current exchange
     Exchange<T> previous;
-    int attempts;
+    volatile Throwable retryCause;
+    volatile boolean expiredOnce;
+    volatile HttpResponse<T> response = null;
+
     // Maximum number of times a request will be retried/redirected
     // for any reason
 
@@ -91,93 +99,55 @@ class MultiExchange<U,T> {
     /**
      * MultiExchange with one final response.
      */
-    MultiExchange(HttpRequest req,
+    MultiExchange(HttpRequest userRequest,
+                  HttpRequestImpl requestImpl,
                   HttpClientImpl client,
-                  HttpResponse.BodyHandler<T> responseHandler) {
+                  HttpResponse.BodyHandler<T> responseHandler,
+                  AccessControlContext acc) {
         this.previous = null;
-        this.userRequest = req;
-        this.request = new HttpRequestImpl(req);
+        this.userRequest = userRequest;
+        this.request = requestImpl;
         this.currentreq = request;
-        this.attempts = 0;
         this.client = client;
         this.filters = client.filterChain();
-        if (System.getSecurityManager() != null) {
-            this.acc = AccessController.getContext();
-        } else {
-            this.acc = null;
-        }
-        this.execWrapper = new ExecutorWrapper(client.executor(), acc);
-        this.executor = execWrapper.executor();
+        this.acc = acc;
+        this.executor = client.theExecutor();
         this.responseHandler = responseHandler;
+        if (acc != null) {
+            // Restricts the file publisher with the senders ACC, if any
+            if (responseHandler instanceof UntrustedBodyHandler)
+                ((UntrustedBodyHandler)this.responseHandler).setAccessControlContext(acc);
+        }
         this.exchange = new Exchange<>(request, this);
-        this.multiResponseHandler = null;
+        this.multiResponseSubscriber = null;
         this.pushGroup = null;
     }
 
     /**
      * MultiExchange with multiple responses (HTTP/2 server pushes).
      */
-    MultiExchange(HttpRequest req,
+    MultiExchange(HttpRequest userRequest,
+                  HttpRequestImpl requestImpl,
                   HttpClientImpl client,
-                  HttpResponse.MultiProcessor<U, T> multiResponseHandler) {
+                  HttpResponse.MultiSubscriber<U, T> multiResponseSubscriber,
+                  AccessControlContext acc) {
         this.previous = null;
-        this.userRequest = req;
-        this.request = new HttpRequestImpl(req);
+        this.userRequest = userRequest;
+        this.request = requestImpl;
         this.currentreq = request;
-        this.attempts = 0;
         this.client = client;
         this.filters = client.filterChain();
-        if (System.getSecurityManager() != null) {
-            this.acc = AccessController.getContext();
-        } else {
-            this.acc = null;
-        }
-        this.execWrapper = new ExecutorWrapper(client.executor(), acc);
-        this.executor = execWrapper.executor();
-        this.multiResponseHandler = multiResponseHandler;
-        this.pushGroup = new PushGroup<>(multiResponseHandler, request);
+        this.acc = acc;
+        this.executor = client.theExecutor();
+        this.multiResponseSubscriber = multiResponseSubscriber;
+        this.pushGroup = new PushGroup<>(multiResponseSubscriber, request, acc);
         this.exchange = new Exchange<>(request, this);
         this.responseHandler = pushGroup.mainResponseHandler();
     }
 
-    public HttpResponseImpl<T> response() throws IOException, InterruptedException {
-        HttpRequestImpl r = request;
-        if (r.duration() != null) {
-            timedEvent = new TimedEvent(r.duration());
-            client.registerTimer(timedEvent);
-        }
-        while (attempts < max_attempts) {
-            try {
-                attempts++;
-                Exchange<T> currExchange = getExchange();
-                requestFilters(r);
-                Response response = currExchange.response();
-                HttpRequestImpl newreq = responseFilters(response);
-                if (newreq == null) {
-                    if (attempts > 1) {
-                        Log.logError("Succeeded on attempt: " + attempts);
-                    }
-                    T body = currExchange.readBody(responseHandler);
-                    cancelTimer();
-                    return new HttpResponseImpl<>(userRequest, response, body, currExchange);
-                }
-                //response.body(HttpResponse.ignoreBody());
-                setExchange(new Exchange<>(newreq, this, acc));
-                r = newreq;
-            } catch (IOException e) {
-                if (cancelled) {
-                    throw new HttpTimeoutException("Request timed out");
-                }
-                throw e;
-            }
-        }
-        cancelTimer();
-        throw new IOException("Retry limit exceeded");
-    }
-
-    CompletableFuture<Void> multiCompletionCF() {
-        return pushGroup.groupResult();
-    }
+//    CompletableFuture<Void> multiCompletionCF() {
+//        return pushGroup.groupResult();
+//    }
 
     private synchronized Exchange<T> getExchange() {
         return exchange;
@@ -187,15 +157,18 @@ class MultiExchange<U,T> {
         return client;
     }
 
-    HttpClient.Redirect followRedirects() {
-        return client.followRedirects();
-    }
+//    HttpClient.Redirect followRedirects() {
+//        return client.followRedirects();
+//    }
 
     HttpClient.Version version() {
         return request.version().orElse(client.version());
     }
 
     private synchronized void setExchange(Exchange<T> exchange) {
+        if (this.exchange != null && exchange != this.exchange) {
+            this.exchange.released();
+        }
         this.exchange = exchange;
     }
 
@@ -229,114 +202,117 @@ class MultiExchange<U,T> {
         return null;
     }
 
-    public void cancel() {
-        cancelled = true;
-        getExchange().cancel();
-    }
+//    public void cancel() {
+//        cancelled = true;
+//        getExchange().cancel();
+//    }
 
     public void cancel(IOException cause) {
         cancelled = true;
         getExchange().cancel(cause);
     }
 
-    public CompletableFuture<HttpResponseImpl<T>> responseAsync() {
+    public CompletableFuture<HttpResponse<T>> responseAsync() {
         CompletableFuture<Void> start = new MinimalFuture<>();
-        CompletableFuture<HttpResponseImpl<T>> cf = responseAsync0(start);
+        CompletableFuture<HttpResponse<T>> cf = responseAsync0(start);
         start.completeAsync( () -> null, executor); // trigger execution
         return cf;
     }
 
-    private CompletableFuture<HttpResponseImpl<T>> responseAsync0(CompletableFuture<Void> start) {
+    private CompletableFuture<HttpResponse<T>>
+    responseAsync0(CompletableFuture<Void> start) {
         return start.thenCompose( v -> responseAsyncImpl())
-            .thenCompose((Response r) -> {
-                Exchange<T> exch = getExchange();
-                return exch.readBodyAsync(responseHandler)
-                        .thenApply((T body) ->  new HttpResponseImpl<>(userRequest, r, body, exch));
-            });
+                    .thenCompose((Response r) -> {
+                        Exchange<T> exch = getExchange();
+                        return exch.readBodyAsync(responseHandler)
+                            .thenApply((T body) -> {
+                                this.response =
+                                    new HttpResponseImpl<>(userRequest, r, this.response, body, exch);
+                                return this.response;
+                            });
+                    });
     }
 
     CompletableFuture<U> multiResponseAsync() {
         CompletableFuture<Void> start = new MinimalFuture<>();
-        CompletableFuture<HttpResponseImpl<T>> cf = responseAsync0(start);
+        CompletableFuture<HttpResponse<T>> cf = responseAsync0(start);
         CompletableFuture<HttpResponse<T>> mainResponse =
-                cf.thenApply((HttpResponseImpl<T> b) -> {
-                      multiResponseHandler.onResponse(b);
-                      return (HttpResponse<T>)b;
-                   });
-
+                cf.thenApply(b -> {
+                        multiResponseSubscriber.onResponse(b);
+                        pushGroup.noMorePushes(true);
+                        return b; });
         pushGroup.setMainResponse(mainResponse);
-        // set up house-keeping related to multi-response
-        mainResponse.thenAccept((r) -> {
-            // All push promises received by now.
-            pushGroup.noMorePushes(true);
-        });
-        CompletableFuture<U> res = multiResponseHandler.completion(pushGroup.groupResult(), pushGroup.pushesCF());
+        CompletableFuture<U> res = multiResponseSubscriber.completion(pushGroup.groupResult(),
+                                                                      pushGroup.pushesCF());
         start.completeAsync( () -> null, executor); // trigger execution
         return res;
     }
 
     private CompletableFuture<Response> responseAsyncImpl() {
         CompletableFuture<Response> cf;
-        if (++attempts > max_attempts) {
-            cf = MinimalFuture.failedFuture(new IOException("Too many retries"));
+        if (attempts.incrementAndGet() > max_attempts) {
+            cf = failedFuture(new IOException("Too many retries", retryCause));
         } else {
-            if (currentreq.duration() != null) {
-                timedEvent = new TimedEvent(currentreq.duration());
+            if (currentreq.timeout().isPresent()) {
+                timedEvent = new TimedEvent(currentreq.timeout().get());
                 client.registerTimer(timedEvent);
             }
             try {
-                // 1. Apply request filters
+                // 1. apply request filters
                 requestFilters(currentreq);
             } catch (IOException e) {
-                return MinimalFuture.failedFuture(e);
+                return failedFuture(e);
             }
             Exchange<T> exch = getExchange();
             // 2. get response
             cf = exch.responseAsync()
-                .thenCompose((Response response) -> {
-                    HttpRequestImpl newrequest = null;
-                    try {
-                        // 3. Apply response filters
-                        newrequest = responseFilters(response);
-                    } catch (IOException e) {
-                        return MinimalFuture.failedFuture(e);
-                    }
-                    // 4. Check filter result and repeat or continue
-                    if (newrequest == null) {
-                        if (attempts > 1) {
-                            Log.logError("Succeeded on attempt: " + attempts);
+                     .thenCompose((Response response) -> {
+                        HttpRequestImpl newrequest;
+                        try {
+                            // 3. apply response filters
+                            newrequest = responseFilters(response);
+                        } catch (IOException e) {
+                            return failedFuture(e);
                         }
-                        return MinimalFuture.completedFuture(response);
-                    } else {
-                        currentreq = newrequest;
-                        setExchange(new Exchange<>(currentreq, this, acc));
-                        //reads body off previous, and then waits for next response
-                        return responseAsyncImpl();
-                    }
-                })
-            // 5. Handle errors and cancel any timer set
-            .handle((response, ex) -> {
-                cancelTimer();
-                if (ex == null) {
-                    assert response != null;
-                    return MinimalFuture.completedFuture(response);
-                }
-                // all exceptions thrown are handled here
-                CompletableFuture<Response> error = getExceptionalCF(ex);
-                if (error == null) {
-                    return responseAsyncImpl();
-                } else {
-                    return error;
-                }
-            })
-            .thenCompose(UnaryOperator.identity());
+                        // 4. check filter result and repeat or continue
+                        if (newrequest == null) {
+                            if (attempts.get() > 1) {
+                                Log.logError("Succeeded on attempt: " + attempts);
+                            }
+                            return completedFuture(response);
+                        } else {
+                            this.response =
+                                new HttpResponseImpl<>(currentreq, response, this.response, null, exch);
+                            Exchange<T> oldExch = exch;
+                            return exch.ignoreBody().handle((r,t) -> {
+                                currentreq = newrequest;
+                                expiredOnce = false;
+                                setExchange(new Exchange<>(currentreq, this, acc));
+                                return responseAsyncImpl();
+                            }).thenCompose(Function.identity());
+                        } })
+                     .handle((response, ex) -> {
+                        // 5. handle errors and cancel any timer set
+                        cancelTimer();
+                        if (ex == null) {
+                            assert response != null;
+                            return completedFuture(response);
+                        }
+                        // all exceptions thrown are handled here
+                        CompletableFuture<Response> errorCF = getExceptionalCF(ex);
+                        if (errorCF == null) {
+                            return responseAsyncImpl();
+                        } else {
+                            return errorCF;
+                        } })
+                     .thenCompose(Function.identity());
         }
         return cf;
     }
 
     /**
-     * Take a Throwable and return a suitable CompletableFuture that is
-     * completed exceptionally.
+     * Takes a Throwable and returns a suitable CompletableFuture that is
+     * completed exceptionally, or null.
      */
     private CompletableFuture<Response> getExceptionalCF(Throwable t) {
         if ((t instanceof CompletionException) || (t instanceof ExecutionException)) {
@@ -346,8 +322,24 @@ class MultiExchange<U,T> {
         }
         if (cancelled && t instanceof IOException) {
             t = new HttpTimeoutException("request timed out");
+        } else if (t instanceof ConnectionExpiredException) {
+            // allow the retry mechanism to do its work
+            // ####: method (GET,HEAD, not POST?), no bytes written or read ( differentiate? )
+            if (t.getCause() != null) retryCause = t.getCause();
+            if (!expiredOnce) {
+                DEBUG_LOGGER.log(Level.DEBUG,
+                    "MultiExchange: ConnectionExpiredException (async): retrying...",
+                    t);
+                expiredOnce = true;
+                return null;
+            } else {
+                DEBUG_LOGGER.log(Level.DEBUG,
+                    "MultiExchange: ConnectionExpiredException (async): already retried once.",
+                    t);
+                if (t.getCause() != null) t = t.getCause();
+            }
         }
-        return MinimalFuture.failedFuture(t);
+        return failedFuture(t);
     }
 
     class TimedEvent extends TimeoutEvent {
@@ -356,6 +348,9 @@ class MultiExchange<U,T> {
         }
         @Override
         public void handle() {
+            DEBUG_LOGGER.log(Level.DEBUG,
+                    "Cancelling MultiExchange due to timeout for request %s",
+                     request);
             cancel(new HttpTimeoutException("request timed out"));
         }
     }
diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/MultiMapResult.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/MultiMapResult.java
index 4676d867ef9..7396709e8b0 100644
--- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/MultiMapResult.java
+++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/MultiMapResult.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2017, 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
@@ -35,7 +35,7 @@ import java.util.concurrent.CompletableFuture;
  * {@Incubating}
  * <p>
  * This is one possible implementation of the aggregate result type {@code <U>} returned
- * from {@link HttpClient#sendAsync(HttpRequest,MultiProcessor) }.
+ * from {@link HttpClient#sendAsync(HttpRequest,HttpResponse.MultiSubscriber) }.
  * The map is indexed by {@link HttpRequest} and each value is a
  * {@link java.util.concurrent.CompletableFuture}&lt;
  * {@link HttpResponse}{@code <V>}&gt;
@@ -44,9 +44,9 @@ import java.util.concurrent.CompletableFuture;
  * <p>
  * {@link CompletableFuture}&lt;{@code MultiMapResult<V>}&gt;
  * {@link HttpClient#sendAsync(HttpRequest,
- * HttpResponse.MultiProcessor) HttpClient.sendAsync(}{@link
- * HttpResponse.MultiProcessor#asMap(java.util.function.Function)
- * MultiProcessor.asMap(Function)})
+ * HttpResponse.MultiSubscriber) HttpClient.sendAsync(}{@link
+ * HttpResponse.MultiSubscriber#asMap(java.util.function.Function)
+ * MultiSubscriber.asMap(Function)})
  *
  * @param <V> the response body type for all responses
  */
@@ -117,4 +117,3 @@ public class MultiMapResult<V> implements Map<HttpRequest,CompletableFuture<Http
         return map.entrySet();
     }
 }
-
diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/PlainHttpConnection.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/PlainHttpConnection.java
index 55bf82b29fe..38ecd4065d5 100644
--- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/PlainHttpConnection.java
+++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/PlainHttpConnection.java
@@ -26,73 +26,42 @@
 package jdk.incubator.http;
 
 import java.io.IOException;
+import java.lang.System.Logger.Level;
 import java.net.InetSocketAddress;
 import java.net.StandardSocketOptions;
 import java.nio.ByteBuffer;
 import java.nio.channels.SelectableChannel;
 import java.nio.channels.SelectionKey;
 import java.nio.channels.SocketChannel;
+import java.security.AccessController;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
 import java.util.concurrent.CompletableFuture;
-import java.util.function.Consumer;
-import java.util.function.Supplier;
-
-import jdk.incubator.http.internal.common.AsyncWriteQueue;
-import jdk.incubator.http.internal.common.ByteBufferReference;
+import jdk.incubator.http.internal.common.FlowTube;
 import jdk.incubator.http.internal.common.Log;
 import jdk.incubator.http.internal.common.MinimalFuture;
 import jdk.incubator.http.internal.common.Utils;
 
 /**
- * Plain raw TCP connection direct to destination. 2 modes
- * 1) Blocking used by http/1. In this case the connect is actually non
- *    blocking but the request is sent blocking. The first byte of a response
- *    is received non-blocking and the remainder of the response is received
- *    blocking
- * 2) Non-blocking. In this case (for http/2) the connection is actually opened
- *    blocking but all reads and writes are done non-blocking under the
- *    control of a Http2Connection object.
+ * Plain raw TCP connection direct to destination.
+ * The connection operates in asynchronous non-blocking mode.
+ * All reads and writes are done non-blocking.
  */
-class PlainHttpConnection extends HttpConnection implements AsyncConnection {
+class PlainHttpConnection extends HttpConnection {
 
+    private final Object reading = new Object();
     protected final SocketChannel chan;
+    private final FlowTube tube;
+    private final PlainHttpPublisher writePublisher = new PlainHttpPublisher(reading);
     private volatile boolean connected;
     private boolean closed;
 
     // should be volatile to provide proper synchronization(visibility) action
-    private volatile Consumer<ByteBufferReference> asyncReceiver;
-    private volatile Consumer<Throwable> errorReceiver;
-    private volatile Supplier<ByteBufferReference> readBufferSupplier;
-    private boolean asyncReading;
 
-    private final AsyncWriteQueue asyncOutputQ = new AsyncWriteQueue(this::asyncOutput);
-
-    private final Object reading = new Object();
-
-    @Override
-    public void startReading() {
-        try {
-            synchronized(reading) {
-                asyncReading = true;
-            }
-            client.registerEvent(new ReadEvent());
-        } catch (IOException e) {
-            shutdown();
-        }
-    }
-
-    @Override
-    public void stopAsyncReading() {
-        synchronized(reading) {
-            asyncReading = false;
-        }
-        client.cancelRegistration(chan);
-    }
-
-    class ConnectEvent extends AsyncEvent {
-        CompletableFuture<Void> cf;
+    final class ConnectEvent extends AsyncEvent {
+        private final CompletableFuture<Void> cf;
 
         ConnectEvent(CompletableFuture<Void> cf) {
-            super(AsyncEvent.BLOCKING);
             this.cf = cf;
         }
 
@@ -109,38 +78,53 @@ class PlainHttpConnection extends HttpConnection implements AsyncConnection {
         @Override
         public void handle() {
             try {
-                chan.finishConnect();
-            } catch (IOException e) {
-                cf.completeExceptionally(e);
-                return;
+                assert !connected : "Already connected";
+                assert !chan.isBlocking() : "Unexpected blocking channel";
+                debug.log(Level.DEBUG, "ConnectEvent: finishing connect");
+                boolean finished = chan.finishConnect();
+                assert finished : "Expected channel to be connected";
+                debug.log(Level.DEBUG,
+                          "ConnectEvent: connect finished: %s", finished);
+                connected = true;
+                // complete async since the event runs on the SelectorManager thread
+                cf.completeAsync(() -> null, client().theExecutor());
+            } catch (Throwable e) {
+                client().theExecutor().execute( () -> cf.completeExceptionally(e));
             }
-            connected = true;
-            cf.complete(null);
         }
 
         @Override
-        public void abort() {
+        public void abort(IOException ioe) {
             close();
+            client().theExecutor().execute( () -> cf.completeExceptionally(ioe));
         }
     }
 
     @Override
     public CompletableFuture<Void> connectAsync() {
-        CompletableFuture<Void> plainFuture = new MinimalFuture<>();
+        CompletableFuture<Void> cf = new MinimalFuture<>();
         try {
-            chan.configureBlocking(false);
-            chan.connect(address);
-            client.registerEvent(new ConnectEvent(plainFuture));
-        } catch (IOException e) {
-            plainFuture.completeExceptionally(e);
+            assert !connected : "Already connected";
+            assert !chan.isBlocking() : "Unexpected blocking channel";
+            boolean finished = false;
+            PrivilegedExceptionAction<Boolean> pa = () -> chan.connect(address);
+            try {
+                 finished = AccessController.doPrivileged(pa);
+            } catch (PrivilegedActionException e) {
+                cf.completeExceptionally(e.getCause());
+            }
+            if (finished) {
+                debug.log(Level.DEBUG, "connect finished without blocking");
+                connected = true;
+                cf.complete(null);
+            } else {
+                debug.log(Level.DEBUG, "registering connect event");
+                client().registerEvent(new ConnectEvent(cf));
+            }
+        } catch (Throwable throwable) {
+            cf.completeExceptionally(throwable);
         }
-        return plainFuture;
-    }
-
-    @Override
-    public void connect() throws IOException {
-        chan.connect(address);
-        connected = true;
+        return cf;
     }
 
     @Override
@@ -148,106 +132,29 @@ class PlainHttpConnection extends HttpConnection implements AsyncConnection {
         return chan;
     }
 
+    @Override
+    final FlowTube getConnectionFlow() {
+        return tube;
+    }
+
     PlainHttpConnection(InetSocketAddress addr, HttpClientImpl client) {
         super(addr, client);
         try {
             this.chan = SocketChannel.open();
+            chan.configureBlocking(false);
             int bufsize = client.getReceiveBufferSize();
             chan.setOption(StandardSocketOptions.SO_RCVBUF, bufsize);
             chan.setOption(StandardSocketOptions.TCP_NODELAY, true);
+            // wrap the connected channel in a Tube for async reading and writing
+            tube = new SocketTube(client(), chan, Utils::getBuffer);
         } catch (IOException e) {
             throw new InternalError(e);
         }
     }
 
     @Override
-    long write(ByteBuffer[] buffers, int start, int number) throws IOException {
-        if (getMode() != Mode.ASYNC) {
-            return chan.write(buffers, start, number);
-        }
-        // async
-        buffers = Utils.reduce(buffers, start, number);
-        long n = Utils.remaining(buffers);
-        asyncOutputQ.put(ByteBufferReference.toReferences(buffers));
-        flushAsync();
-        return n;
-    }
+    HttpPublisher publisher() { return writePublisher; }
 
-    @Override
-    long write(ByteBuffer buffer) throws IOException {
-        if (getMode() != Mode.ASYNC) {
-            return chan.write(buffer);
-        }
-        // async
-        long n = buffer.remaining();
-        asyncOutputQ.put(ByteBufferReference.toReferences(buffer));
-        flushAsync();
-        return n;
-    }
-
-    // handle registered WriteEvent; invoked from SelectorManager thread
-    void flushRegistered() {
-        if (getMode() == Mode.ASYNC) {
-            try {
-                asyncOutputQ.flushDelayed();
-            } catch (IOException e) {
-                // Only IOException caused by closed Queue is expected here
-                shutdown();
-            }
-        }
-    }
-
-    @Override
-    public void writeAsync(ByteBufferReference[] buffers) throws IOException {
-        if (getMode() != Mode.ASYNC) {
-            chan.write(ByteBufferReference.toBuffers(buffers));
-            ByteBufferReference.clear(buffers);
-        } else {
-            asyncOutputQ.put(buffers);
-        }
-    }
-
-    @Override
-    public void writeAsyncUnordered(ByteBufferReference[] buffers) throws IOException {
-        if (getMode() != Mode.ASYNC) {
-            chan.write(ByteBufferReference.toBuffers(buffers));
-            ByteBufferReference.clear(buffers);
-        } else {
-            // Unordered frames are sent before existing frames.
-            asyncOutputQ.putFirst(buffers);
-        }
-    }
-
-    @Override
-    public void flushAsync() throws IOException {
-        if (getMode() == Mode.ASYNC) {
-            asyncOutputQ.flush();
-        }
-    }
-
-    @Override
-    public void enableCallback() {
-        // not used
-        assert false;
-    }
-
-    boolean asyncOutput(ByteBufferReference[] refs, AsyncWriteQueue delayCallback) {
-        try {
-            ByteBuffer[] bufs = ByteBufferReference.toBuffers(refs);
-            while (Utils.remaining(bufs) > 0) {
-                long n = chan.write(bufs);
-                if (n == 0) {
-                    delayCallback.setDelayed(refs);
-                    client.registerEvent(new WriteEvent());
-                    return false;
-                }
-            }
-            ByteBufferReference.clear(refs);
-        } catch (IOException e) {
-            shutdown();
-        }
-        return true;
-    }
 
     @Override
     public String toString() {
@@ -255,7 +162,7 @@ class PlainHttpConnection extends HttpConnection implements AsyncConnection {
     }
 
     /**
-     * Close this connection
+     * Closes this connection
      */
     @Override
     public synchronized void close() {
@@ -264,80 +171,23 @@ class PlainHttpConnection extends HttpConnection implements AsyncConnection {
         }
         closed = true;
         try {
-            Log.logError("Closing: " + toString());
+            Log.logTrace("Closing: " + toString());
             chan.close();
         } catch (IOException e) {}
     }
 
     @Override
     void shutdownInput() throws IOException {
+        debug.log(Level.DEBUG, "Shutting down input");
         chan.shutdownInput();
     }
 
     @Override
     void shutdownOutput() throws IOException {
+        debug.log(Level.DEBUG, "Shutting down output");
         chan.shutdownOutput();
     }
 
-    void shutdown() {
-        close();
-        errorReceiver.accept(new IOException("Connection aborted"));
-    }
-
-    void asyncRead() {
-        synchronized (reading) {
-            try {
-                while (asyncReading) {
-                    ByteBufferReference buf = readBufferSupplier.get();
-                    int n = chan.read(buf.get());
-                    if (n == -1) {
-                        throw new IOException();
-                    }
-                    if (n == 0) {
-                        buf.clear();
-                        return;
-                    }
-                    buf.get().flip();
-                    asyncReceiver.accept(buf);
-                }
-            } catch (IOException e) {
-                shutdown();
-            }
-        }
-    }
-
-    @Override
-    protected ByteBuffer readImpl() throws IOException {
-        ByteBuffer dst = ByteBuffer.allocate(8192);
-        int n = readImpl(dst);
-        if (n > 0) {
-            return dst;
-        } else if (n == 0) {
-            return Utils.EMPTY_BYTEBUFFER;
-        } else {
-            return null;
-        }
-    }
-
-    private int readImpl(ByteBuffer buf) throws IOException {
-        int mark = buf.position();
-        int n;
-        // FIXME: this hack works in conjunction with the corresponding change
-        // in jdk.incubator.http.RawChannel.registerEvent
-        //if ((n = buffer.remaining()) != 0) {
-            //buf.put(buffer);
-        //} else {
-            n = chan.read(buf);
-        //}
-        if (n == -1) {
-            return -1;
-        }
-        Utils.flipToMark(buf, mark);
-        // String s = "Receive (" + n + " bytes) ";
-        //debugPrint(s, buf);
-        return n;
-    }
-
     @Override
     ConnectionPool.CacheKey cacheKey() {
         return new ConnectionPool.CacheKey(address, null);
@@ -348,98 +198,6 @@ class PlainHttpConnection extends HttpConnection implements AsyncConnection {
         return connected;
     }
 
-    // used for all output in HTTP/2
-    class WriteEvent extends AsyncEvent {
-        WriteEvent() {
-            super(0);
-        }
-
-        @Override
-        public SelectableChannel channel() {
-            return chan;
-        }
-
-        @Override
-        public int interestOps() {
-            return SelectionKey.OP_WRITE;
-        }
-
-        @Override
-        public void handle() {
-            flushRegistered();
-        }
-
-        @Override
-        public void abort() {
-            shutdown();
-        }
-    }
-
-    // used for all input in HTTP/2
-    class ReadEvent extends AsyncEvent {
-        ReadEvent() {
-            super(AsyncEvent.REPEATING); // && !BLOCKING
-        }
-
-        @Override
-        public SelectableChannel channel() {
-            return chan;
-        }
-
-        @Override
-        public int interestOps() {
-            return SelectionKey.OP_READ;
-        }
-
-        @Override
-        public void handle() {
-            asyncRead();
-        }
-
-        @Override
-        public void abort() {
-            shutdown();
-        }
-
-        @Override
-        public String toString() {
-            return super.toString() + "/" + chan;
-        }
-    }
-
-    // used in blocking channels only
-    class ReceiveResponseEvent extends AsyncEvent {
-        CompletableFuture<Void> cf;
-
-        ReceiveResponseEvent(CompletableFuture<Void> cf) {
-            super(AsyncEvent.BLOCKING);
-            this.cf = cf;
-        }
-        @Override
-        public SelectableChannel channel() {
-            return chan;
-        }
-
-        @Override
-        public void handle() {
-            cf.complete(null);
-        }
-
-        @Override
-        public int interestOps() {
-            return SelectionKey.OP_READ;
-        }
-
-        @Override
-        public void abort() {
-            close();
-        }
-
-        @Override
-        public String toString() {
-            return super.toString() + "/" + chan;
-        }
-    }
 
     @Override
     boolean isSecure() {
@@ -451,24 +209,91 @@ class PlainHttpConnection extends HttpConnection implements AsyncConnection {
         return false;
     }
 
-    @Override
-    public void setAsyncCallbacks(Consumer<ByteBufferReference> asyncReceiver,
-                                  Consumer<Throwable> errorReceiver,
-                                  Supplier<ByteBufferReference> readBufferSupplier) {
-        this.asyncReceiver = asyncReceiver;
-        this.errorReceiver = errorReceiver;
-        this.readBufferSupplier = readBufferSupplier;
+    // Support for WebSocket/RawChannelImpl which unfortunately
+    // still depends on synchronous read/writes.
+    // It should be removed when RawChannelImpl moves to using asynchronous APIs.
+    private static final class PlainDetachedChannel
+            extends DetachedConnectionChannel {
+        final PlainHttpConnection plainConnection;
+        boolean closed;
+        PlainDetachedChannel(PlainHttpConnection conn) {
+            // We're handing the connection channel over to a web socket.
+            // We need the selector manager's thread to stay alive until
+            // the WebSocket is closed.
+            conn.client().webSocketOpen();
+            this.plainConnection = conn;
+        }
+
+        @Override
+        SocketChannel channel() {
+            return plainConnection.channel();
+        }
+
+        @Override
+        ByteBuffer read() throws IOException {
+            ByteBuffer dst = ByteBuffer.allocate(8192);
+            int n = readImpl(dst);
+            if (n > 0) {
+                return dst;
+            } else if (n == 0) {
+                return Utils.EMPTY_BYTEBUFFER;
+            } else {
+                return null;
+            }
+        }
+
+        @Override
+        public void close() {
+            HttpClientImpl client = plainConnection.client();
+            try {
+                plainConnection.close();
+            } finally {
+                // notify the HttpClientImpl that the websocket is no
+                // no longer operating.
+                synchronized(this) {
+                    if (closed == true) return;
+                    closed = true;
+                }
+                client.webSocketClose();
+            }
+        }
+
+        @Override
+        public long write(ByteBuffer[] buffers, int start, int number)
+                throws IOException
+        {
+            return channel().write(buffers, start, number);
+        }
+
+        @Override
+        public void shutdownInput() throws IOException {
+            plainConnection.shutdownInput();
+        }
+
+        @Override
+        public void shutdownOutput() throws IOException {
+            plainConnection.shutdownOutput();
+        }
+
+        private int readImpl(ByteBuffer buf) throws IOException {
+            int mark = buf.position();
+            int n;
+            n = channel().read(buf);
+            if (n == -1) {
+                return -1;
+            }
+            Utils.flipToMark(buf, mark);
+            return n;
+        }
     }
 
+    // Support for WebSocket/RawChannelImpl which unfortunately
+    // still depends on synchronous read/writes.
+    // It should be removed when RawChannelImpl moves to using asynchronous APIs.
     @Override
-    CompletableFuture<Void> whenReceivingResponse() {
-        CompletableFuture<Void> cf = new MinimalFuture<>();
-        try {
-            ReceiveResponseEvent evt = new ReceiveResponseEvent(cf);
-            client.registerEvent(evt);
-        } catch (IOException e) {
-            cf.completeExceptionally(e);
-        }
-        return cf;
+    DetachedConnectionChannel detachChannel() {
+        client().cancelRegistration(channel());
+        return new PlainDetachedChannel(this);
     }
+
 }
diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/PlainProxyConnection.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/PlainProxyConnection.java
index fd2230241b3..97ed4d05165 100644
--- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/PlainProxyConnection.java
+++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/PlainProxyConnection.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2017, 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
diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/PlainTunnelingConnection.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/PlainTunnelingConnection.java
index c167c53f2b3..98eea0c9818 100644
--- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/PlainTunnelingConnection.java
+++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/PlainTunnelingConnection.java
@@ -25,71 +25,27 @@
 
 package jdk.incubator.http;
 
-import jdk.incubator.http.internal.common.ByteBufferReference;
-import jdk.incubator.http.internal.common.MinimalFuture;
-import jdk.incubator.http.HttpResponse.BodyHandler;
-
 import java.io.IOException;
+import java.lang.System.Logger.Level;
 import java.net.InetSocketAddress;
 import java.nio.ByteBuffer;
 import java.nio.channels.SocketChannel;
 import java.util.concurrent.CompletableFuture;
-import java.util.function.Consumer;
-import java.util.function.Supplier;
+import jdk.incubator.http.internal.common.FlowTube;
+import jdk.incubator.http.internal.common.MinimalFuture;
+import static jdk.incubator.http.HttpResponse.BodyHandler.discard;
 
 /**
  * A plain text socket tunnel through a proxy. Uses "CONNECT" but does not
  * encrypt. Used by WebSocket, as well as HTTP over SSL + Proxy.
  * Wrapped in SSLTunnelConnection or AsyncSSLTunnelConnection for encryption.
  */
-class PlainTunnelingConnection extends HttpConnection implements AsyncConnection {
+final class PlainTunnelingConnection extends HttpConnection {
 
     final PlainHttpConnection delegate;
     protected final InetSocketAddress proxyAddr;
     private volatile boolean connected;
 
-    @Override
-    public CompletableFuture<Void> connectAsync() {
-        return delegate.connectAsync()
-            .thenCompose((Void v) -> {
-                HttpRequestImpl req = new HttpRequestImpl("CONNECT", client, address);
-                MultiExchange<Void,Void> mconnectExchange = new MultiExchange<>(req, client, this::ignore);
-                return mconnectExchange.responseAsync()
-                    .thenCompose((HttpResponseImpl<Void> resp) -> {
-                        CompletableFuture<Void> cf = new MinimalFuture<>();
-                        if (resp.statusCode() != 200) {
-                            cf.completeExceptionally(new IOException("Tunnel failed"));
-                        } else {
-                            connected = true;
-                            cf.complete(null);
-                        }
-                        return cf;
-                    });
-            });
-    }
-
-    private HttpResponse.BodyProcessor<Void> ignore(int status, HttpHeaders hdrs) {
-        return HttpResponse.BodyProcessor.discard((Void)null);
-    }
-
-    @Override
-    public void connect() throws IOException, InterruptedException {
-        delegate.connect();
-        HttpRequestImpl req = new HttpRequestImpl("CONNECT", client, address);
-        MultiExchange<Void,Void> mul = new MultiExchange<>(req, client, BodyHandler.<Void>discard(null));
-        Exchange<Void> connectExchange = new Exchange<>(req, mul);
-        Response r = connectExchange.responseImpl(delegate);
-        if (r.statusCode() != 200) {
-            throw new IOException("Tunnel failed");
-        }
-        connected = true;
-    }
-
-    @Override
-    boolean connected() {
-        return connected;
-    }
-
     protected PlainTunnelingConnection(InetSocketAddress addr,
                                        InetSocketAddress proxy,
                                        HttpClientImpl client) {
@@ -98,41 +54,62 @@ class PlainTunnelingConnection extends HttpConnection implements AsyncConnection
         delegate = new PlainHttpConnection(proxy, client);
     }
 
+    @Override
+    public CompletableFuture<Void> connectAsync() {
+        debug.log(Level.DEBUG, "Connecting plain connection");
+        return delegate.connectAsync()
+            .thenCompose((Void v) -> {
+                debug.log(Level.DEBUG, "sending HTTP/1.1 CONNECT");
+                HttpClientImpl client = client();
+                assert client != null;
+                HttpRequestImpl req = new HttpRequestImpl("CONNECT", address);
+                MultiExchange<Void,Void> mulEx = new MultiExchange<>(null, req, client, discard(null), null);
+                Exchange<Void> connectExchange = new Exchange<>(req, mulEx);
+
+                return connectExchange
+                        .responseAsyncImpl(delegate)
+                        .thenCompose((Response resp) -> {
+                            CompletableFuture<Void> cf = new MinimalFuture<>();
+                            debug.log(Level.DEBUG, "got response: %d", resp.statusCode());
+                            if (resp.statusCode() != 200) {
+                                cf.completeExceptionally(new IOException(
+                                        "Tunnel failed, got: "+ resp.statusCode()));
+                            } else {
+                                // get the initial/remaining bytes
+                                ByteBuffer b = ((Http1Exchange<?>)connectExchange.exchImpl).drainLeftOverBytes();
+                                int remaining = b.remaining();
+                                assert remaining == 0: "Unexpected remaining: " + remaining;
+                                connected = true;
+                                cf.complete(null);
+                            }
+                            return cf;
+                        });
+            });
+    }
+
+    @Override
+    HttpPublisher publisher() { return delegate.publisher(); }
+
+    @Override
+    boolean connected() {
+        return connected;
+    }
+
     @Override
     SocketChannel channel() {
         return delegate.channel();
     }
 
+    @Override
+    FlowTube getConnectionFlow() {
+        return delegate.getConnectionFlow();
+    }
+
     @Override
     ConnectionPool.CacheKey cacheKey() {
         return new ConnectionPool.CacheKey(null, proxyAddr);
     }
 
-    @Override
-    long write(ByteBuffer[] buffers, int start, int number) throws IOException {
-        return delegate.write(buffers, start, number);
-    }
-
-    @Override
-    long write(ByteBuffer buffer) throws IOException {
-        return delegate.write(buffer);
-    }
-
-    @Override
-    public void writeAsync(ByteBufferReference[] buffers) throws IOException {
-        delegate.writeAsync(buffers);
-    }
-
-    @Override
-    public void writeAsyncUnordered(ByteBufferReference[] buffers) throws IOException {
-        delegate.writeAsyncUnordered(buffers);
-    }
-
-    @Override
-    public void flushAsync() throws IOException {
-        delegate.flushAsync();
-    }
-
     @Override
     public void close() {
         delegate.close();
@@ -149,16 +126,6 @@ class PlainTunnelingConnection extends HttpConnection implements AsyncConnection
         delegate.shutdownOutput();
     }
 
-    @Override
-    CompletableFuture<Void> whenReceivingResponse() {
-        return delegate.whenReceivingResponse();
-    }
-
-    @Override
-    protected ByteBuffer readImpl() throws IOException {
-        return delegate.readImpl();
-    }
-
     @Override
     boolean isSecure() {
         return false;
@@ -169,31 +136,11 @@ class PlainTunnelingConnection extends HttpConnection implements AsyncConnection
         return true;
     }
 
+    // Support for WebSocket/RawChannelImpl which unfortunately
+    // still depends on synchronous read/writes.
+    // It should be removed when RawChannelImpl moves to using asynchronous APIs.
     @Override
-    public void setAsyncCallbacks(Consumer<ByteBufferReference> asyncReceiver,
-            Consumer<Throwable> errorReceiver,
-            Supplier<ByteBufferReference> readBufferSupplier) {
-        delegate.setAsyncCallbacks(asyncReceiver, errorReceiver, readBufferSupplier);
-    }
-
-    @Override
-    public void startReading() {
-        delegate.startReading();
-    }
-
-    @Override
-    public void stopAsyncReading() {
-        delegate.stopAsyncReading();
-    }
-
-    @Override
-    public void enableCallback() {
-        delegate.enableCallback();
-    }
-
-    @Override
-    synchronized void configureMode(Mode mode) throws IOException {
-        super.configureMode(mode);
-        delegate.configureMode(mode);
+    DetachedConnectionChannel detachChannel() {
+        return delegate.detachChannel();
     }
 }
diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/PrivilegedExecutor.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/PrivilegedExecutor.java
new file mode 100644
index 00000000000..da91dc0ae09
--- /dev/null
+++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/PrivilegedExecutor.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2015, 2017, 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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 jdk.incubator.http;
+
+import java.security.AccessControlContext;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.Objects;
+import java.util.concurrent.Executor;
+
+/**
+ * Executes tasks within a given access control context, and by a given executor.
+ */
+class PrivilegedExecutor implements Executor {
+
+    /** The underlying executor. May be provided by the user. */
+    final Executor executor;
+    /** The ACC to execute the tasks within. */
+    final AccessControlContext acc;
+
+    public PrivilegedExecutor(Executor executor, AccessControlContext acc) {
+        Objects.requireNonNull(executor);
+        Objects.requireNonNull(acc);
+        this.executor = executor;
+        this.acc = acc;
+    }
+
+    private static class PrivilegedRunnable implements Runnable {
+        private final Runnable r;
+        private final AccessControlContext acc;
+        PrivilegedRunnable(Runnable r, AccessControlContext acc) {
+            this.r = r;
+            this.acc = acc;
+        }
+        @Override
+        public void run() {
+            PrivilegedAction<Void> pa = () -> { r.run(); return null; };
+            AccessController.doPrivileged(pa, acc);
+        }
+    }
+
+    @Override
+    public void execute(Runnable r) {
+        executor.execute(new PrivilegedRunnable(r, acc));
+    }
+}
diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/PseudoPublisher.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/PseudoPublisher.java
deleted file mode 100644
index 5945cd95c72..00000000000
--- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/PseudoPublisher.java
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- * Copyright (c) 2016, 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.  Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * 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 jdk.incubator.http;
-
-import java.util.concurrent.Flow;
-import java.util.concurrent.atomic.AtomicBoolean;
-
-// Completes the subscription on first request. Never calls onNext()
-
-class PseudoPublisher<T> implements Flow.Publisher<T> {
-
-    private final Throwable throwable;
-
-    PseudoPublisher() {
-        this(null);
-    }
-
-    PseudoPublisher(Throwable throwable) {
-        this.throwable = throwable;
-    }
-
-    @Override
-    public void subscribe(Flow.Subscriber<? super T> subscriber) {
-        subscriber.onSubscribe(new Subscription(subscriber));
-    }
-
-    private class Subscription implements Flow.Subscription {
-
-        private final Flow.Subscriber<? super T> subscriber;
-        private final AtomicBoolean done = new AtomicBoolean();
-
-        Subscription(Flow.Subscriber<? super T> subscriber) {
-            this.subscriber = subscriber;
-        }
-
-        @Override
-        public void request(long n) {
-            if (done.compareAndSet(false, true)) {
-                if (n > 0) {
-                    if (throwable == null) {
-                        subscriber.onComplete();
-                    } else {
-                        subscriber.onError(throwable);
-                    }
-                } else {
-                    subscriber.onError(new IllegalArgumentException("request(" + n + ")"));
-                }
-            }
-        }
-
-        @Override
-        public void cancel() {
-            done.set(true);
-        }
-
-    }
-}
diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/PullPublisher.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/PullPublisher.java
index e6880fb60cb..89ed515172a 100644
--- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/PullPublisher.java
+++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/PullPublisher.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2017, 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
@@ -27,63 +27,112 @@ package jdk.incubator.http;
 
 import java.util.Iterator;
 import java.util.concurrent.Flow;
+import jdk.incubator.http.internal.common.Demand;
+import jdk.incubator.http.internal.common.SequentialScheduler;
 
 /**
- * A Publisher that is expected to run in same thread as subscriber.
- * Items are obtained from Iterable. Each new subscription gets a new Iterator.
+ * A Publisher that publishes items obtained from the given Iterable. Each new
+ * subscription gets a new Iterator.
  */
 class PullPublisher<T> implements Flow.Publisher<T> {
 
+    // Only one of `iterable` and `throwable` can be non-null. throwable is
+    // non-null when an error has been encountered, by the creator of
+    // PullPublisher, while subscribing the subscriber, but before subscribe has
+    // completed.
     private final Iterable<T> iterable;
+    private final Throwable throwable;
+
+    PullPublisher(Iterable<T> iterable, Throwable throwable) {
+        this.iterable = iterable;
+        this.throwable = throwable;
+    }
 
     PullPublisher(Iterable<T> iterable) {
-        this.iterable = iterable;
+        this(iterable, null);
     }
 
     @Override
     public void subscribe(Flow.Subscriber<? super T> subscriber) {
-        subscriber.onSubscribe(new Subscription(subscriber, iterable.iterator()));
+        Subscription sub;
+        if (throwable != null) {
+            assert iterable == null : "non-null iterable: " + iterable;
+            sub = new Subscription(subscriber, null, throwable);
+        } else {
+            assert throwable == null : "non-null exception: " + throwable;
+            sub = new Subscription(subscriber, iterable.iterator(), null);
+        }
+        subscriber.onSubscribe(sub);
+
+        if (throwable != null) {
+            sub.pullScheduler.runOrSchedule();
+        }
     }
 
     private class Subscription implements Flow.Subscription {
 
         private final Flow.Subscriber<? super T> subscriber;
         private final Iterator<T> iter;
-        private boolean done = false;
-        private long demand = 0;
-        private int recursion = 0;
+        private volatile boolean completed;
+        private volatile boolean cancelled;
+        private volatile Throwable error;
+        final SequentialScheduler pullScheduler = new SequentialScheduler(new PullTask());
+        private final Demand demand = new Demand();
 
-        Subscription(Flow.Subscriber<? super T> subscriber, Iterator<T> iter) {
+        Subscription(Flow.Subscriber<? super T> subscriber,
+                     Iterator<T> iter,
+                     Throwable throwable) {
             this.subscriber = subscriber;
             this.iter = iter;
+            this.error = throwable;
+        }
+
+        final class PullTask extends SequentialScheduler.CompleteRestartableTask {
+            @Override
+            protected void run() {
+                if (completed || cancelled) {
+                    return;
+                }
+
+                Throwable t = error;
+                if (t != null) {
+                    completed = true;
+                    pullScheduler.stop();
+                    subscriber.onError(t);
+                    return;
+                }
+
+                while (demand.tryDecrement() && !cancelled) {
+                    if (!iter.hasNext()) {
+                        break;
+                    } else {
+                        subscriber.onNext(iter.next());
+                    }
+                }
+                if (!iter.hasNext() && !cancelled) {
+                    completed = true;
+                    pullScheduler.stop();
+                    subscriber.onComplete();
+                }
+            }
         }
 
         @Override
         public void request(long n) {
-            if (done) {
-                subscriber.onError(new IllegalArgumentException("request(" + n + ")"));
-            }
-            demand += n;
-            recursion ++;
-            if (recursion > 1) {
-                return;
-            }
-            while (demand > 0) {
-                done = !iter.hasNext();
-                if (done) {
-                    subscriber.onComplete();
-                    recursion --;
-                    return;
-                }
-                subscriber.onNext(iter.next());
-                demand --;
+            if (cancelled)
+                return;  // no-op
+
+            if (n <= 0) {
+                error = new IllegalArgumentException("illegal non-positive request:" + n);
+            } else {
+                demand.increase(n);
             }
+            pullScheduler.runOrSchedule();
         }
 
         @Override
         public void cancel() {
-            done = true;
+            cancelled = true;
         }
-
     }
 }
diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/PushGroup.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/PushGroup.java
index 48d07481d12..0f38092a487 100644
--- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/PushGroup.java
+++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/PushGroup.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2017, 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
@@ -25,7 +25,11 @@
 
 package jdk.incubator.http;
 
+import java.security.AccessControlContext;
+import java.util.Optional;
 import java.util.concurrent.CompletableFuture;
+import jdk.incubator.http.HttpResponse.BodyHandler;
+import jdk.incubator.http.HttpResponse.UntrustedBodyHandler;
 import jdk.incubator.http.internal.common.MinimalFuture;
 import jdk.incubator.http.internal.common.Log;
 
@@ -38,59 +42,82 @@ class PushGroup<U,T> {
     final CompletableFuture<Void> resultCF;
     final CompletableFuture<Void> noMorePushesCF;
 
-    volatile Throwable error; // any exception that occured during pushes
+    volatile Throwable error; // any exception that occurred during pushes
 
     // CF for main response
     final CompletableFuture<HttpResponse<T>> mainResponse;
 
-    // user's processor object
-    final HttpResponse.MultiProcessor<U, T> multiProcessor;
+    // user's subscriber object
+    final HttpResponse.MultiSubscriber<U, T> multiSubscriber;
 
     final HttpResponse.BodyHandler<T> mainBodyHandler;
 
+    private final AccessControlContext acc;
+
     int numberOfPushes;
     int remainingPushes;
     boolean noMorePushes = false;
 
-    PushGroup(HttpResponse.MultiProcessor<U, T> multiProcessor, HttpRequestImpl req) {
-        this(multiProcessor, req, new MinimalFuture<>());
+    PushGroup(HttpResponse.MultiSubscriber<U, T> multiSubscriber,
+              HttpRequestImpl req,
+              AccessControlContext acc) {
+        this(multiSubscriber, req, new MinimalFuture<>(), acc);
     }
 
     // Check mainBodyHandler before calling nested constructor.
-    private PushGroup(HttpResponse.MultiProcessor<U, T> multiProcessor,
-            HttpRequestImpl req,
-            CompletableFuture<HttpResponse<T>> mainResponse) {
-        this(multiProcessor, mainResponse,
-             multiProcessor.onRequest(req).orElseThrow(
-                    () -> new IllegalArgumentException(
-                     "A valid body processor for the main response is required")));
+    private PushGroup(HttpResponse.MultiSubscriber<U, T> multiSubscriber,
+                      HttpRequestImpl req,
+                      CompletableFuture<HttpResponse<T>> mainResponse,
+                      AccessControlContext acc) {
+        this(multiSubscriber,
+             mainResponse,
+             multiSubscriber.onRequest(req),
+             acc);
     }
 
-    // This private constructor is called after all parameters have been
-    // checked.
-    private PushGroup(HttpResponse.MultiProcessor<U, T> multiProcessor,
+    // This private constructor is called after all parameters have been checked.
+    private PushGroup(HttpResponse.MultiSubscriber<U, T> multiSubscriber,
                       CompletableFuture<HttpResponse<T>> mainResponse,
-                      HttpResponse.BodyHandler<T> mainBodyHandler) {
+                      HttpResponse.BodyHandler<T> mainBodyHandler,
+                      AccessControlContext acc) {
 
         assert mainResponse != null; // A new instance is created above
         assert mainBodyHandler != null; // should have been checked above
 
         this.resultCF = new MinimalFuture<>();
         this.noMorePushesCF = new MinimalFuture<>();
-        this.multiProcessor = multiProcessor;
+        this.multiSubscriber = multiSubscriber;
         this.mainResponse = mainResponse.thenApply(r -> {
-            multiProcessor.onResponse(r);
+            multiSubscriber.onResponse(r);
             return r;
         });
         this.mainBodyHandler = mainBodyHandler;
+        if (acc != null) {
+            // Restricts the file publisher with the senders ACC, if any
+            if (mainBodyHandler instanceof UntrustedBodyHandler)
+                ((UntrustedBodyHandler)this.mainBodyHandler).setAccessControlContext(acc);
+        }
+        this.acc = acc;
     }
 
     CompletableFuture<Void> groupResult() {
         return resultCF;
     }
 
-    HttpResponse.MultiProcessor<U, T> processor() {
-        return multiProcessor;
+    HttpResponse.MultiSubscriber<U, T> subscriber() {
+        return multiSubscriber;
+    }
+
+    Optional<BodyHandler<T>> handlerForPushRequest(HttpRequest ppRequest) {
+        Optional<BodyHandler<T>> bh = multiSubscriber.onPushPromise(ppRequest);
+        if (acc != null && bh.isPresent()) {
+            // Restricts the file publisher with the senders ACC, if any
+            BodyHandler<T> x = bh.get();
+            if (x instanceof UntrustedBodyHandler)
+                ((UntrustedBodyHandler)x).setAccessControlContext(acc);
+            bh = Optional.of(x);
+        }
+        return bh;
     }
 
     HttpResponse.BodyHandler<T> mainResponseHandler() {
@@ -106,18 +133,11 @@ class PushGroup<U,T> {
         });
     }
 
-    synchronized CompletableFuture<HttpResponse<T>> mainResponse() {
-        return mainResponse;
-    }
-
     synchronized void addPush() {
         numberOfPushes++;
         remainingPushes++;
     }
 
-    synchronized int numberOfPushes() {
-        return numberOfPushes;
-    }
     // This is called when the main body response completes because it means
     // no more PUSH_PROMISEs are possible
 
diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/PushPublisher.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/PushPublisher.java
deleted file mode 100644
index c253a2fff74..00000000000
--- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/PushPublisher.java
+++ /dev/null
@@ -1,102 +0,0 @@
-/*
- * Copyright (c) 2016, 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.  Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * 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 jdk.incubator.http;
-
-import java.util.Optional;
-import java.util.concurrent.Flow;
-import java.util.function.Consumer;
-
-/**
- * A single threaded Publisher which runs in same thread as subscriber.
- */
-class PushPublisher<T> extends AbstractPushPublisher<T> {
-    Subscription subscription;
-    Flow.Subscriber<? super T> subscriber;
-    SubscriptionState state;
-    long demand;
-
-    /**
-     * Pushes/consumes the incoming objects.
-     * The consumer blocks until subscriber ready to receive.
-     */
-    @Override
-    public void acceptData(Optional<T> item) {
-        SubscriptionState s = this.state;
-
-        if (s == SubscriptionState.CANCELLED) return;
-        if (s == SubscriptionState.DONE) {
-            throw new IllegalStateException("subscription complete");
-        }
-
-        if (!item.isPresent()) {
-            subscriber.onComplete();
-            this.state = SubscriptionState.DONE;
-            return;
-        }
-        if (demand == 0) {
-            throw new IllegalStateException("demand == 0");
-        }
-        demand--;
-        subscriber.onNext(item.get());
-    }
-
-    @Override
-    public Consumer<Optional<T>> asDataConsumer() {
-        return this::acceptData;
-    }
-
-    @Override
-    public void subscribe(Flow.Subscriber<? super T> subscriber) {
-        subscription = new Subscription(subscriber);
-        subscriber.onSubscribe(subscription);
-    }
-
-    private class Subscription implements Flow.Subscription {
-
-        Subscription(Flow.Subscriber<? super T> subscriber) {
-            PushPublisher.this.subscriber = subscriber;
-        }
-
-        @Override
-        public void request(long n) {
-            demand += n;
-        }
-
-        @Override
-        public void cancel() {
-            state = SubscriptionState.CANCELLED;
-        }
-    }
-
-    @Override
-    public void acceptError(Throwable t) {
-        if (this.state == SubscriptionState.DONE) {
-            throw new IllegalStateException("subscription complete");
-        }
-        this.state = SubscriptionState.CANCELLED;
-        subscriber.onError(t);
-    }
-}
diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/RawChannelImpl.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/RawChannelImpl.java
index 92814fcce8d..32c8f73c7e7 100644
--- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/RawChannelImpl.java
+++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/RawChannelImpl.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2017, 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,6 +32,7 @@ import java.io.IOException;
 import java.nio.ByteBuffer;
 import java.nio.channels.SelectableChannel;
 import java.nio.channels.SocketChannel;
+import java.util.function.Supplier;
 
 /*
  * Each RawChannel corresponds to a TCP connection (SocketChannel) but is
@@ -41,17 +42,19 @@ import java.nio.channels.SocketChannel;
 final class RawChannelImpl implements RawChannel {
 
     private final HttpClientImpl client;
-    private final HttpConnection connection;
+    private final HttpConnection.DetachedConnectionChannel detachedChannel;
     private final Object         initialLock = new Object();
-    private ByteBuffer           initial;
+    private Supplier<ByteBuffer> initial;
 
     RawChannelImpl(HttpClientImpl client,
                    HttpConnection connection,
-                   ByteBuffer initial)
+                   Supplier<ByteBuffer> initial)
             throws IOException
     {
         this.client = client;
-        this.connection = connection;
+        this.detachedChannel = connection.detachChannel();
+        this.initial = initial;
+
         SocketChannel chan = connection.channel();
         client.cancelRegistration(chan);
         // Constructing a RawChannel is supposed to have a "hand over"
@@ -64,15 +67,11 @@ final class RawChannelImpl implements RawChannel {
                 chan.close();
             } catch (IOException e1) {
                 e.addSuppressed(e1);
+            } finally {
+                detachedChannel.close();
             }
             throw e;
         }
-        // empty the initial buffer into our own copy.
-        synchronized (initialLock) {
-            this.initial = initial.hasRemaining()
-                    ? Utils.copy(initial)
-                    : Utils.EMPTY_BYTEBUFFER;
-        }
     }
 
     private class NonBlockingRawAsyncEvent extends AsyncEvent {
@@ -80,13 +79,13 @@ final class RawChannelImpl implements RawChannel {
         private final RawEvent re;
 
         NonBlockingRawAsyncEvent(RawEvent re) {
-            super(0); // !BLOCKING & !REPEATING
+            // !BLOCKING & !REPEATING
             this.re = re;
         }
 
         @Override
         public SelectableChannel channel() {
-            return connection.channel();
+            return detachedChannel.channel();
         }
 
         @Override
@@ -100,7 +99,7 @@ final class RawChannelImpl implements RawChannel {
         }
 
         @Override
-        public void abort() { }
+        public void abort(IOException ioe) { }
     }
 
     @Override
@@ -110,8 +109,9 @@ final class RawChannelImpl implements RawChannel {
 
     @Override
     public ByteBuffer read() throws IOException {
-        assert !connection.channel().isBlocking();
-        return connection.read();
+        assert !detachedChannel.channel().isBlocking();
+        // connection.read() will no longer be available.
+        return detachedChannel.read();
     }
 
     @Override
@@ -120,7 +120,9 @@ final class RawChannelImpl implements RawChannel {
             if (initial == null) {
                 throw new IllegalStateException();
             }
-            ByteBuffer ref = initial;
+            ByteBuffer ref = initial.get();
+            ref = ref.hasRemaining() ? Utils.copy(ref)
+                    : Utils.EMPTY_BYTEBUFFER;
             initial = null;
             return ref;
         }
@@ -128,21 +130,29 @@ final class RawChannelImpl implements RawChannel {
 
     @Override
     public long write(ByteBuffer[] src, int offset, int len) throws IOException {
-        return connection.write(src, offset, len);
+        // this makes the whitebox driver test fail.
+        return detachedChannel.write(src, offset, len);
     }
 
     @Override
     public void shutdownInput() throws IOException {
-        connection.shutdownInput();
+        detachedChannel.shutdownInput();
     }
 
     @Override
     public void shutdownOutput() throws IOException {
-        connection.shutdownOutput();
+        detachedChannel.shutdownOutput();
     }
 
     @Override
     public void close() throws IOException {
-        connection.close();
+        detachedChannel.close();
     }
+
+    @Override
+    public String toString() {
+        return super.toString()+"("+ detachedChannel.toString() + ")";
+    }
+
+
 }
diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/RedirectFilter.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/RedirectFilter.java
index 1b342f39fb6..e6bce3c5049 100644
--- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/RedirectFilter.java
+++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/RedirectFilter.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2017, 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,6 +44,9 @@ class RedirectFilter implements HeaderFilter {
             "jdk.httpclient.redirects.retrylimit", DEFAULT_MAX_REDIRECTS
     );
 
+    // A public no-arg constructor is required by FilterFactory
+    public RedirectFilter() {}
+
     @Override
     public synchronized void request(HttpRequestImpl r, MultiExchange<?,?> e) throws IOException {
         this.request = r;
@@ -84,9 +87,8 @@ class RedirectFilter implements HeaderFilter {
 
     private URI getRedirectedURI(HttpHeaders headers) {
         URI redirectedURI;
-        String ss = headers.firstValue("Location").orElse("Not present");
         redirectedURI = headers.firstValue("Location")
-                .map((s) -> URI.create(s))
+                .map(URI::create)
                 .orElseThrow(() -> new UncheckedIOException(
                         new IOException("Invalid redirection")));
 
diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/RequestProcessors.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/RequestPublishers.java
similarity index 67%
rename from src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/RequestProcessors.java
rename to src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/RequestPublishers.java
index 851ae4b5f9f..d79310d3549 100644
--- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/RequestProcessors.java
+++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/RequestPublishers.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2017, 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
@@ -27,44 +27,57 @@ package jdk.incubator.http;
 
 import java.io.File;
 import java.io.FileInputStream;
-import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.UncheckedIOException;
 import java.nio.ByteBuffer;
 import java.nio.charset.Charset;
 import java.nio.file.Path;
+import java.security.AccessControlContext;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.Iterator;
 import java.util.List;
 import java.util.NoSuchElementException;
 import java.util.concurrent.ConcurrentLinkedQueue;
 import java.util.concurrent.Flow;
 import java.util.function.Supplier;
+import jdk.incubator.http.HttpRequest.BodyPublisher;
 import jdk.incubator.http.internal.common.Utils;
 
-class RequestProcessors {
+class RequestPublishers {
 
-    static class ByteArrayProcessor implements HttpRequest.BodyProcessor {
+    static class ByteArrayPublisher implements HttpRequest.BodyPublisher {
         private volatile Flow.Publisher<ByteBuffer> delegate;
         private final int length;
         private final byte[] content;
         private final int offset;
+        private final int bufSize;
 
-        ByteArrayProcessor(byte[] content) {
+        ByteArrayPublisher(byte[] content) {
             this(content, 0, content.length);
         }
 
-        ByteArrayProcessor(byte[] content, int offset, int length) {
+        ByteArrayPublisher(byte[] content, int offset, int length) {
+            this(content, offset, length, Utils.BUFSIZE);
+        }
+
+        /* bufSize exposed for testing purposes */
+        ByteArrayPublisher(byte[] content, int offset, int length, int bufSize) {
             this.content = content;
             this.offset = offset;
             this.length = length;
+            this.bufSize = bufSize;
         }
 
         List<ByteBuffer> copy(byte[] content, int offset, int length) {
             List<ByteBuffer> bufs = new ArrayList<>();
             while (length > 0) {
-                ByteBuffer b = ByteBuffer.allocate(Math.min(Utils.BUFSIZE, length));
+                ByteBuffer b = ByteBuffer.allocate(Math.min(bufSize, length));
                 int max = b.capacity();
                 int tocopy = Math.min(max, length);
                 b.put(content, offset, tocopy);
@@ -90,12 +103,12 @@ class RequestProcessors {
     }
 
     // This implementation has lots of room for improvement.
-    static class IterableProcessor implements HttpRequest.BodyProcessor {
+    static class IterablePublisher implements HttpRequest.BodyPublisher {
         private volatile Flow.Publisher<ByteBuffer> delegate;
         private final Iterable<byte[]> content;
         private volatile long contentLength;
 
-        IterableProcessor(Iterable<byte[]> content) {
+        IterablePublisher(Iterable<byte[]> content) {
             this.content = content;
         }
 
@@ -179,14 +192,15 @@ class RequestProcessors {
         }
     }
 
-    static class StringProcessor extends ByteArrayProcessor {
-        public StringProcessor(String content, Charset charset) {
+    static class StringPublisher extends ByteArrayPublisher {
+        public StringPublisher(String content, Charset charset) {
             super(content.getBytes(charset));
         }
     }
 
-    static class EmptyProcessor implements HttpRequest.BodyProcessor {
-        PseudoPublisher<ByteBuffer> delegate = new PseudoPublisher<>();
+    static class EmptyPublisher implements HttpRequest.BodyPublisher {
+        private final Flow.Publisher<ByteBuffer> delegate =
+                new PullPublisher<ByteBuffer>(Collections.emptyList(), null);
 
         @Override
         public long contentLength() {
@@ -199,26 +213,42 @@ class RequestProcessors {
         }
     }
 
-    static class FileProcessor extends InputStreamProcessor
-        implements HttpRequest.BodyProcessor
-    {
-        File file;
+    static class FilePublisher implements BodyPublisher  {
+        private final File file;
+        private volatile AccessControlContext acc;
 
-        FileProcessor(Path name) {
-            super(() -> create(name));
+        FilePublisher(Path name) {
             file = name.toFile();
         }
 
-        static FileInputStream create(Path name) {
-            try {
-                return new FileInputStream(name.toFile());
-            } catch (FileNotFoundException e) {
-                throw new UncheckedIOException(e);
-            }
+        void setAccessControlContext(AccessControlContext acc) {
+            this.acc = acc;
         }
+
+        @Override
+        public void subscribe(Flow.Subscriber<? super ByteBuffer> subscriber) {
+            if (System.getSecurityManager() != null && acc == null)
+                throw new InternalError(
+                        "Unexpected null acc when security manager has been installed");
+
+            InputStream is;
+            try {
+                PrivilegedExceptionAction<FileInputStream> pa =
+                        () -> new FileInputStream(file);
+                is = AccessController.doPrivileged(pa, acc);
+            } catch (PrivilegedActionException pae) {
+                throw new UncheckedIOException((IOException)pae.getCause());
+            }
+            PullPublisher<ByteBuffer> publisher =
+                    new PullPublisher<>(() -> new StreamIterator(is));
+            publisher.subscribe(subscriber);
+        }
+
         @Override
         public long contentLength() {
-            return file.length();
+            assert System.getSecurityManager() != null ? acc != null: true;
+            PrivilegedAction<Long> pa = () -> file.length();
+            return AccessController.doPrivileged(pa, acc);
         }
     }
 
@@ -227,21 +257,26 @@ class RequestProcessors {
      */
     static class StreamIterator implements Iterator<ByteBuffer> {
         final InputStream is;
-        ByteBuffer nextBuffer;
-        boolean need2Read = true;
-        boolean haveNext;
-        Throwable error;
+        final Supplier<? extends ByteBuffer> bufSupplier;
+        volatile ByteBuffer nextBuffer;
+        volatile boolean need2Read = true;
+        volatile boolean haveNext;
 
         StreamIterator(InputStream is) {
-            this.is = is;
+            this(is, Utils::getBuffer);
         }
 
-        Throwable error() {
-            return error;
+        StreamIterator(InputStream is, Supplier<? extends ByteBuffer> bufSupplier) {
+            this.is = is;
+            this.bufSupplier = bufSupplier;
         }
 
+//        Throwable error() {
+//            return error;
+//        }
+
         private int read() {
-            nextBuffer = Utils.getBuffer();
+            nextBuffer = bufSupplier.get();
             nextBuffer.clear();
             byte[] buf = nextBuffer.array();
             int offset = nextBuffer.arrayOffset();
@@ -257,7 +292,6 @@ class RequestProcessors {
                 nextBuffer.position(0);
                 return n;
             } catch (IOException ex) {
-                error = ex;
                 return -1;
             }
         }
@@ -285,23 +319,28 @@ class RequestProcessors {
 
     }
 
-    static class InputStreamProcessor implements HttpRequest.BodyProcessor {
+    static class InputStreamPublisher implements BodyPublisher {
         private final Supplier<? extends InputStream> streamSupplier;
-        private Flow.Publisher<ByteBuffer> delegate;
 
-        InputStreamProcessor(Supplier<? extends InputStream> streamSupplier) {
+        InputStreamPublisher(Supplier<? extends InputStream> streamSupplier) {
             this.streamSupplier = streamSupplier;
         }
 
         @Override
-        public synchronized void subscribe(Flow.Subscriber<? super ByteBuffer> subscriber) {
-
+        public void subscribe(Flow.Subscriber<? super ByteBuffer> subscriber) {
+            PullPublisher<ByteBuffer> publisher;
             InputStream is = streamSupplier.get();
             if (is == null) {
-                throw new UncheckedIOException(new IOException("no inputstream supplied"));
+                Throwable t = new IOException("streamSupplier returned null");
+                publisher = new PullPublisher<>(null, t);
+            } else  {
+                publisher = new PullPublisher<>(iterableOf(is), null);
             }
-            this.delegate = new PullPublisher<>(() -> new StreamIterator(is));
-            delegate.subscribe(subscriber);
+            publisher.subscribe(subscriber);
+        }
+
+        protected Iterable<ByteBuffer> iterableOf(InputStream is) {
+            return () -> new StreamIterator(is);
         }
 
         @Override
diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/Response.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/Response.java
index 9d317135288..817af717206 100644
--- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/Response.java
+++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/Response.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2017, 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
@@ -25,6 +25,8 @@
 
 package jdk.incubator.http;
 
+import java.net.URI;
+
 /**
  * Response headers and status code.
  */
@@ -59,11 +61,26 @@ class Response {
         return headers;
     }
 
-    Exchange<?> exchange() {
-        return exchange;
-    }
+//    Exchange<?> exchange() {
+//        return exchange;
+//    }
 
     int statusCode() {
         return statusCode;
     }
+
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+        String method = request().method();
+        URI uri = request().uri();
+        String uristring = uri == null ? "" : uri.toString();
+        sb.append('(')
+          .append(method)
+          .append(" ")
+          .append(uristring)
+          .append(") ")
+          .append(statusCode());
+        return sb.toString();
+    }
 }
diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/ResponseContent.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/ResponseContent.java
index 7641dcc3134..d95e51d3963 100644
--- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/ResponseContent.java
+++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/ResponseContent.java
@@ -26,8 +26,10 @@
 package jdk.incubator.http;
 
 import java.io.IOException;
+import java.lang.System.Logger.Level;
 import java.nio.ByteBuffer;
-import java.util.Optional;
+import java.util.ArrayList;
+import java.util.List;
 import java.util.function.Consumer;
 import jdk.incubator.http.internal.common.Utils;
 
@@ -39,45 +41,35 @@ import jdk.incubator.http.internal.common.Utils;
  */
 class ResponseContent {
 
-    final HttpResponse.BodyProcessor<?> pusher;
-    final HttpConnection connection;
+    static final boolean DEBUG = Utils.DEBUG; // Revisit: temporary dev flag.
+
+    final HttpResponse.BodySubscriber<?> pusher;
     final int contentLength;
-    ByteBuffer buffer;
-    //ByteBuffer lastBufferUsed;
-    final ResponseHeaders headers;
-    private final Consumer<Optional<ByteBuffer>> dataConsumer;
-    private final Consumer<IOException> errorConsumer;
-    private final HttpClientImpl client;
+    final HttpHeaders headers;
     // this needs to run before we complete the body
     // so that connection can be returned to pool
     private final Runnable onFinished;
+    private final String dbgTag;
 
     ResponseContent(HttpConnection connection,
                     int contentLength,
-                    ResponseHeaders h,
-                    HttpResponse.BodyProcessor<?> userProcessor,
-                    Consumer<Optional<ByteBuffer>> dataConsumer,
-                    Consumer<IOException> errorConsumer,
+                    HttpHeaders h,
+                    HttpResponse.BodySubscriber<?> userSubscriber,
                     Runnable onFinished)
     {
-        this.pusher = (HttpResponse.BodyProcessor)userProcessor;
-        this.connection = connection;
+        this.pusher = userSubscriber;
         this.contentLength = contentLength;
         this.headers = h;
-        this.dataConsumer = dataConsumer;
-        this.errorConsumer = errorConsumer;
-        this.client = connection.client;
         this.onFinished = onFinished;
+        this.dbgTag = connection.dbgString() + "/ResponseContent";
     }
 
     static final int LF = 10;
     static final int CR = 13;
-    static final int SP = 0x20;
-    static final int BUF_SIZE = 1024;
 
-    boolean chunkedContent, chunkedContentInitialized;
+    private boolean chunkedContent, chunkedContentInitialized;
 
-    private boolean contentChunked() throws IOException {
+    boolean contentChunked() throws IOException {
         if (chunkedContentInitialized) {
             return chunkedContent;
         }
@@ -98,182 +90,375 @@ class ResponseContent {
         return chunkedContent;
     }
 
-    /**
-     * Entry point for pusher. b is an initial ByteBuffer that may
-     * have some data in it. When this method returns, the body
-     * has been fully processed.
-     */
-    void pushBody(ByteBuffer b) {
-        try {
-            // TODO: check status
-            if (contentChunked()) {
-                pushBodyChunked(b);
-            } else {
-                pushBodyFixed(b);
-            }
-        } catch (IOException t) {
-            errorConsumer.accept(t);
+    interface BodyParser extends Consumer<ByteBuffer> {
+        void onSubscribe(AbstractSubscription sub);
+    }
+
+    // Returns a parser that will take care of parsing the received byte
+    // buffers and forward them to the BodySubscriber.
+    // When the parser is done, it will call onComplete.
+    // If parsing was successful, the throwable parameter will be null.
+    // Otherwise it will be the exception that occurred
+    // Note: revisit: it might be better to use a CompletableFuture than
+    //       a completion handler.
+    BodyParser getBodyParser(Consumer<Throwable> onComplete)
+        throws IOException {
+        if (contentChunked()) {
+            return new ChunkedBodyParser(onComplete);
+        } else {
+            return new FixedLengthBodyParser(contentLength, onComplete);
         }
     }
 
-    // reads and returns chunklen. Position of chunkbuf is first byte
-    // of chunk on return. chunklen includes the CR LF at end of chunk
-    int readChunkLen() throws IOException {
-        chunklen = 0;
-        boolean cr = false;
-        while (true) {
-            getHunk();
-            int c = chunkbuf.get();
-            if (cr) {
-                if (c == LF) {
-                    return chunklen + 2;
-                } else {
-                    throw new IOException("invalid chunk header");
-                }
-            }
-            if (c == CR) {
-                cr = true;
-            } else {
-                int digit = toDigit(c);
-                chunklen = chunklen * 16 + digit;
-            }
-        }
-    }
 
-    int chunklen = -1;      // number of bytes in chunk (fixed)
-    int bytesremaining;     // number of bytes in chunk left to be read incl CRLF
-    int bytesread;
-    ByteBuffer chunkbuf;    // initialise
+    static enum ChunkState {READING_LENGTH, READING_DATA, DONE}
+    class ChunkedBodyParser implements BodyParser {
+        final ByteBuffer READMORE = Utils.EMPTY_BYTEBUFFER;
+        final Consumer<Throwable> onComplete;
+        final System.Logger debug = Utils.getDebugLogger(this::dbgString, DEBUG);
+        final String dbgTag = ResponseContent.this.dbgTag + "/ChunkedBodyParser";
 
-    // make sure we have at least 1 byte to look at
-    private void getHunk() throws IOException {
-        if (chunkbuf == null || !chunkbuf.hasRemaining()) {
-            chunkbuf = connection.read();
-        }
-    }
-
-    private void consumeBytes(int n) throws IOException {
-        getHunk();
-        while (n > 0) {
-            int e = Math.min(chunkbuf.remaining(), n);
-            chunkbuf.position(chunkbuf.position() + e);
-            n -= e;
-            if (n > 0) {
-                getHunk();
-            }
-        }
-    }
-
-    /**
-     * Returns a ByteBuffer containing a chunk of data or a "hunk" of data
-     * (a chunk of a chunk if the chunk size is larger than our ByteBuffers).
-     * ByteBuffer returned is obtained from response processor.
-     */
-    ByteBuffer readChunkedBuffer() throws IOException {
-        if (chunklen == -1) {
-            // new chunk
-            chunklen = readChunkLen() - 2;
-            bytesremaining =  chunklen;
-            if (chunklen == 0) {
-                consumeBytes(2);
-                return null;
-            }
+        volatile Throwable closedExceptionally;
+        volatile int partialChunklen = 0; // partially read chunk len
+        volatile int chunklen = -1;  // number of bytes in chunk
+        volatile int bytesremaining;  // number of bytes in chunk left to be read incl CRLF
+        volatile boolean cr = false;  // tryReadChunkLength has found CR
+        volatile int bytesToConsume;  // number of bytes that still need to be consumed before proceeding
+        volatile ChunkState state = ChunkState.READING_LENGTH; // current state
+        volatile AbstractSubscription sub;
+        ChunkedBodyParser(Consumer<Throwable> onComplete) {
+            this.onComplete = onComplete;
         }
 
-        getHunk();
-        bytesread = chunkbuf.remaining();
-        ByteBuffer returnBuffer = Utils.getBuffer();
-        int space = returnBuffer.remaining();
-
-        int bytes2Copy = Math.min(bytesread, Math.min(bytesremaining, space));
-        Utils.copy(chunkbuf, returnBuffer, bytes2Copy);
-        returnBuffer.flip();
-        bytesremaining -= bytes2Copy;
-        if (bytesremaining == 0) {
-            consumeBytes(2);
-            chunklen = -1;
+        String dbgString() {
+            return dbgTag;
         }
-        return returnBuffer;
-    }
 
-    ByteBuffer initialBuffer;
-    int fixedBytesReturned;
+        @Override
+        public void onSubscribe(AbstractSubscription sub) {
+            debug.log(Level.DEBUG, () ->  "onSubscribe: "
+                        + pusher.getClass().getName());
+            pusher.onSubscribe(this.sub = sub);
+        }
 
-    //ByteBuffer getResidue() {
-        //return lastBufferUsed;
-    //}
-
-    private void compactBuffer(ByteBuffer buf) {
-        buf.compact()
-           .flip();
-    }
-
-    /**
-     * Copies inbuf (numBytes from its position) to new buffer. The returned
-     * buffer's position is zero and limit is at end (numBytes)
-     */
-    private ByteBuffer copyBuffer(ByteBuffer inbuf, int numBytes) {
-        ByteBuffer b1 = Utils.getBuffer();
-        assert b1.remaining() >= numBytes;
-        byte[] b = b1.array();
-        inbuf.get(b, 0, numBytes);
-        b1.limit(numBytes);
-        return b1;
-    }
-
-    private void pushBodyChunked(ByteBuffer b) throws IOException {
-        chunkbuf = b;
-        while (true) {
-            ByteBuffer b1 = readChunkedBuffer();
-            if (b1 != null) {
-                if (b1.hasRemaining()) {
-                    dataConsumer.accept(Optional.of(b1));
-                }
-            } else {
-                onFinished.run();
-                dataConsumer.accept(Optional.empty());
+        @Override
+        public void accept(ByteBuffer b) {
+            if (closedExceptionally != null) {
+                debug.log(Level.DEBUG, () ->  "already closed: "
+                            + closedExceptionally);
                 return;
             }
-        }
-    }
+            boolean completed = false;
+            try {
+                List<ByteBuffer> out = new ArrayList<>();
+                do {
+                    if (tryPushOneHunk(b, out))  {
+                        // We're done! (true if the final chunk was parsed).
+                        if (!out.isEmpty()) {
+                            // push what we have and complete
+                            // only reduce demand if we actually push something.
+                            // we would not have come here if there was no
+                            // demand.
+                            boolean hasDemand = sub.demand().tryDecrement();
+                            assert hasDemand;
+                            pusher.onNext(out);
+                        }
+                        debug.log(Level.DEBUG, () ->  "done!");
+                        assert closedExceptionally == null;
+                        assert state == ChunkState.DONE;
+                        onFinished.run();
+                        pusher.onComplete();
+                        completed = true;
+                        onComplete.accept(closedExceptionally); // should be null
+                        break;
+                    }
+                    // the buffer may contain several hunks, and therefore
+                    // we must loop while it's not exhausted.
+                } while (b.hasRemaining());
 
-    private int toDigit(int b) throws IOException {
-        if (b >= 0x30 && b <= 0x39) {
-            return b - 0x30;
-        }
-        if (b >= 0x41 && b <= 0x46) {
-            return b - 0x41 + 10;
-        }
-        if (b >= 0x61 && b <= 0x66) {
-            return b - 0x61 + 10;
-        }
-        throw new IOException("Invalid chunk header byte " + b);
-    }
-
-    private void pushBodyFixed(ByteBuffer b) throws IOException {
-        int remaining = contentLength;
-        while (b.hasRemaining() && remaining > 0) {
-            ByteBuffer buffer = Utils.getBuffer();
-            int amount = Math.min(b.remaining(), remaining);
-            Utils.copy(b, buffer, amount);
-            remaining -= amount;
-            buffer.flip();
-            dataConsumer.accept(Optional.of(buffer));
-        }
-        while (remaining > 0) {
-            ByteBuffer buffer = connection.read();
-            if (buffer == null)
-                throw new IOException("connection closed");
-
-            int bytesread = buffer.remaining();
-            // assume for now that pipelining not implemented
-            if (bytesread > remaining) {
-                throw new IOException("too many bytes read");
+                if (!completed && !out.isEmpty()) {
+                    // push what we have.
+                    // only reduce demand if we actually push something.
+                    // we would not have come here if there was no
+                    // demand.
+                    boolean hasDemand = sub.demand().tryDecrement();
+                    assert hasDemand;
+                    pusher.onNext(out);
+                }
+                assert state == ChunkState.DONE || !b.hasRemaining();
+            } catch(Throwable t) {
+                closedExceptionally = t;
+                if (!completed) onComplete.accept(t);
+            }
+        }
+
+        // reads and returns chunklen. Position of chunkbuf is first byte
+        // of chunk on return. chunklen includes the CR LF at end of chunk
+        // returns -1 if needs more bytes
+        private int tryReadChunkLen(ByteBuffer chunkbuf) throws IOException {
+            assert state == ChunkState.READING_LENGTH;
+            while (chunkbuf.hasRemaining()) {
+                int c = chunkbuf.get();
+                if (cr) {
+                    if (c == LF) {
+                        return partialChunklen;
+                    } else {
+                        throw new IOException("invalid chunk header");
+                    }
+                }
+                if (c == CR) {
+                    cr = true;
+                } else {
+                    int digit = toDigit(c);
+                    partialChunklen = partialChunklen * 16 + digit;
+                }
+            }
+            return -1;
+        }
+
+
+        // try to consume as many bytes as specified by bytesToConsume.
+        // returns the number of bytes that still need to be consumed.
+        // In practice this method is only called to consume one CRLF pair
+        // with bytesToConsume set to 2, so it will only return 0 (if completed),
+        // 1, or 2 (if chunkbuf doesn't have the 2 chars).
+        private int tryConsumeBytes(ByteBuffer chunkbuf) throws IOException {
+            int n = bytesToConsume;
+            if (n > 0) {
+                int e = Math.min(chunkbuf.remaining(), n);
+
+                // verifies some assertions
+                // this methods is called only to consume CRLF
+                if (Utils.ASSERTIONSENABLED) {
+                    assert n <= 2 && e <= 2;
+                    ByteBuffer tmp = chunkbuf.slice();
+                    // if n == 2 assert that we will first consume CR
+                    assert (n == 2 && e > 0) ? tmp.get() == CR : true;
+                    // if n == 1 || n == 2 && e == 2 assert that we then consume LF
+                    assert (n == 1 || e == 2) ? tmp.get() == LF : true;
+                }
+
+                chunkbuf.position(chunkbuf.position() + e);
+                n -= e;
+                bytesToConsume = n;
+            }
+            assert n >= 0;
+            return n;
+        }
+
+        /**
+         * Returns a ByteBuffer containing chunk of data or a "hunk" of data
+         * (a chunk of a chunk if the chunk size is larger than our ByteBuffers).
+         * If the given chunk does not have enough data this method return
+         * an empty ByteBuffer (READMORE).
+         * If we encounter the final chunk (an empty chunk) this method
+         * returns null.
+         */
+        ByteBuffer tryReadOneHunk(ByteBuffer chunk) throws IOException {
+            int unfulfilled = bytesremaining;
+            int toconsume = bytesToConsume;
+            ChunkState st = state;
+            if (st == ChunkState.READING_LENGTH && chunklen == -1) {
+                debug.log(Level.DEBUG, () ->  "Trying to read chunk len"
+                        + " (remaining in buffer:"+chunk.remaining()+")");
+                int clen = chunklen = tryReadChunkLen(chunk);
+                if (clen == -1) return READMORE;
+                debug.log(Level.DEBUG, "Got chunk len %d", clen);
+                cr = false; partialChunklen = 0;
+                unfulfilled = bytesremaining =  clen;
+                if (clen == 0) toconsume = bytesToConsume = 2; // that was the last chunk
+                else st = state = ChunkState.READING_DATA; // read the data
+            }
+
+            if (toconsume > 0) {
+                debug.log(Level.DEBUG,
+                        "Trying to consume bytes: %d (remaining in buffer: %s)",
+                        toconsume, chunk.remaining());
+                if (tryConsumeBytes(chunk) > 0) {
+                    return READMORE;
+                }
+            }
+
+            toconsume = bytesToConsume;
+            assert toconsume == 0;
+
+
+            if (st == ChunkState.READING_LENGTH) {
+                // we will come here only if chunklen was 0, after having
+                // consumed the trailing CRLF
+                int clen = chunklen;
+                assert clen == 0;
+                debug.log(Level.DEBUG, "No more chunks: %d", clen);
+                // the DONE state is not really needed but it helps with
+                // assertions...
+                state = ChunkState.DONE;
+                return null;
+            }
+
+            int clen = chunklen;
+            assert clen > 0;
+            assert st == ChunkState.READING_DATA;
+
+            ByteBuffer returnBuffer = READMORE; // May be a hunk or a chunk
+            if (unfulfilled > 0) {
+                int bytesread = chunk.remaining();
+                debug.log(Level.DEBUG, "Reading chunk: available %d, needed %d",
+                          bytesread, unfulfilled);
+
+                int bytes2return = Math.min(bytesread, unfulfilled);
+                debug.log(Level.DEBUG,  "Returning chunk bytes: %d", bytes2return);
+                returnBuffer = Utils.slice(chunk, bytes2return);
+                unfulfilled = bytesremaining -= bytes2return;
+                if (unfulfilled == 0) bytesToConsume = 2;
+            }
+
+            assert unfulfilled >= 0;
+
+            if (unfulfilled == 0) {
+                debug.log(Level.DEBUG,
+                        "No more bytes to read - %d yet to consume.",
+                        unfulfilled);
+                // check whether the trailing CRLF is consumed, try to
+                // consume it if not. If tryConsumeBytes needs more bytes
+                // then we will come back here later - skipping the block
+                // that reads data because remaining==0, and finding
+                // that the two bytes are now consumed.
+                if (tryConsumeBytes(chunk) == 0) {
+                    // we're done for this chunk! reset all states and
+                    // prepare to read the next chunk.
+                    chunklen = -1;
+                    partialChunklen = 0;
+                    cr = false;
+                    state = ChunkState.READING_LENGTH;
+                    debug.log(Level.DEBUG, "Ready to read next chunk");
+                }
+            }
+            if (returnBuffer == READMORE) {
+                debug.log(Level.DEBUG, "Need more data");
+            }
+            return returnBuffer;
+        }
+
+
+        // Attempt to parse and push one hunk from the buffer.
+        // Returns true if the final chunk was parsed.
+        // Returns false if we need to push more chunks.
+        private boolean tryPushOneHunk(ByteBuffer b, List<ByteBuffer> out)
+                throws IOException {
+            assert state != ChunkState.DONE;
+            ByteBuffer b1 = tryReadOneHunk(b);
+            if (b1 != null) {
+                //assert b1.hasRemaining() || b1 == READMORE;
+                if (b1.hasRemaining()) {
+                    debug.log(Level.DEBUG, "Sending chunk to consumer (%d)",
+                              b1.remaining());
+                    out.add(b1);
+                    debug.log(Level.DEBUG, "Chunk sent.");
+                }
+                return false; // we haven't parsed the final chunk yet.
+            } else {
+                return true; // we're done! the final chunk was parsed.
+            }
+        }
+
+        private int toDigit(int b) throws IOException {
+            if (b >= 0x30 && b <= 0x39) {
+                return b - 0x30;
+            }
+            if (b >= 0x41 && b <= 0x46) {
+                return b - 0x41 + 10;
+            }
+            if (b >= 0x61 && b <= 0x66) {
+                return b - 0x61 + 10;
+            }
+            throw new IOException("Invalid chunk header byte " + b);
+        }
+
+    }
+
+    class FixedLengthBodyParser implements BodyParser {
+        final int contentLength;
+        final Consumer<Throwable> onComplete;
+        final System.Logger debug = Utils.getDebugLogger(this::dbgString, DEBUG);
+        final String dbgTag = ResponseContent.this.dbgTag + "/FixedLengthBodyParser";
+        volatile int remaining;
+        volatile Throwable closedExceptionally;
+        volatile AbstractSubscription sub;
+        FixedLengthBodyParser(int contentLength, Consumer<Throwable> onComplete) {
+            this.contentLength = this.remaining = contentLength;
+            this.onComplete = onComplete;
+        }
+
+        String dbgString() {
+            return dbgTag;
+        }
+
+        @Override
+        public void onSubscribe(AbstractSubscription sub) {
+            debug.log(Level.DEBUG, () -> "length="
+                        + contentLength +", onSubscribe: "
+                        + pusher.getClass().getName());
+            pusher.onSubscribe(this.sub = sub);
+            try {
+                if (contentLength == 0) {
+                    pusher.onComplete();
+                    onFinished.run();
+                    onComplete.accept(null);
+                }
+            } catch (Throwable t) {
+                closedExceptionally = t;
+                try {
+                    pusher.onError(t);
+                } finally {
+                    onComplete.accept(t);
+                }
+            }
+        }
+
+        @Override
+        public void accept(ByteBuffer b) {
+            if (closedExceptionally != null) {
+                debug.log(Level.DEBUG, () -> "already closed: "
+                            + closedExceptionally);
+                return;
+            }
+            boolean completed = false;
+            try {
+                int unfulfilled = remaining;
+                debug.log(Level.DEBUG, "Parser got %d bytes (%d remaining / %d)",
+                        b.remaining(), unfulfilled, contentLength);
+                assert unfulfilled != 0 || contentLength == 0 || b.remaining() == 0;
+
+                if (unfulfilled == 0 && contentLength > 0) return;
+
+                if (b.hasRemaining() && unfulfilled > 0) {
+                    // only reduce demand if we actually push something.
+                    // we would not have come here if there was no
+                    // demand.
+                    boolean hasDemand = sub.demand().tryDecrement();
+                    assert hasDemand;
+                    int amount = Math.min(b.remaining(), unfulfilled);
+                    unfulfilled = remaining -= amount;
+                    ByteBuffer buffer = Utils.slice(b, amount);
+                    pusher.onNext(List.of(buffer));
+                }
+                if (unfulfilled == 0) {
+                    // We're done! All data has been received.
+                    assert closedExceptionally == null;
+                    onFinished.run();
+                    pusher.onComplete();
+                    completed = true;
+                    onComplete.accept(closedExceptionally); // should be null
+                } else {
+                    assert b.remaining() == 0;
+                }
+            } catch (Throwable t) {
+                debug.log(Level.DEBUG, "Unexpected exception", t);
+                closedExceptionally = t;
+                if (!completed) {
+                    onComplete.accept(t);
+                }
             }
-            remaining -= bytesread;
-            dataConsumer.accept(Optional.of(buffer));
         }
-        onFinished.run();
-        dataConsumer.accept(Optional.empty());
     }
 }
diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/ResponseHeaders.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/ResponseHeaders.java
deleted file mode 100644
index 22db4285e69..00000000000
--- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/ResponseHeaders.java
+++ /dev/null
@@ -1,191 +0,0 @@
-/*
- * Copyright (c) 2015, 2017, 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.  Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * 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 jdk.incubator.http;
-
-import sun.net.www.MessageHeader;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.net.ProtocolException;
-import java.nio.ByteBuffer;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Locale;
-import java.util.Map;
-import java.util.Optional;
-import java.util.OptionalLong;
-
-import static java.lang.String.format;
-import static jdk.incubator.http.internal.common.Utils.isValidName;
-import static jdk.incubator.http.internal.common.Utils.isValidValue;
-import static java.util.Objects.requireNonNull;
-
-/*
- * Reads entire header block off channel, in blocking mode.
- * This class is not thread-safe.
- */
-final class ResponseHeaders implements HttpHeaders {
-
-    private static final char CR = '\r';
-    private static final char LF = '\n';
-
-    private final ImmutableHeaders delegate;
-
-    /*
-     * This constructor takes a connection from which the header block is read
-     * and a buffer which may contain an initial portion of this header block.
-     *
-     * After the headers have been parsed (this constructor has returned) the
-     * leftovers (i.e. data, if any, beyond the header block) are accessible
-     * from this same buffer from its position to its limit.
-     */
-    ResponseHeaders(HttpConnection connection, ByteBuffer buffer) throws IOException {
-        requireNonNull(connection);
-        requireNonNull(buffer);
-        InputStreamWrapper input = new InputStreamWrapper(connection, buffer);
-        delegate = ImmutableHeaders.of(parse(input));
-    }
-
-    static final class InputStreamWrapper extends InputStream {
-        final HttpConnection connection;
-        ByteBuffer buffer;
-        int lastRead = -1; // last byte read from the buffer
-        int consumed = 0; // number of bytes consumed.
-        InputStreamWrapper(HttpConnection connection, ByteBuffer buffer) {
-            super();
-            this.connection = connection;
-            this.buffer = buffer;
-        }
-        @Override
-        public int read() throws IOException {
-            if (!buffer.hasRemaining()) {
-                buffer = connection.read();
-                if (buffer == null) {
-                    return lastRead = -1;
-                }
-            }
-            // don't let consumed become positive again if it overflowed
-            // we just want to make sure that consumed == 1 really means
-            // that only one byte was consumed.
-            if (consumed >= 0) consumed++;
-            return lastRead = buffer.get();
-        }
-    }
-
-    private static void display(Map<String, List<String>> map) {
-        map.forEach((k,v) -> {
-            System.out.print (k + ": ");
-            for (String val : v) {
-                System.out.print(val + ", ");
-            }
-            System.out.println("");
-        });
-    }
-
-    private Map<String, List<String>> parse(InputStreamWrapper input)
-         throws IOException
-    {
-        // The bulk of work is done by this time-proven class
-        MessageHeader h = new MessageHeader();
-        h.parseHeader(input);
-
-        // When there are no headers (and therefore no body), the status line
-        // will be followed by an empty CRLF line.
-        // In that case MessageHeader.parseHeader() will consume the first
-        // CR character and stop there. In this case we must consume the
-        // remaining LF.
-        if (input.consumed == 1 && CR == (char) input.lastRead) {
-            // MessageHeader will not consume LF if the first character it
-            // finds is CR. This only happens if there are no headers, and
-            // only one byte will be consumed from the buffer. In this case
-            // the next byte MUST be LF
-            if (input.read() != LF) {
-                throw new IOException("Unexpected byte sequence when no headers: "
-                     + ((int)CR) + " " + input.lastRead
-                     + "(" + ((int)CR) + " " + ((int)LF) + " expected)");
-            }
-        }
-
-        Map<String, List<String>> rawHeaders = h.getHeaders();
-
-        // Now some additional post-processing to adapt the results received
-        // from MessageHeader to what is needed here
-        Map<String, List<String>> cookedHeaders = new HashMap<>();
-        for (Map.Entry<String, List<String>> e : rawHeaders.entrySet()) {
-            String key = e.getKey();
-            if (key == null) {
-                throw new ProtocolException("Bad header-field");
-            }
-            if (!isValidName(key)) {
-                throw new ProtocolException(format(
-                        "Bad header-name: '%s'", key));
-            }
-            List<String> newValues = e.getValue();
-            for (String v : newValues) {
-                if (!isValidValue(v)) {
-                    throw new ProtocolException(format(
-                            "Bad header-value for header-name: '%s'", key));
-                }
-            }
-            String k = key.toLowerCase(Locale.US);
-            cookedHeaders.merge(k, newValues,
-                    (v1, v2) -> {
-                        ArrayList<String> newV = new ArrayList<>();
-                        if (v1 != null) {
-                            newV.addAll(v1);
-                        }
-                        newV.addAll(v2);
-                        return newV;
-                    });
-        }
-        return cookedHeaders;
-    }
-
-    int getContentLength() throws IOException {
-        return (int) firstValueAsLong("Content-Length").orElse(-1);
-    }
-
-    @Override
-    public Optional<String> firstValue(String name) {
-        return delegate.firstValue(name);
-    }
-
-    @Override
-    public OptionalLong firstValueAsLong(String name) {
-        return delegate.firstValueAsLong(name);
-    }
-
-    @Override
-    public List<String> allValues(String name) {
-        return delegate.allValues(name);
-    }
-
-    @Override
-    public Map<String, List<String>> map() {
-        return delegate.map();
-    }
-}
diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/ResponseProcessors.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/ResponseProcessors.java
deleted file mode 100644
index dd21b1aabf1..00000000000
--- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/ResponseProcessors.java
+++ /dev/null
@@ -1,331 +0,0 @@
-/*
- * Copyright (c) 2016, 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.  Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * 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 jdk.incubator.http;
-
-import java.io.IOException;
-import java.io.UncheckedIOException;
-import java.net.URI;
-import java.nio.ByteBuffer;
-import java.nio.channels.FileChannel;
-import java.nio.file.Files;
-import java.nio.file.OpenOption;
-import java.nio.file.Path;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Optional;
-import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.CompletionStage;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.Flow;
-import java.util.function.Consumer;
-import java.util.function.Function;
-import jdk.incubator.http.internal.common.MinimalFuture;
-import jdk.incubator.http.internal.common.Utils;
-import jdk.incubator.http.internal.common.Log;
-
-class ResponseProcessors {
-
-    static class ConsumerProcessor implements HttpResponse.BodyProcessor<Void> {
-        private final Consumer<Optional<byte[]>> consumer;
-        private Flow.Subscription subscription;
-        private final CompletableFuture<Void> result = new MinimalFuture<>();
-
-        ConsumerProcessor(Consumer<Optional<byte[]>> consumer) {
-            this.consumer = consumer;
-        }
-
-        @Override
-        public CompletionStage<Void> getBody() {
-            return result;
-        }
-
-        @Override
-        public void onSubscribe(Flow.Subscription subscription) {
-            this.subscription = subscription;
-            subscription.request(1);
-        }
-
-        @Override
-        public void onNext(ByteBuffer item) {
-            byte[] buf = new byte[item.remaining()];
-            item.get(buf);
-            consumer.accept(Optional.of(buf));
-            subscription.request(1);
-        }
-
-        @Override
-        public void onError(Throwable throwable) {
-            result.completeExceptionally(throwable);
-        }
-
-        @Override
-        public void onComplete() {
-            consumer.accept(Optional.empty());
-            result.complete(null);
-        }
-
-    }
-
-    static class PathProcessor implements HttpResponse.BodyProcessor<Path> {
-
-        private final Path file;
-        private final CompletableFuture<Path> result = new MinimalFuture<>();
-
-        private Flow.Subscription subscription;
-        private FileChannel out;
-        private final OpenOption[] options;
-
-        PathProcessor(Path file, OpenOption... options) {
-            this.file = file;
-            this.options = options;
-        }
-
-        @Override
-        public void onSubscribe(Flow.Subscription subscription) {
-            this.subscription = subscription;
-            try {
-                out = FileChannel.open(file, options);
-            } catch (IOException e) {
-                result.completeExceptionally(e);
-                subscription.cancel();
-                return;
-            }
-            subscription.request(1);
-        }
-
-        @Override
-        public void onNext(ByteBuffer item) {
-            try {
-                out.write(item);
-            } catch (IOException ex) {
-                Utils.close(out);
-                subscription.cancel();
-                result.completeExceptionally(ex);
-            }
-            subscription.request(1);
-        }
-
-        @Override
-        public void onError(Throwable e) {
-            result.completeExceptionally(e);
-            Utils.close(out);
-        }
-
-        @Override
-        public void onComplete() {
-            Utils.close(out);
-            result.complete(file);
-        }
-
-        @Override
-        public CompletionStage<Path> getBody() {
-            return result;
-        }
-    }
-
-    static class ByteArrayProcessor<T> implements HttpResponse.BodyProcessor<T> {
-        private final Function<byte[], T> finisher;
-        private final CompletableFuture<T> result = new MinimalFuture<>();
-        private final List<ByteBuffer> received = new ArrayList<>();
-
-        private Flow.Subscription subscription;
-
-        ByteArrayProcessor(Function<byte[],T> finisher) {
-            this.finisher = finisher;
-        }
-
-        @Override
-        public void onSubscribe(Flow.Subscription subscription) {
-            if (this.subscription != null) {
-                subscription.cancel();
-                return;
-            }
-            this.subscription = subscription;
-            // We can handle whatever you've got
-            subscription.request(Long.MAX_VALUE);
-        }
-
-        @Override
-        public void onNext(ByteBuffer item) {
-            // incoming buffers are allocated by http client internally,
-            // and won't be used anywhere except this place.
-            // So it's free simply to store them for further processing.
-            if(item.hasRemaining()) {
-                received.add(item);
-            }
-        }
-
-        @Override
-        public void onError(Throwable throwable) {
-            received.clear();
-            result.completeExceptionally(throwable);
-        }
-
-        static private byte[] join(List<ByteBuffer> bytes) {
-            int size = Utils.remaining(bytes);
-            byte[] res = new byte[size];
-            int from = 0;
-            for (ByteBuffer b : bytes) {
-                int l = b.remaining();
-                b.get(res, from, l);
-                from += l;
-            }
-            return res;
-        }
-
-        @Override
-        public void onComplete() {
-            try {
-                result.complete(finisher.apply(join(received)));
-                received.clear();
-            } catch (IllegalArgumentException e) {
-                result.completeExceptionally(e);
-            }
-        }
-
-        @Override
-        public CompletionStage<T> getBody() {
-            return result;
-        }
-    }
-
-    static class MultiProcessorImpl<V> implements HttpResponse.MultiProcessor<MultiMapResult<V>,V> {
-        private final MultiMapResult<V> results;
-        private final Function<HttpRequest,Optional<HttpResponse.BodyHandler<V>>> pushHandler;
-        private final boolean completion; // aggregate completes on last PP received or overall completion
-
-        MultiProcessorImpl(Function<HttpRequest,Optional<HttpResponse.BodyHandler<V>>> pushHandler, boolean completion) {
-            this.results = new MultiMapResult<V>(new ConcurrentHashMap<>());
-            this.pushHandler = pushHandler;
-            this.completion = completion;
-        }
-
-        @Override
-        public Optional<HttpResponse.BodyHandler<V>> onRequest(HttpRequest request) {
-            return pushHandler.apply(request);
-        }
-
-        @Override
-        public void onResponse(HttpResponse<V> response) {
-            results.put(response.request(), CompletableFuture.completedFuture(response));
-        }
-
-        @Override
-        public void onError(HttpRequest request, Throwable t) {
-            results.put(request, MinimalFuture.failedFuture(t));
-        }
-
-        @Override
-        public CompletableFuture<MultiMapResult<V>> completion(
-                CompletableFuture<Void> onComplete, CompletableFuture<Void> onFinalPushPromise) {
-            if (completion)
-                return onComplete.thenApply((ignored)-> results);
-            else
-                return onFinalPushPromise.thenApply((ignored) -> results);
-        }
-    }
-
-    static class MultiFile {
-
-        final Path pathRoot;
-
-        MultiFile(Path destination) {
-            if (!destination.toFile().isDirectory())
-                throw new UncheckedIOException(new IOException("destination is not a directory"));
-            pathRoot = destination;
-        }
-
-        Optional<HttpResponse.BodyHandler<Path>> handlePush(HttpRequest request) {
-            final URI uri = request.uri();
-            String path = uri.getPath();
-            while (path.startsWith("/"))
-                path = path.substring(1);
-            Path p = pathRoot.resolve(path);
-            if (Log.trace()) {
-                Log.logTrace("Creating file body processor for URI={0}, path={1}",
-                             uri, p);
-            }
-            try {
-                Files.createDirectories(p.getParent());
-            } catch (IOException ex) {
-                throw new UncheckedIOException(ex);
-            }
-
-            final HttpResponse.BodyHandler<Path> proc =
-                 HttpResponse.BodyHandler.asFile(p);
-
-            return Optional.of(proc);
-        }
-    }
-
-    /**
-     * Currently this consumes all of the data and ignores it
-     */
-    static class NullProcessor<T> implements HttpResponse.BodyProcessor<T> {
-
-        Flow.Subscription subscription;
-        final CompletableFuture<T> cf = new MinimalFuture<>();
-        final Optional<T> result;
-
-        NullProcessor(Optional<T> result) {
-            this.result = result;
-        }
-
-        @Override
-        public void onSubscribe(Flow.Subscription subscription) {
-            this.subscription = subscription;
-            subscription.request(Long.MAX_VALUE);
-        }
-
-        @Override
-        public void onNext(ByteBuffer item) {
-            // TODO: check whether this should consume the buffer, as in:
-            item.position(item.limit());
-        }
-
-        @Override
-        public void onError(Throwable throwable) {
-            cf.completeExceptionally(throwable);
-        }
-
-        @Override
-        public void onComplete() {
-            if (result.isPresent()) {
-                cf.complete(result.get());
-            } else {
-                cf.complete(null);
-            }
-        }
-
-        @Override
-        public CompletionStage<T> getBody() {
-            return cf;
-        }
-    }
-}
diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/ResponseSubscribers.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/ResponseSubscribers.java
new file mode 100644
index 00000000000..466f8d82c06
--- /dev/null
+++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/ResponseSubscribers.java
@@ -0,0 +1,561 @@
+/*
+ * Copyright (c) 2016, 2017, 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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 jdk.incubator.http;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.System.Logger.Level;
+import java.nio.ByteBuffer;
+import java.nio.channels.FileChannel;
+import java.nio.file.OpenOption;
+import java.nio.file.Path;
+import java.security.AccessControlContext;
+import java.security.AccessController;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.concurrent.ArrayBlockingQueue;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.CompletionStage;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.Flow;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.function.Consumer;
+import java.util.function.Function;
+import jdk.incubator.http.internal.common.MinimalFuture;
+import jdk.incubator.http.internal.common.Utils;
+
+class ResponseSubscribers {
+
+    static class ConsumerSubscriber implements HttpResponse.BodySubscriber<Void> {
+        private final Consumer<Optional<byte[]>> consumer;
+        private Flow.Subscription subscription;
+        private final CompletableFuture<Void> result = new MinimalFuture<>();
+        private final AtomicBoolean subscribed = new AtomicBoolean();
+
+        ConsumerSubscriber(Consumer<Optional<byte[]>> consumer) {
+            this.consumer = consumer;
+        }
+
+        @Override
+        public CompletionStage<Void> getBody() {
+            return result;
+        }
+
+        @Override
+        public void onSubscribe(Flow.Subscription subscription) {
+            if (!subscribed.compareAndSet(false, true)) {
+                subscription.cancel();
+            } else {
+                this.subscription = subscription;
+                subscription.request(1);
+            }
+        }
+
+        @Override
+        public void onNext(List<ByteBuffer> items) {
+            for (ByteBuffer item : items) {
+                byte[] buf = new byte[item.remaining()];
+                item.get(buf);
+                consumer.accept(Optional.of(buf));
+            }
+            subscription.request(1);
+        }
+
+        @Override
+        public void onError(Throwable throwable) {
+            result.completeExceptionally(throwable);
+        }
+
+        @Override
+        public void onComplete() {
+            consumer.accept(Optional.empty());
+            result.complete(null);
+        }
+
+    }
+
+    static class PathSubscriber implements HttpResponse.BodySubscriber<Path> {
+
+        private final Path file;
+        private final CompletableFuture<Path> result = new MinimalFuture<>();
+
+        private volatile Flow.Subscription subscription;
+        private volatile FileChannel out;
+        private volatile AccessControlContext acc;
+        private final OpenOption[] options;
+
+        PathSubscriber(Path file, OpenOption... options) {
+            this.file = file;
+            this.options = options;
+        }
+
+        void setAccessControlContext(AccessControlContext acc) {
+            this.acc = acc;
+        }
+
+        @Override
+        public void onSubscribe(Flow.Subscription subscription) {
+            if (System.getSecurityManager() != null && acc == null)
+                throw new InternalError(
+                        "Unexpected null acc when security manager has been installed");
+
+            this.subscription = subscription;
+            try {
+                PrivilegedExceptionAction<FileChannel> pa =
+                        () -> FileChannel.open(file, options);
+                out = AccessController.doPrivileged(pa, acc);
+            } catch (PrivilegedActionException pae) {
+                Throwable t = pae.getCause() != null ? pae.getCause() : pae;
+                result.completeExceptionally(t);
+                subscription.cancel();
+                return;
+            }
+            subscription.request(1);
+        }
+
+        @Override
+        public void onNext(List<ByteBuffer> items) {
+            try {
+                out.write(items.toArray(Utils.EMPTY_BB_ARRAY));
+            } catch (IOException ex) {
+                Utils.close(out);
+                subscription.cancel();
+                result.completeExceptionally(ex);
+            }
+            subscription.request(1);
+        }
+
+        @Override
+        public void onError(Throwable e) {
+            result.completeExceptionally(e);
+            Utils.close(out);
+        }
+
+        @Override
+        public void onComplete() {
+            Utils.close(out);
+            result.complete(file);
+        }
+
+        @Override
+        public CompletionStage<Path> getBody() {
+            return result;
+        }
+    }
+
+    static class ByteArraySubscriber<T> implements HttpResponse.BodySubscriber<T> {
+        private final Function<byte[], T> finisher;
+        private final CompletableFuture<T> result = new MinimalFuture<>();
+        private final List<ByteBuffer> received = new ArrayList<>();
+
+        private volatile Flow.Subscription subscription;
+
+        ByteArraySubscriber(Function<byte[],T> finisher) {
+            this.finisher = finisher;
+        }
+
+        @Override
+        public void onSubscribe(Flow.Subscription subscription) {
+            if (this.subscription != null) {
+                subscription.cancel();
+                return;
+            }
+            this.subscription = subscription;
+            // We can handle whatever you've got
+            subscription.request(Long.MAX_VALUE);
+        }
+
+        @Override
+        public void onNext(List<ByteBuffer> items) {
+            // incoming buffers are allocated by http client internally,
+            // and won't be used anywhere except this place.
+            // So it's free simply to store them for further processing.
+            assert Utils.hasRemaining(items);
+            Utils.accumulateBuffers(received, items);
+        }
+
+        @Override
+        public void onError(Throwable throwable) {
+            received.clear();
+            result.completeExceptionally(throwable);
+        }
+
+        static private byte[] join(List<ByteBuffer> bytes) {
+            int size = Utils.remaining(bytes, Integer.MAX_VALUE);
+            byte[] res = new byte[size];
+            int from = 0;
+            for (ByteBuffer b : bytes) {
+                int l = b.remaining();
+                b.get(res, from, l);
+                from += l;
+            }
+            return res;
+        }
+
+        @Override
+        public void onComplete() {
+            try {
+                result.complete(finisher.apply(join(received)));
+                received.clear();
+            } catch (IllegalArgumentException e) {
+                result.completeExceptionally(e);
+            }
+        }
+
+        @Override
+        public CompletionStage<T> getBody() {
+            return result;
+        }
+    }
+
+    /**
+     * An InputStream built on top of the Flow API.
+     */
+    static class HttpResponseInputStream extends InputStream
+        implements HttpResponse.BodySubscriber<InputStream>
+    {
+        final static boolean DEBUG = Utils.DEBUG;
+        final static int MAX_BUFFERS_IN_QUEUE = 1;  // lock-step with the producer
+
+        // An immutable ByteBuffer sentinel to mark that the last byte was received.
+        private static final ByteBuffer LAST_BUFFER = ByteBuffer.wrap(new byte[0]);
+        private static final List<ByteBuffer> LAST_LIST = List.of(LAST_BUFFER);
+        private static final System.Logger DEBUG_LOGGER =
+                Utils.getDebugLogger("HttpResponseInputStream"::toString, DEBUG);
+
+        // A queue of yet unprocessed ByteBuffers received from the flow API.
+        private final BlockingQueue<List<ByteBuffer>> buffers;
+        private volatile Flow.Subscription subscription;
+        private volatile boolean closed;
+        private volatile Throwable failed;
+        private volatile Iterator<ByteBuffer> currentListItr;
+        private volatile ByteBuffer currentBuffer;
+        private final AtomicBoolean subscribed = new AtomicBoolean();
+
+        HttpResponseInputStream() {
+            this(MAX_BUFFERS_IN_QUEUE);
+        }
+
+        HttpResponseInputStream(int maxBuffers) {
+            int capacity = (maxBuffers <= 0 ? MAX_BUFFERS_IN_QUEUE : maxBuffers);
+            // 1 additional slot needed for LAST_LIST added by onComplete
+            this.buffers = new ArrayBlockingQueue<>(capacity + 1);
+        }
+
+        @Override
+        public CompletionStage<InputStream> getBody() {
+            // Returns the stream immediately, before the
+            // response body is received.
+            // This makes it possible for senAsync().get().body()
+            // to complete before the response body is received.
+            return CompletableFuture.completedStage(this);
+        }
+
+        // Returns the current byte buffer to read from.
+        // If the current buffer has no remaining data, this method will take the
+        // next buffer from the buffers queue, possibly blocking until
+        // a new buffer is made available through the Flow API, or the
+        // end of the flow has been reached.
+        private ByteBuffer current() throws IOException {
+            while (currentBuffer == null || !currentBuffer.hasRemaining()) {
+                // Check whether the stream is closed or exhausted
+                if (closed || failed != null) {
+                    throw new IOException("closed", failed);
+                }
+                if (currentBuffer == LAST_BUFFER) break;
+
+                try {
+                    if (currentListItr == null || !currentListItr.hasNext()) {
+                        // Take a new list of buffers from the queue, blocking
+                        // if none is available yet...
+
+                        DEBUG_LOGGER.log(Level.DEBUG, "Taking list of Buffers");
+                        List<ByteBuffer> lb = buffers.take();
+                        currentListItr = lb.iterator();
+                        DEBUG_LOGGER.log(Level.DEBUG, "List of Buffers Taken");
+
+                        // Check whether an exception was encountered upstream
+                        if (closed || failed != null)
+                            throw new IOException("closed", failed);
+
+                        // Check whether we're done.
+                        if (lb == LAST_LIST) {
+                            currentListItr = null;
+                            currentBuffer = LAST_BUFFER;
+                            break;
+                        }
+
+                        // Request another upstream item ( list of buffers )
+                        Flow.Subscription s = subscription;
+                        if (s != null) {
+                            DEBUG_LOGGER.log(Level.DEBUG, "Increased demand by 1");
+                            s.request(1);
+                        }
+                        assert currentListItr != null;
+                        if (lb.isEmpty()) continue;
+                    }
+                    assert currentListItr != null;
+                    assert currentListItr.hasNext();
+                    DEBUG_LOGGER.log(Level.DEBUG, "Next Buffer");
+                    currentBuffer = currentListItr.next();
+                } catch (InterruptedException ex) {
+                    // continue
+                }
+            }
+            assert currentBuffer == LAST_BUFFER || currentBuffer.hasRemaining();
+            return currentBuffer;
+        }
+
+        @Override
+        public int read(byte[] bytes, int off, int len) throws IOException {
+            // get the buffer to read from, possibly blocking if
+            // none is available
+            ByteBuffer buffer;
+            if ((buffer = current()) == LAST_BUFFER) return -1;
+
+            // don't attempt to read more than what is available
+            // in the current buffer.
+            int read = Math.min(buffer.remaining(), len);
+            assert read > 0 && read <= buffer.remaining();
+
+            // buffer.get() will do the boundary check for us.
+            buffer.get(bytes, off, read);
+            return read;
+        }
+
+        @Override
+        public int read() throws IOException {
+            ByteBuffer buffer;
+            if ((buffer = current()) == LAST_BUFFER) return -1;
+            return buffer.get() & 0xFF;
+        }
+
+        @Override
+        public void onSubscribe(Flow.Subscription s) {
+            try {
+                if (!subscribed.compareAndSet(false, true)) {
+                    s.cancel();
+                } else {
+                    // check whether the stream is already closed.
+                    // if so, we should cancel the subscription
+                    // immediately.
+                    boolean closed;
+                    synchronized (this) {
+                        closed = this.closed;
+                        if (!closed) {
+                            this.subscription = s;
+                        }
+                    }
+                    if (closed) {
+                        s.cancel();
+                        return;
+                    }
+                    assert buffers.remainingCapacity() > 1; // should contain at least 2
+                    DEBUG_LOGGER.log(Level.DEBUG, () -> "onSubscribe: requesting "
+                            + Math.max(1, buffers.remainingCapacity() - 1));
+                    s.request(Math.max(1, buffers.remainingCapacity() - 1));
+                }
+            } catch (Throwable t) {
+                failed = t;
+                try {
+                    close();
+                } catch (IOException x) {
+                    // OK
+                } finally {
+                    onError(t);
+                }
+            }
+        }
+
+        @Override
+        public void onNext(List<ByteBuffer> t) {
+            Objects.requireNonNull(t);
+            try {
+                DEBUG_LOGGER.log(Level.DEBUG, "next item received");
+                if (!buffers.offer(t)) {
+                    throw new IllegalStateException("queue is full");
+                }
+                DEBUG_LOGGER.log(Level.DEBUG, "item offered");
+            } catch (Throwable ex) {
+                failed = ex;
+                try {
+                    close();
+                } catch (IOException ex1) {
+                    // OK
+                } finally {
+                    onError(ex);
+                }
+            }
+        }
+
+        @Override
+        public void onError(Throwable thrwbl) {
+            subscription = null;
+            failed = Objects.requireNonNull(thrwbl);
+            // The client process that reads the input stream might
+            // be blocked in queue.take().
+            // Tries to offer LAST_LIST to the queue. If the queue is
+            // full we don't care if we can't insert this buffer, as
+            // the client can't be blocked in queue.take() in that case.
+            // Adding LAST_LIST to the queue is harmless, as the client
+            // should find failed != null before handling LAST_LIST.
+            buffers.offer(LAST_LIST);
+        }
+
+        @Override
+        public void onComplete() {
+            subscription = null;
+            onNext(LAST_LIST);
+        }
+
+        @Override
+        public void close() throws IOException {
+            Flow.Subscription s;
+            synchronized (this) {
+                if (closed) return;
+                closed = true;
+                s = subscription;
+                subscription = null;
+            }
+            // s will be null if already completed
+            try {
+                if (s != null) {
+                    s.cancel();
+                }
+            } finally {
+                buffers.offer(LAST_LIST);
+                super.close();
+            }
+        }
+
+    }
+
+    static class MultiSubscriberImpl<V>
+        implements HttpResponse.MultiSubscriber<MultiMapResult<V>,V>
+    {
+        private final MultiMapResult<V> results;
+        private final Function<HttpRequest,Optional<HttpResponse.BodyHandler<V>>> pushHandler;
+        private final Function<HttpRequest,HttpResponse.BodyHandler<V>> requestHandler;
+        private final boolean completion; // aggregate completes on last PP received or overall completion
+
+        MultiSubscriberImpl(
+                Function<HttpRequest,HttpResponse.BodyHandler<V>> requestHandler,
+                Function<HttpRequest,Optional<HttpResponse.BodyHandler<V>>> pushHandler, boolean completion) {
+            this.results = new MultiMapResult<>(new ConcurrentHashMap<>());
+            this.requestHandler = requestHandler;
+            this.pushHandler = pushHandler;
+            this.completion = completion;
+        }
+
+        @Override
+        public HttpResponse.BodyHandler<V> onRequest(HttpRequest request) {
+            CompletableFuture<HttpResponse<V>> cf = MinimalFuture.newMinimalFuture();
+            results.put(request, cf);
+            return requestHandler.apply(request);
+        }
+
+        @Override
+        public Optional<HttpResponse.BodyHandler<V>> onPushPromise(HttpRequest push) {
+            CompletableFuture<HttpResponse<V>> cf = MinimalFuture.newMinimalFuture();
+            results.put(push, cf);
+            return pushHandler.apply(push);
+        }
+
+        @Override
+        public void onResponse(HttpResponse<V> response) {
+            CompletableFuture<HttpResponse<V>> cf = results.get(response.request());
+            cf.complete(response);
+        }
+
+        @Override
+        public void onError(HttpRequest request, Throwable t) {
+            CompletableFuture<HttpResponse<V>> cf = results.get(request);
+            cf.completeExceptionally(t);
+        }
+
+        @Override
+        public CompletableFuture<MultiMapResult<V>> completion(
+                CompletableFuture<Void> onComplete, CompletableFuture<Void> onFinalPushPromise) {
+            if (completion)
+                return onComplete.thenApply((ignored)-> results);
+            else
+                return onFinalPushPromise.thenApply((ignored) -> results);
+        }
+    }
+
+    /**
+     * Currently this consumes all of the data and ignores it
+     */
+    static class NullSubscriber<T> implements HttpResponse.BodySubscriber<T> {
+
+        private final CompletableFuture<T> cf = new MinimalFuture<>();
+        private final Optional<T> result;
+        private final AtomicBoolean subscribed = new AtomicBoolean();
+
+        NullSubscriber(Optional<T> result) {
+            this.result = result;
+        }
+
+        @Override
+        public void onSubscribe(Flow.Subscription subscription) {
+            if (!subscribed.compareAndSet(false, true)) {
+                subscription.cancel();
+            } else {
+                subscription.request(Long.MAX_VALUE);
+            }
+        }
+
+        @Override
+        public void onNext(List<ByteBuffer> items) {
+            Objects.requireNonNull(items);
+        }
+
+        @Override
+        public void onError(Throwable throwable) {
+            cf.completeExceptionally(throwable);
+        }
+
+        @Override
+        public void onComplete() {
+            if (result.isPresent()) {
+                cf.complete(result.get());
+            } else {
+                cf.complete(null);
+            }
+        }
+
+        @Override
+        public CompletionStage<T> getBody() {
+            return cf;
+        }
+    }
+}
diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/SSLConnection.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/SSLConnection.java
deleted file mode 100644
index 9eb6a37e250..00000000000
--- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/SSLConnection.java
+++ /dev/null
@@ -1,205 +0,0 @@
-/*
- * Copyright (c) 2015, 2017, 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.  Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * 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 jdk.incubator.http;
-
-import java.io.IOException;
-import java.net.InetSocketAddress;
-import java.nio.ByteBuffer;
-import java.nio.channels.SocketChannel;
-import java.util.concurrent.CompletableFuture;
-import javax.net.ssl.SSLEngineResult.Status;
-import javax.net.ssl.SSLParameters;
-import jdk.incubator.http.SSLDelegate.WrapperResult;
-
-import jdk.incubator.http.internal.common.ByteBufferReference;
-import jdk.incubator.http.internal.common.MinimalFuture;
-import jdk.incubator.http.internal.common.Utils;
-
-/**
- * An SSL connection built on a Plain TCP connection.
- */
-class SSLConnection extends HttpConnection {
-
-    PlainHttpConnection delegate;
-    SSLDelegate sslDelegate;
-    final String[] alpn;
-    final String serverName;
-
-    @Override
-    public CompletableFuture<Void> connectAsync() {
-        return delegate.connectAsync()
-                .thenCompose((Void v) ->
-                                MinimalFuture.supply( () -> {
-                                    this.sslDelegate = new SSLDelegate(delegate.channel(), client, alpn, serverName);
-                                    return null;
-                                }));
-    }
-
-    @Override
-    public void connect() throws IOException {
-        delegate.connect();
-        this.sslDelegate = new SSLDelegate(delegate.channel(), client, alpn, serverName);
-    }
-
-    SSLConnection(InetSocketAddress addr, HttpClientImpl client, String[] ap) {
-        super(addr, client);
-        this.alpn = ap;
-        this.serverName = Utils.getServerName(addr);
-        delegate = new PlainHttpConnection(addr, client);
-    }
-
-    /**
-     * Create an SSLConnection from an existing connected AsyncSSLConnection.
-     * Used when downgrading from HTTP/2 to HTTP/1.1
-     */
-    SSLConnection(AsyncSSLConnection c) {
-        super(c.address, c.client);
-        this.delegate = c.plainConnection();
-        AsyncSSLDelegate adel = c.sslDelegate();
-        this.sslDelegate = new SSLDelegate(adel.engine, delegate.channel(), client, adel.serverName);
-        this.alpn = adel.alpn;
-        this.serverName = adel.serverName;
-    }
-
-    @Override
-    SSLParameters sslParameters() {
-        return sslDelegate.getSSLParameters();
-    }
-
-    @Override
-    public String toString() {
-        return "SSLConnection: " + super.toString();
-    }
-
-    private static long countBytes(ByteBuffer[] buffers, int start, int length) {
-        long c = 0;
-        for (int i=0; i<length; i++) {
-            c+= buffers[start+i].remaining();
-        }
-        return c;
-    }
-
-    @Override
-    ConnectionPool.CacheKey cacheKey() {
-        return ConnectionPool.cacheKey(address, null);
-    }
-
-    @Override
-    long write(ByteBuffer[] buffers, int start, int number) throws IOException {
-        //debugPrint("Send", buffers, start, number);
-        long l = countBytes(buffers, start, number);
-        WrapperResult r = sslDelegate.sendData(buffers, start, number);
-        if (r.result.getStatus() == Status.CLOSED) {
-            if (l > 0) {
-                throw new IOException("SSLHttpConnection closed");
-            }
-        }
-        return l;
-    }
-
-    @Override
-    long write(ByteBuffer buffer) throws IOException {
-        //debugPrint("Send", buffer);
-        long l = buffer.remaining();
-        WrapperResult r = sslDelegate.sendData(buffer);
-        if (r.result.getStatus() == Status.CLOSED) {
-            if (l > 0) {
-                throw new IOException("SSLHttpConnection closed");
-            }
-        }
-        return l;
-    }
-
-    @Override
-    void writeAsync(ByteBufferReference[] buffers) throws IOException {
-        write(ByteBufferReference.toBuffers(buffers), 0, buffers.length);
-    }
-
-    @Override
-    void writeAsyncUnordered(ByteBufferReference[] buffers) throws IOException {
-        write(ByteBufferReference.toBuffers(buffers), 0, buffers.length);
-    }
-
-    @Override
-    void flushAsync() throws IOException {
-        // nothing to do
-    }
-
-    @Override
-    public void close() {
-        Utils.close(delegate.channel());
-    }
-
-    @Override
-    void shutdownInput() throws IOException {
-        delegate.channel().shutdownInput();
-    }
-
-    @Override
-    void shutdownOutput() throws IOException {
-        delegate.channel().shutdownOutput();
-    }
-
-    @Override
-    protected ByteBuffer readImpl() throws IOException {
-        WrapperResult r = sslDelegate.recvData(ByteBuffer.allocate(8192));
-        // TODO: check for closure
-        int n = r.result.bytesProduced();
-        if (n > 0) {
-            return r.buf;
-        } else if (n == 0) {
-            return Utils.EMPTY_BYTEBUFFER;
-        } else {
-            return null;
-        }
-    }
-
-    @Override
-    boolean connected() {
-        return delegate.connected();
-    }
-
-    @Override
-    SocketChannel channel() {
-        return delegate.channel();
-    }
-
-    @Override
-    CompletableFuture<Void> whenReceivingResponse() {
-        return delegate.whenReceivingResponse();
-    }
-
-    @Override
-    boolean isSecure() {
-        return true;
-    }
-
-    @Override
-    boolean isProxied() {
-        return false;
-    }
-
-}
diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/SSLDelegate.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/SSLDelegate.java
index 85f206fae05..0af6fd6d6f4 100644
--- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/SSLDelegate.java
+++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/SSLDelegate.java
@@ -28,8 +28,6 @@ package jdk.incubator.http;
 import java.io.IOException;
 import java.nio.ByteBuffer;
 import java.nio.channels.SocketChannel;
-import java.util.Arrays;
-import java.util.List;
 import java.util.concurrent.locks.Lock;
 import java.util.concurrent.locks.ReentrantLock;
 import javax.net.ssl.SSLEngineResult.HandshakeStatus;
@@ -41,60 +39,56 @@ import static javax.net.ssl.SSLEngineResult.HandshakeStatus.*;
 
 /**
  * Implements the mechanics of SSL by managing an SSLEngine object.
- * One of these is associated with each SSLConnection.
+ * <p>
+ * This class is only used to implement the {@link
+ * AbstractAsyncSSLConnection.SSLConnectionChannel} which is handed of
+ * to RawChannelImpl when creating a WebSocket.
  */
 class SSLDelegate {
 
     final SSLEngine engine;
     final EngineWrapper wrapper;
     final Lock handshaking = new ReentrantLock();
-    final SSLParameters sslParameters;
     final SocketChannel chan;
-    final HttpClientImpl client;
-    final String serverName;
 
-    SSLDelegate(SSLEngine eng, SocketChannel chan, HttpClientImpl client, String sn)
+    SSLDelegate(SSLEngine eng, SocketChannel chan)
     {
         this.engine = eng;
         this.chan = chan;
-        this.client = client;
         this.wrapper = new EngineWrapper(chan, engine);
-        this.sslParameters = engine.getSSLParameters();
-        this.serverName = sn;
     }
 
     // alpn[] may be null
-    SSLDelegate(SocketChannel chan, HttpClientImpl client, String[] alpn, String sn)
-        throws IOException
-    {
-        serverName = sn;
-        SSLContext context = client.sslContext();
-        engine = context.createSSLEngine();
-        engine.setUseClientMode(true);
-        SSLParameters sslp = client.sslParameters()
-                                   .orElseGet(context::getSupportedSSLParameters);
-        sslParameters = Utils.copySSLParameters(sslp);
-        if (sn != null) {
-            SNIHostName sni = new SNIHostName(sn);
-            sslParameters.setServerNames(List.of(sni));
-        }
-        if (alpn != null) {
-            sslParameters.setApplicationProtocols(alpn);
-            Log.logSSL("SSLDelegate: Setting application protocols: {0}" + Arrays.toString(alpn));
-        } else {
-            Log.logSSL("SSLDelegate: No application protocols proposed");
-        }
-        engine.setSSLParameters(sslParameters);
-        wrapper = new EngineWrapper(chan, engine);
-        this.chan = chan;
-        this.client = client;
-    }
+//    SSLDelegate(SocketChannel chan, HttpClientImpl client, String[] alpn, String sn)
+//        throws IOException
+//    {
+//        serverName = sn;
+//        SSLContext context = client.sslContext();
+//        engine = context.createSSLEngine();
+//        engine.setUseClientMode(true);
+//        SSLParameters sslp = client.sslParameters();
+//        sslParameters = Utils.copySSLParameters(sslp);
+//        if (sn != null) {
+//            SNIHostName sni = new SNIHostName(sn);
+//            sslParameters.setServerNames(List.of(sni));
+//        }
+//        if (alpn != null) {
+//            sslParameters.setApplicationProtocols(alpn);
+//            Log.logSSL("SSLDelegate: Setting application protocols: {0}" + Arrays.toString(alpn));
+//        } else {
+//            Log.logSSL("SSLDelegate: No application protocols proposed");
+//        }
+//        engine.setSSLParameters(sslParameters);
+//        wrapper = new EngineWrapper(chan, engine);
+//        this.chan = chan;
+//        this.client = client;
+//    }
 
-    SSLParameters getSSLParameters() {
-        return sslParameters;
-    }
+//    SSLParameters getSSLParameters() {
+//        return sslParameters;
+//    }
 
-    private static long countBytes(ByteBuffer[] buffers, int start, int number) {
+    static long countBytes(ByteBuffer[] buffers, int start, int number) {
         long c = 0;
         for (int i=0; i<number; i++) {
             c+= buffers[start+i].remaining();
@@ -191,7 +185,8 @@ class SSLDelegate {
 
         SocketChannel chan;
         SSLEngine engine;
-        Object wrapLock, unwrapLock;
+        final Object wrapLock;
+        final Object unwrapLock;
         ByteBuffer unwrap_src, wrap_dst;
         boolean closed = false;
         int u_remaining; // the number of bytes left in unwrap_src after an unwrap()
@@ -205,8 +200,8 @@ class SSLDelegate {
             wrap_dst = allocate(BufType.PACKET);
         }
 
-        void close () throws IOException {
-        }
+//        void close () throws IOException {
+//        }
 
         WrapperResult wrapAndSend(ByteBuffer src, boolean ignoreClose)
             throws IOException
@@ -320,11 +315,11 @@ class SSLDelegate {
         }
     }
 
-    WrapperResult sendData (ByteBuffer src) throws IOException {
-        ByteBuffer[] buffers = new ByteBuffer[1];
-        buffers[0] = src;
-        return sendData(buffers, 0, 1);
-    }
+//    WrapperResult sendData (ByteBuffer src) throws IOException {
+//        ByteBuffer[] buffers = new ByteBuffer[1];
+//        buffers[0] = src;
+//        return sendData(buffers, 0, 1);
+//    }
 
     /**
      * send the data in the given ByteBuffer. If a handshake is needed
@@ -407,7 +402,7 @@ class SSLDelegate {
      */
     @SuppressWarnings("fallthrough")
     void doHandshake (HandshakeStatus hs_status) throws IOException {
-        boolean wasBlocking = false;
+        boolean wasBlocking;
         try {
             wasBlocking = chan.isBlocking();
             handshaking.lock();
@@ -453,29 +448,29 @@ class SSLDelegate {
         }
     }
 
-    static void printParams(SSLParameters p) {
-        System.out.println("SSLParameters:");
-        if (p == null) {
-            System.out.println("Null params");
-            return;
-        }
-        for (String cipher : p.getCipherSuites()) {
-                System.out.printf("cipher: %s\n", cipher);
-        }
-        // JDK 8 EXCL START
-        for (String approto : p.getApplicationProtocols()) {
-                System.out.printf("application protocol: %s\n", approto);
-        }
-        // JDK 8 EXCL END
-        for (String protocol : p.getProtocols()) {
-                System.out.printf("protocol: %s\n", protocol);
-        }
-        if (p.getServerNames() != null) {
-            for (SNIServerName sname : p.getServerNames()) {
-                System.out.printf("server name: %s\n", sname.toString());
-            }
-        }
-    }
+//    static void printParams(SSLParameters p) {
+//        System.out.println("SSLParameters:");
+//        if (p == null) {
+//            System.out.println("Null params");
+//            return;
+//        }
+//        for (String cipher : p.getCipherSuites()) {
+//                System.out.printf("cipher: %s\n", cipher);
+//        }
+//        // JDK 8 EXCL START
+//        for (String approto : p.getApplicationProtocols()) {
+//                System.out.printf("application protocol: %s\n", approto);
+//        }
+//        // JDK 8 EXCL END
+//        for (String protocol : p.getProtocols()) {
+//                System.out.printf("protocol: %s\n", protocol);
+//        }
+//        if (p.getServerNames() != null) {
+//            for (SNIServerName sname : p.getServerNames()) {
+//                System.out.printf("server name: %s\n", sname.toString());
+//            }
+//        }
+//    }
 
     String getSessionInfo() {
         StringBuilder sb = new StringBuilder();
diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/SSLTunnelConnection.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/SSLTunnelConnection.java
deleted file mode 100644
index d5cade109b7..00000000000
--- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/SSLTunnelConnection.java
+++ /dev/null
@@ -1,207 +0,0 @@
-/*
- * Copyright (c) 2015, 2017, 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.  Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * 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 jdk.incubator.http;
-
-import java.io.IOException;
-import java.io.UncheckedIOException;
-import java.net.InetSocketAddress;
-import java.nio.ByteBuffer;
-import java.nio.channels.SocketChannel;
-import java.util.concurrent.CompletableFuture;
-import javax.net.ssl.SSLEngineResult.Status;
-import javax.net.ssl.SSLParameters;
-import jdk.incubator.http.SSLDelegate.WrapperResult;
-
-import jdk.incubator.http.internal.common.ByteBufferReference;
-import jdk.incubator.http.internal.common.Utils;
-
-/**
- * An SSL tunnel built on a Plain (CONNECT) TCP tunnel.
- */
-class SSLTunnelConnection extends HttpConnection {
-
-    final PlainTunnelingConnection delegate;
-    protected SSLDelegate sslDelegate;
-    private volatile boolean connected;
-    final String serverName;
-
-    @Override
-    public void connect() throws IOException, InterruptedException {
-        delegate.connect();
-        this.sslDelegate = new SSLDelegate(delegate.channel(), client, null, serverName);
-        connected = true;
-    }
-
-    @Override
-    boolean connected() {
-        return connected && delegate.connected();
-    }
-
-    @Override
-    public CompletableFuture<Void> connectAsync() {
-        return delegate.connectAsync()
-            .thenAccept((Void v) -> {
-                try {
-                    // can this block?
-                    this.sslDelegate = new SSLDelegate(delegate.channel(),
-                                                       client,
-                                                       null, serverName);
-                    connected = true;
-                } catch (IOException e) {
-                    throw new UncheckedIOException(e);
-                }
-            });
-    }
-
-    SSLTunnelConnection(InetSocketAddress addr,
-                        HttpClientImpl client,
-                        InetSocketAddress proxy)
-    {
-        super(addr, client);
-        this.serverName = Utils.getServerName(addr);
-        delegate = new PlainTunnelingConnection(addr, proxy, client);
-    }
-
-    /**
-     * Create an SSLTunnelConnection from an existing connected AsyncSSLTunnelConnection.
-     * Used when downgrading from HTTP/2 to HTTP/1.1
-     */
-    SSLTunnelConnection(AsyncSSLTunnelConnection c) {
-        super(c.address, c.client);
-        this.delegate = c.plainConnection();
-        AsyncSSLDelegate adel = c.sslDelegate();
-        this.sslDelegate = new SSLDelegate(adel.engine, delegate.channel(), client, adel.serverName);
-        this.serverName = adel.serverName;
-        connected = c.connected();
-    }
-
-    @Override
-    SSLParameters sslParameters() {
-        return sslDelegate.getSSLParameters();
-    }
-
-    @Override
-    public String toString() {
-        return "SSLTunnelConnection: " + super.toString();
-    }
-
-    private static long countBytes(ByteBuffer[] buffers, int start, int number) {
-        long c = 0;
-        for (int i=0; i<number; i++) {
-            c+= buffers[start+i].remaining();
-        }
-        return c;
-    }
-
-    @Override
-    ConnectionPool.CacheKey cacheKey() {
-        return ConnectionPool.cacheKey(address, delegate.proxyAddr);
-    }
-
-    @Override
-    long write(ByteBuffer[] buffers, int start, int number) throws IOException {
-        //debugPrint("Send", buffers, start, number);
-        long l = countBytes(buffers, start, number);
-        WrapperResult r = sslDelegate.sendData(buffers, start, number);
-        if (r.result.getStatus() == Status.CLOSED) {
-            if (l > 0) {
-                throw new IOException("SSLHttpConnection closed");
-            }
-        }
-        return l;
-    }
-
-    @Override
-    long write(ByteBuffer buffer) throws IOException {
-        //debugPrint("Send", buffer);
-        long l = buffer.remaining();
-        WrapperResult r = sslDelegate.sendData(buffer);
-        if (r.result.getStatus() == Status.CLOSED) {
-            if (l > 0) {
-                throw new IOException("SSLHttpConnection closed");
-            }
-        }
-        return l;
-    }
-
-    @Override
-    void writeAsync(ByteBufferReference[] buffers) throws IOException {
-        write(ByteBufferReference.toBuffers(buffers), 0, buffers.length);
-    }
-
-    @Override
-    void writeAsyncUnordered(ByteBufferReference[] buffers) throws IOException {
-        write(ByteBufferReference.toBuffers(buffers), 0, buffers.length);
-    }
-
-    @Override
-    void flushAsync() throws IOException {
-        // nothing to do
-    }
-
-    @Override
-    public void close() {
-        Utils.close(delegate.channel());
-    }
-
-    @Override
-    void shutdownInput() throws IOException {
-        delegate.channel().shutdownInput();
-    }
-
-    @Override
-    void shutdownOutput() throws IOException {
-        delegate.channel().shutdownOutput();
-    }
-
-    @Override
-    protected ByteBuffer readImpl() throws IOException {
-        ByteBuffer buf = Utils.getBuffer();
-
-        WrapperResult r = sslDelegate.recvData(buf);
-        return r.buf;
-    }
-
-    @Override
-    SocketChannel channel() {
-        return delegate.channel();
-    }
-
-    @Override
-    CompletableFuture<Void> whenReceivingResponse() {
-        return delegate.whenReceivingResponse();
-    }
-
-    @Override
-    boolean isSecure() {
-        return true;
-    }
-
-    @Override
-    boolean isProxied() {
-        return true;
-    }
-}
diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/SocketTube.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/SocketTube.java
new file mode 100644
index 00000000000..546ed617121
--- /dev/null
+++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/SocketTube.java
@@ -0,0 +1,956 @@
+/*
+ * Copyright (c) 2017, 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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 jdk.incubator.http;
+
+import java.io.EOFException;
+import java.io.IOException;
+import java.lang.System.Logger.Level;
+import java.nio.ByteBuffer;
+import java.util.List;
+import java.util.Objects;
+import java.util.concurrent.Flow;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.concurrent.atomic.AtomicReference;
+import java.nio.channels.SelectableChannel;
+import java.nio.channels.SelectionKey;
+import java.nio.channels.SocketChannel;
+import java.util.ArrayList;
+import java.util.function.Consumer;
+import java.util.function.Supplier;
+
+import jdk.incubator.http.internal.common.Demand;
+import jdk.incubator.http.internal.common.FlowTube;
+import jdk.incubator.http.internal.common.SequentialScheduler;
+import jdk.incubator.http.internal.common.SequentialScheduler.DeferredCompleter;
+import jdk.incubator.http.internal.common.SequentialScheduler.RestartableTask;
+import jdk.incubator.http.internal.common.Utils;
+
+/**
+ * A SocketTube is a terminal tube plugged directly into the socket.
+ * The read subscriber should call {@code subscribe} on the SocketTube before
+ * the SocketTube can be subscribed to the write publisher.
+ */
+final class SocketTube implements FlowTube {
+
+    static final boolean DEBUG = Utils.DEBUG; // revisit: temporary developer's flag
+    final System.Logger  debug = Utils.getDebugLogger(this::dbgString, DEBUG);
+    static final AtomicLong IDS = new AtomicLong();
+
+    private final HttpClientImpl client;
+    private final SocketChannel channel;
+    private final Supplier<ByteBuffer> buffersSource;
+    private final Object lock = new Object();
+    private final AtomicReference<Throwable> errorRef = new AtomicReference<>();
+    private final InternalReadPublisher readPublisher;
+    private final InternalWriteSubscriber writeSubscriber;
+    private final long id = IDS.incrementAndGet();
+
+    public SocketTube(HttpClientImpl client, SocketChannel channel,
+                      Supplier<ByteBuffer> buffersSource) {
+        this.client = client;
+        this.channel = channel;
+        this.buffersSource = buffersSource;
+        this.readPublisher = new InternalReadPublisher();
+        this.writeSubscriber = new InternalWriteSubscriber();
+    }
+
+//    private static Flow.Subscription nopSubscription() {
+//        return new Flow.Subscription() {
+//            @Override public void request(long n) { }
+//            @Override public void cancel() { }
+//        };
+//    }
+
+    /**
+     * Returns {@code true} if this flow is finished.
+     * This happens when this flow internal read subscription is completed,
+     * either normally (EOF reading) or exceptionally  (EOF writing, or
+     * underlying socket closed, or some exception occurred while reading or
+     * writing to the socket).
+     *
+     * @return {@code true} if this flow is finished.
+     */
+    public boolean isFinished() {
+        InternalReadPublisher.InternalReadSubscription subscription =
+                readPublisher.subscriptionImpl;
+        return subscription != null && subscription.completed
+                || subscription == null && errorRef.get() != null;
+    }
+
+    // ===================================================================== //
+    //                       Flow.Publisher                                  //
+    // ======================================================================//
+
+    /**
+     * {@inheritDoc }
+     * @apiNote This method should be called first. In particular, the caller
+     *          must ensure that this method must be called by the read
+     *          subscriber before the write publisher can call {@code onSubscribe}.
+     *          Failure to adhere to this contract may result in assertion errors.
+     */
+    @Override
+    public void subscribe(Flow.Subscriber<? super List<ByteBuffer>> s) {
+        Objects.requireNonNull(s);
+        assert s instanceof TubeSubscriber : "Expected TubeSubscriber, got:" + s;
+        readPublisher.subscribe(s);
+    }
+
+
+    // ===================================================================== //
+    //                       Flow.Subscriber                                 //
+    // ======================================================================//
+
+    /**
+     * {@inheritDoc }
+     * @apiNote The caller must ensure that {@code subscribe} is called by
+     *          the read subscriber before {@code onSubscribe} is called by
+     *          the write publisher.
+     *          Failure to adhere to this contract may result in assertion errors.
+     */
+    @Override
+    public void onSubscribe(Flow.Subscription subscription) {
+        writeSubscriber.onSubscribe(subscription);
+    }
+
+    @Override
+    public void onNext(List<ByteBuffer> item) {
+        writeSubscriber.onNext(item);
+    }
+
+    @Override
+    public void onError(Throwable throwable) {
+        writeSubscriber.onError(throwable);
+    }
+
+    @Override
+    public void onComplete() {
+        writeSubscriber.onComplete();
+    }
+
+    // ===================================================================== //
+    //                           Events                                      //
+    // ======================================================================//
+
+    /**
+     * A restartable task used to process tasks in sequence.
+     */
+    private static class SocketFlowTask implements RestartableTask {
+        final Runnable task;
+        private final Object monitor = new Object();
+        SocketFlowTask(Runnable task) {
+            this.task = task;
+        }
+        @Override
+        public final void run(DeferredCompleter taskCompleter) {
+            try {
+                // non contentious synchronized for visibility.
+                synchronized(monitor) {
+                    task.run();
+                }
+            } finally {
+                taskCompleter.complete();
+            }
+        }
+    }
+
+    // This is best effort - there's no guarantee that the printed set
+    // of values is consistent. It should only be considered as
+    // weakly accurate - in particular in what concerns the events states,
+    // especially when displaying a read event state from a write event
+    // callback and conversely.
+    void debugState(String when) {
+        if (debug.isLoggable(Level.DEBUG)) {
+            StringBuilder state = new StringBuilder();
+
+            InternalReadPublisher.InternalReadSubscription sub =
+                    readPublisher.subscriptionImpl;
+            InternalReadPublisher.ReadEvent readEvent =
+                    sub == null ? null : sub.readEvent;
+            Demand rdemand = sub == null ? null : sub.demand;
+            InternalWriteSubscriber.WriteEvent writeEvent =
+                    writeSubscriber.writeEvent;
+            AtomicLong wdemand = writeSubscriber.writeDemand;
+            int rops = readEvent == null ? 0 : readEvent.interestOps();
+            long rd = rdemand == null ? 0 : rdemand.get();
+            int wops = writeEvent == null ? 0 : writeEvent.interestOps();
+            long wd = wdemand == null ? 0 : wdemand.get();
+
+            state.append(when).append(" Reading: [ops=")
+                    .append(rops).append(", demand=").append(rd)
+                    .append(", stopped=")
+                    .append((sub == null ? false : sub.readScheduler.isStopped()))
+                    .append("], Writing: [ops=").append(wops)
+                    .append(", demand=").append(wd)
+                    .append("]");
+            debug.log(Level.DEBUG, state.toString());
+        }
+    }
+
+    /**
+     * A repeatable event that can be paused or resumed by changing
+     * its interestOps.
+     * When the event is fired, it is first paused before being signaled.
+     * It is the responsibility of the code triggered by {@code signalEvent}
+     * to resume the event if required.
+     */
+    private static abstract class SocketFlowEvent extends AsyncEvent {
+        final SocketChannel channel;
+        final int defaultInterest;
+        volatile int interestOps;
+        volatile boolean registered;
+        SocketFlowEvent(int defaultInterest, SocketChannel channel) {
+            super(AsyncEvent.REPEATING);
+            this.defaultInterest = defaultInterest;
+            this.channel = channel;
+        }
+        final boolean registered() {return registered;}
+        final void resume() {
+            interestOps = defaultInterest;
+            registered = true;
+        }
+        final void pause() {interestOps = 0;}
+        @Override
+        public final SelectableChannel channel() {return channel;}
+        @Override
+        public final int interestOps() {return interestOps;}
+
+        @Override
+        public final void handle() {
+            pause();       // pause, then signal
+            signalEvent(); // won't be fired again until resumed.
+        }
+        @Override
+        public final void abort(IOException error) {
+            debug().log(Level.DEBUG, () -> "abort: " + error);
+            pause();              // pause, then signal
+            signalError(error);   // should not be resumed after abort (not checked)
+        }
+
+        protected abstract void signalEvent();
+        protected abstract void signalError(Throwable error);
+        abstract System.Logger debug();
+    }
+
+    // ===================================================================== //
+    //                              Writing                                  //
+    // ======================================================================//
+
+    // This class makes the assumption that the publisher will call
+    // onNext sequentially, and that onNext won't be called if the demand
+    // has not been incremented by request(1).
+    // It has a 'queue of 1' meaning that it will call request(1) in
+    // onSubscribe, and then only after its 'current' buffer list has been
+    // fully written and current set to null;
+    private final class InternalWriteSubscriber
+            implements Flow.Subscriber<List<ByteBuffer>> {
+
+        volatile Flow.Subscription subscription;
+        volatile List<ByteBuffer> current;
+        volatile boolean completed;
+        final WriteEvent writeEvent = new WriteEvent(channel, this);
+        final AtomicLong writeDemand = new AtomicLong();
+
+        @Override
+        public void onSubscribe(Flow.Subscription subscription) {
+            Flow.Subscription previous = this.subscription;
+            this.subscription = subscription;
+            debug.log(Level.DEBUG, "subscribed for writing");
+            if (current == null) {
+                if (previous == subscription || previous == null) {
+                    if (writeDemand.compareAndSet(0, 1)) {
+                        subscription.request(1);
+                    }
+                } else {
+                    writeDemand.set(1);
+                    subscription.request(1);
+                }
+            }
+        }
+
+        @Override
+        public void onNext(List<ByteBuffer> bufs) {
+            assert current == null; // this is a queue of 1.
+            assert subscription != null;
+            current = bufs;
+            tryFlushCurrent(client.isSelectorThread()); // may be in selector thread
+            // For instance in HTTP/2, a received SETTINGS frame might trigger
+            // the sending of a SETTINGS frame in turn which might cause
+            // onNext to be called from within the same selector thread that the
+            // original SETTINGS frames arrived on. If rs is the read-subscriber
+            // and ws is the write-subscriber then the following can occur:
+            // ReadEvent -> rs.onNext(bytes) -> process server SETTINGS -> write
+            // client SETTINGS -> ws.onNext(bytes) -> tryFlushCurrent
+            debugState("leaving w.onNext");
+        }
+
+        // we don't use a SequentialScheduler here: we rely on
+        // onNext() being called sequentially, and not being called
+        // if we haven't call request(1)
+        // onNext is usually called from within a user/executor thread.
+        // we will perform the initial writing in that thread.
+        // if for some reason, not all data can be written, the writeEvent
+        // will be resumed, and the rest of the data will be written from
+        // the selector manager thread when the writeEvent is fired.
+        // If we are in the selector manager thread, then we will use the executor
+        // to call request(1), ensuring that onNext() won't be called from
+        // within the selector thread.
+        // If we are not in the selector manager thread, then we don't care.
+        void tryFlushCurrent(boolean inSelectorThread) {
+            List<ByteBuffer> bufs = current;
+            if (bufs == null) return;
+            try {
+                assert inSelectorThread == client.isSelectorThread() :
+                       "should " + (inSelectorThread ? "" : "not ")
+                        + " be in the selector thread";
+                long remaining = Utils.remaining(bufs);
+                debug.log(Level.DEBUG, "trying to write: %d", remaining);
+                long written = writeAvailable(bufs);
+                debug.log(Level.DEBUG, "wrote: %d", remaining);
+                if (written == -1) {
+                    signalError(new EOFException("EOF reached while writing"));
+                    return;
+                }
+                assert written <= remaining;
+                if (remaining - written == 0) {
+                    current = null;
+                    writeDemand.decrementAndGet();
+                    Runnable requestMore = this::requestMore;
+                    if (inSelectorThread) {
+                        assert client.isSelectorThread();
+                        client.theExecutor().execute(requestMore);
+                    } else {
+                        assert !client.isSelectorThread();
+                        requestMore.run();
+                    }
+                } else {
+                    resumeWriteEvent(inSelectorThread);
+                }
+            } catch (Throwable t) {
+                signalError(t);
+                subscription.cancel();
+            }
+        }
+
+        void requestMore() {
+            try {
+                if (completed) return;
+                long d =  writeDemand.get();
+                if (writeDemand.compareAndSet(0,1)) {
+                    debug.log(Level.DEBUG, "write: requesting more...");
+                    subscription.request(1);
+                } else {
+                    debug.log(Level.DEBUG, "write: no need to request more: %d", d);
+                }
+            } catch (Throwable t) {
+                debug.log(Level.DEBUG, () ->
+                        "write: error while requesting more: " + t);
+                signalError(t);
+                subscription.cancel();
+            } finally {
+                debugState("leaving requestMore: ");
+            }
+        }
+
+        @Override
+        public void onError(Throwable throwable) {
+            signalError(throwable);
+        }
+
+        @Override
+        public void onComplete() {
+            completed = true;
+            // no need to pause the write event here: the write event will
+            // be paused if there is nothing more to write.
+            List<ByteBuffer> bufs = current;
+            long remaining = bufs == null ? 0 : Utils.remaining(bufs);
+            debug.log(Level.DEBUG,  "write completed, %d yet to send", remaining);
+            debugState("InternalWriteSubscriber::onComplete");
+        }
+
+        void resumeWriteEvent(boolean inSelectorThread) {
+            debug.log(Level.DEBUG, "scheduling write event");
+            resumeEvent(writeEvent, this::signalError);
+        }
+
+//        void pauseWriteEvent() {
+//            debug.log(Level.DEBUG, "pausing write event");
+//            pauseEvent(writeEvent, this::signalError);
+//        }
+
+        void signalWritable() {
+            debug.log(Level.DEBUG, "channel is writable");
+            tryFlushCurrent(true);
+        }
+
+        void signalError(Throwable error) {
+            debug.log(Level.DEBUG, () -> "write error: " + error);
+            completed = true;
+            readPublisher.signalError(error);
+        }
+
+        // A repeatable WriteEvent which is paused after firing and can
+        // be resumed if required - see SocketFlowEvent;
+        final class WriteEvent extends SocketFlowEvent {
+            final InternalWriteSubscriber sub;
+            WriteEvent(SocketChannel channel, InternalWriteSubscriber sub) {
+                super(SelectionKey.OP_WRITE, channel);
+                this.sub = sub;
+            }
+            @Override
+            protected final void signalEvent() {
+                try {
+                    client.eventUpdated(this);
+                    sub.signalWritable();
+                } catch(Throwable t) {
+                    sub.signalError(t);
+                }
+            }
+
+            @Override
+            protected void signalError(Throwable error) {
+                sub.signalError(error);
+            }
+
+            @Override
+            System.Logger debug() {
+                return debug;
+            }
+
+        }
+
+    }
+
+    // ===================================================================== //
+    //                              Reading                                  //
+    // ===================================================================== //
+
+    // The InternalReadPublisher uses a SequentialScheduler to ensure that
+    // onNext/onError/onComplete are called sequentially on the caller's
+    // subscriber.
+    // However, it relies on the fact that the only time where
+    // runOrSchedule() is called from a user/executor thread is in signalError,
+    // right after the errorRef has been set.
+    // Because the sequential scheduler's task always checks for errors first,
+    // and always terminate the scheduler on error, then it is safe to assume
+    // that if it reaches the point where it reads from the channel, then
+    // it is running in the SelectorManager thread. This is because all
+    // other invocation of runOrSchedule() are triggered from within a
+    // ReadEvent.
+    //
+    // When pausing/resuming the event, some shortcuts can then be taken
+    // when we know we're running in the selector manager thread
+    // (in that case there's no need to call client.eventUpdated(readEvent);
+    //
+    private final class InternalReadPublisher
+            implements Flow.Publisher<List<ByteBuffer>> {
+        private final InternalReadSubscription subscriptionImpl
+                = new InternalReadSubscription();
+        AtomicReference<ReadSubscription> pendingSubscription = new AtomicReference<>();
+        private volatile ReadSubscription subscription;
+
+        @Override
+        public void subscribe(Flow.Subscriber<? super List<ByteBuffer>> s) {
+            Objects.requireNonNull(s);
+
+            TubeSubscriber sub = FlowTube.asTubeSubscriber(s);
+            ReadSubscription target = new ReadSubscription(subscriptionImpl, sub);
+            ReadSubscription previous = pendingSubscription.getAndSet(target);
+
+            if (previous != null && previous != target) {
+                debug.log(Level.DEBUG,
+                        () -> "read publisher: dropping pending subscriber: "
+                        + previous.subscriber);
+                previous.errorRef.compareAndSet(null, errorRef.get());
+                previous.signalOnSubscribe();
+                if (subscriptionImpl.completed) {
+                    previous.signalCompletion();
+                } else {
+                    previous.subscriber.dropSubscription();
+                }
+            }
+
+            debug.log(Level.DEBUG, "read publisher got subscriber");
+            subscriptionImpl.signalSubscribe();
+            debugState("leaving read.subscribe: ");
+        }
+
+        void signalError(Throwable error) {
+            if (!errorRef.compareAndSet(null, error)) {
+                return;
+            }
+            subscriptionImpl.handleError();
+        }
+
+        final class ReadSubscription implements Flow.Subscription {
+            final InternalReadSubscription impl;
+            final TubeSubscriber  subscriber;
+            final AtomicReference<Throwable> errorRef = new AtomicReference<>();
+            volatile boolean subscribed;
+            volatile boolean cancelled;
+            volatile boolean completed;
+
+            public ReadSubscription(InternalReadSubscription impl,
+                                    TubeSubscriber subscriber) {
+                this.impl = impl;
+                this.subscriber = subscriber;
+            }
+
+            @Override
+            public void cancel() {
+                cancelled = true;
+            }
+
+            @Override
+            public void request(long n) {
+                if (!cancelled) {
+                    impl.request(n);
+                } else {
+                    debug.log(Level.DEBUG,
+                              "subscription cancelled, ignoring request %d", n);
+                }
+            }
+
+            void signalCompletion() {
+                assert subscribed || cancelled;
+                if (completed || cancelled) return;
+                synchronized (this) {
+                    if (completed) return;
+                    completed = true;
+                }
+                Throwable error = errorRef.get();
+                if (error != null) {
+                    debug.log(Level.DEBUG, () ->
+                        "forwarding error to subscriber: "
+                        + error);
+                    subscriber.onError(error);
+                } else {
+                    debug.log(Level.DEBUG, "completing subscriber");
+                    subscriber.onComplete();
+                }
+            }
+
+            void signalOnSubscribe() {
+                if (subscribed || cancelled) return;
+                synchronized (this) {
+                    if (subscribed || cancelled) return;
+                    subscribed = true;
+                }
+                subscriber.onSubscribe(this);
+                debug.log(Level.DEBUG, "onSubscribe called");
+                if (errorRef.get() != null) {
+                    signalCompletion();
+                }
+            }
+        }
+
+        final class InternalReadSubscription implements Flow.Subscription {
+
+            private final Demand demand = new Demand();
+            final SequentialScheduler readScheduler;
+            private volatile boolean completed;
+            private final ReadEvent readEvent;
+            private final AsyncEvent subscribeEvent;
+
+            InternalReadSubscription() {
+                readScheduler = new SequentialScheduler(new SocketFlowTask(this::read));
+                subscribeEvent = new AsyncTriggerEvent(this::signalError,
+                                                       this::handleSubscribeEvent);
+                readEvent = new ReadEvent(channel, this);
+            }
+
+            /*
+             * This method must be invoked before any other method of this class.
+             */
+            final void signalSubscribe() {
+                if (readScheduler.isStopped() || completed) {
+                    // if already completed or stopped we can handle any
+                    // pending connection directly from here.
+                    debug.log(Level.DEBUG,
+                              "handling pending subscription while completed");
+                    handlePending();
+                } else {
+                    try {
+                        debug.log(Level.DEBUG,
+                                  "registering subscribe event");
+                        client.registerEvent(subscribeEvent);
+                    } catch (Throwable t) {
+                        signalError(t);
+                        handlePending();
+                    }
+                }
+            }
+
+            final void handleSubscribeEvent() {
+                assert client.isSelectorThread();
+                debug.log(Level.DEBUG, "subscribe event raised");
+                readScheduler.runOrSchedule();
+                if (readScheduler.isStopped() || completed) {
+                    // if already completed or stopped we can handle any
+                    // pending connection directly from here.
+                    debug.log(Level.DEBUG,
+                              "handling pending subscription when completed");
+                    handlePending();
+                }
+            }
+
+
+            /*
+             * Although this method is thread-safe, the Reactive-Streams spec seems
+             * to not require it to be as such. It's a responsibility of the
+             * subscriber to signal demand in a thread-safe manner.
+             *
+             * https://github.com/reactive-streams/reactive-streams-jvm/blob/dd24d2ab164d7de6c316f6d15546f957bec29eaa/README.md
+             * (rules 2.7 and 3.4)
+             */
+            @Override
+            public final void request(long n) {
+                if (n > 0L) {
+                    boolean wasFulfilled = demand.increase(n);
+                    if (wasFulfilled) {
+                        debug.log(Level.DEBUG, "got some demand for reading");
+                        resumeReadEvent();
+                        // if demand has been changed from fulfilled
+                        // to unfulfilled register read event;
+                    }
+                } else {
+                    signalError(new IllegalArgumentException("non-positive request"));
+                }
+                debugState("leaving request("+n+"): ");
+            }
+
+            @Override
+            public final void cancel() {
+                pauseReadEvent();
+                readScheduler.stop();
+            }
+
+            private void resumeReadEvent() {
+                debug.log(Level.DEBUG, "resuming read event");
+                resumeEvent(readEvent, this::signalError);
+            }
+
+            private void pauseReadEvent() {
+                debug.log(Level.DEBUG, "pausing read event");
+                pauseEvent(readEvent, this::signalError);
+            }
+
+
+            final void handleError() {
+                assert errorRef.get() != null;
+                readScheduler.runOrSchedule();
+            }
+
+            final void signalError(Throwable error) {
+                if (!errorRef.compareAndSet(null, error)) {
+                    return;
+                }
+                debug.log(Level.DEBUG, () -> "got read error: " + error);
+                readScheduler.runOrSchedule();
+            }
+
+            final void signalReadable() {
+                readScheduler.runOrSchedule();
+            }
+
+            /** The body of the task that runs in SequentialScheduler. */
+            final void read() {
+                // It is important to only call pauseReadEvent() when stopping
+                // the scheduler. The event is automatically paused before
+                // firing, and trying to pause it again could cause a race
+                // condition between this loop, which calls tryDecrementDemand(),
+                // and the thread that calls request(n), which will try to resume
+                // reading.
+                try {
+                    while(!readScheduler.isStopped()) {
+                        if (completed) return;
+
+                        // make sure we have a subscriber
+                        if (handlePending()) {
+                            debug.log(Level.DEBUG, "pending subscriber subscribed");
+                            return;
+                        }
+
+                        // If an error was signaled, we might not be in the
+                        // the selector thread, and that is OK, because we
+                        // will just call onError and return.
+                        ReadSubscription current = subscription;
+                        TubeSubscriber subscriber = current.subscriber;
+                        Throwable error = errorRef.get();
+                        if (error != null) {
+                            completed = true;
+                            // safe to pause here because we're finished anyway.
+                            pauseReadEvent();
+                            debug.log(Level.DEBUG, () -> "Sending error " + error
+                                  + " to subscriber " + subscriber);
+                            current.errorRef.compareAndSet(null, error);
+                            current.signalCompletion();
+                            readScheduler.stop();
+                            debugState("leaving read() loop with error: ");
+                            return;
+                        }
+
+                        // If we reach here then we must be in the selector thread.
+                        assert client.isSelectorThread();
+                        if (demand.tryDecrement()) {
+                            // we have demand.
+                            try {
+                                List<ByteBuffer> bytes = readAvailable();
+                                if (bytes == EOF) {
+                                    if (!completed) {
+                                        debug.log(Level.DEBUG, "got read EOF");
+                                        completed = true;
+                                        // safe to pause here because we're finished
+                                        // anyway.
+                                        pauseReadEvent();
+                                        current.signalCompletion();
+                                        readScheduler.stop();
+                                    }
+                                    debugState("leaving read() loop after EOF: ");
+                                    return;
+                                } else if (Utils.remaining(bytes) > 0) {
+                                    // the subscriber is responsible for offloading
+                                    // to another thread if needed.
+                                    debug.log(Level.DEBUG, () -> "read bytes: "
+                                            + Utils.remaining(bytes));
+                                    assert !current.completed;
+                                    subscriber.onNext(bytes);
+                                    // we could continue looping until the demand
+                                    // reaches 0. However, that would risk starving
+                                    // other connections (bound to other socket
+                                    // channels) - as other selected keys activated
+                                    // by the selector manager thread might be
+                                    // waiting for this event to terminate.
+                                    // So resume the read event and return now...
+                                    resumeReadEvent();
+                                    debugState("leaving read() loop after onNext: ");
+                                    return;
+                                } else {
+                                    // nothing available!
+                                    debug.log(Level.DEBUG, "no more bytes available");
+                                    // re-increment the demand and resume the read
+                                    // event. This ensures that this loop is
+                                    // executed again when the socket becomes
+                                    // readable again.
+                                    demand.increase(1);
+                                    resumeReadEvent();
+                                    debugState("leaving read() loop with no bytes");
+                                    return;
+                                }
+                            } catch (Throwable x) {
+                                signalError(x);
+                                continue;
+                            }
+                        } else {
+                            debug.log(Level.DEBUG, "no more demand for reading");
+                            // the event is paused just after firing, so it should
+                            // still be paused here, unless the demand was just
+                            // incremented from 0 to n, in which case, the
+                            // event will be resumed, causing this loop to be
+                            // invoked again when the socket becomes readable:
+                            // This is what we want.
+                            // Trying to pause the event here would actually
+                            // introduce a race condition between this loop and
+                            // request(n).
+                            debugState("leaving read() loop with no demand");
+                            break;
+                        }
+                    }
+                } catch (Throwable t) {
+                    debug.log(Level.DEBUG, "Unexpected exception in read loop", t);
+                    signalError(t);
+                } finally {
+                    handlePending();
+                }
+            }
+
+            boolean handlePending() {
+                ReadSubscription pending = pendingSubscription.getAndSet(null);
+                if (pending == null) return false;
+                debug.log(Level.DEBUG, "handling pending subscription for %s",
+                          pending.subscriber);
+                ReadSubscription current = subscription;
+                if (current != null && current != pending && !completed) {
+                    current.subscriber.dropSubscription();
+                }
+                debug.log(Level.DEBUG, "read demand reset to 0");
+                subscriptionImpl.demand.reset(); // subscriber will increase demand if it needs to.
+                pending.errorRef.compareAndSet(null, errorRef.get());
+                if (!readScheduler.isStopped()) {
+                    subscription = pending;
+                } else {
+                    debug.log(Level.DEBUG, "socket tube is already stopped");
+                }
+                debug.log(Level.DEBUG, "calling onSubscribe");
+                pending.signalOnSubscribe();
+                if (completed) {
+                    pending.errorRef.compareAndSet(null, errorRef.get());
+                    pending.signalCompletion();
+                }
+                return true;
+            }
+        }
+
+
+        // A repeatable ReadEvent which is paused after firing and can
+        // be resumed if required - see SocketFlowEvent;
+        final class ReadEvent extends SocketFlowEvent {
+            final InternalReadSubscription sub;
+            ReadEvent(SocketChannel channel, InternalReadSubscription sub) {
+                super(SelectionKey.OP_READ, channel);
+                this.sub = sub;
+            }
+            @Override
+            protected final void signalEvent() {
+                try {
+                    client.eventUpdated(this);
+                    sub.signalReadable();
+                } catch(Throwable t) {
+                    sub.signalError(t);
+                }
+            }
+
+            @Override
+            protected final void signalError(Throwable error) {
+                sub.signalError(error);
+            }
+
+            @Override
+            System.Logger debug() {
+                return debug;
+            }
+        }
+
+    }
+
+    // ===================================================================== //
+    //                   Socket Channel Read/Write                           //
+    // ===================================================================== //
+    static final int MAX_BUFFERS = 3;
+    static final List<ByteBuffer> EOF = List.of();
+
+    private List<ByteBuffer> readAvailable() throws IOException {
+        ByteBuffer buf = buffersSource.get();
+        assert buf.hasRemaining();
+
+        int read;
+        int pos = buf.position();
+        List<ByteBuffer> list = null;
+        while (buf.hasRemaining()) {
+            while ((read = channel.read(buf)) > 0) {
+               if (!buf.hasRemaining()) break;
+            }
+
+            // nothing read;
+            if (buf.position() == pos) {
+                // An empty list signal the end of data, and should only be
+                // returned if read == -1.
+                // If we already read some data, then we must return what we have
+                // read, and -1 will be returned next time the caller attempts to
+                // read something.
+                if (list == null && read == -1) {  // eof
+                    list = EOF;
+                    break;
+                }
+            }
+            buf.limit(buf.position());
+            buf.position(pos);
+            if (list == null) {
+                list = List.of(buf);
+            } else {
+                if (!(list instanceof ArrayList)) {
+                    list = new ArrayList<>(list);
+                }
+                list.add(buf);
+            }
+            if (read <= 0 || list.size() == MAX_BUFFERS) break;
+            buf = buffersSource.get();
+            pos = buf.position();
+            assert buf.hasRemaining();
+        }
+        return list;
+    }
+
+    private long writeAvailable(List<ByteBuffer> bytes) throws IOException {
+        ByteBuffer[] srcs = bytes.toArray(Utils.EMPTY_BB_ARRAY);
+        final long remaining = Utils.remaining(srcs);
+        long written = 0;
+        while (remaining > written) {
+            long w = channel.write(srcs);
+            if (w == -1 && written == 0) return -1;
+            if (w == 0) break;
+            written += w;
+        }
+        return written;
+    }
+
+    private void resumeEvent(SocketFlowEvent event,
+                             Consumer<Throwable> errorSignaler) {
+        boolean registrationRequired;
+        synchronized(lock) {
+            registrationRequired = !event.registered();
+            event.resume();
+        }
+        try {
+            if (registrationRequired) {
+                client.registerEvent(event);
+             } else {
+                client.eventUpdated(event);
+            }
+        } catch(Throwable t) {
+            errorSignaler.accept(t);
+        }
+   }
+
+    private void pauseEvent(SocketFlowEvent event,
+                            Consumer<Throwable> errorSignaler) {
+        synchronized(lock) {
+            event.pause();
+        }
+        try {
+            client.eventUpdated(event);
+        } catch(Throwable t) {
+            errorSignaler.accept(t);
+        }
+    }
+
+    @Override
+    public void connectFlows(TubePublisher writePublisher,
+                             TubeSubscriber readSubscriber) {
+        debug.log(Level.DEBUG, "connecting flows");
+        this.subscribe(readSubscriber);
+        writePublisher.subscribe(this);
+    }
+
+
+    @Override
+    public String toString() {
+        return dbgString();
+    }
+
+    final String dbgString() {
+        return "SocketTube("+id+")";
+    }
+}
diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/Stream.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/Stream.java
index 814e617a7a5..cdf5366e042 100644
--- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/Stream.java
+++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/Stream.java
@@ -26,21 +26,21 @@
 package jdk.incubator.http;
 
 import java.io.IOException;
+import java.lang.System.Logger.Level;
 import java.net.URI;
 import java.nio.ByteBuffer;
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.List;
 import java.util.Optional;
 import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.CompletionException;
-import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ConcurrentLinkedDeque;
+import java.util.concurrent.ConcurrentLinkedQueue;
 import java.util.concurrent.Executor;
 import java.util.concurrent.Flow;
 import java.util.concurrent.Flow.Subscription;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
-import java.util.function.Consumer;
-
+import java.util.concurrent.atomic.AtomicReference;
+import jdk.incubator.http.HttpResponse.BodySubscriber;
 import jdk.incubator.http.internal.common.*;
 import jdk.incubator.http.internal.frame.*;
 import jdk.incubator.http.internal.hpack.DecodingCallback;
@@ -54,10 +54,6 @@ import jdk.incubator.http.internal.hpack.DecodingCallback;
  *
  * sendRequest() -- sendHeadersOnly() + sendBody()
  *
- * sendBody() -- in calling thread: obeys all flow control (so may block)
- *               obtains data from request body processor and places on connection
- *               outbound Q.
- *
  * sendBodyAsync() -- calls sendBody() in an executor thread.
  *
  * sendHeadersAsync() -- calls sendHeadersOnly() which does not block
@@ -77,10 +73,6 @@ import jdk.incubator.http.internal.hpack.DecodingCallback;
  *
  * getResponse() -- calls getResponseAsync() and waits for CF to complete
  *
- * responseBody() -- in calling thread: blocks for incoming DATA frames on
- *               stream inputQ. Obeys remote and local flow control so may block.
- *               Calls user response body processor with data buffers.
- *
  * responseBodyAsync() -- calls responseBody() in an executor thread.
  *
  * incoming() -- entry point called from connection reader thread. Frames are
@@ -98,7 +90,13 @@ import jdk.incubator.http.internal.hpack.DecodingCallback;
  */
 class Stream<T> extends ExchangeImpl<T> {
 
-    final AsyncDataReadQueue inputQ = new AsyncDataReadQueue();
+    final static boolean DEBUG = Utils.DEBUG; // Revisit: temporary developer's flag
+    final System.Logger  debug = Utils.getDebugLogger(this::dbgString, DEBUG);
+
+    final ConcurrentLinkedQueue<Http2Frame> inputQ = new ConcurrentLinkedQueue<>();
+    final SequentialScheduler sched =
+            SequentialScheduler.synchronizedScheduler(this::schedule);
+    final SubscriptionBase userSubscription = new SubscriptionBase(sched, this::cancel);
 
     /**
      * This stream's identifier. Assigned lazily by the HTTP2Connection before
@@ -106,24 +104,21 @@ class Stream<T> extends ExchangeImpl<T> {
      */
     protected volatile int streamid;
 
-    long responseContentLen = -1;
-    long responseBytesProcessed = 0;
     long requestContentLen;
 
     final Http2Connection connection;
-    HttpClientImpl client;
     final HttpRequestImpl request;
     final DecodingCallback rspHeadersConsumer;
     HttpHeadersImpl responseHeaders;
-    final HttpHeadersImpl requestHeaders;
     final HttpHeadersImpl requestPseudoHeaders;
-    HttpResponse.BodyProcessor<T> responseProcessor;
-    final HttpRequest.BodyProcessor requestProcessor;
+    volatile HttpResponse.BodySubscriber<T> responseSubscriber;
+    final HttpRequest.BodyPublisher requestPublisher;
+    volatile RequestSubscriber requestSubscriber;
     volatile int responseCode;
     volatile Response response;
-    volatile CompletableFuture<Response> responseCF;
-    final AbstractPushPublisher<ByteBuffer> publisher;
+    volatile Throwable failed; // The exception with which this stream was canceled.
     final CompletableFuture<Void> requestBodyCF = new MinimalFuture<>();
+    volatile CompletableFuture<T> responseBodyCF;
 
     /** True if END_STREAM has been seen in a frame received on this stream. */
     private volatile boolean remotelyClosed;
@@ -131,7 +126,7 @@ class Stream<T> extends ExchangeImpl<T> {
     private volatile boolean endStreamSent;
 
     // state flags
-    boolean requestSent, responseReceived, responseHeadersReceived;
+    private boolean requestSent, responseReceived;
 
     /**
      * A reference to this Stream's connection Send Window controller. The
@@ -146,15 +141,88 @@ class Stream<T> extends ExchangeImpl<T> {
         return connection.connection;
     }
 
+    /**
+     * Invoked either from incoming() -> {receiveDataFrame() or receiveResetFrame() }
+     * of after user subscription window has re-opened, from SubscriptionBase.request()
+     */
+    private void schedule() {
+        if (responseSubscriber == null)
+            // can't process anything yet
+            return;
+
+        while (!inputQ.isEmpty()) {
+            Http2Frame frame  = inputQ.peek();
+            if (frame instanceof ResetFrame) {
+                inputQ.remove();
+                handleReset((ResetFrame)frame);
+                return;
+            }
+            DataFrame df = (DataFrame)frame;
+            boolean finished = df.getFlag(DataFrame.END_STREAM);
+
+            List<ByteBuffer> buffers = df.getData();
+            List<ByteBuffer> dsts = Collections.unmodifiableList(buffers);
+            int size = Utils.remaining(dsts, Integer.MAX_VALUE);
+            if (size == 0 && finished) {
+                inputQ.remove();
+                Log.logTrace("responseSubscriber.onComplete");
+                debug.log(Level.DEBUG, "incoming: onComplete");
+                sched.stop();
+                responseSubscriber.onComplete();
+                setEndStreamReceived();
+                return;
+            } else if (userSubscription.tryDecrement()) {
+                inputQ.remove();
+                Log.logTrace("responseSubscriber.onNext {0}", size);
+                debug.log(Level.DEBUG, "incoming: onNext(%d)", size);
+                responseSubscriber.onNext(dsts);
+                if (consumed(df)) {
+                    Log.logTrace("responseSubscriber.onComplete");
+                    debug.log(Level.DEBUG, "incoming: onComplete");
+                    sched.stop();
+                    responseSubscriber.onComplete();
+                    setEndStreamReceived();
+                    return;
+                }
+            } else {
+                return;
+            }
+        }
+        Throwable t = failed;
+        if (t != null) {
+            sched.stop();
+            responseSubscriber.onError(t);
+            close();
+        }
+    }
+
+    // Callback invoked after the Response BodySubscriber has consumed the
+    // buffers contained in a DataFrame.
+    // Returns true if END_STREAM is reached, false otherwise.
+    private boolean consumed(DataFrame df) {
+        // RFC 7540 6.1:
+        // The entire DATA frame payload is included in flow control,
+        // including the Pad Length and Padding fields if present
+        int len = df.payloadLength();
+        connection.windowUpdater.update(len);
+
+        if (!df.getFlag(DataFrame.END_STREAM)) {
+            // Don't send window update on a stream which is
+            // closed or half closed.
+            windowUpdater.update(len);
+            return false; // more data coming
+        }
+        return true; // end of stream
+    }
+
     @Override
     CompletableFuture<T> readBodyAsync(HttpResponse.BodyHandler<T> handler,
                                        boolean returnConnectionToPool,
                                        Executor executor)
     {
         Log.logTrace("Reading body on stream {0}", streamid);
-        responseProcessor = handler.apply(responseCode, responseHeaders);
-        publisher.subscribe(responseProcessor);
-        CompletableFuture<T> cf = receiveData(executor);
+        BodySubscriber<T> bodySubscriber = handler.apply(responseCode, responseHeaders);
+        CompletableFuture<T> cf = receiveData(bodySubscriber);
 
         PushGroup<?,?> pg = exchange.getPushGroup();
         if (pg != null) {
@@ -164,20 +232,6 @@ class Stream<T> extends ExchangeImpl<T> {
         return cf;
     }
 
-    @Override
-    T readBody(HttpResponse.BodyHandler<T> handler, boolean returnConnectionToPool)
-        throws IOException
-    {
-        CompletableFuture<T> cf = readBodyAsync(handler,
-                                                returnConnectionToPool,
-                                                null);
-        try {
-            return cf.join();
-        } catch (CompletionException e) {
-            throw Utils.getIOException(e);
-        }
-    }
-
     @Override
     public String toString() {
         StringBuilder sb = new StringBuilder();
@@ -186,85 +240,51 @@ class Stream<T> extends ExchangeImpl<T> {
         return sb.toString();
     }
 
-    private boolean receiveDataFrame(Http2Frame frame) throws IOException, InterruptedException {
-        if (frame instanceof ResetFrame) {
-            handleReset((ResetFrame) frame);
-            return true;
-        } else if (!(frame instanceof DataFrame)) {
-            assert false;
-            return true;
-        }
-        DataFrame df = (DataFrame) frame;
-        // RFC 7540 6.1:
-        // The entire DATA frame payload is included in flow control,
-        // including the Pad Length and Padding fields if present
-        int len = df.payloadLength();
-        ByteBufferReference[] buffers = df.getData();
-        for (ByteBufferReference b : buffers) {
-            ByteBuffer buf = b.get();
-            if (buf.hasRemaining()) {
-                publisher.acceptData(Optional.of(buf));
-            }
-        }
-        connection.windowUpdater.update(len);
-        if (df.getFlag(DataFrame.END_STREAM)) {
-            setEndStreamReceived();
-            publisher.acceptData(Optional.empty());
-            return false;
-        }
-        // Don't send window update on a stream which is
-        // closed or half closed.
-        windowUpdater.update(len);
-        return true;
+    private void receiveDataFrame(DataFrame df) {
+        inputQ.add(df);
+        sched.runOrSchedule();
     }
 
-    // pushes entire response body into response processor
+    /** Handles a RESET frame. RESET is always handled inline in the queue. */
+    private void receiveResetFrame(ResetFrame frame) {
+        inputQ.add(frame);
+        sched.runOrSchedule();
+    }
+
+    // pushes entire response body into response subscriber
     // blocking when required by local or remote flow control
-    CompletableFuture<T> receiveData(Executor executor) {
-        CompletableFuture<T> cf = responseProcessor
-                .getBody()
-                .toCompletableFuture();
-        Consumer<Throwable> onError = e -> {
-            Log.logTrace("receiveData: {0}", e.toString());
-            e.printStackTrace();
-            cf.completeExceptionally(e);
-            publisher.acceptError(e);
-        };
-        if (executor == null) {
-            inputQ.blockingReceive(this::receiveDataFrame, onError);
+    CompletableFuture<T> receiveData(BodySubscriber<T> bodySubscriber) {
+        responseBodyCF = MinimalFuture.of(bodySubscriber.getBody());
+
+        if (isCanceled()) {
+            Throwable t = getCancelCause();
+            responseBodyCF.completeExceptionally(t);
         } else {
-            inputQ.asyncReceive(executor, this::receiveDataFrame, onError);
+            bodySubscriber.onSubscribe(userSubscription);
         }
-        return cf;
+        // Set the responseSubscriber field now that onSubscribe has been called.
+        // This effectively allows the scheduler to start invoking the callbacks.
+        responseSubscriber = bodySubscriber;
+        sched.runOrSchedule(); // in case data waiting already to be processed
+        return responseBodyCF;
     }
 
     @Override
-    void sendBody() throws IOException {
-        try {
-            sendBodyImpl().join();
-        } catch (CompletionException e) {
-            throw Utils.getIOException(e);
-        }
-    }
-
     CompletableFuture<ExchangeImpl<T>> sendBodyAsync() {
         return sendBodyImpl().thenApply( v -> this);
     }
 
     @SuppressWarnings("unchecked")
-    Stream(HttpClientImpl client,
-           Http2Connection connection,
+    Stream(Http2Connection connection,
            Exchange<T> e,
            WindowController windowController)
     {
         super(e);
-        this.client = client;
         this.connection = connection;
         this.windowController = windowController;
         this.request = e.request();
-        this.requestProcessor = request.requestProcessor;
+        this.requestPublisher = request.requestPublisher;  // may be null
         responseHeaders = new HttpHeadersImpl();
-        requestHeaders = new HttpHeadersImpl();
         rspHeadersConsumer = (name, value) -> {
             responseHeaders.addHeader(name.toString(), value.toString());
             if (Log.headers() && Log.trace()) {
@@ -274,7 +294,6 @@ class Stream<T> extends ExchangeImpl<T> {
         };
         this.requestPseudoHeaders = new HttpHeadersImpl();
         // NEW
-        this.publisher = new BlockingPushPublisher<>();
         this.windowUpdater = new StreamWindowUpdateSender(connection);
     }
 
@@ -284,17 +303,18 @@ class Stream<T> extends ExchangeImpl<T> {
      * Data frames will be removed by response body thread.
      */
     void incoming(Http2Frame frame) throws IOException {
+        debug.log(Level.DEBUG, "incoming: %s", frame);
         if ((frame instanceof HeaderFrame)) {
             HeaderFrame hframe = (HeaderFrame)frame;
             if (hframe.endHeaders()) {
                 Log.logTrace("handling response (streamid={0})", streamid);
                 handleResponse();
                 if (hframe.getFlag(HeaderFrame.END_STREAM)) {
-                    inputQ.put(new DataFrame(streamid, DataFrame.END_STREAM, new ByteBufferReference[0]));
+                    receiveDataFrame(new DataFrame(streamid, DataFrame.END_STREAM, List.of()));
                 }
             }
         } else if (frame instanceof DataFrame) {
-            inputQ.put(frame);
+            receiveDataFrame((DataFrame)frame);
         } else {
             otherFrame(frame);
         }
@@ -324,10 +344,6 @@ class Stream<T> extends ExchangeImpl<T> {
     }
 
     protected void handleResponse() throws IOException {
-        synchronized(this) {
-            responseHeadersReceived = true;
-        }
-        HttpConnection c = connection.connection; // TODO: improve
         responseCode = (int)responseHeaders
                 .firstValueAsLong(":status")
                 .orElseThrow(() -> new IOException("no statuscode in response"));
@@ -336,9 +352,11 @@ class Stream<T> extends ExchangeImpl<T> {
                 request, exchange, responseHeaders,
                 responseCode, HttpClient.Version.HTTP_2);
 
-        this.responseContentLen = responseHeaders
-                .firstValueAsLong("content-length")
-                .orElse(-1L);
+        /* TODO: review if needs to be removed
+           the value is not used, but in case `content-length` doesn't parse as
+           long, there will be NumberFormatException. If left as is, make sure
+           code up the stack handles NFE correctly. */
+        responseHeaders.firstValueAsLong("content-length");
 
         if (Log.headers()) {
             StringBuilder sb = new StringBuilder("RESPONSE HEADERS:\n");
@@ -349,50 +367,32 @@ class Stream<T> extends ExchangeImpl<T> {
         completeResponse(response);
     }
 
-    void incoming_reset(ResetFrame frame) throws IOException {
+    void incoming_reset(ResetFrame frame) {
         Log.logTrace("Received RST_STREAM on stream {0}", streamid);
         if (endStreamReceived()) {
             Log.logTrace("Ignoring RST_STREAM frame received on remotely closed stream {0}", streamid);
         } else if (closed) {
             Log.logTrace("Ignoring RST_STREAM frame received on closed stream {0}", streamid);
         } else {
-            boolean pushedToQueue = false;
-            synchronized(this) {
-                // if the response headers are not yet
-                // received, or the inputQueue is closed, handle reset directly.
-                // Otherwise, put it in the input queue in order to read all
-                // pending data frames first. Indeed, a server may send
-                // RST_STREAM after sending END_STREAM, in which case we should
-                // ignore it. However, we won't know if we have received END_STREAM
-                // or not until all pending data frames are read.
-                // Because the inputQ will not be read until the response
-                // headers are received, and because response headers won't be
-                // sent if the server sent RST_STREAM, then we must handle
-                // reset here directly unless responseHeadersReceived is true.
-                pushedToQueue = !closed && responseHeadersReceived && inputQ.tryPut(frame);
-            }
-            if (!pushedToQueue) {
-                // RST_STREAM was not pushed to the queue: handle it.
-                try {
-                    handleReset(frame);
-                } catch (IOException io) {
-                    completeResponseExceptionally(io);
-                }
-            } else {
-                // RST_STREAM was pushed to the queue. It will be handled by
-                // asyncReceive after all pending data frames have been
-                // processed.
-                Log.logTrace("RST_STREAM pushed in queue for stream {0}", streamid);
-            }
+            // put it in the input queue in order to read all
+            // pending data frames first. Indeed, a server may send
+            // RST_STREAM after sending END_STREAM, in which case we should
+            // ignore it. However, we won't know if we have received END_STREAM
+            // or not until all pending data frames are read.
+            receiveResetFrame(frame);
+            // RST_STREAM was pushed to the queue. It will be handled by
+            // asyncReceive after all pending data frames have been
+            // processed.
+            Log.logTrace("RST_STREAM pushed in queue for stream {0}", streamid);
         }
     }
 
-    void handleReset(ResetFrame frame) throws IOException {
+    void handleReset(ResetFrame frame) {
         Log.logTrace("Handling RST_STREAM on stream {0}", streamid);
         if (!closed) {
             close();
             int error = frame.getErrorCode();
-            throw new IOException(ErrorFrame.stringForCode(error));
+            completeResponseExceptionally(new IOException(ErrorFrame.stringForCode(error)));
         } else {
             Log.logTrace("Ignoring RST_STREAM frame received on closed stream {0}", streamid);
         }
@@ -431,20 +431,21 @@ class Stream<T> extends ExchangeImpl<T> {
         if (pushGroup == null || pushGroup.noMorePushes()) {
             cancelImpl(new IllegalStateException("unexpected push promise"
                 + " on stream " + streamid));
+            return;
         }
 
-        HttpResponse.MultiProcessor<?,T> proc = pushGroup.processor();
+        HttpResponse.MultiSubscriber<?,T> proc = pushGroup.subscriber();
 
         CompletableFuture<HttpResponse<T>> cf = pushStream.responseCF();
 
-        Optional<HttpResponse.BodyHandler<T>> bpOpt = proc.onRequest(
-                pushReq);
+        Optional<HttpResponse.BodyHandler<T>> bpOpt =
+                pushGroup.handlerForPushRequest(pushReq);
 
         if (!bpOpt.isPresent()) {
             IOException ex = new IOException("Stream "
                  + streamid + " cancelled by user");
             if (Log.trace()) {
-                Log.logTrace("No body processor for {0}: {1}", pushReq,
+                Log.logTrace("No body subscriber for {0}: {1}", pushReq,
                             ex.getMessage());
             }
             pushStream.cancelImpl(ex);
@@ -458,6 +459,7 @@ class Stream<T> extends ExchangeImpl<T> {
         // setup housekeeping for when the push is received
         // TODO: deal with ignoring of CF anti-pattern
         cf.whenComplete((HttpResponse<T> resp, Throwable t) -> {
+            t = Utils.getCompletionCause(t);
             if (Log.trace()) {
                 Log.logTrace("Push completed on stream {0} for {1}{2}",
                              pushStream.streamid, resp,
@@ -516,34 +518,6 @@ class Stream<T> extends ExchangeImpl<T> {
         return requestPseudoHeaders;
     }
 
-    @Override
-    Response getResponse() throws IOException {
-        try {
-            if (request.duration() != null) {
-                Log.logTrace("Waiting for response (streamid={0}, timeout={1}ms)",
-                             streamid,
-                             request.duration().toMillis());
-                return getResponseAsync(null).get(
-                        request.duration().toMillis(), TimeUnit.MILLISECONDS);
-            } else {
-                Log.logTrace("Waiting for response (streamid={0})", streamid);
-                return getResponseAsync(null).join();
-            }
-        } catch (TimeoutException e) {
-            Log.logTrace("Response timeout (streamid={0})", streamid);
-            throw new HttpTimeoutException("Response timed out");
-        } catch (InterruptedException | ExecutionException | CompletionException e) {
-            Throwable t = e.getCause();
-            Log.logTrace("Response failed (streamid={0}): {1}", streamid, t);
-            if (t instanceof IOException) {
-                throw (IOException)t;
-            }
-            throw new IOException(e);
-        } finally {
-            Log.logTrace("Got response or failed (streamid={0})", streamid);
-        }
-    }
-
     /** Sets endStreamReceived. Should be called only once. */
     void setEndStreamReceived() {
         assert remotelyClosed == false: "Unexpected endStream already set";
@@ -558,100 +532,247 @@ class Stream<T> extends ExchangeImpl<T> {
     }
 
     @Override
-    void sendHeadersOnly() throws IOException, InterruptedException {
+    CompletableFuture<ExchangeImpl<T>> sendHeadersAsync() {
+        debug.log(Level.DEBUG, "sendHeadersOnly()");
         if (Log.requests() && request != null) {
             Log.logRequest(request.toString());
         }
-        requestContentLen = requestProcessor.contentLength();
+        if (requestPublisher != null) {
+            requestContentLen = requestPublisher.contentLength();
+        } else {
+            requestContentLen = 0;
+        }
         OutgoingHeaders<Stream<T>> f = headerFrame(requestContentLen);
         connection.sendFrame(f);
+        CompletableFuture<ExchangeImpl<T>> cf = new MinimalFuture<>();
+        cf.complete(this);  // #### good enough for now
+        return cf;
+    }
+
+    @Override
+    void released() {
+        if (streamid > 0) {
+            debug.log(Level.DEBUG, "Released stream %d", streamid);
+            // remove this stream from the Http2Connection map.
+            connection.closeStream(streamid);
+        } else {
+            debug.log(Level.DEBUG, "Can't release stream %d", streamid);
+        }
+    }
+
+    @Override
+    void completed() {
+        // There should be nothing to do here: the stream should have
+        // been already closed (or will be closed shortly after).
     }
 
     void registerStream(int id) {
         this.streamid = id;
         connection.putStream(this, streamid);
+        debug.log(Level.DEBUG, "Registered stream %d", id);
     }
 
+    void signalWindowUpdate() {
+        RequestSubscriber subscriber = requestSubscriber;
+        assert subscriber != null;
+        debug.log(Level.DEBUG, "Signalling window update");
+        subscriber.sendScheduler.runOrSchedule();
+    }
+
+    static final ByteBuffer COMPLETED = ByteBuffer.allocate(0);
     class RequestSubscriber implements Flow.Subscriber<ByteBuffer> {
         // can be < 0 if the actual length is not known.
+        private final long contentLength;
         private volatile long remainingContentLength;
         private volatile Subscription subscription;
 
+        // Holds the outgoing data. There will be at most 2 outgoing ByteBuffers.
+        //  1) The data that was published by the request body Publisher, and
+        //  2) the COMPLETED sentinel, since onComplete can be invoked without demand.
+        final ConcurrentLinkedDeque<ByteBuffer> outgoing = new ConcurrentLinkedDeque<>();
+
+        private final AtomicReference<Throwable> errorRef = new AtomicReference<>();
+        // A scheduler used to honor window updates. Writing must be paused
+        // when the window is exhausted, and resumed when the window acquires
+        // some space. The sendScheduler makes it possible to implement this
+        // behaviour in an asynchronous non-blocking way.
+        // See RequestSubscriber::trySend below.
+        final SequentialScheduler sendScheduler;
+
         RequestSubscriber(long contentLen) {
+            this.contentLength = contentLen;
             this.remainingContentLength = contentLen;
+            this.sendScheduler =
+                    SequentialScheduler.synchronizedScheduler(this::trySend);
         }
 
         @Override
         public void onSubscribe(Flow.Subscription subscription) {
             if (this.subscription != null) {
-                throw new IllegalStateException();
+                throw new IllegalStateException("already subscribed");
             }
             this.subscription = subscription;
+            debug.log(Level.DEBUG, "RequestSubscriber: onSubscribe, request 1");
             subscription.request(1);
         }
 
         @Override
         public void onNext(ByteBuffer item) {
-            if (requestBodyCF.isDone()) {
-                throw new IllegalStateException();
-            }
+            debug.log(Level.DEBUG, "RequestSubscriber: onNext(%d)", item.remaining());
+            int size = outgoing.size();
+            assert size == 0 : "non-zero size: " + size;
+            onNextImpl(item);
+        }
 
+        private void onNextImpl(ByteBuffer item) {
+            // Got some more request body bytes to send.
+            if (requestBodyCF.isDone()) {
+                // stream already cancelled, probably in timeout
+                sendScheduler.stop();
+                subscription.cancel();
+                return;
+            }
+            outgoing.add(item);
+            sendScheduler.runOrSchedule();
+        }
+
+        @Override
+        public void onError(Throwable throwable) {
+            debug.log(Level.DEBUG, () -> "RequestSubscriber: onError: " + throwable);
+            // ensure that errors are handled within the flow.
+            if (errorRef.compareAndSet(null, throwable)) {
+                sendScheduler.runOrSchedule();
+            }
+        }
+
+        @Override
+        public void onComplete() {
+            debug.log(Level.DEBUG, "RequestSubscriber: onComplete");
+            int size = outgoing.size();
+            assert size == 0 || size == 1 : "non-zero or one size: " + size;
+            // last byte of request body has been obtained.
+            // ensure that everything is completed within the flow.
+            onNextImpl(COMPLETED);
+        }
+
+        // Attempts to send the data, if any.
+        // Handles errors and completion state.
+        // Pause writing if the send window is exhausted, resume it if the
+        // send window has some bytes that can be acquired.
+        void trySend() {
             try {
-                while (item.hasRemaining()) {
-                    assert !endStreamSent : "internal error, send data after END_STREAM flag";
-                    DataFrame df = getDataFrame(item);
-                    if (remainingContentLength > 0) {
-                        remainingContentLength -= df.getDataLength();
-                        assert remainingContentLength >= 0;
-                        if (remainingContentLength == 0) {
-                            df.setFlag(DataFrame.END_STREAM);
-                            endStreamSent = true;
-                        }
-                    }
-                    connection.sendDataFrame(df);
+                // handle errors raised by onError;
+                Throwable t = errorRef.get();
+                if (t != null) {
+                    sendScheduler.stop();
+                    if (requestBodyCF.isDone()) return;
+                    subscription.cancel();
+                    requestBodyCF.completeExceptionally(t);
+                    return;
                 }
+
+                do {
+                    // handle COMPLETED;
+                    ByteBuffer item = outgoing.peekFirst();
+                    if (item == null) return;
+                    else if (item == COMPLETED) {
+                        sendScheduler.stop();
+                        complete();
+                        return;
+                    }
+
+                    // handle bytes to send downstream
+                    while (item.hasRemaining()) {
+                        debug.log(Level.DEBUG, "trySend: %d", item.remaining());
+                        assert !endStreamSent : "internal error, send data after END_STREAM flag";
+                        DataFrame df = getDataFrame(item);
+                        if (df == null) {
+                            debug.log(Level.DEBUG, "trySend: can't send yet: %d",
+                                    item.remaining());
+                            return; // the send window is exhausted: come back later
+                        }
+
+                        if (contentLength > 0) {
+                            remainingContentLength -= df.getDataLength();
+                            if (remainingContentLength < 0) {
+                                String msg = connection().getConnectionFlow()
+                                        + " stream=" + streamid + " "
+                                        + "[" + Thread.currentThread().getName() + "] "
+                                        + "Too many bytes in request body. Expected: "
+                                        + contentLength + ", got: "
+                                        + (contentLength - remainingContentLength);
+                                connection.resetStream(streamid, ResetFrame.PROTOCOL_ERROR);
+                                throw new IOException(msg);
+                            } else if (remainingContentLength == 0) {
+                                df.setFlag(DataFrame.END_STREAM);
+                                endStreamSent = true;
+                            }
+                        }
+                        debug.log(Level.DEBUG, "trySend: sending: %d", df.getDataLength());
+                        connection.sendDataFrame(df);
+                    }
+                    assert !item.hasRemaining();
+                    ByteBuffer b = outgoing.removeFirst();
+                    assert b == item;
+                } while (outgoing.peekFirst() != null);
+
+                debug.log(Level.DEBUG, "trySend: request 1");
                 subscription.request(1);
-            } catch (InterruptedException ex) {
+            } catch (Throwable ex) {
+                debug.log(Level.DEBUG, "trySend: ", ex);
+                sendScheduler.stop();
                 subscription.cancel();
                 requestBodyCF.completeExceptionally(ex);
             }
         }
 
-        @Override
-        public void onError(Throwable throwable) {
-            if (requestBodyCF.isDone()) {
-                return;
+        private void complete() throws IOException {
+            long remaining = remainingContentLength;
+            long written = contentLength - remaining;
+            if (remaining > 0) {
+                connection.resetStream(streamid, ResetFrame.PROTOCOL_ERROR);
+                // let trySend() handle the exception
+                throw new IOException(connection().getConnectionFlow()
+                                     + " stream=" + streamid + " "
+                                     + "[" + Thread.currentThread().getName() +"] "
+                                     + "Too few bytes returned by the publisher ("
+                                              + written + "/"
+                                              + contentLength + ")");
             }
-            subscription.cancel();
-            requestBodyCF.completeExceptionally(throwable);
-        }
-
-        @Override
-        public void onComplete() {
-            assert endStreamSent || remainingContentLength < 0;
-            try {
-                if (!endStreamSent) {
-                    endStreamSent = true;
-                    connection.sendDataFrame(getEmptyEndStreamDataFrame());
-                }
-                requestBodyCF.complete(null);
-            } catch (InterruptedException ex) {
-                requestBodyCF.completeExceptionally(ex);
+            if (!endStreamSent) {
+                endStreamSent = true;
+                connection.sendDataFrame(getEmptyEndStreamDataFrame());
             }
+            requestBodyCF.complete(null);
         }
     }
 
-    DataFrame getDataFrame(ByteBuffer buffer) throws InterruptedException {
+    /**
+     * Send a RESET frame to tell server to stop sending data on this stream
+     */
+    @Override
+    public CompletableFuture<Void> ignoreBody() {
+        try {
+            connection.resetStream(streamid, ResetFrame.STREAM_CLOSED);
+            return MinimalFuture.completedFuture(null);
+        } catch (Throwable e) {
+            Log.logTrace("Error resetting stream {0}", e.toString());
+            return MinimalFuture.failedFuture(e);
+        }
+    }
+
+    DataFrame getDataFrame(ByteBuffer buffer) {
         int requestAmount = Math.min(connection.getMaxSendFrameSize(), buffer.remaining());
         // blocks waiting for stream send window, if exhausted
-        int actualAmount = windowController.tryAcquire(requestAmount, streamid);
+        int actualAmount = windowController.tryAcquire(requestAmount, streamid, this);
+        if (actualAmount <= 0) return null;
         ByteBuffer outBuf = Utils.slice(buffer,  actualAmount);
-        DataFrame df = new DataFrame(streamid, 0 , ByteBufferReference.of(outBuf));
+        DataFrame df = new DataFrame(streamid, 0 , outBuf);
         return df;
     }
 
-    private DataFrame getEmptyEndStreamDataFrame() throws InterruptedException {
-        return new DataFrame(streamid, DataFrame.END_STREAM, new ByteBufferReference[0]);
+    private DataFrame getEmptyEndStreamDataFrame()  {
+        return new DataFrame(streamid, DataFrame.END_STREAM, List.of());
     }
 
     /**
@@ -666,7 +787,7 @@ class Stream<T> extends ExchangeImpl<T> {
 
     @Override
     CompletableFuture<Response> getResponseAsync(Executor executor) {
-        CompletableFuture<Response> cf = null;
+        CompletableFuture<Response> cf;
         // The code below deals with race condition that can be caused when
         // completeResponse() is being called before getResponseAsync()
         synchronized (response_cfs) {
@@ -693,7 +814,7 @@ class Stream<T> extends ExchangeImpl<T> {
         PushGroup<?,?> pg = exchange.getPushGroup();
         if (pg != null) {
             // if an error occurs make sure it is recorded in the PushGroup
-            cf = cf.whenComplete((t,e) -> pg.pushError(e));
+            cf = cf.whenComplete((t,e) -> pg.pushError(Utils.getCompletionCause(e)));
         }
         return cf;
     }
@@ -732,10 +853,6 @@ class Stream<T> extends ExchangeImpl<T> {
         }
     }
 
-    final synchronized boolean isResponseReceived() {
-        return responseReceived;
-    }
-
     synchronized void responseReceived() {
         responseReceived = true;
         if (requestSent) {
@@ -763,9 +880,15 @@ class Stream<T> extends ExchangeImpl<T> {
     }
 
     CompletableFuture<Void> sendBodyImpl() {
-        RequestSubscriber subscriber = new RequestSubscriber(requestContentLen);
-        requestProcessor.subscribe(subscriber);
-        requestBodyCF.whenComplete((v,t) -> requestSent());
+        requestBodyCF.whenComplete((v, t) -> requestSent());
+        if (requestPublisher != null) {
+            final RequestSubscriber subscriber = new RequestSubscriber(requestContentLen);
+            requestPublisher.subscribe(requestSubscriber = subscriber);
+        } else {
+            // there is no request body, therefore the request is complete,
+            // END_STREAM has already sent with outgoing headers
+            requestBodyCF.complete(null);
+        }
         return requestBodyCF;
     }
 
@@ -781,21 +904,30 @@ class Stream<T> extends ExchangeImpl<T> {
 
     // This method sends a RST_STREAM frame
     void cancelImpl(Throwable e) {
+        debug.log(Level.DEBUG, "cancelling stream {0}: {1}", streamid, e);
         if (Log.trace()) {
             Log.logTrace("cancelling stream {0}: {1}\n", streamid, e);
         }
         boolean closing;
         if (closing = !closed) { // assigning closing to !closed
             synchronized (this) {
+                failed = e;
                 if (closing = !closed) { // assigning closing to !closed
                     closed=true;
                 }
             }
         }
         if (closing) { // true if the stream has not been closed yet
-            inputQ.close();
+            if (responseSubscriber != null)
+                sched.runOrSchedule();
         }
         completeResponseExceptionally(e);
+        if (!requestBodyCF.isDone()) {
+            requestBodyCF.completeExceptionally(e); // we may be sending the body..
+        }
+        if (responseBodyCF != null) {
+            responseBodyCF.completeExceptionally(e);
+        }
         try {
             // will send a RST_STREAM frame
             if (streamid != 0) {
@@ -814,14 +946,12 @@ class Stream<T> extends ExchangeImpl<T> {
             closed = true;
         }
         Log.logTrace("Closing stream {0}", streamid);
-        inputQ.close();
         connection.closeStream(streamid);
         Log.logTrace("Stream {0} closed", streamid);
     }
 
     static class PushedStream<U,T> extends Stream<T> {
         final PushGroup<U,T> pushGroup;
-        private final Stream<T> parent;      // used by server push streams
         // push streams need the response CF allocated up front as it is
         // given directly to user via the multi handler callback function.
         final CompletableFuture<Response> pushCF;
@@ -829,16 +959,15 @@ class Stream<T> extends ExchangeImpl<T> {
         final HttpRequestImpl pushReq;
         HttpResponse.BodyHandler<T> pushHandler;
 
-        PushedStream(PushGroup<U,T> pushGroup, HttpClientImpl client,
-                Http2Connection connection, Stream<T> parent,
-                Exchange<T> pushReq) {
+        PushedStream(PushGroup<U,T> pushGroup,
+                     Http2Connection connection,
+                     Exchange<T> pushReq) {
             // ## no request body possible, null window controller
-            super(client, connection, pushReq, null);
+            super(connection, pushReq, null);
             this.pushGroup = pushGroup;
             this.pushReq = pushReq.request();
             this.pushCF = new MinimalFuture<>();
             this.responseCF = new MinimalFuture<>();
-            this.parent = parent;
         }
 
         CompletableFuture<HttpResponse<T>> responseCF() {
@@ -860,18 +989,21 @@ class Stream<T> extends ExchangeImpl<T> {
         @Override
         CompletableFuture<ExchangeImpl<T>> sendBodyAsync() {
             return super.sendBodyAsync()
-                        .whenComplete((ExchangeImpl<T> v, Throwable t) -> pushGroup.pushError(t));
+                        .whenComplete((ExchangeImpl<T> v, Throwable t)
+                                -> pushGroup.pushError(Utils.getCompletionCause(t)));
         }
 
         @Override
         CompletableFuture<ExchangeImpl<T>> sendHeadersAsync() {
             return super.sendHeadersAsync()
-                        .whenComplete((ExchangeImpl<T> ex, Throwable t) -> pushGroup.pushError(t));
+                        .whenComplete((ExchangeImpl<T> ex, Throwable t)
+                                -> pushGroup.pushError(Utils.getCompletionCause(t)));
         }
 
         @Override
         CompletableFuture<Response> getResponseAsync(Executor executor) {
-            CompletableFuture<Response> cf = pushCF.whenComplete((v, t) -> pushGroup.pushError(t));
+            CompletableFuture<Response> cf = pushCF.whenComplete(
+                    (v, t) -> pushGroup.pushError(Utils.getCompletionCause(t)));
             if(executor!=null && !cf.isDone()) {
                 cf  = cf.thenApplyAsync( r -> r, executor);
             }
@@ -890,17 +1022,18 @@ class Stream<T> extends ExchangeImpl<T> {
 
         @Override
         void completeResponse(Response r) {
-            HttpResponseImpl.logResponse(r);
+            Log.logResponse(r::toString);
             pushCF.complete(r); // not strictly required for push API
-            // start reading the body using the obtained BodyProcessor
+            // start reading the body using the obtained BodySubscriber
             CompletableFuture<Void> start = new MinimalFuture<>();
             start.thenCompose( v -> readBodyAsync(getPushHandler(), false, getExchange().executor()))
                 .whenComplete((T body, Throwable t) -> {
                     if (t != null) {
                         responseCF.completeExceptionally(t);
                     } else {
-                        HttpResponseImpl<T> response = new HttpResponseImpl<>(r.request, r, body, getExchange());
-                        responseCF.complete(response);
+                        HttpResponseImpl<T> resp =
+                                new HttpResponseImpl<>(r.request, r, null, body, getExchange());
+                        responseCF.complete(resp);
                     }
                 });
             start.completeAsync(() -> null, getExchange().executor());
@@ -911,15 +1044,14 @@ class Stream<T> extends ExchangeImpl<T> {
             pushCF.completeExceptionally(t);
         }
 
-        @Override
-        synchronized void responseReceived() {
-            super.responseReceived();
-        }
+//        @Override
+//        synchronized void responseReceived() {
+//            super.responseReceived();
+//        }
 
         // create and return the PushResponseImpl
         @Override
         protected void handleResponse() {
-            HttpConnection c = connection.connection; // TODO: improve
             responseCode = (int)responseHeaders
                 .firstValueAsLong(":status")
                 .orElse(-1);
@@ -932,9 +1064,11 @@ class Stream<T> extends ExchangeImpl<T> {
                 pushReq, exchange, responseHeaders,
                 responseCode, HttpClient.Version.HTTP_2);
 
-            this.responseContentLen = responseHeaders
-                .firstValueAsLong("content-length")
-                .orElse(-1L);
+            /* TODO: review if needs to be removed
+               the value is not used, but in case `content-length` doesn't parse
+               as long, there will be NumberFormatException. If left as is, make
+               sure code up the stack handles NFE correctly. */
+            responseHeaders.firstValueAsLong("content-length");
 
             if (Log.headers()) {
                 StringBuilder sb = new StringBuilder("RESPONSE HEADERS");
@@ -960,4 +1094,23 @@ class Stream<T> extends ExchangeImpl<T> {
         }
     }
 
+    /**
+     * Returns true if this exchange was canceled.
+     * @return true if this exchange was canceled.
+     */
+    synchronized boolean isCanceled() {
+        return failed != null;
+    }
+
+    /**
+     * Returns the cause for which this exchange was canceled, if available.
+     * @return the cause for which this exchange was canceled, if available.
+     */
+    synchronized Throwable getCancelCause() {
+        return failed;
+    }
+
+    final String dbgString() {
+        return connection.dbgString() + "/Stream("+streamid+")";
+    }
 }
diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/WebSocket.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/WebSocket.java
index 2433e2499e7..e992f9efccc 100644
--- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/WebSocket.java
+++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/WebSocket.java
@@ -26,7 +26,6 @@
 package jdk.incubator.http;
 
 import java.io.IOException;
-import java.net.ProtocolException;
 import java.net.URI;
 import java.nio.ByteBuffer;
 import java.time.Duration;
@@ -34,50 +33,54 @@ import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.CompletionStage;
 
 /**
- * A WebSocket client conforming to RFC&nbsp;6455.
+ * A WebSocket client.
  * {@Incubating}
  *
- * <p> A {@code WebSocket} provides full-duplex communication over a TCP
- * connection.
+ * <p> To create a {@code WebSocket} use the {@link HttpClient#newWebSocketBuilder}
+ * method. To close a {@code WebSocket} use one of the {@code sendClose} or
+ * {@code abort} methods.
  *
- * <p> To create a {@code WebSocket} use a {@linkplain HttpClient#newWebSocketBuilder(
- * URI, Listener) builder}. Once a {@code WebSocket} is built, it's ready
- * to send and receive messages. When the {@code WebSocket} is no longer needed
- * it must be closed: a Close message must both be {@linkplain #sendClose
- * sent} and {@linkplain Listener#onClose(WebSocket, int, String) received}.
- * The {@code WebSocket} may be also closed {@linkplain #abort() abruptly}.
+ * <p> WebSocket messages are sent through a {@code WebSocket} and received
+ * through the {@code WebSocket}'s {@code Listener}. Messages can be sent until
+ * the output is closed, and received until the input is closed.
+ * A {@code WebSocket} whose output and input are both closed may be considered
+ * itself closed. To check these states use {@link #isOutputClosed()} and
+ * {@link #isInputClosed()}.
  *
- * <p> Once closed the {@code WebSocket} remains {@linkplain #isClosed() closed}
- * and cannot be reopened.
+ * <p> Methods that send messages return {@code CompletableFuture} which
+ * completes normally if the message is sent or completes exceptionally if an
+ * error occurs.
  *
- * <p> Messages of type {@code X} (where {@code X} is one of: Text, Binary,
- * Ping, Pong or Close) are sent and received asynchronously through the {@code
- * WebSocket.send{X}} and {@link WebSocket.Listener}{@code .on{X}} methods
- * respectively. Each method returns a {@link CompletionStage} which completes
- * when the operation has completed.
+ * <p> To receive a message, first request it. If {@code n} messages are
+ * requested, the listener will receive up to {@code n} more invocations of the
+ * designated methods from the {@code WebSocket}. To request messages use
+ * {@link #request(long)}. Request is an additive operation, that is
+ * {@code request(n)} followed by {@code request(m)} is equivalent to
+ * {@code request(n + m)}.
  *
- * <p> Note that messages (of any type) are received only if {@linkplain
- * #request(long) requested}.
+ * <p> When sending or receiving a message in parts, a whole message is
+ * transferred as a sequence of one or more invocations where the last
+ * invocation is identified via an additional method argument.
  *
- * <p> One outstanding send operation is permitted. No further send operation
- * can be initiated before the previous one has completed. When sending, a
- * message must not be modified until the returned {@link CompletableFuture}
- * completes (either normally or exceptionally).
+ * <p> Unless otherwise stated, {@code null} arguments will cause methods
+ * of {@code WebSocket} to throw {@code NullPointerException}, similarly,
+ * {@code WebSocket} will not pass {@code null} arguments to methods of
+ * {@code Listener}.
  *
- * <p> Text and Binary messages can be sent and received as a whole or in parts.
- * A whole message is transferred as a sequence of one or more invocations of a
- * corresponding method where the last invocation is identified via an
- * additional method argument.
+ * @implSpec Methods of {@code WebSocket} are failure-atomic in respect to
+ * {@code NullPointerException}, {@code IllegalArgumentException} and
+ * {@code IllegalStateException}. That is, if a method throws said exception, or
+ * a returned {@code CompletableFuture} completes exceptionally with said
+ * exception, the {@code WebSocket} will behave as if the method has not been
+ * invoked at all.
  *
- * <p> If the message is contained in a {@link ByteBuffer}, bytes are considered
- * arranged from the {@code buffer}'s {@link ByteBuffer#position() position} to
- * the {@code buffer}'s {@link ByteBuffer#limit() limit}.
+ * <p> A {@code WebSocket} invokes methods of its listener in a thread-safe
+ * manner.
  *
- * <p> Unless otherwise stated, {@code null} parameter values will cause methods
- * and constructors to throw {@link NullPointerException}.
- *
- * @implNote This implementation's methods do not block before returning
- * a {@code CompletableFuture}.
+ * <p> {@code WebSocket} handles Ping and Close messages automatically (as per
+ * RFC 6455) by replying with Pong and Close messages respectively. If the
+ * listener receives Ping or Close messages, no mandatory actions from the
+ * listener are required.
  *
  * @since 9
  */
@@ -97,29 +100,24 @@ public interface WebSocket {
      * A builder for creating {@code WebSocket} instances.
      * {@Incubating}
      *
-     * <p> To build a {@code WebSocket}, {@linkplain HttpClient#newWebSocketBuilder(
-     * URI, Listener) create} a builder, configure it as required by
+     * <p> To obtain a {@code WebSocket} configure a builder as required by
      * calling intermediate methods (the ones that return the builder itself),
-     * then finally call {@link #buildAsync()} to get a {@link
-     * CompletableFuture} with resulting {@code WebSocket}.
+     * then call {@code buildAsync()}. If an intermediate method is not called,
+     * an appropriate default value (or behavior) will be assumed.
      *
-     * <p> If an intermediate method has not been called, an appropriate
-     * default value (or behavior) will be used. Unless otherwise noted, a
-     * repeated call to an intermediate method overwrites the previous value (or
-     * overrides the previous behaviour).
-     *
-     * <p> Instances of {@code Builder} are not safe for use by multiple threads
-     * without external synchronization.
+     * <p> Unless otherwise stated, {@code null} arguments will cause methods of
+     * {@code Builder} to throw {@code NullPointerException}.
      *
      * @since 9
      */
     interface Builder {
 
         /**
-         * Adds the given name-value pair to the list of additional headers for
-         * the opening handshake.
+         * Adds the given name-value pair to the list of additional HTTP headers
+         * sent during the opening handshake.
          *
-         * <p> Headers defined in WebSocket Protocol are not allowed to be added.
+         * <p> Headers defined in WebSocket Protocol are illegal. If this method
+         * is not invoked, no additional HTTP headers will be sent.
          *
          * @param name
          *         the header name
@@ -131,38 +129,12 @@ public interface WebSocket {
         Builder header(String name, String value);
 
         /**
-         * Includes a request for the given subprotocols during the opening
-         * handshake.
+         * Sets a timeout for establishing a WebSocket connection.
          *
-         * <p> Among the requested subprotocols at most one will be chosen by
-         * the server. This subprotocol will be available from {@link
-         * WebSocket#getSubprotocol}. Subprotocols are specified in the order of
-         * preference.
-         *
-         * <p> Each of the given subprotocols must conform to the relevant
-         * rules defined in the WebSocket Protocol.
-         *
-         * <p> If this method is not invoked then no subprotocols are requested.
-         *
-         * @param mostPreferred
-         *         the most preferred subprotocol
-         * @param lesserPreferred
-         *         the lesser preferred subprotocols, with the least preferred
-         *         at the end
-         *
-         * @return this builder
-         */
-        Builder subprotocols(String mostPreferred, String... lesserPreferred);
-
-        /**
-         * Sets a timeout for the opening handshake.
-         *
-         * <p> If the opening handshake does not complete within the specified
-         * duration then the {@code CompletableFuture} returned from {@link
-         * #buildAsync()} completes exceptionally with a {@link
-         * HttpTimeoutException}.
-         *
-         * <p> If this method is not invoked then the timeout is deemed infinite.
+         * <p> If the connection is not established within the specified
+         * duration then building of the {@code WebSocket} will fail with
+         * {@link HttpTimeoutException}. If this method is not invoked then the
+         * infinite timeout is assumed.
          *
          * @param timeout
          *         the timeout, non-{@linkplain Duration#isNegative() negative},
@@ -173,14 +145,37 @@ public interface WebSocket {
         Builder connectTimeout(Duration timeout);
 
         /**
-         * Builds a {@code WebSocket}.
+         * Sets a request for the given subprotocols.
          *
-         * <p> Returns a {@code CompletableFuture<WebSocket>} which completes
-         * normally with the {@code WebSocket} when it is connected or completes
-         * exceptionally if an error occurs.
+         * <p> After the {@code WebSocket} has been built, the actual
+         * subprotocol can be queried via
+         * {@link WebSocket#getSubprotocol WebSocket.getSubprotocol()}.
          *
-         * <p> {@code CompletableFuture} may complete exceptionally with the
-         * following errors:
+         * <p> Subprotocols are specified in the order of preference. The most
+         * preferred subprotocol is specified first. If there are any additional
+         * subprotocols they are enumerated from the most preferred to the least
+         * preferred.
+         *
+         * <p> Subprotocols not conforming to the syntax of subprotocol
+         * identifiers are illegal. If this method is not invoked then no
+         * subprotocols will be requested.
+         *
+         * @param mostPreferred
+         *         the most preferred subprotocol
+         * @param lesserPreferred
+         *         the lesser preferred subprotocols
+         *
+         * @return this builder
+         */
+        Builder subprotocols(String mostPreferred, String... lesserPreferred);
+
+        /**
+         * Builds a {@link WebSocket} connected to the given {@code URI} and
+         * associated with the given {@code Listener}.
+         *
+         * <p> Returns a {@code CompletableFuture} which will either complete
+         * normally with the resulting {@code WebSocket} or complete
+         * exceptionally with one of the following errors:
          * <ul>
          * <li> {@link IOException} -
          *          if an I/O error occurs
@@ -188,112 +183,56 @@ public interface WebSocket {
          *          if the opening handshake fails
          * <li> {@link HttpTimeoutException} -
          *          if the opening handshake does not complete within
-         *          the specified {@linkplain #connectTimeout(Duration) duration}
+         *          the timeout
          * <li> {@link InterruptedException} -
-         *          if the operation was interrupted
+         *          if the operation is interrupted
          * <li> {@link SecurityException} -
-         *          if a security manager is set, and the caller does not
-         *          have a {@link java.net.URLPermission} for the WebSocket URI
+         *          if a security manager has been installed and it denies
+         *          {@link java.net.URLPermission access} to {@code uri}.
+         *          <a href="HttpRequest.html#securitychecks">Security checks</a>
+         *          contains more information relating to the security context
+         *          in which the the listener is invoked.
          * <li> {@link IllegalArgumentException} -
-         *          if any of the additional {@link #header(String, String)
-         *          headers} are illegal;
-         *          or if any of the WebSocket Protocol rules relevant to {@link
-         *          #subprotocols(String, String...) subprotocols} are violated;
-         *          or if the {@link #connectTimeout(Duration) connect timeout}
-         *          is invalid;
+         *          if any of the arguments of this builder's methods are
+         *          illegal
          * </ul>
          *
+         * @param uri
+         *         the WebSocket URI
+         * @param listener
+         *         the listener
+         *
          * @return a {@code CompletableFuture} with the {@code WebSocket}
          */
-        CompletableFuture<WebSocket> buildAsync();
+        CompletableFuture<WebSocket> buildAsync(URI uri, Listener listener);
     }
 
     /**
-     * A listener for events and messages on a {@code WebSocket}.
+     * The receiving interface of {@code WebSocket}.
      * {@Incubating}
      *
-     * <p> Each method of {@code Listener} corresponds to a type of event or a
-     * type of message. The {@code WebSocket} argument of the method is the
-     * {@code WebSocket} the event has occurred (the message has been received)
-     * on. All methods with the same {@code WebSocket} argument are invoked in a
-     * sequential
-     * (and <a href="../../../java/util/concurrent/package-summary.html#MemoryVisibility">happens-before</a>)
-     * order, one after another, possibly by different threads.
+     * <p> A {@code WebSocket} invokes methods on its listener when it receives
+     * messages or encounters events. The invoking {@code WebSocket} is passed
+     * as an argument to {@code Listener}'s methods. A {@code WebSocket} invokes
+     * methods on its listener in a thread-safe manner.
      *
-     * <ul>
-     * <li> {@link #onOpen(WebSocket) onOpen} <br>
-     * This method is invoked first.
-     * <li> {@link #onText(WebSocket, CharSequence, WebSocket.MessagePart)
-     * onText}, {@link #onBinary(WebSocket, ByteBuffer, WebSocket.MessagePart)
-     * onBinary}, {@link #onPing(WebSocket, ByteBuffer) onPing} and {@link
-     * #onPong(WebSocket, ByteBuffer) onPong} <br>
-     * These methods are invoked zero or more times after {@code onOpen}.
-     * <li> {@link #onClose(WebSocket, int, String) onClose}, {@link
-     * #onError(WebSocket, Throwable) onError} <br>
-     * Only one of these methods is invoked, and that method is invoked last.
-     * </ul>
+     * <p> Unless otherwise stated if a listener's method throws an exception or
+     * a {@code CompletionStage} returned from a method completes exceptionally,
+     * the {@code WebSocket} will invoke {@code onError} with this exception.
      *
-     * <p> Messages received by the {@code Listener} conform to the WebSocket
-     * Protocol, otherwise {@code onError} with a {@link ProtocolException} is
-     * invoked.
-     *
-     * <p> If a whole message is received, then the corresponding method
-     * ({@code onText} or {@code onBinary}) will be invoked with {@link
-     * WebSocket.MessagePart#WHOLE WHOLE} marker. Otherwise the method will be
-     * invoked with {@link WebSocket.MessagePart#FIRST FIRST}, zero or more
-     * times with {@link WebSocket.MessagePart#PART PART} and, finally, with
-     * {@link WebSocket.MessagePart#LAST LAST} markers.
-     *
-     * If any of the methods above throws an exception, {@code onError} is then
-     * invoked with the same {@code WebSocket} and this exception. Exceptions
-     * thrown from {@code onError} or {@code onClose} are ignored.
-     *
-     * <p> When the method returns, the message is deemed received (in
-     * particular, if contained in a {@code ByteBuffer buffer}, the data is
-     * deemed received completely regardless of the result {@code
-     * buffer.hasRemaining()} upon the method's return. After this further
-     * messages may be received.
-     *
-     * <p> These invocations begin asynchronous processing which might not end
-     * with the invocation. To provide coordination, methods of {@code Listener}
-     * return a {@link CompletionStage CompletionStage}.
-     * The {@code CompletionStage} signals the {@code WebSocket} that the
-     * processing of a message has ended. For convenience, methods may return
-     * {@code null}, which (by convention) means the same as returning an
-     * already completed (normally) {@code CompletionStage}.
-     * If the returned {@code CompletionStage} completes exceptionally, then
-     * {@link #onError(WebSocket, Throwable) onError} will be invoked with the
-     * same {@code WebSocket} and this exception.
-     *
-     * <p> Control of the message passes to the {@code Listener} with the
-     * invocation of the method. Control of the message returns to the {@code
-     * WebSocket} at the earliest of, either returning {@code null} from the
-     * method, or the completion of the {@code CompletionStage} returned from
-     * the method. The {@code WebSocket} does not access the message while it's
-     * not in its control. The {@code Listener} must not access the message
-     * after its control has been returned to the {@code WebSocket}.
-     *
-     * <p> A {@code WebSocket} implementation never invokes {@code Listener}'s
-     * methods with {@code null}s as their arguments.
+     * <p> If a listener's method returns {@code null} rather than a
+     * {@code CompletionStage}, {@code WebSocket} will behave as if the listener
+     * returned a {@code CompletionStage} that is already completed normally.
      *
      * @since 9
      */
     interface Listener {
 
         /**
-         * Notifies the {@code Listener} that it is connected to the provided
-         * {@code WebSocket}.
+         * A {@code WebSocket} has been connected.
          *
-         * <p> The {@code onOpen} method does not correspond to any message from
-         * the WebSocket Protocol. It is a synthetic event and the first {@code
-         * Listener}'s method to be invoked.
-         *
-         * <p> This method is usually used to make an initial {@linkplain
-         * WebSocket#request(long) request} for messages.
-         *
-         * <p> If an exception is thrown from this method then {@link
-         * #onError(WebSocket, Throwable) onError} will be invoked with the same
-         * {@code WebSocket} and this exception.
+         * <p> This is the first invocation and it is made at most once. This
+         * method is typically used to make an initial request for messages.
          *
          * @implSpec The default implementation of this method behaves as if:
          *
@@ -302,24 +241,24 @@ public interface WebSocket {
          * }</pre>
          *
          * @param webSocket
-         *         the WebSocket
+         *         the WebSocket that has been connected
          */
         default void onOpen(WebSocket webSocket) { webSocket.request(1); }
 
         /**
-         * Receives a Text message.
+         * A Text message has been received.
          *
-         * <p> The {@code onText} method is invoked zero or more times between
-         * {@code onOpen} and ({@code onClose} or {@code onError}).
+         * <p> If a whole message has been received, this method will be invoked
+         * with {@code MessagePart.WHOLE} marker. Otherwise, it will be invoked
+         * with {@code FIRST}, possibly a number of times with {@code PART} and,
+         * finally, with {@code LAST} markers. If this message is partial, it
+         * may be an incomplete UTF-16 sequence. However, the concatenation of
+         * all messages through the last will be a complete UTF-16 sequence.
          *
-         * <p> This message may be a partial UTF-16 sequence. However, the
-         * concatenation of all messages through the last will be a whole UTF-16
-         * sequence.
-         *
-         * <p> If an exception is thrown from this method or the returned {@code
-         * CompletionStage} completes exceptionally, then {@link
-         * #onError(WebSocket, Throwable) onError} will be invoked with the same
-         * {@code WebSocket} and this exception.
+         * <p> Return a {@code CompletionStage} which will be used by the
+         * {@code WebSocket} as a signal it may reclaim the
+         * {@code CharSequence}. Do not access the {@code CharSequence} after
+         * this {@ode CompletionStage} has completed.
          *
          * @implSpec The default implementation of this method behaves as if:
          *
@@ -328,18 +267,19 @@ public interface WebSocket {
          *     return null;
          * }</pre>
          *
-         * @implNote This implementation passes only complete UTF-16 sequences
-         * to the {@code onText} method.
+         * @implNote This method is always invoked with character sequences
+         * which are complete UTF-16 sequences.
          *
          * @param webSocket
-         *         the WebSocket
+         *         the WebSocket on which the message has been received
          * @param message
          *         the message
          * @param part
          *         the part
          *
-         * @return a {@code CompletionStage} which completes when the message
-         * processing is done; or {@code null} if already done
+         * @return a {@code CompletionStage} which completes when the
+         * {@code CharSequence} may be reclaimed; or {@code null} if it may be
+         * reclaimed immediately
          */
         default CompletionStage<?> onText(WebSocket webSocket,
                                           CharSequence message,
@@ -349,15 +289,20 @@ public interface WebSocket {
         }
 
         /**
-         * Receives a Binary message.
+         * A Binary message has been received.
          *
-         * <p> The {@code onBinary} method is invoked zero or more times
-         * between {@code onOpen} and ({@code onClose} or {@code onError}).
+         * <p> If a whole message has been received, this method will be invoked
+         * with {@code MessagePart.WHOLE} marker. Otherwise, it will be invoked
+         * with {@code FIRST}, possibly a number of times with {@code PART} and,
+         * finally, with {@code LAST} markers.
          *
-         * <p> If an exception is thrown from this method or the returned {@code
-         * CompletionStage} completes exceptionally, then {@link
-         * #onError(WebSocket, Throwable) onError} will be invoked with the same
-         * {@code WebSocket} and this exception.
+         * <p> This message consists of bytes from the buffer's position to
+         * its limit.
+         *
+         * <p> Return a {@code CompletionStage} which will be used by the
+         * {@code WebSocket} as a signal it may reclaim the
+         * {@code ByteBuffer}. Do not access the {@code ByteBuffer} after
+         * this {@ode CompletionStage} has completed.
          *
          * @implSpec The default implementation of this method behaves as if:
          *
@@ -367,14 +312,15 @@ public interface WebSocket {
          * }</pre>
          *
          * @param webSocket
-         *         the WebSocket
+         *         the WebSocket on which the message has been received
          * @param message
          *         the message
          * @param part
          *         the part
          *
-         * @return a {@code CompletionStage} which completes when the message
-         * processing is done; or {@code null} if already done
+         * @return a {@code CompletionStage} which completes when the
+         * {@code ByteBuffer} may be reclaimed; or {@code null} if it may be
+         * reclaimed immediately
          */
         default CompletionStage<?> onBinary(WebSocket webSocket,
                                             ByteBuffer message,
@@ -384,34 +330,15 @@ public interface WebSocket {
         }
 
         /**
-         * Receives a Ping message.
+         * A Ping message has been received.
          *
-         * <p> A Ping message may be sent or received by either client or
-         * server. It may serve either as a keepalive or as a means to verify
-         * that the remote endpoint is still responsive.
+         * <p> The message consists of not more than {@code 125} bytes from
+         * the buffer's position to its limit.
          *
-         * <p> The {@code WebSocket} handles Ping messages by replying with
-         * appropriate Pong messages using a strategy of its choice, but within
-         * the boundaries of the WebSocket Protocol. The {@code WebSocket} may
-         * invoke {@code onPing} after handling a Ping message, before doing so
-         * or in parallel with it. In other words no particular ordering is
-         * guaranteed. If an error occurs while implementation handles this Ping
-         * message, then {@code onError} will be invoked with this error. For
-         * more details on handling Ping messages see RFC 6455 sections
-         * <a href="https://tools.ietf.org/html/rfc6455#section-5.5.2">5.5.2. Ping</a>
-         * and
-         * <a href="https://tools.ietf.org/html/rfc6455#section-5.5.3">5.5.3. Pong</a>.
-         *
-         * <p> The message will consist of not more than {@code 125} bytes:
-         * {@code message.remaining() <= 125}.
-         *
-         * <p> The {@code onPing} is invoked zero or more times in between
-         * {@code onOpen} and ({@code onClose} or {@code onError}).
-         *
-         * <p> If an exception is thrown from this method or the returned {@code
-         * CompletionStage} completes exceptionally, then {@link
-         * #onError(WebSocket, Throwable) onError} will be invoked with this
-         * exception.
+         * <p> Return a {@code CompletionStage} which will be used by the
+         * {@code WebSocket} as a signal it may reclaim the
+         * {@code ByteBuffer}. Do not access the {@code ByteBuffer} after
+         * this {@ode CompletionStage} has completed.
          *
          * @implSpec The default implementation of this method behaves as if:
          *
@@ -421,12 +348,13 @@ public interface WebSocket {
          * }</pre>
          *
          * @param webSocket
-         *         the WebSocket
+         *         the WebSocket on which the message has been received
          * @param message
          *         the message
          *
-         * @return a {@code CompletionStage} which completes when the message
-         * processing is done; or {@code null} if already done
+         * @return a {@code CompletionStage} which completes when the
+         * {@code ByteBuffer} may be reclaimed; or {@code null} if it may be
+         * reclaimed immediately
          */
         default CompletionStage<?> onPing(WebSocket webSocket,
                                           ByteBuffer message) {
@@ -435,22 +363,15 @@ public interface WebSocket {
         }
 
         /**
-         * Receives a Pong message.
+         * A Pong message has been received.
          *
-         * <p> A Pong message may be unsolicited or may be received in response
-         * to a previously sent Ping. In the latter case, the contents of the
-         * Pong is identical to the originating Ping.
+         * <p> The message consists of not more than {@code 125} bytes from
+         * the buffer's position to its limit.
          *
-         * <p> The message will consist of not more than {@code 125} bytes:
-         * {@code message.remaining() <= 125}.
-         *
-         * <p> The {@code onPong} method is invoked zero or more times in
-         * between {@code onOpen} and ({@code onClose} or {@code onError}).
-         *
-         * <p> If an exception is thrown from this method or the returned {@code
-         * CompletionStage} completes exceptionally, then {@link
-         * #onError(WebSocket, Throwable) onError} will be invoked with this
-         * exception.
+         * <p> Return a {@code CompletionStage} which will be used by the
+         * {@code WebSocket} as a signal it may reclaim the
+         * {@code ByteBuffer}. Do not access the {@code ByteBuffer} after
+         * this {@ode CompletionStage} has completed.
          *
          * @implSpec The default implementation of this method behaves as if:
          *
@@ -460,12 +381,13 @@ public interface WebSocket {
          * }</pre>
          *
          * @param webSocket
-         *         the WebSocket
+         *         the WebSocket on which the message has been received
          * @param message
          *         the message
          *
-         * @return a {@code CompletionStage} which completes when the message
-         * processing is done; or {@code null} if already done
+         * @return a {@code CompletionStage} which completes when the
+         * {@code ByteBuffer} may be reclaimed; or {@code null} if it may be
+         * reclaimed immediately
          */
         default CompletionStage<?> onPong(WebSocket webSocket,
                                           ByteBuffer message) {
@@ -474,52 +396,39 @@ public interface WebSocket {
         }
 
         /**
-         * Receives a Close message.
+         * A Close message has been received.
+         *
+         * <p> This is the last invocation from the {@code WebSocket}. By the
+         * time this invocation begins the {@code WebSocket}'s input will have
+         * been closed. Be prepared to receive this invocation at any time after
+         * {@code onOpen} regardless of whether or not any messages have been
+         * requested from the {@code WebSocket}.
          *
          * <p> A Close message consists of a status code and a reason for
-         * closing. The status code is an integer in the range {@code 1000 <=
-         * code <= 65535}. The {@code reason} is a short string that has an
-         * UTF-8 representation not longer than {@code 123} bytes. For more
-         * details on Close message, status codes and reason see RFC 6455 sections
-         * <a href="https://tools.ietf.org/html/rfc6455#section-5.5.1">5.5.1. Close</a>
-         * and
-         * <a href="https://tools.ietf.org/html/rfc6455#section-7.4">7.4. Status Codes</a>.
+         * closing. The status code is an integer from the range
+         * {@code 1000 <= code <= 65535}. The {@code reason} is a string which
+         * has an UTF-8 representation not longer than {@code 123} bytes.
          *
-         * <p> After the returned {@code CompletionStage} has completed
-         * (normally or exceptionally), the {@code WebSocket} completes the
-         * closing handshake by replying with an appropriate Close message.
+         * <p> Return a {@code CompletionStage} that will be used by the
+         * {@code WebSocket} as a signal that it may close the output. The
+         * {@code WebSocket} will close the output at the earliest of completion
+         * of the returned {@code CompletionStage} or invoking a
+         * {@link WebSocket#sendClose(int, String) sendClose} method.
          *
-         * <p> This implementation replies with a Close message that has the
-         * same code this message has and an empty reason.
-         *
-         * <p> {@code onClose} is the last invocation on the {@code Listener}.
-         * It is invoked at most once, but after {@code onOpen}. If an exception
-         * is thrown from this method, it is ignored.
-         *
-         * <p> The {@code WebSocket} will close at the earliest of completion of
-         * the returned {@code CompletionStage} or sending a Close message. In
-         * particular, if a Close message has been {@linkplain WebSocket#sendClose
-         * sent} before, then this invocation completes the closing handshake
-         * and by the time this method is invoked, the {@code WebSocket} will
-         * have been closed.
-         *
-         * @implSpec The default implementation of this method behaves as if:
-         *
-         * <pre>{@code
-         *     return null;
-         * }</pre>
+         * <p> If an exception is thrown from this method or a
+         * {@code CompletionStage} returned from it completes exceptionally,
+         * the resulting behaviour is undefined.
          *
          * @param webSocket
-         *         the WebSocket
+         *         the WebSocket on which the message has been received
          * @param statusCode
          *         the status code
          * @param reason
          *         the reason
          *
-         * @return a {@code CompletionStage} which completes when the {@code
-         * WebSocket} can be closed; or {@code null} if it can be closed immediately
-         *
-         * @see #NORMAL_CLOSURE
+         * @return a {@code CompletionStage} which completes when the
+         * {@code WebSocket} may be closed; or {@code null} if it may be
+         * closed immediately
          */
         default CompletionStage<?> onClose(WebSocket webSocket,
                                            int statusCode,
@@ -528,31 +437,19 @@ public interface WebSocket {
         }
 
         /**
-         * Notifies an I/O or protocol error has occurred.
+         * An unrecoverable error has occurred.
          *
-         * <p> The {@code onError} method does not correspond to any message
-         * from the WebSocket Protocol. It is a synthetic event and the last
-         * {@code Listener}'s method to be invoked. It is invoked at most once
-         * but after {@code onOpen}. If an exception is thrown from this method,
-         * it is ignored.
+         * <p> This is the last invocation from the {@code WebSocket}. By the
+         * time this invocation begins both {@code WebSocket}'s input and output
+         * will have been closed. Be prepared to receive this invocation at any
+         * time after {@code onOpen} regardless of whether or not any messages
+         * have been requested from the {@code WebSocket}.
          *
-         * <p> Note that the WebSocket Protocol requires <i>some</i> errors
-         * occur in the incoming destination must be fatal to the connection. In
-         * such cases the implementation takes care of <i>Failing the WebSocket
-         * Connection</i>: by the time {@code onError} is invoked, the {@code
-         * WebSocket} will have been closed. Any outstanding or subsequent send
-         * operation will complete exceptionally with an {@code IOException}.
-         * For more details on Failing the WebSocket Connection see RFC 6455
-         * section <a href="https://tools.ietf.org/html/rfc6455#section-7.1.7">7.1.7. Fail the WebSocket Connection</a>.
-         *
-         * @apiNote Errors associated with sending messages are reported to the
-         * {@code CompletableFuture}s {@code sendX} methods return, rather than
-         * to this this method.
-         *
-         * @implSpec The default implementation of this method does nothing.
+         * <p> If an exception is thrown from this method, resulting behavior is
+         * undefined.
          *
          * @param webSocket
-         *         the WebSocket
+         *         the WebSocket on which the error has occurred
          * @param error
          *         the error
          */
@@ -560,29 +457,26 @@ public interface WebSocket {
     }
 
     /**
-     * A marker used by {@link WebSocket.Listener} in cases where a partial
-     * message may be received.
+     * A marker used by {@link WebSocket.Listener} for identifying partial
+     * messages.
      * {@Incubating}
      *
-     * @see Listener#onText(WebSocket, CharSequence, MessagePart)
-     * @see Listener#onBinary(WebSocket, ByteBuffer, MessagePart)
-     *
      * @since 9
      */
     enum MessagePart {
 
         /**
-         * The first part of a message in a sequence.
+         * The first part of a message.
          */
         FIRST,
 
         /**
-         * A middle part of a message in a sequence.
+         * A middle part of a message.
          */
         PART,
 
         /**
-         * The last part of a message in a sequence.
+         * The last part of a message.
          */
         LAST,
 
@@ -595,31 +489,28 @@ public interface WebSocket {
     /**
      * Sends a Text message with characters from the given {@code CharSequence}.
      *
-     * <p> Returns a {@code CompletableFuture<WebSocket>} which completes
-     * normally when the message has been sent or completes exceptionally if an
-     * error occurs.
+     * <p> To send a Text message invoke this method only after the previous
+     * Text or Binary message has been sent. The character sequence must not be
+     * modified until the {@code CompletableFuture} returned from this method
+     * has completed.
      *
-     * <p> The {@code CharSequence} must not be modified until the returned
-     * {@code CompletableFuture} completes (either normally or exceptionally).
-     *
-     * <p> The returned {@code CompletableFuture} can complete exceptionally
-     * with:
+     * <p> A {@code CompletableFuture} returned from this method can
+     * complete exceptionally with:
      * <ul>
      * <li> {@link IllegalArgumentException} -
      *          if {@code message} is a malformed UTF-16 sequence
      * <li> {@link IllegalStateException} -
-     *          if the {@code WebSocket} is closed;
-     *          or if a Close message has been sent;
-     *          or if there is an outstanding send operation;
-     *          or if a previous Binary message was sent with {@code isLast == false}
+     *          if {@code sendClose} has been invoked
+     *          or if the previous message has not been sent yet
+     *          or if a previous Binary message was sent with
+     *          {@code isLast == false}
      * <li> {@link IOException} -
-     *          if an I/O error occurs during this operation;
-     *          or if the {@code WebSocket} has been closed due to an error;
+     *          if an I/O error occurs
      * </ul>
      *
-     * @implNote This implementation does not accept partial UTF-16 sequences.
-     * In case such a sequence is passed, a returned {@code CompletableFuture}
-     * completes exceptionally with {@code IOException}.
+     * @implNote If a partial UTF-16 sequence is passed to this method, a
+     * {@code CompletableFuture} returned will complete exceptionally with
+     * {@code IOException}.
      *
      * @param message
      *         the message
@@ -627,28 +518,30 @@ public interface WebSocket {
      *         {@code true} if this is the last part of the message,
      *         {@code false} otherwise
      *
-     * @return a {@code CompletableFuture} with this {@code WebSocket}
+     * @return a {@code CompletableFuture} that completes, with this
+     * {@code WebSocket}, when the message has been sent
      */
     CompletableFuture<WebSocket> sendText(CharSequence message, boolean isLast);
 
     /**
      * Sends a Binary message with bytes from the given {@code ByteBuffer}.
      *
-     * <p> Returns a {@code CompletableFuture<WebSocket>} which completes
-     * normally when the message has been sent or completes exceptionally if an
-     * error occurs.
+     * <p> To send a Binary message invoke this method only after the previous
+     * Text or Binary message has been sent. The message consists of bytes from
+     * the buffer's position to its limit. Upon normal completion of a
+     * {@code CompletableFuture} returned from this method the buffer will have
+     * no remaining bytes. The buffer must not be accessed until after that.
      *
-     * <p> The returned {@code CompletableFuture} can complete exceptionally
-     * with:
+     * <p> The {@code CompletableFuture} returned from this method can
+     * complete exceptionally with:
      * <ul>
      * <li> {@link IllegalStateException} -
-     *          if the {@code WebSocket} is closed;
-     *          or if a Close message has been sent;
-     *          or if there is an outstanding send operation;
-     *          or if a previous Text message was sent with {@code isLast == false}
+     *          if {@code sendClose} has been invoked
+     *          or if the previous message has not been sent yet
+     *          or if a previous Text message was sent with
+     *              {@code isLast == false}
      * <li> {@link IOException} -
-     *          if an I/O error occurs during this operation;
-     *          or if the {@code WebSocket} has been closed due to an error
+     *          if an I/O error occurs
      * </ul>
      *
      * @param message
@@ -657,200 +550,169 @@ public interface WebSocket {
      *         {@code true} if this is the last part of the message,
      *         {@code false} otherwise
      *
-     * @return a {@code CompletableFuture} with this {@code WebSocket}
+     * @return a {@code CompletableFuture} that completes, with this
+     * {@code WebSocket}, when the message has been sent
      */
     CompletableFuture<WebSocket> sendBinary(ByteBuffer message, boolean isLast);
 
     /**
-     * Sends a Ping message with bytes from the given ByteBuffer.
+     * Sends a Ping message with bytes from the given {@code ByteBuffer}.
      *
-     * <p> Returns a {@code CompletableFuture<WebSocket>} which completes
-     * normally when the message has been sent or completes exceptionally if an
-     * error occurs.
+     * <p> The message consists of not more than {@code 125} bytes from the
+     * buffer's position to its limit. Upon normal completion of a
+     * {@code CompletableFuture} returned from this method the buffer will
+     * have no remaining bytes. The buffer must not be accessed until after that.
      *
-     * <p> A Ping message may be sent or received by either client or server.
-     * It may serve either as a keepalive or as a means to verify that the
-     * remote endpoint is still responsive.
-     *
-     * <p> The message must consist of not more than {@code 125} bytes: {@code
-     * message.remaining() <= 125}.
-     *
-     * <p> The returned {@code CompletableFuture} can complete exceptionally
-     * with:
+     * <p> The {@code CompletableFuture} returned from this method can
+     * complete exceptionally with:
      * <ul>
      * <li> {@link IllegalArgumentException} -
-     *          if {@code message.remaining() > 125}
+     *          if the message is too long
      * <li> {@link IllegalStateException} -
-     *          if the {@code WebSocket} is closed;
-     *          or if a Close message has been sent;
-     *          or if there is an outstanding send operation
+     *          if {@code sendClose} has been invoked
      * <li> {@link IOException} -
-     *          if an I/O error occurs during this operation;
-     *          or if the {@code WebSocket} has been closed due to an error
+     *          if an I/O error occurs
      * </ul>
      *
      * @param message
      *         the message
      *
-     * @return a {@code CompletableFuture} with this {@code WebSocket}
+     * @return a {@code CompletableFuture} that completes, with this
+     * {@code WebSocket}, when the Ping message has been sent
      */
     CompletableFuture<WebSocket> sendPing(ByteBuffer message);
 
     /**
-     * Sends a Pong message with bytes from the given ByteBuffer.
+     * Sends a Pong message with bytes from the given {@code ByteBuffer}.
      *
-     * <p> Returns a {@code CompletableFuture<WebSocket>} which completes
-     * normally when the message has been sent or completes exceptionally if an
-     * error occurs.
+     * <p> The message consists of not more than {@code 125} bytes from the
+     * buffer's position to its limit. Upon normal completion of a
+     * {@code CompletableFuture} returned from this method the buffer will have
+     * no remaining bytes. The buffer must not be accessed until after that.
      *
-     * <p> A Pong message may be unsolicited or may be sent in response to a
-     * previously received Ping. In latter case the contents of the Pong must be
-     * identical to the originating Ping.
-     *
-     * <p> The message must consist of not more than {@code 125} bytes: {@code
-     * message.remaining() <= 125}.
-     *
-     * <p> The returned {@code CompletableFuture} can complete exceptionally
-     * with:
+     * <p> The {@code CompletableFuture} returned from this method can
+     * complete exceptionally with:
      * <ul>
      * <li> {@link IllegalArgumentException} -
-     *          if {@code message.remaining() > 125}
+     *          if the message is too long
      * <li> {@link IllegalStateException} -
-     *          if the {@code WebSocket} is closed;
-     *          or if a Close message has been sent;
-     *          or if there is an outstanding send operation
+     *          if {@code sendClose} has been invoked
      * <li> {@link IOException} -
-     *          if an I/O error occurs during this operation;
-     *          or if the {@code WebSocket} has been closed due to an error
+     *          if an I/O error occurs
      * </ul>
      *
      * @param message
      *         the message
      *
-     * @return a {@code CompletableFuture} with this {@code WebSocket}
+     * @return a {@code CompletableFuture} that completes, with this
+     * {@code WebSocket}, when the Pong message has been sent
      */
     CompletableFuture<WebSocket> sendPong(ByteBuffer message);
 
     /**
-     * Sends a Close message with the given status code and the reason.
+     * Sends a Close message with the given status code and the reason,
+     * initiating an orderly closure.
      *
-     * <p> When this method has been invoked, no further messages can be sent.
+     * <p> When this method returns the output will have been closed.
      *
-     * <p> The {@code statusCode} is an integer in the range {@code 1000 <= code
-     * <= 4999}. However, not all status codes may be legal in some
-     * implementations. Regardless of an implementation,
-     * <code>{@value jdk.incubator.http.WebSocket#NORMAL_CLOSURE}</code>
-     * is always legal and {@code 1002}, {@code 1003}, {@code 1005}, {@code
-     * 1006}, {@code 1007}, {@code 1009}, {@code 1010}, {@code 1012}, {@code
-     * 1013} and {@code 1015} are always illegal codes.
+     * <p> The {@code statusCode} is an integer from the range
+     * {@code 1000 <= code <= 4999}. Status codes {@code 1002}, {@code 1003},
+     * {@code 1006}, {@code 1007}, {@code 1009}, {@code 1010}, {@code 1012},
+     * {@code 1013} and {@code 1015} are illegal. Behaviour in respect to other
+     * status codes is implementation-specific. The {@code reason} is a string
+     * that has an UTF-8 representation not longer than {@code 123} bytes.
      *
-     * <p> The {@code reason} is a short string that must have an UTF-8
-     * representation not longer than {@code 123} bytes. For more details on
-     * Close message, status codes and reason see RFC 6455 sections
-     * <a href="https://tools.ietf.org/html/rfc6455#section-5.5.1">5.5.1. Close</a>
-     * and
-     * <a href="https://tools.ietf.org/html/rfc6455#section-7.4">7.4. Status Codes</a>.
+     * <p> Use the provided integer constant {@link #NORMAL_CLOSURE} as a status
+     * code and an empty string as a reason in a typical case.
      *
-     * <p> The method returns a {@code CompletableFuture<WebSocket>} which
-     * completes normally when the message has been sent or completes
-     * exceptionally if an error occurs.
-     *
-     * <p> The returned {@code CompletableFuture} can complete exceptionally
-     * with:
+     * <p> A {@code CompletableFuture} returned from this method can
+     * complete exceptionally with:
      * <ul>
      * <li> {@link IllegalArgumentException} -
-     *          if the {@code statusCode} has an illegal value;
-     *          or if {@code reason} doesn't have an UTF-8 representation of
-     *          length {@code <= 123}
+     *          if {@code statusCode} or {@code reason} are illegal
      * <li> {@link IOException} -
-     *          if an I/O error occurs during this operation;
-     *          or the {@code WebSocket} has been closed due to an error
+     *          if an I/O error occurs
      * </ul>
      *
-     * <p> If this method has already been invoked or the {@code WebSocket} is
-     * closed, then subsequent invocations of this method have no effect and the
-     * returned {@code CompletableFuture} completes normally.
-     *
-     * <p> If a Close message has been {@linkplain Listener#onClose(WebSocket,
-     * int, String) received} before, then this invocation completes the closing
-     * handshake and by the time the returned {@code CompletableFuture}
-     * completes, the {@code WebSocket} will have been closed.
+     * @implSpec An endpoint sending a Close message might not receive a
+     * complementing Close message in a timely manner for a variety of reasons.
+     * The {@code WebSocket} implementation is responsible for providing a
+     * closure mechanism that guarantees that once {@code sendClose} method has
+     * been invoked the {@code WebSocket} will close regardless of whether or
+     * not a Close frame has been received and without further intervention from
+     * the user of this API. Method {@code sendClose} is designed to be,
+     * possibly, the last call from the user of this API.
      *
      * @param statusCode
      *         the status code
      * @param reason
      *         the reason
      *
-     * @return a {@code CompletableFuture} with this {@code WebSocket}
+     * @return a {@code CompletableFuture} that completes, with this
+     * {@code WebSocket}, when the Close message has been sent
      */
     CompletableFuture<WebSocket> sendClose(int statusCode, String reason);
 
     /**
-     * Allows {@code n} more messages to be received by the {@link Listener
-     * Listener}.
+     * Requests {@code n} more messages from this {@code WebSocket}.
      *
-     * <p> The actual number of received messages might be fewer if a Close
-     * message is received, the {@code WebSocket} closes or an error occurs.
+     * <p> This {@code WebSocket} will invoke its listener's {@code onText},
+     * {@code onBinary}, {@code onPing}, {@code onPong} or {@code onClose}
+     * methods up to {@code n} more times.
      *
-     * <p> A {@code WebSocket} that has just been built, hasn't requested
-     * anything yet. Usually the initial request for messages is made in {@link
-     * Listener#onOpen(jdk.incubator.http.WebSocket) Listener.onOpen}.
-     *
-     * <p> If the {@code WebSocket} is closed then invoking this method has no
-     * effect.
-     *
-     * @implNote This implementation does not distinguish between partial and
-     * whole messages, because it's not known beforehand how a message will be
-     * received.
-     *
-     * <p> If a server sends more messages than requested, this implementation
-     * queues up these messages on the TCP connection and may eventually force
-     * the sender to stop sending through TCP flow control.
+     * <p> This method may be invoked at any time.
      *
      * @param n
-     *         the number of messages
+     *         the number of messages requested
      *
      * @throws IllegalArgumentException
-     *         if {@code n < 0}
+     *         if {@code n <= 0}
      */
     void request(long n);
 
     /**
-     * Returns a {@linkplain Builder#subprotocols(String, String...) subprotocol}
-     * which has been chosen for this {@code WebSocket}.
+     * Returns the subprotocol for this {@code WebSocket}.
      *
-     * @return a subprotocol, or an empty {@code String} if there is none
+     * <p> This method may be invoked at any time.
+     *
+     * @return the subprotocol for this {@code WebSocket}, or an empty
+     * {@code String} if there's no subprotocol
      */
     String getSubprotocol();
 
     /**
-     * Tells whether the {@code WebSocket} is closed.
+     * Tells whether or not this {@code WebSocket} is permanently closed
+     * for sending messages.
      *
-     * <p> When a {@code WebSocket} is closed no further messages can be sent or
-     * received.
+     * <p> If this method returns {@code true}, subsequent invocations will also
+     * return {@code true}. This method may be invoked at any time.
      *
-     * @return {@code true} if the {@code WebSocket} is closed,
-     *         {@code false} otherwise
+     * @return {@code true} if closed, {@code false} otherwise
      */
-    boolean isClosed();
+    boolean isOutputClosed();
 
     /**
-     * Closes the {@code WebSocket} abruptly.
+     * Tells whether or not this {@code WebSocket} is permanently closed
+     * for receiving messages.
      *
-     * <p> This method may be invoked at any time. This method closes the
-     * underlying TCP connection and puts the {@code WebSocket} into a closed
-     * state.
+     * <p> If this method returns {@code true}, subsequent invocations will also
+     * return {@code true}. This method may be invoked at any time.
      *
-     * <p> As the result {@link Listener#onClose(WebSocket, int, String)
-     * Listener.onClose} will be invoked unless either {@code onClose} or {@link
-     * Listener#onError(WebSocket, Throwable) onError} has been invoked before.
-     * In which case no additional invocation will happen.
-     *
-     * <p> If the {@code WebSocket} is already closed then invoking this method
-     * has no effect.
-     *
-     * @throws IOException
-     *         if an I/O error occurs
+     * @return {@code true} if closed, {@code false} otherwise
      */
-    void abort() throws IOException;
+    boolean isInputClosed();
+
+    /**
+     * Closes this {@code WebSocket} abruptly.
+     *
+     * <p> When this method returns both the input and output will have been
+     * closed. This method may be invoked at any time. Subsequent invocations
+     * have no effect.
+     *
+     * @apiNote Depending on its implementation, the state (for example, whether
+     * or not a message is being transferred at the moment) and possible errors
+     * while releasing associated resources, this {@code WebSocket} may invoke
+     * its listener's {@code onError}.
+     */
+    void abort();
 }
diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/WebSocketHandshakeException.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/WebSocketHandshakeException.java
index 8c354083302..988778f4df6 100644
--- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/WebSocketHandshakeException.java
+++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/WebSocketHandshakeException.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2017, 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
diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/WindowController.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/WindowController.java
index 76ac343befa..765f9386a46 100644
--- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/WindowController.java
+++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/WindowController.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2017, 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
@@ -25,14 +25,19 @@
 
 package jdk.incubator.http;
 
+import java.lang.System.Logger.Level;
+import java.util.ArrayList;
 import java.util.Map;
 import java.util.HashMap;
-import java.util.concurrent.locks.Condition;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.List;
 import java.util.concurrent.locks.ReentrantLock;
+import jdk.incubator.http.internal.common.Utils;
 
 /**
- * A Simple blocking Send Window Flow-Controller that is used to control
- * outgoing Connection and Stream flows, per HTTP/2 connection.
+ * A Send Window Flow-Controller that is used to control outgoing Connection
+ * and Stream flows, per HTTP/2 connection.
  *
  * A Http2Connection has its own unique single instance of a WindowController
  * that it shares with its Streams. Each stream must acquire the appropriate
@@ -44,6 +49,10 @@ import java.util.concurrent.locks.ReentrantLock;
  */
 final class WindowController {
 
+    static final boolean DEBUG = Utils.DEBUG; // Revisit: temporary developer's flag
+    static final System.Logger DEBUG_LOGGER =
+            Utils.getDebugLogger("WindowController"::toString, DEBUG);
+
     /**
      * Default initial connection Flow-Control Send Window size, as per HTTP/2.
      */
@@ -54,20 +63,23 @@ final class WindowController {
     /** A Map of the active streams, where the key is the stream id, and the
      *  value is the stream's Send Window size, which may be negative. */
     private final Map<Integer,Integer> streams = new HashMap<>();
+    /** A Map of streams awaiting Send Window. The key is the stream id. The
+     * value is a pair of the Stream ( representing the key's stream id ) and
+     * the requested amount of send Window. */
+    private final Map<Integer, Map.Entry<Stream<?>, Integer>> pending
+            = new LinkedHashMap<>();
 
     private final ReentrantLock controllerLock = new ReentrantLock();
 
-    private final Condition notExhausted = controllerLock.newCondition();
-
     /** A Controller with the default initial window size. */
     WindowController() {
         connectionWindowSize = DEFAULT_INITIAL_WINDOW_SIZE;
     }
 
-    /** A Controller with the given initial window size. */
-    WindowController(int initialConnectionWindowSize) {
-        connectionWindowSize = initialConnectionWindowSize;
-    }
+//    /** A Controller with the given initial window size. */
+//    WindowController(int initialConnectionWindowSize) {
+//        connectionWindowSize = initialConnectionWindowSize;
+//    }
 
     /** Registers the given stream with this controller. */
     void registerStream(int streamid, int initialStreamWindowSize) {
@@ -75,7 +87,8 @@ final class WindowController {
         try {
             Integer old = streams.put(streamid, initialStreamWindowSize);
             if (old != null)
-                throw new InternalError("Unexpected entry [" + old + "] for streamid: " + streamid);
+                throw new InternalError("Unexpected entry ["
+                        + old + "] for streamid: " + streamid);
         } finally {
             controllerLock.unlock();
         }
@@ -109,28 +122,45 @@ final class WindowController {
      * 1) the requested amount, 2) the stream's Send Window, and 3) the
      * connection's Send Window.
      *
-     * This method ( currently ) blocks until some positive amount of Send
-     * Window is available.
+     * A negative or zero value is returned if there's no window available.
+     * When the result is negative or zero, this method arranges for the
+     * given stream's {@link Stream#signalWindowUpdate()} method to be invoke at
+     * a later time when the connection and/or stream window's have been
+     * increased. The {@code tryAcquire} method should then be invoked again to
+     * attempt to acquire the available window.
      */
-    int tryAcquire(int requestAmount, int streamid) throws InterruptedException {
+    int tryAcquire(int requestAmount, int streamid, Stream<?> stream) {
         controllerLock.lock();
         try {
-            int x = 0;
-            Integer streamSize = 0;
-            while (x <= 0) {
-                streamSize = streams.get(streamid);
-                if (streamSize == null)
-                    throw new InternalError("Expected entry for streamid: " + streamid);
-                x = Math.min(requestAmount,
+            Integer streamSize = streams.get(streamid);
+            if (streamSize == null)
+                throw new InternalError("Expected entry for streamid: "
+                                        + streamid);
+            int x = Math.min(requestAmount,
                              Math.min(streamSize, connectionWindowSize));
 
-                if (x <= 0)  // stream window size may be negative
-                    notExhausted.await();
+            if (x <= 0)  { // stream window size may be negative
+                DEBUG_LOGGER.log(Level.DEBUG,
+                        "Stream %d requesting %d but only %d available (stream: %d, connection: %d)",
+                      streamid, requestAmount, Math.min(streamSize, connectionWindowSize),
+                      streamSize, connectionWindowSize);
+                // If there's not enough window size available, put the
+                // caller in a pending list.
+                pending.put(streamid, Map.entry(stream, requestAmount));
+                return x;
             }
 
+            // Remove the caller from the pending list ( if was waiting ).
+            pending.remove(streamid);
+
+            // Update window sizes and return the allocated amount to the caller.
             streamSize -= x;
             streams.put(streamid, streamSize);
             connectionWindowSize -= x;
+            DEBUG_LOGGER.log(Level.DEBUG,
+                  "Stream %d amount allocated %d, now %d available (stream: %d, connection: %d)",
+                  streamid, x, Math.min(streamSize, connectionWindowSize),
+                  streamSize, connectionWindowSize);
             return x;
         } finally {
             controllerLock.unlock();
@@ -140,10 +170,15 @@ final class WindowController {
     /**
      * Increases the Send Window size for the connection.
      *
+     * A number of awaiting requesters, from unfulfilled tryAcquire requests,
+     * may have their stream's {@link Stream#signalWindowUpdate()} method
+     * scheduled to run ( i.e. awake awaiters ).
+     *
      * @return false if, and only if, the addition of the given amount would
      *         cause the Send Window to exceed 2^31-1 (overflow), otherwise true
      */
     boolean increaseConnectionWindow(int amount) {
+        List<Stream<?>> candidates = null;
         controllerLock.lock();
         try {
             int size = connectionWindowSize;
@@ -151,20 +186,54 @@ final class WindowController {
             if (size < 0)
                 return false;
             connectionWindowSize = size;
-            notExhausted.signalAll();
+            DEBUG_LOGGER.log(Level.DEBUG, "Connection window size is now %d", size);
+
+            // Notify waiting streams, until the new increased window size is
+            // effectively exhausted.
+            Iterator<Map.Entry<Integer,Map.Entry<Stream<?>,Integer>>> iter =
+                    pending.entrySet().iterator();
+
+            while (iter.hasNext() && size > 0) {
+                Map.Entry<Integer,Map.Entry<Stream<?>,Integer>> item = iter.next();
+                Integer streamSize = streams.get(item.getKey());
+                if (streamSize == null) {
+                    iter.remove();
+                } else {
+                    Map.Entry<Stream<?>,Integer> e = item.getValue();
+                    int requestedAmount = e.getValue();
+                    // only wakes up the pending streams for which there is
+                    // at least 1 byte of space in both windows
+                    int minAmount = 1;
+                    if (size >= minAmount && streamSize >= minAmount) {
+                        size -= Math.min(streamSize, requestedAmount);
+                        iter.remove();
+                        if (candidates == null)
+                            candidates = new ArrayList<>();
+                        candidates.add(e.getKey());
+                    }
+                }
+            }
         } finally {
             controllerLock.unlock();
         }
+        if (candidates != null) {
+            candidates.forEach(Stream::signalWindowUpdate);
+        }
         return true;
     }
 
     /**
      * Increases the Send Window size for the given stream.
      *
+     * If the given stream is awaiting window size, from an unfulfilled
+     * tryAcquire request, it will have its stream's {@link
+     * Stream#signalWindowUpdate()} method scheduled to run ( i.e. awoken ).
+     *
      * @return false if, and only if, the addition of the given amount would
      *         cause the Send Window to exceed 2^31-1 (overflow), otherwise true
      */
     boolean increaseStreamWindow(int amount, int streamid) {
+        Stream<?> s = null;
         controllerLock.lock();
         try {
             Integer size = streams.get(streamid);
@@ -174,10 +243,27 @@ final class WindowController {
             if (size < 0)
                 return false;
             streams.put(streamid, size);
-            notExhausted.signalAll();
+            DEBUG_LOGGER.log(Level.DEBUG,
+                             "Stream %s window size is now %s", streamid, size);
+
+            Map.Entry<Stream<?>,Integer> p = pending.get(streamid);
+            if (p != null) {
+                int minAmount = 1;
+                // only wakes up the pending stream if there is at least
+                // 1 byte of space in both windows
+                if (size >= minAmount
+                        && connectionWindowSize >= minAmount) {
+                     pending.remove(streamid);
+                     s = p.getKey();
+                }
+            }
         } finally {
             controllerLock.unlock();
         }
+
+        if (s != null)
+            s.signalWindowUpdate();
+
         return true;
     }
 
@@ -199,6 +285,8 @@ final class WindowController {
                     Integer size = entry.getValue();
                     size += adjustAmount;
                     streams.put(streamid, size);
+                    DEBUG_LOGGER.log(Level.DEBUG,
+                        "Stream %s window size is now %s", streamid, size);
                 }
             }
         } finally {
@@ -216,16 +304,17 @@ final class WindowController {
         }
     }
 
-    /** Returns the Send Window size for the given stream. */
-    int streamWindowSize(int streamid) {
-        controllerLock.lock();
-        try {
-            Integer size = streams.get(streamid);
-            if (size == null)
-                throw new InternalError("Expected entry for streamid: " + streamid);
-            return size;
-        } finally {
-            controllerLock.unlock();;
-        }
-    }
+//    /** Returns the Send Window size for the given stream. */
+//    int streamWindowSize(int streamid) {
+//        controllerLock.lock();
+//        try {
+//            Integer size = streams.get(streamid);
+//            if (size == null)
+//                throw new InternalError("Expected entry for streamid: " + streamid);
+//            return size;
+//        } finally {
+//            controllerLock.unlock();
+//        }
+//    }
+
 }
diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/WindowUpdateSender.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/WindowUpdateSender.java
index ecc83de8ca0..1e9975827be 100644
--- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/WindowUpdateSender.java
+++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/WindowUpdateSender.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2017, 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
@@ -24,13 +24,18 @@
  */
 package jdk.incubator.http;
 
+import java.lang.System.Logger.Level;
 import jdk.incubator.http.internal.frame.SettingsFrame;
 import jdk.incubator.http.internal.frame.WindowUpdateFrame;
+import jdk.incubator.http.internal.common.Utils;
 
 import java.util.concurrent.atomic.AtomicInteger;
 
 abstract class WindowUpdateSender {
 
+    final static boolean DEBUG = Utils.DEBUG;
+    final System.Logger debug =
+            Utils.getDebugLogger(this::dbgString, DEBUG);
 
     final int limit;
     final Http2Connection connection;
@@ -59,6 +64,7 @@ abstract class WindowUpdateSender {
     abstract int getStreamId();
 
     void update(int delta) {
+        debug.log(Level.DEBUG, "update: %d", delta);
         if (received.addAndGet(delta) > limit) {
             synchronized (this) {
                 int tosend = received.get();
@@ -71,8 +77,12 @@ abstract class WindowUpdateSender {
     }
 
     void sendWindowUpdate(int delta) {
+        debug.log(Level.DEBUG, "sending window update: %d", delta);
         connection.sendUnorderedFrame(new WindowUpdateFrame(getStreamId(), delta));
     }
 
+    String dbgString() {
+        return "WindowUpdateSender(stream: " + getStreamId() + ")";
+    }
 
 }
diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/common/AsyncDataReadQueue.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/common/AsyncDataReadQueue.java
deleted file mode 100644
index 2557c1f9459..00000000000
--- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/common/AsyncDataReadQueue.java
+++ /dev/null
@@ -1,212 +0,0 @@
-/*
- * Copyright (c) 2017, 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.  Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * 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 jdk.incubator.http.internal.common;
-
-import jdk.incubator.http.internal.frame.DataFrame;
-import jdk.incubator.http.internal.frame.Http2Frame;
-
-import java.io.Closeable;
-import java.io.IOException;
-import java.util.concurrent.BlockingQueue;
-import java.util.concurrent.Executor;
-import java.util.concurrent.LinkedBlockingQueue;
-import java.util.concurrent.atomic.AtomicInteger;
-import java.util.function.Consumer;
-
-/**
- * Http2Frame Producer-Consumer queue which either allows to consume all frames in blocking way
- * or allows to consume it asynchronously. In the latter case put operation from the producer thread
- * executes consume operation in the given executor.
- */
-public class AsyncDataReadQueue implements Closeable {
-
-    @FunctionalInterface
-    public interface DataConsumer {
-        /**
-         *
-         * @param t - frame
-         * @return true if consuming should be continued. false when END_STREAM was received.
-         * @throws Throwable
-         */
-        boolean accept(Http2Frame t) throws Throwable;
-    }
-
-    private static final int BLOCKING = 0;
-    private static final int FLUSHING = 1;
-    private static final int REFLUSHING = 2;
-    private static final int ASYNC  = 3;
-    private static final int CLOSED = 4;
-
-
-    private final AtomicInteger state = new AtomicInteger(BLOCKING);
-    private final BlockingQueue<Http2Frame> queue = new LinkedBlockingQueue<>();
-    private Executor executor;
-    private DataConsumer onData;
-    private Consumer<Throwable> onError;
-
-    public AsyncDataReadQueue() {
-    }
-
-    public boolean tryPut(Http2Frame f) {
-        if(state.get() == CLOSED) {
-            return false;
-        } else {
-            queue.offer(f);
-            flushAsync(false);
-            return true;
-        }
-    }
-
-    public void put(Http2Frame f) throws IOException {
-        if(!tryPut(f))
-            throw new IOException("stream closed");
-    }
-
-    public void blockingReceive(DataConsumer onData, Consumer<Throwable> onError) {
-        if (state.get() == CLOSED) {
-            onError.accept(new IOException("stream closed"));
-            return;
-        }
-        assert state.get() == BLOCKING;
-        try {
-            while (onData.accept(queue.take()));
-            assert state.get() == CLOSED;
-        } catch (Throwable e) {
-            onError.accept(e);
-        }
-    }
-
-    public void asyncReceive(Executor executor, DataConsumer onData,
-                             Consumer<Throwable> onError) {
-        if (state.get() == CLOSED) {
-            onError.accept(new IOException("stream closed"));
-            return;
-        }
-
-        assert state.get() == BLOCKING;
-
-        // Validates that fields not already set.
-        if (!checkCanSet("executor", this.executor, onError)
-            || !checkCanSet("onData", this.onData, onError)
-            || !checkCanSet("onError", this.onError, onError)) {
-            return;
-        }
-
-        this.executor = executor;
-        this.onData = onData;
-        this.onError = onError;
-
-        // This will report an error if asyncReceive is called twice,
-        // because we won't be in BLOCKING state if that happens
-        if (!this.state.compareAndSet(BLOCKING, ASYNC)) {
-            onError.accept(new IOException(
-                  new IllegalStateException("State: "+this.state.get())));
-            return;
-        }
-
-        flushAsync(true);
-    }
-
-    private static <T> boolean checkCanSet(String name, T oldval, Consumer<Throwable> onError) {
-        if (oldval != null) {
-            onError.accept(new IOException(
-                     new IllegalArgumentException(name)));
-            return false;
-        }
-        return true;
-    }
-
-    @Override
-    public void close() {
-        int prevState = state.getAndSet(CLOSED);
-        if(prevState == BLOCKING) {
-            // wake up blocked take()
-            queue.offer(new DataFrame(0, DataFrame.END_STREAM, new ByteBufferReference[0]));
-        }
-    }
-
-    private void flushAsync(boolean alreadyInExecutor) {
-        while(true) {
-            switch (state.get()) {
-                case BLOCKING:
-                case CLOSED:
-                case REFLUSHING:
-                    return;
-                case ASYNC:
-                    if(state.compareAndSet(ASYNC, FLUSHING)) {
-                        if(alreadyInExecutor) {
-                            flushLoop();
-                        } else {
-                            executor.execute(this::flushLoop);
-                        }
-                        return;
-                    }
-                    break;
-                case FLUSHING:
-                    if(state.compareAndSet(FLUSHING, REFLUSHING)) {
-                        return;
-                    }
-                    break;
-            }
-        }
-    }
-
-    private void flushLoop() {
-        try {
-            while(true) {
-                Http2Frame frame = queue.poll();
-                while (frame != null) {
-                    if(!onData.accept(frame)) {
-                        assert state.get() == CLOSED;
-                        return; // closed
-                    }
-                    frame = queue.poll();
-                }
-                switch (state.get()) {
-                    case BLOCKING:
-                        assert false;
-                        break;
-                    case ASYNC:
-                        throw new RuntimeException("Shouldn't happen");
-                    case FLUSHING:
-                        if(state.compareAndSet(FLUSHING, ASYNC)) {
-                            return;
-                        }
-                        break;
-                    case REFLUSHING:
-                        // We need to check if new elements were put after last
-                        // poll() and do graceful exit
-                        state.compareAndSet(REFLUSHING, FLUSHING);
-                        break;
-                    case CLOSED:
-                        return;
-                }
-            }
-        } catch (Throwable e) {
-            onError.accept(e);
-            close();
-        }
-    }
-}
diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/common/AsyncWriteQueue.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/common/AsyncWriteQueue.java
deleted file mode 100644
index cfa69bf61d9..00000000000
--- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/common/AsyncWriteQueue.java
+++ /dev/null
@@ -1,235 +0,0 @@
-/*
- * Copyright (c) 2015, 2016, 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.  Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * 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 jdk.incubator.http.internal.common;
-
-
-import java.io.Closeable;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Deque;
-import java.util.List;
-import java.util.concurrent.ConcurrentLinkedDeque;
-import java.util.concurrent.atomic.AtomicInteger;
-
-public class AsyncWriteQueue implements Closeable {
-
-    @FunctionalInterface
-    public static interface AsyncConsumer {
-        /**
-         * Takes an array of buffer reference and attempt to send the data
-         * downstream. If not all the data can be sent, then push back
-         * to the source queue by calling {@code source.setDelayed(buffers)}
-         * and return false. If all the data was successfully sent downstream
-         * then returns true.
-         * @param buffers An array of ButeBufferReference containing data
-         *                to send downstream.
-         * @param source This AsyncWriteQueue.
-         * @return true if all the data could be sent downstream, false otherwise.
-         */
-        boolean trySend(ByteBufferReference[] buffers, AsyncWriteQueue source);
-    }
-
-    private static final int IDLE    = 0;     // nobody is flushing from the queue
-    private static final int FLUSHING = 1;    // there is the only thread flushing from the queue
-    private static final int REFLUSHING = 2;  // while one thread was flushing from the queue
-                                              // the other thread put data into the queue.
-                                              // flushing thread should recheck queue before switching to idle state.
-    private static final int DELAYED = 3;     // flushing is delayed
-                                              // either by PlainHttpConnection.WriteEvent registration, or
-                                              // SSL handshaking
-
-    private static final int CLOSED = 4;      // queue is closed
-
-    private final AtomicInteger state = new AtomicInteger(IDLE);
-    private final Deque<ByteBufferReference[]> queue = new ConcurrentLinkedDeque<>();
-    private final AsyncConsumer consumeAction;
-
-    // Queue may be processed in two modes:
-    // 1. if(!doFullDrain) - invoke callback on each chunk
-    // 2. if(doFullDrain)  - drain the whole queue, merge all chunks into the single array and invoke callback
-    private final boolean doFullDrain;
-
-    private ByteBufferReference[] delayedElement = null;
-
-    public AsyncWriteQueue(AsyncConsumer consumeAction) {
-        this(consumeAction, true);
-    }
-
-    public AsyncWriteQueue(AsyncConsumer consumeAction, boolean doFullDrain) {
-        this.consumeAction = consumeAction;
-        this.doFullDrain = doFullDrain;
-    }
-
-    public void put(ByteBufferReference[] e) throws IOException {
-        ensureOpen();
-        queue.addLast(e);
-    }
-
-    public void putFirst(ByteBufferReference[] e) throws IOException {
-        ensureOpen();
-        queue.addFirst(e);
-    }
-
-    /**
-     * retruns true if flushing was performed
-     * @return
-     * @throws IOException
-     */
-    public boolean flush() throws IOException {
-        while(true) {
-            switch (state.get()) {
-                case IDLE:
-                    if(state.compareAndSet(IDLE, FLUSHING)) {
-                        flushLoop();
-                        return true;
-                    }
-                    break;
-                case FLUSHING:
-                    if(state.compareAndSet(FLUSHING, REFLUSHING)) {
-                        return false;
-                    }
-                    break;
-                case REFLUSHING:
-                case DELAYED:
-                    return false;
-                case CLOSED:
-                    throw new IOException("Queue closed");
-            }
-        }
-    }
-
-    /*
-     *  race invocations of flushDelayed are not allowed.
-     *  flushDelayed should be invoked only from:
-     *   - SelectorManager thread
-     *   - Handshaking thread
-     */
-    public void flushDelayed() throws IOException {
-        ensureOpen();
-        if(!state.compareAndSet(DELAYED, FLUSHING)) {
-            ensureOpen(); // if CAS failed when close was set - throw proper exception
-            throw new RuntimeException("Shouldn't happen");
-        }
-        flushLoop();
-    }
-
-    private ByteBufferReference[] drain(ByteBufferReference[] prev) {
-        assert prev != null;
-        if(doFullDrain) {
-            ByteBufferReference[] next = queue.poll();
-            if(next == null) {
-                return prev;
-            }
-            List<ByteBufferReference> drained = new ArrayList<>();
-            drained.addAll(Arrays.asList(prev));
-            drained.addAll(Arrays.asList(next));
-            while ((next = queue.poll()) != null) {
-                drained.addAll(Arrays.asList(next));
-            }
-            return drained.toArray(new ByteBufferReference[0]);
-        } else {
-            return prev;
-        }
-    }
-
-    private ByteBufferReference[] drain() {
-        ByteBufferReference[] next = queue.poll();
-        return next == null ? null : drain(next);
-    }
-
-    private void flushLoop() throws IOException {
-        ByteBufferReference[] element;
-        if (delayedElement != null) {
-            element = drain(delayedElement);
-            delayedElement = null;
-        } else {
-            element = drain();
-        }
-        while(true) {
-            while (element != null) {
-                if (!consumeAction.trySend(element, this)) {
-                    return;
-                }
-                element = drain();
-            }
-            switch (state.get()) {
-                case IDLE:
-                case DELAYED:
-                    throw new RuntimeException("Shouldn't happen");
-                case FLUSHING:
-                    if(state.compareAndSet(FLUSHING, IDLE)) {
-                        return;
-                    }
-                    break;
-                case REFLUSHING:
-                    // We need to check if new elements were put after last poll() and do graceful exit
-                    state.compareAndSet(REFLUSHING, FLUSHING);
-                    break;
-                case CLOSED:
-                    throw new IOException("Queue closed");
-            }
-            element = drain();
-        }
-    }
-
-    /*
-     * The methods returns unprocessed chunk of buffers into beginning of the queue.
-     * Invocation of the method allowed only inside consume callback,
-     * and consume callback is invoked only when the queue in FLUSHING or REFLUSHING state.
-     */
-    public void setDelayed(ByteBufferReference[] delayedElement) throws IOException {
-        while(true) {
-            int state = this.state.get();
-            switch (state) {
-                case IDLE:
-                case DELAYED:
-                    throw new RuntimeException("Shouldn't happen");
-                case FLUSHING:
-                case REFLUSHING:
-                    if(this.state.compareAndSet(state, DELAYED)) {
-                        this.delayedElement = delayedElement;
-                        return;
-                    }
-                    break;
-                case CLOSED:
-                    throw new IOException("Queue closed");
-            }
-        }
-
-    }
-
-    private void ensureOpen() throws IOException {
-        if (state.get() == CLOSED) {
-            throw new IOException("Queue closed");
-        }
-    }
-
-    @Override
-    public void close() throws IOException {
-        state.getAndSet(CLOSED);
-    }
-
-}
diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/common/ByteBufferPool.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/common/ByteBufferPool.java
index f91e07b0cf2..a8f9e5c4b8a 100644
--- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/common/ByteBufferPool.java
+++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/common/ByteBufferPool.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2017, 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
diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/common/ByteBufferReference.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/common/ByteBufferReference.java
index 0d218b15a5c..365dc6d4f9c 100644
--- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/common/ByteBufferReference.java
+++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/common/ByteBufferReference.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2017, 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
@@ -25,10 +25,8 @@
 package jdk.incubator.http.internal.common;
 
 import java.nio.ByteBuffer;
-import java.util.List;
 import java.util.Objects;
 import java.util.function.Supplier;
-import java.util.stream.Collectors;
 
 public class ByteBufferReference  implements Supplier<ByteBuffer> {
 
diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/AbstractPushPublisher.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/common/ConnectionExpiredException.java
similarity index 58%
rename from src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/AbstractPushPublisher.java
rename to src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/common/ConnectionExpiredException.java
index 310261601ea..23f3fb2fab8 100644
--- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/AbstractPushPublisher.java
+++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/common/ConnectionExpiredException.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2017, 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
@@ -23,35 +23,25 @@
  * questions.
  */
 
-package jdk.incubator.http;
+package jdk.incubator.http.internal.common;
 
-import java.util.Optional;
-import java.util.concurrent.CompletionException;
-import java.util.concurrent.Flow;
-import java.util.function.Consumer;
+import java.io.IOException;
 
 /**
- * A super class for PushPublisher implementation.
+ * Signals that an end of file or end of stream has been reached
+ * unexpectedly before any protocol specific data has been received.
  */
-abstract class AbstractPushPublisher<T> implements Flow.Publisher<T> {
+public final class ConnectionExpiredException extends IOException {
+    private static final long serialVersionUID = 0;
 
-    static enum SubscriptionState { OPENED, DONE, CANCELLED };
-
-    public abstract void acceptData(Optional<T> item)
-        throws InterruptedException;
-
-    public abstract void acceptError(Throwable t);
-
-    public Consumer<Optional<T>> asDataConsumer() {
-        return this::consume;
+    /**
+     * Constructs a {@code ConnectionExpiredException} with the specified detail
+     * message and cause.
+     *
+     * @param   s     the detail message
+     * @param   cause the throwable cause
+     */
+    public ConnectionExpiredException(String s, Throwable cause) {
+        super(s, cause);
     }
-
-    void consume(Optional<T> item) {
-        try {
-            acceptData(item);
-        } catch (InterruptedException x) {
-            throw new CompletionException(x);
-        }
-    }
-
 }
diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/common/DebugLogger.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/common/DebugLogger.java
new file mode 100644
index 00000000000..583fc86d9f6
--- /dev/null
+++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/common/DebugLogger.java
@@ -0,0 +1,251 @@
+/*
+ * Copyright (c) 2015, 2017, 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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 jdk.incubator.http.internal.common;
+
+import java.io.PrintStream;
+import java.util.Objects;
+import java.util.ResourceBundle;
+import java.util.function.Supplier;
+import java.lang.System.Logger;
+
+/**
+ * A {@code System.Logger} that forwards all messages to an underlying
+ * {@code System.Logger}, after adding some decoration.
+ * The logger also has the ability to additionally send the logged messages
+ * to System.err or System.out, whether the underlying logger is activated or not.
+ * In addition instance of {@code DebugLogger} support both
+ * {@link String#format(String, Object...)} and
+ * {@link java.text.MessageFormat#format(String, Object...)} formatting.
+ * String-like formatting is enabled by the presence of "%s" or "%d" in the format
+ * string. MessageFormat-like formatting is enabled by the presence of "{0" or "{1".
+ * <p>
+ * See {@link Utils#getDebugLogger(Supplier, boolean)} and
+ * {@link Utils#getHpackLogger(Supplier, boolean)}.
+ */
+class DebugLogger implements Logger {
+    // deliberately not in the same subtree than standard loggers.
+    final static String HTTP_NAME  = "jdk.internal.httpclient.debug";
+    final static String HPACK_NAME = "jdk.internal.httpclient.hpack.debug";
+    final static Logger HTTP = System.getLogger(HTTP_NAME);
+    final static Logger HPACK = System.getLogger(HPACK_NAME);
+    final static long START_NANOS = System.nanoTime();
+
+    private final Supplier<String> dbgTag;
+    private final Level errLevel;
+    private final Level outLevel;
+    private final Logger logger;
+    private final boolean debugOn;
+    private final boolean traceOn;
+
+    /**
+     * Create a logger for debug traces.The logger should only be used
+     * with levels whose severity is {@code <= DEBUG}.
+     *
+     * By default, this logger will forward all messages logged to the supplied
+     * {@code logger}.
+     * But in addition, if the message severity level is {@code >=} to
+     * the provided {@code errLevel} it will print the messages on System.err,
+     * and if the message severity level is {@code >=} to
+     * the provided {@code outLevel} it will also print the messages on System.out.
+     * <p>
+     * The logger will add some decoration to the printed message, in the form of
+     * {@code <Level>:[<thread-name>] [<elapsed-time>] <dbgTag>: <formatted message>}
+     *
+     * @apiNote To obtain a logger that will always print things on stderr in
+     *          addition to forwarding to the internal logger, use
+     *          {@code new DebugLogger(logger, this::dbgTag, Level.OFF, Level.ALL);}.
+     *          To obtain a logger that will only forward to the internal logger,
+     *          use {@code new DebugLogger(logger, this::dbgTag, Level.OFF, Level.OFF);}.
+     *
+     * @param logger The internal logger to which messages will be forwarded.
+     *               This should be either {@link #HPACK} or {@link #HTTP};
+     *
+     * @param dbgTag A lambda that returns a string that identifies the caller
+     *               (e.g: "SocketTube(3)", or "Http2Connection(SocketTube(3))")
+     * @param outLevel The level above which messages will be also printed on
+     *               System.out (in addition to being forwarded to the internal logger).
+     * @param errLevel The level above which messages will be also printed on
+     *               System.err (in addition to being forwarded to the internal logger).
+     *
+     * @return A logger for HTTP internal debug traces
+     */
+    private DebugLogger(Logger logger,
+                Supplier<String> dbgTag,
+                Level outLevel,
+                Level errLevel) {
+        this.dbgTag = dbgTag;
+        this.errLevel = errLevel;
+        this.outLevel = outLevel;
+        this.logger = Objects.requireNonNull(logger);
+        // support only static configuration.
+        this.debugOn = isEnabled(Level.DEBUG);
+        this.traceOn = isEnabled(Level.TRACE);
+    }
+
+    @Override
+    public String getName() {
+        return logger.getName();
+    }
+
+    private boolean isEnabled(Level level) {
+        if (level == Level.OFF) return false;
+        int severity = level.getSeverity();
+        return severity >= errLevel.getSeverity()
+                || severity >= outLevel.getSeverity()
+                || logger.isLoggable(level);
+    }
+
+    @Override
+    public boolean isLoggable(Level level) {
+        // fast path, we assume these guys never change.
+        // support only static configuration.
+        if (level == Level.DEBUG) return debugOn;
+        if (level == Level.TRACE) return traceOn;
+        return isEnabled(level);
+    }
+
+    @Override
+    public void log(Level level, ResourceBundle unused,
+                    String format, Object... params) {
+        // fast path, we assume these guys never change.
+        // support only static configuration.
+        if (level == Level.DEBUG && !debugOn) return;
+        if (level == Level.TRACE && !traceOn) return;
+
+        int severity = level.getSeverity();
+        if (errLevel != Level.OFF
+                && errLevel.getSeverity() <= severity) {
+            print(System.err, level, format, params, null);
+        }
+        if (outLevel != Level.OFF
+                && outLevel.getSeverity() <= severity) {
+            print(System.out, level, format, params, null);
+        }
+        if (logger.isLoggable(level)) {
+            logger.log(level, unused,
+                    getFormat(new StringBuilder(), format, params).toString(),
+                    params);
+        }
+    }
+
+    @Override
+    public void log(Level level, ResourceBundle unused, String msg,
+                    Throwable thrown) {
+        // fast path, we assume these guys never change.
+        if (level == Level.DEBUG && !debugOn) return;
+        if (level == Level.TRACE && !traceOn) return;
+
+        if (errLevel != Level.OFF
+                && errLevel.getSeverity() <= level.getSeverity()) {
+            print(System.err, level, msg, null, thrown);
+        }
+        if (outLevel != Level.OFF
+                && outLevel.getSeverity() <= level.getSeverity()) {
+            print(System.out, level, msg, null, thrown);
+        }
+        if (logger.isLoggable(level)) {
+            logger.log(level, unused,
+                    getFormat(new StringBuilder(), msg, null).toString(),
+                    thrown);
+        }
+    }
+
+    private void print(PrintStream out, Level level, String msg,
+                       Object[] params, Throwable t) {
+        StringBuilder sb = new StringBuilder();
+        sb.append(level.name()).append(':').append(' ');
+        sb = format(sb, msg, params);
+        if (t != null) sb.append(' ').append(t.toString());
+        out.println(sb.toString());
+        if (t != null) {
+            t.printStackTrace(out);
+        }
+    }
+
+    private StringBuilder decorate(StringBuilder sb, String msg) {
+        String tag = dbgTag == null ? null : dbgTag.get();
+        String res = msg == null ? "" : msg;
+        long elapsed = System.nanoTime() - START_NANOS;
+        long millis = elapsed / 1000_000;
+        long secs   = millis / 1000;
+        sb.append('[').append(Thread.currentThread().getName()).append(']')
+                .append(' ').append('[');
+        if (secs > 0) {
+            sb.append(secs).append('s');
+        }
+        millis = millis % 1000;
+        if (millis > 0) {
+            if (secs > 0) sb.append(' ');
+            sb.append(millis).append("ms");
+        }
+        sb.append(']').append(' ');
+        if (tag != null) {
+            sb.append(tag).append(' ');
+        }
+        sb.append(res);
+        return sb;
+    }
+
+
+    private StringBuilder getFormat(StringBuilder sb, String format, Object[] params) {
+        if (format == null || params == null || params.length == 0) {
+            return decorate(sb, format);
+        } else if (format.contains("{0}") || format.contains("{1}")) {
+            return decorate(sb, format);
+        } else if (format.contains("%s") || format.contains("%d")) {
+            try {
+                return decorate(sb, String.format(format, params));
+            } catch (Throwable t) {
+                return decorate(sb, format);
+            }
+        } else {
+            return decorate(sb, format);
+        }
+    }
+
+    private StringBuilder format(StringBuilder sb, String format, Object[] params) {
+        if (format == null || params == null || params.length == 0) {
+            return decorate(sb, format);
+        } else if (format.contains("{0}") || format.contains("{1}")) {
+            return decorate(sb, java.text.MessageFormat.format(format, params));
+        } else if (format.contains("%s") || format.contains("%d")) {
+            try {
+                return decorate(sb, String.format(format, params));
+            } catch (Throwable t) {
+                return decorate(sb, format);
+            }
+        } else {
+            return decorate(sb, format);
+        }
+    }
+
+    public static DebugLogger createHttpLogger(Supplier<String> dbgTag, Level outLevel, Level errLevel) {
+        return new DebugLogger(HTTP, dbgTag, outLevel, errLevel);
+    }
+
+    public static DebugLogger createHpackLogger(Supplier<String> dbgTag, Level outLevel, Level errLevel) {
+        return new DebugLogger(HPACK, dbgTag, outLevel, errLevel);
+    }
+}
diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/common/Demand.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/common/Demand.java
new file mode 100644
index 00000000000..5eb498018e0
--- /dev/null
+++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/common/Demand.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright (c) 2017, 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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 jdk.incubator.http.internal.common;
+
+import java.util.concurrent.atomic.AtomicLong;
+
+/**
+ * Encapsulates operations with demand (Reactive Streams).
+ *
+ * <p> Demand is the aggregated number of elements requested by a Subscriber
+ * which is yet to be delivered (fulfilled) by the Publisher.
+ */
+public final class Demand {
+
+    private final AtomicLong val = new AtomicLong();
+
+    /**
+     * Increases this demand by the specified positive value.
+     *
+     * @param n
+     *         increment {@code > 0}
+     *
+     * @return {@code true} iff prior to this operation this demand was fulfilled
+     */
+    public boolean increase(long n) {
+        if (n <= 0) {
+            throw new IllegalArgumentException(String.valueOf(n));
+        }
+        long prev = val.getAndAccumulate(n, (p, i) -> p + i < 0 ? Long.MAX_VALUE : p + i);
+        return prev == 0;
+    }
+
+    /**
+     * Tries to decrease this demand by the specified positive value.
+     *
+     * <p> The actual value this demand has been decreased by might be less than
+     * {@code n}, including {@code 0} (no decrease at all).
+     *
+     * @param n
+     *         decrement {@code > 0}
+     *
+     * @return a value {@code m} ({@code 0 <= m <= n}) this demand has been
+     *         actually decreased by
+     */
+    public long decreaseAndGet(long n) {
+        if (n <= 0) {
+            throw new IllegalArgumentException(String.valueOf(n));
+        }
+        long p, d;
+        do {
+            d = val.get();
+            p = Math.min(d, n);
+        } while (!val.compareAndSet(d, d - p));
+        return p;
+    }
+
+    /**
+     * Tries to decrease this demand by {@code 1}.
+     *
+     * @return {@code true} iff this demand has been decreased by {@code 1}
+     */
+    public boolean tryDecrement() {
+        return decreaseAndGet(1) == 1;
+    }
+
+    /**
+     * @return {@code true} iff there is no unfulfilled demand
+     */
+    public boolean isFulfilled() {
+        return val.get() == 0;
+    }
+
+    /**
+     * Resets this object to its initial state.
+     */
+    public void reset() {
+        val.set(0);
+    }
+
+    /**
+     * Returns the current value of this demand.
+     *
+     * @return the current value of this demand
+     */
+    public long get() {
+        return val.get();
+    }
+
+    @Override
+    public String toString() {
+        return String.valueOf(val.get());
+    }
+}
diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/common/FlowTube.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/common/FlowTube.java
new file mode 100644
index 00000000000..7230ac158ce
--- /dev/null
+++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/common/FlowTube.java
@@ -0,0 +1,185 @@
+/*
+ * Copyright (c) 2017, 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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 jdk.incubator.http.internal.common;
+
+import java.nio.ByteBuffer;
+import java.util.List;
+import java.util.concurrent.Flow;
+
+/**
+ * FlowTube is an I/O abstraction that allows reading from and writing to a
+ * destination asynchronously.
+ * This is not a {@link Flow.Processor
+ * Flow.Processor&lt;List&lt;ByteBuffer&gt;, List&lt;ByteBuffer&gt;&gt;},
+ * but rather models a publisher source and a subscriber sink in a bidirectional flow.
+ * <p>
+ * The {@code connectFlows} method should be called to connect the bidirectional
+ * flow. A FlowTube supports handing over the same read subscription to different
+ * sequential read subscribers over time. When {@code connectFlows(writePublisher,
+ * readSubscriber} is called, the FlowTube will call {@code dropSubscription} on
+ * its former readSubscriber, and {@code onSubscribe} on its new readSubscriber.
+ */
+public interface FlowTube extends
+       Flow.Publisher<List<ByteBuffer>>,
+       Flow.Subscriber<List<ByteBuffer>> {
+
+    /**
+     * A subscriber for reading from the bidirectional flow.
+     * A TubeSubscriber is a {@code Flow.Subscriber} that can be canceled
+     * by calling {@code dropSubscription()}.
+     * Once {@code dropSubscription()} is called, the {@code TubeSubscriber}
+     * should stop calling any method on its subscription.
+     */
+    static interface TubeSubscriber extends Flow.Subscriber<List<ByteBuffer>> {
+
+        /**
+         * Called when the flow is connected again, and the subscription
+         * is handed over to a new subscriber.
+         * Once {@code dropSubscription()} is called, the {@code TubeSubscriber}
+         * should stop calling any method on its subscription.
+         */
+        default void dropSubscription() { }
+
+    }
+
+    /**
+     * A publisher for writing to the bidirectional flow.
+     */
+    static interface TubePublisher extends Flow.Publisher<List<ByteBuffer>> {
+
+    }
+
+    /**
+     * Connects the bidirectional flows to a write {@code Publisher} and a
+     * read {@code Subscriber}. This method can be called sequentially
+     * several times to switch existing publishers and subscribers to a new
+     * pair of write subscriber and read publisher.
+     * @param writePublisher A new publisher for writing to the bidirectional flow.
+     * @param readSubscriber A new subscriber for reading from the bidirectional
+     *                       flow.
+     */
+    default void connectFlows(TubePublisher writePublisher,
+                              TubeSubscriber readSubscriber) {
+
+        this.subscribe(readSubscriber);
+        writePublisher.subscribe(this);
+    }
+
+    /**
+     * Returns true if this flow was completed, either exceptionally
+     * or normally (EOF reached).
+     * @return true if the flow is finished
+     */
+    boolean isFinished();
+
+
+    /**
+     * Returns {@code s} if {@code s} is a {@code TubeSubscriber}, otherwise
+     * wraps it in a {@code TubeSubscriber}.
+     * Using the wrapper is only appropriate in the case where
+     * {@code dropSubscription} doesn't need to be implemented, and the
+     * {@code TubeSubscriber} is subscribed only once.
+     *
+     * @param s a subscriber for reading.
+     * @return a {@code TubeSubscriber}: either {@code s} if {@code s} is a
+     *           {@code TubeSubscriber}, otherwise a {@code TubeSubscriber}
+     *           wrapper that delegates to {@code s}
+     */
+    static TubeSubscriber asTubeSubscriber(Flow.Subscriber<? super List<ByteBuffer>> s) {
+        if (s instanceof TubeSubscriber) {
+            return (TubeSubscriber) s;
+        }
+        return new AbstractTubeSubscriber.TubeSubscriberWrapper(s);
+    }
+
+    /**
+     * Returns {@code s} if {@code s} is a {@code  TubePublisher}, otherwise
+     * wraps it in a {@code  TubePublisher}.
+     *
+     * @param p a publisher for writing.
+     * @return a {@code TubePublisher}: either {@code s} if {@code s} is a
+     *           {@code  TubePublisher}, otherwise a {@code TubePublisher}
+     *           wrapper that delegates to {@code s}
+     */
+    static TubePublisher asTubePublisher(Flow.Publisher<List<ByteBuffer>> p) {
+        if (p instanceof TubePublisher) {
+            return (TubePublisher) p;
+        }
+        return new AbstractTubePublisher.TubePublisherWrapper(p);
+    }
+
+    /**
+     * Convenience abstract class for {@code TubePublisher} implementations.
+     * It is not required that a {@code TubePublisher} implementation extends
+     * this class.
+     */
+    static abstract class AbstractTubePublisher implements TubePublisher {
+        static final class TubePublisherWrapper extends AbstractTubePublisher {
+            final Flow.Publisher<List<ByteBuffer>> delegate;
+            public TubePublisherWrapper(Flow.Publisher<List<ByteBuffer>> delegate) {
+                this.delegate = delegate;
+            }
+            @Override
+            public void subscribe(Flow.Subscriber<? super List<ByteBuffer>> subscriber) {
+                delegate.subscribe(subscriber);
+            }
+        }
+    }
+
+    /**
+     * Convenience abstract class for {@code TubeSubscriber} implementations.
+     * It is not required that a {@code TubeSubscriber} implementation extends
+     * this class.
+     */
+    static abstract class AbstractTubeSubscriber implements TubeSubscriber {
+        static final class TubeSubscriberWrapper extends  AbstractTubeSubscriber {
+            final Flow.Subscriber<? super List<ByteBuffer>> delegate;
+            TubeSubscriberWrapper(Flow.Subscriber<? super List<ByteBuffer>> delegate) {
+                this.delegate = delegate;
+            }
+            @Override
+            public void dropSubscription() {}
+            @Override
+            public void onSubscribe(Flow.Subscription subscription) {
+                delegate.onSubscribe(subscription);
+            }
+            @Override
+            public void onNext(List<ByteBuffer> item) {
+                delegate.onNext(item);
+            }
+            @Override
+            public void onError(Throwable throwable) {
+                delegate.onError(throwable);
+            }
+            @Override
+            public void onComplete() {
+                delegate.onComplete();
+            }
+        }
+
+    }
+
+}
diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/common/HttpHeadersImpl.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/common/HttpHeadersImpl.java
index b3ac7d51912..3c7a52647b6 100644
--- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/common/HttpHeadersImpl.java
+++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/common/HttpHeadersImpl.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2017, 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
@@ -30,15 +30,13 @@ import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
 import java.util.Map;
-import java.util.Optional;
-import java.util.OptionalLong;
 import java.util.Set;
 import java.util.TreeMap;
 
 /**
  * Implementation of HttpHeaders.
  */
-public class HttpHeadersImpl implements HttpHeaders {
+public class HttpHeadersImpl extends HttpHeaders {
 
     private final TreeMap<String,List<String>> headers;
 
@@ -46,36 +44,20 @@ public class HttpHeadersImpl implements HttpHeaders {
         headers = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
     }
 
-    @Override
-    public Optional<String> firstValue(String name) {
-        List<String> l = headers.get(name);
-        return Optional.ofNullable(l == null ? null : l.get(0));
-    }
-
-    @Override
-    public List<String> allValues(String name) {
-        return headers.get(name);
-    }
-
     @Override
     public Map<String, List<String>> map() {
         return Collections.unmodifiableMap(headers);
     }
 
-    public Map<String, List<String>> directMap() {
-        return headers;
-    }
-
     // package private mutators
 
     public HttpHeadersImpl deepCopy() {
         HttpHeadersImpl h1 = new HttpHeadersImpl();
-        TreeMap<String,List<String>> headers1 = h1.headers;
         Set<String> keys = headers.keySet();
         for (String key : keys) {
             List<String> vals = headers.get(key);
             List<String> vals1 = new ArrayList<>(vals);
-            headers1.put(key, vals1);
+            h1.headers.put(key, vals1);
         }
         return h1;
     }
@@ -90,19 +72,4 @@ public class HttpHeadersImpl implements HttpHeaders {
         values.add(value);
         headers.put(name, values);
     }
-
-    @Override
-    public OptionalLong firstValueAsLong(String name) {
-        List<String> l = headers.get(name);
-        if (l == null) {
-            return OptionalLong.empty();
-        } else {
-            String v = l.get(0);
-            return OptionalLong.of(Long.parseLong(v));
-        }
-    }
-
-    public void clear() {
-        headers.clear();
-    }
 }
diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/common/Log.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/common/Log.java
index 9fcf9f023a3..e042993634b 100644
--- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/common/Log.java
+++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/common/Log.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2017, 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
@@ -25,8 +25,9 @@
 
 package jdk.incubator.http.internal.common;
 
-import java.io.IOException;
 import jdk.incubator.http.HttpHeaders;
+
+import java.util.ArrayList;
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
@@ -36,6 +37,9 @@ import jdk.incubator.http.internal.frame.DataFrame;
 import jdk.incubator.http.internal.frame.Http2Frame;
 import jdk.incubator.http.internal.frame.WindowUpdateFrame;
 
+import javax.net.ssl.SNIServerName;
+import javax.net.ssl.SSLParameters;
+
 /**
  * -Djava.net.HttpClient.log=
  *          errors,requests,headers,
@@ -196,9 +200,9 @@ public abstract class Log implements System.Logger {
         }
     }
 
-    public static void logResponse(String s, Object... s1) {
+    public static void logResponse(Supplier<String> supplier) {
         if (requests()) {
-            logger.log(Level.INFO, "RESPONSE: " + s, s1);
+            logger.log(Level.INFO, "RESPONSE: " + supplier.get());
         }
     }
 
@@ -227,6 +231,55 @@ public abstract class Log implements System.Logger {
         }
     }
 
+    public static void logParams(SSLParameters p) {
+        if (!Log.ssl()) {
+            return;
+        }
+
+        if (p == null) {
+            Log.logSSL("SSLParameters: Null params");
+            return;
+        }
+
+        final StringBuilder sb = new StringBuilder("SSLParameters:");
+        final List<Object> params = new ArrayList<>();
+        if (p.getCipherSuites() != null) {
+            for (String cipher : p.getCipherSuites()) {
+                sb.append("\n    cipher: {")
+                        .append(params.size()).append("}");
+                params.add(cipher);
+            }
+        }
+
+        // SSLParameters.getApplicationProtocols() can't return null
+        // JDK 8 EXCL START
+        for (String approto : p.getApplicationProtocols()) {
+            sb.append("\n    application protocol: {")
+                    .append(params.size()).append("}");
+            params.add(approto);
+        }
+        // JDK 8 EXCL END
+
+        if (p.getProtocols() != null) {
+            for (String protocol : p.getProtocols()) {
+                sb.append("\n    protocol: {")
+                        .append(params.size()).append("}");
+                params.add(protocol);
+            }
+        }
+
+        if (p.getServerNames() != null) {
+            for (SNIServerName sname : p.getServerNames()) {
+                sb.append("\n    server name: {")
+                        .append(params.size()).append("}");
+                params.add(sname.toString());
+            }
+        }
+        sb.append('\n');
+
+        Log.logSSL(sb.toString(), params.toArray());
+    }
+
     public static void dumpHeaders(StringBuilder sb, String prefix, HttpHeaders headers) {
         if (headers != null) {
             Map<String,List<String>> h = headers.map();
diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/common/MinimalFuture.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/common/MinimalFuture.java
index bd338e4b294..b50f24c8554 100644
--- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/common/MinimalFuture.java
+++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/common/MinimalFuture.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2017, 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
@@ -139,7 +139,7 @@ public final class MinimalFuture<T> extends CompletableFuture<T> {
         }
     }
 
-    public <U> MinimalFuture<U> newIncompleteFuture() {
+    public static <U> MinimalFuture<U> newMinimalFuture() {
         return new MinimalFuture<>();
     }
 
@@ -157,4 +157,18 @@ public final class MinimalFuture<T> extends CompletableFuture<T> {
     public String toString() {
         return super.toString() + " (id=" + id +")";
     }
+
+    public static <U> MinimalFuture<U> of(CompletionStage<U> stage) {
+        MinimalFuture<U> cf = newMinimalFuture();
+        stage.whenComplete((r,t) -> complete(cf, r, t));
+        return cf;
+    }
+
+    private static <U> void complete(CompletableFuture<U> cf, U result, Throwable t) {
+        if (t == null) {
+            cf.complete(result);
+        } else {
+            cf.completeExceptionally(t);
+        }
+    }
 }
diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/common/Pair.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/common/Pair.java
index 805869eb4b7..c3bae587a7b 100644
--- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/common/Pair.java
+++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/common/Pair.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2017, 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
diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/common/SSLFlowDelegate.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/common/SSLFlowDelegate.java
new file mode 100644
index 00000000000..e4523bfe283
--- /dev/null
+++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/common/SSLFlowDelegate.java
@@ -0,0 +1,903 @@
+/*
+ * Copyright (c) 2017, 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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 jdk.incubator.http.internal.common;
+
+import java.io.IOException;
+import java.lang.System.Logger.Level;
+import java.nio.ByteBuffer;
+import java.util.concurrent.Executor;
+import java.util.concurrent.Flow;
+import java.util.concurrent.Flow.Subscriber;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ConcurrentLinkedQueue;
+import java.util.concurrent.atomic.AtomicInteger;
+import javax.net.ssl.SSLEngine;
+import javax.net.ssl.SSLEngineResult;
+import javax.net.ssl.SSLEngineResult.HandshakeStatus;
+import javax.net.ssl.SSLEngineResult.Status;
+import javax.net.ssl.SSLException;
+import jdk.incubator.http.internal.common.SubscriberWrapper.SchedulingAction;
+
+/**
+ * Implements SSL using two SubscriberWrappers.
+ *
+ * <p> Constructor takes two Flow.Subscribers: one that receives the network
+ * data (after it has been encrypted by SSLFlowDelegate) data, and one that
+ * receives the application data (before it has been encrypted by SSLFlowDelegate).
+ *
+ * <p> Methods upstreamReader() and upstreamWriter() return the corresponding
+ * Flow.Subscribers containing Flows for the encrypted/decrypted upstream data.
+ * See diagram below.
+ *
+ * <p> How Flow.Subscribers are used in this class, and where they come from:
+ * <pre>
+ * {@code
+ *
+ *
+ *
+ * --------->  data flow direction
+ *
+ *
+ *                         +------------------+
+ *        upstreamWriter   |                  | downWriter
+ *        ---------------> |                  | ------------>
+ *  obtained from this     |                  | supplied to constructor
+ *                         | SSLFlowDelegate  |
+ *        downReader       |                  | upstreamReader
+ *        <--------------- |                  | <--------------
+ * supplied to constructor |                  | obtained from this
+ *                         +------------------+
+ * }
+ * </pre>
+ */
+public class SSLFlowDelegate {
+
+    static final boolean DEBUG = Utils.DEBUG; // Revisit: temporary dev flag.
+    final System.Logger debug =
+            Utils.getDebugLogger(this::dbgString, DEBUG);
+
+    final Executor exec;
+    final Reader reader;
+    final Writer writer;
+    final SSLEngine engine;
+    final String tubeName; // hack
+    final CompletableFuture<Void> cf;
+    final CompletableFuture<String> alpnCF; // completes on initial handshake
+    final static ByteBuffer SENTINEL = Utils.EMPTY_BYTEBUFFER;
+    volatile boolean close_notify_received;
+
+    /**
+     * Creates an SSLFlowDelegate fed from two Flow.Subscribers. Each
+     * Flow.Subscriber requires an associated {@link CompletableFuture}
+     * for errors that need to be signaled from downstream to upstream.
+     */
+    public SSLFlowDelegate(SSLEngine engine,
+                           Executor exec,
+                           Subscriber<? super List<ByteBuffer>> downReader,
+                           Subscriber<? super List<ByteBuffer>> downWriter)
+    {
+        this.tubeName = String.valueOf(downWriter);
+        this.reader = new Reader();
+        this.writer = new Writer();
+        this.engine = engine;
+        this.exec = exec;
+        this.handshakeState = new AtomicInteger(NOT_HANDSHAKING);
+        this.cf = CompletableFuture.allOf(reader.completion(), writer.completion())
+                                   .thenRun(this::normalStop);
+        this.alpnCF = new MinimalFuture<>();
+
+        // connect the Reader to the downReader and the
+        // Writer to the downWriter.
+        connect(downReader, downWriter);
+
+        //Monitor.add(this::monitor);
+    }
+
+    /**
+     * Returns true if the SSLFlowDelegate has detected a TLS
+     * close_notify from the server.
+     * @return true, if a close_notify was detected.
+     */
+    public boolean closeNotifyReceived() {
+        return close_notify_received;
+    }
+
+    /**
+     * Connects the read sink (downReader) to the SSLFlowDelegate Reader,
+     * and the write sink (downWriter) to the SSLFlowDelegate Writer.
+     * Called from within the constructor. Overwritten by SSLTube.
+     *
+     * @param downReader  The left hand side read sink (typically, the
+     *                    HttpConnection read subscriber).
+     * @param downWriter  The right hand side write sink (typically
+     *                    the SocketTube write subscriber).
+     */
+    void connect(Subscriber<? super List<ByteBuffer>> downReader,
+                 Subscriber<? super List<ByteBuffer>> downWriter) {
+        this.reader.subscribe(downReader);
+        this.writer.subscribe(downWriter);
+    }
+
+   /**
+    * Returns a CompletableFuture<String> which completes after
+    * the initial handshake completes, and which contains the negotiated
+    * alpn.
+    */
+    public CompletableFuture<String> alpn() {
+        return alpnCF;
+    }
+
+    private void setALPN() {
+        // Handshake is finished. So, can retrieve the ALPN now
+        if (alpnCF.isDone())
+            return;
+        String alpn = engine.getApplicationProtocol();
+        debug.log(Level.DEBUG, "setALPN = %s", alpn);
+        alpnCF.complete(alpn);
+    }
+
+    public String monitor() {
+        StringBuilder sb = new StringBuilder();
+        sb.append("SSL: HS state: " + states(handshakeState));
+        sb.append(" Engine state: " + engine.getHandshakeStatus().toString());
+        sb.append(" LL : ");
+        synchronized(stateList) {
+            for (String s: stateList) {
+                sb.append(s).append(" ");
+            }
+        }
+        sb.append("\r\n");
+        sb.append("Reader:: ").append(reader.toString());
+        sb.append("\r\n");
+        sb.append("Writer:: ").append(writer.toString());
+        sb.append("\r\n===================================");
+        return sb.toString();
+    }
+
+    protected SchedulingAction enterReadScheduling() {
+        return SchedulingAction.CONTINUE;
+    }
+
+
+    /**
+     * Processing function for incoming data. Pass it thru SSLEngine.unwrap().
+     * Any decrypted buffers returned to be passed downstream.
+     * Status codes:
+     *     NEED_UNWRAP: do nothing. Following incoming data will contain
+     *                  any required handshake data
+     *     NEED_WRAP: call writer.addData() with empty buffer
+     *     NEED_TASK: delegate task to executor
+     *     BUFFER_OVERFLOW: allocate larger output buffer. Repeat unwrap
+     *     BUFFER_UNDERFLOW: keep buffer and wait for more data
+     *     OK: return generated buffers.
+     *
+     * Upstream subscription strategy is to try and keep no more than
+     * TARGET_BUFSIZE bytes in readBuf
+     */
+    class Reader extends SubscriberWrapper {
+        final SequentialScheduler scheduler;
+        static final int TARGET_BUFSIZE = 16 * 1024;
+        volatile ByteBuffer readBuf;
+        volatile boolean completing = false;
+        final Object readBufferLock = new Object();
+        final System.Logger debugr =
+            Utils.getDebugLogger(this::dbgString, DEBUG);
+
+        class ReaderDownstreamPusher implements Runnable {
+            @Override public void run() { processData(); }
+        }
+
+        Reader() {
+            super();
+            scheduler = SequentialScheduler.synchronizedScheduler(
+                                                new ReaderDownstreamPusher());
+            this.readBuf = ByteBuffer.allocate(1024);
+            readBuf.limit(0); // keep in read mode
+        }
+
+        protected SchedulingAction enterScheduling() {
+            return enterReadScheduling();
+        }
+
+        public final String dbgString() {
+            return "SSL Reader(" + tubeName + ")";
+        }
+
+        /**
+         * entry point for buffers delivered from upstream Subscriber
+         */
+        @Override
+        public void incoming(List<ByteBuffer> buffers, boolean complete) {
+            debugr.log(Level.DEBUG, () -> "Adding " + Utils.remaining(buffers)
+                        + " bytes to read buffer");
+            addToReadBuf(buffers, complete);
+            scheduler.runOrSchedule();
+        }
+
+        @Override
+        public String toString() {
+            return "READER: " + super.toString() + " readBuf: " + readBuf.toString()
+                    + " count: " + count.toString();
+        }
+
+        private void reallocReadBuf() {
+            int sz = readBuf.capacity();
+            ByteBuffer newb = ByteBuffer.allocate(sz*2);
+            readBuf.flip();
+            Utils.copy(readBuf, newb);
+            readBuf = newb;
+        }
+
+        @Override
+        protected long upstreamWindowUpdate(long currentWindow, long downstreamQsize) {
+            if (readBuf.remaining() > TARGET_BUFSIZE) {
+                return 0;
+            } else {
+                return super.upstreamWindowUpdate(currentWindow, downstreamQsize);
+            }
+        }
+
+        // readBuf is kept ready for reading outside of this method
+        private void addToReadBuf(List<ByteBuffer> buffers, boolean complete) {
+            synchronized (readBufferLock) {
+                for (ByteBuffer buf : buffers) {
+                    readBuf.compact();
+                    while (readBuf.remaining() < buf.remaining())
+                        reallocReadBuf();
+                    readBuf.put(buf);
+                    readBuf.flip();
+                }
+                if (complete) {
+                    this.completing = complete;
+                }
+            }
+        }
+
+        void schedule() {
+            scheduler.runOrSchedule();
+        }
+
+        void stop() {
+            debugr.log(Level.DEBUG, "stop");
+            scheduler.stop();
+        }
+
+        AtomicInteger count = new AtomicInteger(0);
+
+        // work function where it all happens
+        void processData() {
+            try {
+                debugr.log(Level.DEBUG, () -> "processData: " + readBuf.remaining()
+                           + " bytes to unwrap "
+                           + states(handshakeState)
+                           + ", " + engine.getHandshakeStatus());
+                int len;
+                boolean complete = false;
+                while ((len = readBuf.remaining()) > 0) {
+                    boolean handshaking = false;
+                    try {
+                        EngineResult result;
+                        synchronized (readBufferLock) {
+                            complete = this.completing;
+                            result = unwrapBuffer(readBuf);
+                            debugr.log(Level.DEBUG, "Unwrapped: %s", result.result);
+                        }
+                        if (result.bytesProduced() > 0) {
+                            debugr.log(Level.DEBUG, "sending %d", result.bytesProduced());
+                            count.addAndGet(result.bytesProduced());
+                            outgoing(result.destBuffer, false);
+                        }
+                        if (result.status() == Status.BUFFER_UNDERFLOW) {
+                            debugr.log(Level.DEBUG, "BUFFER_UNDERFLOW");
+                            // not enough data in the read buffer...
+                            requestMore();
+                            synchronized (readBufferLock) {
+                                // check if we have received some data
+                                if (readBuf.remaining() > len) continue;
+                                return;
+                            }
+                        }
+                        if (complete && result.status() == Status.CLOSED) {
+                            debugr.log(Level.DEBUG, "Closed: completing");
+                            outgoing(Utils.EMPTY_BB_LIST, true);
+                            return;
+                        }
+                        if (result.handshaking() && !complete) {
+                            debugr.log(Level.DEBUG, "handshaking");
+                            doHandshake(result, READER);
+                            resumeActivity();
+                            handshaking = true;
+                        } else {
+                            if ((handshakeState.getAndSet(NOT_HANDSHAKING) & ~DOING_TASKS) == HANDSHAKING) {
+                                setALPN();
+                                handshaking = false;
+                                resumeActivity();
+                            }
+                        }
+                    } catch (IOException ex) {
+                        errorCommon(ex);
+                        handleError(ex);
+                    }
+                    if (handshaking && !complete)
+                        return;
+                }
+                if (!complete) {
+                    synchronized (readBufferLock) {
+                        complete = this.completing && !readBuf.hasRemaining();
+                    }
+                }
+                if (complete) {
+                    debugr.log(Level.DEBUG, "completing");
+                    // Complete the alpnCF, if not already complete, regardless of
+                    // whether or not the ALPN is available, there will be no more
+                    // activity.
+                    setALPN();
+                    outgoing(Utils.EMPTY_BB_LIST, true);
+                }
+            } catch (Throwable ex) {
+                errorCommon(ex);
+                handleError(ex);
+            }
+        }
+    }
+
+    /**
+     * Returns a CompletableFuture which completes after all activity
+     * in the delegate is terminated (whether normally or exceptionally).
+     *
+     * @return
+     */
+    public CompletableFuture<Void> completion() {
+        return cf;
+    }
+
+    public interface Monitorable {
+        public String getInfo();
+    }
+
+    public static class Monitor extends Thread {
+        final List<Monitorable> list;
+        static Monitor themon;
+
+        static {
+            themon = new Monitor();
+            themon.start(); // uncomment to enable Monitor
+        }
+
+        Monitor() {
+            super("Monitor");
+            setDaemon(true);
+            list = Collections.synchronizedList(new LinkedList<>());
+        }
+
+        void addTarget(Monitorable o) {
+            list.add(o);
+        }
+
+        public static void add(Monitorable o) {
+            themon.addTarget(o);
+        }
+
+        @Override
+        public void run() {
+            System.out.println("Monitor starting");
+            while (true) {
+                try {Thread.sleep(20*1000); } catch (Exception e) {}
+                synchronized (list) {
+                    for (Monitorable o : list) {
+                        System.out.println(o.getInfo());
+                        System.out.println("-------------------------");
+                    }
+                }
+                System.out.println("--o-o-o-o-o-o-o-o-o-o-o-o-o-o-");
+
+            }
+        }
+    }
+
+    /**
+     * Processing function for outgoing data. Pass it thru SSLEngine.wrap()
+     * Any encrypted buffers generated are passed downstream to be written.
+     * Status codes:
+     *     NEED_UNWRAP: call reader.addData() with empty buffer
+     *     NEED_WRAP: call addData() with empty buffer
+     *     NEED_TASK: delegate task to executor
+     *     BUFFER_OVERFLOW: allocate larger output buffer. Repeat wrap
+     *     BUFFER_UNDERFLOW: shouldn't happen on writing side
+     *     OK: return generated buffers
+     */
+    class Writer extends SubscriberWrapper {
+        final SequentialScheduler scheduler;
+        // queues of buffers received from upstream waiting
+        // to be processed by the SSLEngine
+        final List<ByteBuffer> writeList;
+        final System.Logger debugw =
+            Utils.getDebugLogger(this::dbgString, DEBUG);
+        volatile boolean completing;
+        boolean completed; // only accessed in processData
+
+        class WriterDownstreamPusher extends SequentialScheduler.CompleteRestartableTask {
+            @Override public void run() { processData(); }
+        }
+
+        Writer() {
+            super();
+            writeList = Collections.synchronizedList(new LinkedList<>());
+            scheduler = new SequentialScheduler(new WriterDownstreamPusher());
+        }
+
+        @Override
+        protected void incoming(List<ByteBuffer> buffers, boolean complete) {
+            assert complete ? buffers ==  Utils.EMPTY_BB_LIST : true;
+            assert buffers != Utils.EMPTY_BB_LIST ? complete == false : true;
+            if (complete) {
+                debugw.log(Level.DEBUG, "adding SENTINEL");
+                completing = true;
+                writeList.add(SENTINEL);
+            } else {
+                writeList.addAll(buffers);
+            }
+            debugw.log(Level.DEBUG, () -> "added " + buffers.size()
+                        + " (" + Utils.remaining(buffers)
+                        + " bytes) to the writeList");
+            scheduler.runOrSchedule();
+        }
+
+        public final String dbgString() {
+            return "SSL Writer(" + tubeName + ")";
+        }
+
+        protected void onSubscribe() {
+            doHandshake(EngineResult.INIT, INIT);
+            resumeActivity();
+        }
+
+        void schedule() {
+            scheduler.runOrSchedule();
+        }
+
+        void stop() {
+            debugw.log(Level.DEBUG, "stop");
+            scheduler.stop();
+        }
+
+        @Override
+        public boolean closing() {
+            return closeNotifyReceived();
+        }
+
+        private boolean isCompleting() {
+            return completing;
+        }
+
+        @Override
+        protected long upstreamWindowUpdate(long currentWindow, long downstreamQsize) {
+            if (writeList.size() > 10)
+                return 0;
+            else
+                return super.upstreamWindowUpdate(currentWindow, downstreamQsize);
+        }
+
+        private boolean hsTriggered() {
+            synchronized(writeList) {
+                for (ByteBuffer b : writeList)
+                    if (b == HS_TRIGGER)
+                        return true;
+                return false;
+            }
+        }
+
+        private void processData() {
+            boolean completing = isCompleting();
+
+            try {
+                debugw.log(Level.DEBUG, () -> "processData(" + Utils.remaining(writeList) + ")");
+                while (Utils.remaining(writeList) > 0 || hsTriggered()
+                        || needWrap()) {
+                    ByteBuffer[] outbufs = writeList.toArray(Utils.EMPTY_BB_ARRAY);
+                    EngineResult result = wrapBuffers(outbufs);
+                    debugw.log(Level.DEBUG, "wrapBuffer returned %s", result.result);
+
+                    if (result.status() == Status.CLOSED) {
+                        if (result.bytesProduced() <= 0)
+                            return;
+
+                        if (!completing && !completed) {
+                            completing = this.completing = true;
+                            // There could still be some outgoing data in outbufs.
+                            writeList.add(SENTINEL);
+                        }
+                    }
+
+                    boolean handshaking = false;
+                    if (result.handshaking()) {
+                        debugw.log(Level.DEBUG, "handshaking");
+                        doHandshake(result, WRITER);
+                        handshaking = true;
+                    } else {
+                        if ((handshakeState.getAndSet(NOT_HANDSHAKING) & ~DOING_TASKS) == HANDSHAKING) {
+                            setALPN();
+                            resumeActivity();
+                        }
+                    }
+                    cleanList(writeList); // tidy up the source list
+                    sendResultBytes(result);
+                    if (handshaking && !completing) {
+                        if (writeList.isEmpty() && !result.needUnwrap()) {
+                            writer.addData(HS_TRIGGER);
+                        }
+                        if (needWrap()) continue;
+                        return;
+                    }
+                }
+                if (completing && Utils.remaining(writeList) == 0) {
+                    /*
+                    System.out.println("WRITER DOO 3");
+                    engine.closeOutbound();
+                    EngineResult result = wrapBuffers(Utils.EMPTY_BB_ARRAY);
+                    sendResultBytes(result);
+                    */
+                    if (!completed) {
+                        completed = true;
+                        writeList.clear();
+                        outgoing(Utils.EMPTY_BB_LIST, true);
+                    }
+                    return;
+                }
+                if (writeList.isEmpty() && needWrap()) {
+                    writer.addData(HS_TRIGGER);
+                }
+            } catch (Throwable ex) {
+                handleError(ex);
+            }
+        }
+
+        private boolean needWrap() {
+            return engine.getHandshakeStatus() == HandshakeStatus.NEED_WRAP;
+        }
+
+        private void sendResultBytes(EngineResult result) {
+            if (result.bytesProduced() > 0) {
+                debugw.log(Level.DEBUG, "Sending %d bytes downstream",
+                           result.bytesProduced());
+                outgoing(result.destBuffer, false);
+            }
+        }
+
+        @Override
+        public String toString() {
+            return "WRITER: " + super.toString() +
+                    " writeList size " + Integer.toString(writeList.size());
+                    //" writeList: " + writeList.toString();
+        }
+    }
+
+    private void handleError(Throwable t) {
+        debug.log(Level.DEBUG, "handleError", t);
+        cf.completeExceptionally(t);
+        // no-op if already completed
+        alpnCF.completeExceptionally(t);
+        reader.stop();
+        writer.stop();
+    }
+
+    private void normalStop() {
+        reader.stop();
+        writer.stop();
+    }
+
+    private void cleanList(List<ByteBuffer> l) {
+        synchronized (l) {
+            Iterator<ByteBuffer> iter = l.iterator();
+            while (iter.hasNext()) {
+                ByteBuffer b = iter.next();
+                if (!b.hasRemaining() && b != SENTINEL) {
+                    iter.remove();
+                }
+            }
+        }
+    }
+
+    /**
+     * States for handshake. We avoid races when accessing/updating the AtomicInt
+     * because updates always schedule an additional call to both the read()
+     * and write() functions.
+     */
+    private static final int NOT_HANDSHAKING = 0;
+    private static final int HANDSHAKING = 1;
+    private static final int INIT = 2;
+    private static final int DOING_TASKS = 4; // bit added to above state
+    private static final ByteBuffer HS_TRIGGER = ByteBuffer.allocate(0);
+
+    private static final int READER = 1;
+    private static final int WRITER = 2;
+
+    private static String states(AtomicInteger state) {
+        int s = state.get();
+        StringBuilder sb = new StringBuilder();
+        int x = s & ~DOING_TASKS;
+        switch (x) {
+            case NOT_HANDSHAKING:
+                sb.append(" NOT_HANDSHAKING ");
+                break;
+            case HANDSHAKING:
+                sb.append(" HANDSHAKING ");
+                break;
+            case INIT:
+                sb.append(" INIT ");
+                break;
+            default:
+                throw new InternalError();
+        }
+        if ((s & DOING_TASKS) > 0)
+            sb.append("|DOING_TASKS");
+        return sb.toString();
+    }
+
+    private void resumeActivity() {
+        reader.schedule();
+        writer.schedule();
+    }
+
+    final AtomicInteger handshakeState;
+    final ConcurrentLinkedQueue<String> stateList = new ConcurrentLinkedQueue<>();
+
+    private void doHandshake(EngineResult r, int caller) {
+        int s = handshakeState.getAndAccumulate(HANDSHAKING, (current, update) -> update | (current & DOING_TASKS));
+        stateList.add(r.handshakeStatus().toString());
+        stateList.add(Integer.toString(caller));
+        switch (r.handshakeStatus()) {
+            case NEED_TASK:
+                if ((s & DOING_TASKS) > 0) // someone else was doing tasks
+                    return;
+                List<Runnable> tasks = obtainTasks();
+                executeTasks(tasks);
+                break;
+            case NEED_WRAP:
+                writer.addData(HS_TRIGGER);
+                break;
+            case NEED_UNWRAP:
+            case NEED_UNWRAP_AGAIN:
+                // do nothing else
+                break;
+            default:
+                throw new InternalError("Unexpected handshake status:"
+                                        + r.handshakeStatus());
+        }
+    }
+
+    private List<Runnable> obtainTasks() {
+        List<Runnable> l = new ArrayList<>();
+        Runnable r;
+        while ((r = engine.getDelegatedTask()) != null) {
+            l.add(r);
+        }
+        return l;
+    }
+
+    private void executeTasks(List<Runnable> tasks) {
+        exec.execute(() -> {
+            handshakeState.getAndUpdate((current) -> current | DOING_TASKS);
+            List<Runnable> nextTasks = tasks;
+            do {
+                try {
+                    nextTasks.forEach((r) -> {
+                        r.run();
+                    });
+                    if (engine.getHandshakeStatus() == HandshakeStatus.NEED_TASK) {
+                        nextTasks = obtainTasks();
+                    } else break;
+                } catch (Throwable t) {
+                    handleError(t);
+                }
+            } while(true);
+            handshakeState.getAndUpdate((current) -> current & ~DOING_TASKS);
+            writer.addData(HS_TRIGGER);
+            resumeActivity();
+        });
+    }
+
+
+    EngineResult unwrapBuffer(ByteBuffer src) throws IOException {
+        ByteBuffer dst = getAppBuffer();
+        while (true) {
+            SSLEngineResult sslResult = engine.unwrap(src, dst);
+            switch (sslResult.getStatus()) {
+                case BUFFER_OVERFLOW:
+                    // may happen only if app size buffer was changed.
+                    // get it again if app buffer size changed
+                    int appSize = engine.getSession().getApplicationBufferSize();
+                    ByteBuffer b = ByteBuffer.allocate(appSize + dst.position());
+                    dst.flip();
+                    b.put(dst);
+                    dst = b;
+                    break;
+                case CLOSED:
+                    return doClosure(new EngineResult(sslResult));
+                case BUFFER_UNDERFLOW:
+                    // handled implicitly by compaction/reallocation of readBuf
+                    return new EngineResult(sslResult);
+                case OK:
+                     dst.flip();
+                     return new EngineResult(sslResult, dst);
+            }
+        }
+    }
+
+    // FIXME: acknowledge a received CLOSE request from peer
+    EngineResult doClosure(EngineResult r) throws IOException {
+        debug.log(Level.DEBUG,
+                "doClosure(%s): %s [isOutboundDone: %s, isInboundDone: %s]",
+                r.result, engine.getHandshakeStatus(),
+                engine.isOutboundDone(), engine.isInboundDone());
+        if (engine.getHandshakeStatus() == HandshakeStatus.NEED_WRAP) {
+            // we have received TLS close_notify and need to send
+            // an acknowledgement back. We're calling doHandshake
+            // to finish the close handshake.
+            if (engine.isInboundDone() && !engine.isOutboundDone()) {
+                debug.log(Level.DEBUG, "doClosure: close_notify received");
+                close_notify_received = true;
+                doHandshake(r, READER);
+            }
+        }
+        return r;
+    }
+
+    /**
+     * Returns the upstream Flow.Subscriber of the reading (incoming) side.
+     * This flow must be given the encrypted data read from upstream (eg socket)
+     * before it is decrypted.
+     */
+    public Flow.Subscriber<List<ByteBuffer>> upstreamReader() {
+        return reader;
+    }
+
+    /**
+     * Returns the upstream Flow.Subscriber of the writing (outgoing) side.
+     * This flow contains the plaintext data before it is encrypted.
+     */
+    public Flow.Subscriber<List<ByteBuffer>> upstreamWriter() {
+        return writer;
+    }
+
+    public boolean resumeReader() {
+        return reader.signalScheduling();
+    }
+
+    public void resetReaderDemand() {
+        reader.resetDownstreamDemand();
+    }
+
+    static class EngineResult {
+        final SSLEngineResult result;
+        final ByteBuffer destBuffer;
+
+        // normal result
+        EngineResult(SSLEngineResult result) {
+            this(result, null);
+        }
+
+        EngineResult(SSLEngineResult result, ByteBuffer destBuffer) {
+            this.result = result;
+            this.destBuffer = destBuffer;
+        }
+
+        // Special result used to trigger handshaking in constructor
+        static EngineResult INIT =
+            new EngineResult(
+                new SSLEngineResult(SSLEngineResult.Status.OK, HandshakeStatus.NEED_WRAP, 0, 0));
+
+        boolean handshaking() {
+            HandshakeStatus s = result.getHandshakeStatus();
+            return s != HandshakeStatus.FINISHED
+                   && s != HandshakeStatus.NOT_HANDSHAKING
+                   && result.getStatus() != Status.CLOSED;
+        }
+
+        boolean needUnwrap() {
+            HandshakeStatus s = result.getHandshakeStatus();
+            return s == HandshakeStatus.NEED_UNWRAP;
+        }
+
+
+        int bytesConsumed() {
+            return result.bytesConsumed();
+        }
+
+        int bytesProduced() {
+            return result.bytesProduced();
+        }
+
+        SSLEngineResult.HandshakeStatus handshakeStatus() {
+            return result.getHandshakeStatus();
+        }
+
+        SSLEngineResult.Status status() {
+            return result.getStatus();
+        }
+    }
+
+    public ByteBuffer getNetBuffer() {
+        return ByteBuffer.allocate(engine.getSession().getPacketBufferSize());
+    }
+
+    private ByteBuffer getAppBuffer() {
+        return ByteBuffer.allocate(engine.getSession().getApplicationBufferSize());
+    }
+
+    final String dbgString() {
+        return "SSLFlowDelegate(" + tubeName + ")";
+    }
+
+    @SuppressWarnings("fallthrough")
+    EngineResult wrapBuffers(ByteBuffer[] src) throws SSLException {
+        debug.log(Level.DEBUG, () -> "wrapping "
+                    + Utils.remaining(src) + " bytes");
+        ByteBuffer dst = getNetBuffer();
+        while (true) {
+            SSLEngineResult sslResult = engine.wrap(src, dst);
+            debug.log(Level.DEBUG, () -> "SSLResult: " + sslResult);
+            switch (sslResult.getStatus()) {
+                case BUFFER_OVERFLOW:
+                    // Shouldn't happen. We allocated buffer with packet size
+                    // get it again if net buffer size was changed
+                    debug.log(Level.DEBUG, "BUFFER_OVERFLOW");
+                    int appSize = engine.getSession().getApplicationBufferSize();
+                    ByteBuffer b = ByteBuffer.allocate(appSize + dst.position());
+                    dst.flip();
+                    b.put(dst);
+                    dst = b;
+                    break; // try again
+                case CLOSED:
+                    debug.log(Level.DEBUG, "CLOSED");
+                    // fallthrough. There could be some remaining data in dst.
+                    // CLOSED will be handled by the caller.
+                case OK:
+                    dst.flip();
+                    final ByteBuffer dest = dst;
+                    debug.log(Level.DEBUG, () -> "OK => produced: "
+                                           + dest.remaining()
+                                           + " not wrapped: "
+                                           + Utils.remaining(src));
+                    return new EngineResult(sslResult, dest);
+                case BUFFER_UNDERFLOW:
+                    // Shouldn't happen.  Doesn't returns when wrap()
+                    // underflow handled externally
+                    // assert false : "Buffer Underflow";
+                    debug.log(Level.DEBUG, "BUFFER_UNDERFLOW");
+                    return new EngineResult(sslResult);
+                default:
+                    debug.log(Level.DEBUG, "ASSERT");
+                    assert false;
+            }
+        }
+    }
+}
diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/common/SSLTube.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/common/SSLTube.java
new file mode 100644
index 00000000000..32a3026dc15
--- /dev/null
+++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/common/SSLTube.java
@@ -0,0 +1,586 @@
+/*
+ * Copyright (c) 2017, 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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 jdk.incubator.http.internal.common;
+
+import java.lang.System.Logger.Level;
+import java.nio.ByteBuffer;
+import java.util.List;
+import java.util.Objects;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.Executor;
+import java.util.concurrent.Flow;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.Consumer;
+import javax.net.ssl.SSLEngine;
+import javax.net.ssl.SSLHandshakeException;
+import javax.net.ssl.SSLEngineResult.HandshakeStatus;
+import jdk.incubator.http.internal.common.SubscriberWrapper.SchedulingAction;
+import static javax.net.ssl.SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING;
+import static javax.net.ssl.SSLEngineResult.HandshakeStatus.FINISHED;
+
+/**
+ * An implementation of FlowTube that wraps another FlowTube in an
+ * SSL flow.
+ * <p>
+ * The following diagram shows a typical usage of the SSLTube, where
+ * the SSLTube wraps a SocketTube on the right hand side, and is connected
+ * to an HttpConnection on the left hand side.
+ *
+ * <preformatted>{@code
+ *                  +----------  SSLTube -------------------------+
+ *                  |                                             |
+ *                  |                    +---SSLFlowDelegate---+  |
+ *  HttpConnection  |                    |                     |  |   SocketTube
+ *    read sink  <- SSLSubscriberW.   <- Reader <- upstreamR.() <---- read source
+ *  (a subscriber)  |                    |    \         /      |  |  (a publisher)
+ *                  |                    |     SSLEngine       |  |
+ *  HttpConnection  |                    |    /         \      |  |   SocketTube
+ *  write source -> SSLSubscriptionW. -> upstreamW.() -> Writer ----> write sink
+ *  (a publisher)   |                    |                     |  |  (a subscriber)
+ *                  |                    +---------------------+  |
+ *                  |                                             |
+ *                  +---------------------------------------------+
+ * }</preformatted>
+ */
+public class SSLTube implements FlowTube {
+
+    static final boolean DEBUG = Utils.DEBUG; // revisit: temporary developer's flag.
+    final System.Logger debug =
+            Utils.getDebugLogger(this::dbgString, DEBUG);
+
+    private final FlowTube tube;
+    private final SSLSubscriberWrapper readSubscriber;
+    private final SSLSubscriptionWrapper writeSubscription;
+    private final SSLFlowDelegate sslDelegate;
+    private final SSLEngine engine;
+    private volatile boolean finished;
+
+    public SSLTube(SSLEngine engine, Executor executor, FlowTube tube) {
+        Objects.requireNonNull(engine);
+        Objects.requireNonNull(executor);
+        this.tube = Objects.requireNonNull(tube);
+        writeSubscription = new SSLSubscriptionWrapper();
+        readSubscriber = new SSLSubscriberWrapper();
+        this.engine = engine;
+        sslDelegate = new SSLTubeFlowDelegate(engine,
+                                              executor,
+                                              readSubscriber,
+                                              tube);
+    }
+
+    final class SSLTubeFlowDelegate extends SSLFlowDelegate {
+        SSLTubeFlowDelegate(SSLEngine engine, Executor executor,
+                            SSLSubscriberWrapper readSubscriber,
+                            FlowTube tube) {
+            super(engine, executor, readSubscriber, tube);
+        }
+        protected SchedulingAction enterReadScheduling() {
+            readSubscriber.processPendingSubscriber();
+            return SchedulingAction.CONTINUE;
+        }
+        void connect(Flow.Subscriber<? super List<ByteBuffer>> downReader,
+                     Flow.Subscriber<? super List<ByteBuffer>> downWriter) {
+            assert downWriter == tube;
+            assert downReader == readSubscriber;
+
+            // Connect the read sink first. That's the left-hand side
+            // downstream subscriber from the HttpConnection (or more
+            // accurately, the SSLSubscriberWrapper that will wrap it
+            // when SSLTube::connectFlows is called.
+            reader.subscribe(downReader);
+
+            // Connect the right hand side tube (the socket tube).
+            //
+            // The SSLFlowDelegate.writer publishes ByteBuffer to
+            // the SocketTube for writing on the socket, and the
+            // SSLFlowDelegate::upstreamReader subscribes to the
+            // SocketTube to receive ByteBuffers read from the socket.
+            //
+            // Basically this method is equivalent to:
+            //     // connect the read source:
+            //     //   subscribe the SSLFlowDelegate upstream reader
+            //     //   to the socket tube publisher.
+            //     tube.subscribe(upstreamReader());
+            //     // connect the write sink:
+            //     //   subscribe the socket tube write subscriber
+            //     //   with the SSLFlowDelegate downstream writer.
+            //     writer.subscribe(tube);
+            tube.connectFlows(FlowTube.asTubePublisher(writer),
+                              FlowTube.asTubeSubscriber(upstreamReader()));
+
+            // Finally connect the write source. That's the left
+            // hand side publisher which will push ByteBuffer for
+            // writing and encryption to the SSLFlowDelegate.
+            // The writeSubscription is in fact the SSLSubscriptionWrapper
+            // that will wrap the subscription provided by the
+            // HttpConnection publisher when SSLTube::connectFlows
+            // is called.
+            upstreamWriter().onSubscribe(writeSubscription);
+        }
+    }
+
+    public CompletableFuture<String> getALPN() {
+        return sslDelegate.alpn();
+    }
+
+    @Override
+    public void subscribe(Flow.Subscriber<? super List<ByteBuffer>> s) {
+        readSubscriber.dropSubscription();
+        readSubscriber.setDelegate(s);
+        s.onSubscribe(readSubscription);
+    }
+
+    /**
+     * Tells whether, or not, this FlowTube has finished receiving data.
+     *
+     * @return true when one of this FlowTube Subscriber's OnError or onComplete
+     * methods have been invoked
+     */
+    @Override
+    public boolean isFinished() {
+        return finished;
+    }
+
+    private volatile Flow.Subscription readSubscription;
+
+    // The DelegateWrapper wraps a subscribed {@code Flow.Subscriber} and
+    // tracks the subscriber's state. In particular it makes sure that
+    // onComplete/onError are not called before onSubscribed.
+    final static class DelegateWrapper implements FlowTube.TubeSubscriber {
+        private final FlowTube.TubeSubscriber delegate;
+        private final System.Logger debug;
+        volatile boolean subscribedCalled;
+        volatile boolean subscribedDone;
+        volatile boolean completed;
+        volatile Throwable error;
+        DelegateWrapper(Flow.Subscriber<? super List<ByteBuffer>> delegate,
+                        System.Logger debug) {
+            this.delegate = FlowTube.asTubeSubscriber(delegate);
+            this.debug = debug;
+        }
+
+        @Override
+        public void dropSubscription() {
+            if (subscribedCalled && !completed) {
+                delegate.dropSubscription();
+            }
+        }
+
+        @Override
+        public void onNext(List<ByteBuffer> item) {
+            assert subscribedCalled;
+            delegate.onNext(item);
+        }
+
+        @Override
+        public void onSubscribe(Flow.Subscription subscription) {
+            onSubscribe(delegate::onSubscribe, subscription);
+        }
+
+        private void onSubscribe(Consumer<Flow.Subscription> method,
+                                 Flow.Subscription subscription) {
+            subscribedCalled = true;
+            method.accept(subscription);
+            Throwable x;
+            boolean finished;
+            synchronized (this) {
+                subscribedDone = true;
+                x = error;
+                finished = completed;
+            }
+            if (x != null) {
+                debug.log(Level.DEBUG,
+                          "Subscriber completed before subscribe: forwarding %s",
+                          (Object)x);
+                delegate.onError(x);
+            } else if (finished) {
+                debug.log(Level.DEBUG,
+                          "Subscriber completed before subscribe: calling onComplete()");
+                delegate.onComplete();
+            }
+        }
+
+        @Override
+        public void onError(Throwable t) {
+            if (completed) {
+                debug.log(Level.DEBUG,
+                          "Subscriber already completed: ignoring %s",
+                          (Object)t);
+                return;
+            }
+            boolean subscribed;
+            synchronized (this) {
+                if (completed) return;
+                error = t;
+                completed = true;
+                subscribed = subscribedDone;
+            }
+            if (subscribed) {
+                delegate.onError(t);
+            } else {
+                debug.log(Level.DEBUG,
+                          "Subscriber not yet subscribed: stored %s",
+                          (Object)t);
+            }
+        }
+
+        @Override
+        public void onComplete() {
+            if (completed) return;
+            boolean subscribed;
+            synchronized (this) {
+                if (completed) return;
+                completed = true;
+                subscribed = subscribedDone;
+            }
+            if (subscribed) {
+                debug.log(Level.DEBUG, "DelegateWrapper: completing subscriber");
+                delegate.onComplete();
+            } else {
+                debug.log(Level.DEBUG,
+                          "Subscriber not yet subscribed: stored completed=true");
+            }
+        }
+
+        @Override
+        public String toString() {
+            return "DelegateWrapper:" + delegate.toString();
+        }
+
+    }
+
+    // Used to read data from the SSLTube.
+    final class SSLSubscriberWrapper implements FlowTube.TubeSubscriber {
+        private AtomicReference<DelegateWrapper> pendingDelegate =
+                new AtomicReference<>();
+        private volatile DelegateWrapper subscribed;
+        private volatile boolean onCompleteReceived;
+        private final AtomicReference<Throwable> errorRef
+                = new AtomicReference<>();
+
+        // setDelegate can be called asynchronously when the SSLTube flow
+        // is connected. At this time the permanent subscriber (this class)
+        // may already be subscribed (readSubscription != null) or not.
+        // 1. If it's already subscribed (readSubscription != null), we
+        //    are going to signal the SSLFlowDelegate reader, and make sure
+        //    onSubscribed is called within the reader flow
+        // 2. If it's not yet subscribed (readSubscription == null), then
+        //    we're going to wait for onSubscribe to be called.
+        //
+        void setDelegate(Flow.Subscriber<? super List<ByteBuffer>> delegate) {
+            debug.log(Level.DEBUG, "SSLSubscriberWrapper (reader) got delegate: %s",
+                      delegate);
+            assert delegate != null;
+            DelegateWrapper delegateWrapper = new DelegateWrapper(delegate, debug);
+            DelegateWrapper previous;
+            Flow.Subscription subscription;
+            boolean handleNow;
+            synchronized (this) {
+                previous = pendingDelegate.getAndSet(delegateWrapper);
+                subscription = readSubscription;
+                handleNow = this.errorRef.get() != null || finished;
+            }
+            if (previous != null) {
+                previous.dropSubscription();
+            }
+            if (subscription == null) {
+                debug.log(Level.DEBUG, "SSLSubscriberWrapper (reader) no subscription yet");
+                return;
+            }
+            if (handleNow || !sslDelegate.resumeReader()) {
+                processPendingSubscriber();
+            }
+        }
+
+        // Can be called outside of the flow if an error has already been
+        // raise. Otherwise, must be called within the SSLFlowDelegate
+        // downstream reader flow.
+        // If there is a subscription, and if there is a pending delegate,
+        // calls dropSubscription() on the previous delegate (if any),
+        // then subscribe the pending delegate.
+        void processPendingSubscriber() {
+            Flow.Subscription subscription;
+            DelegateWrapper delegateWrapper, previous;
+            synchronized (this) {
+                delegateWrapper = pendingDelegate.get();
+                if (delegateWrapper == null) return;
+                subscription = readSubscription;
+                previous = subscribed;
+            }
+            if (subscription == null) {
+                debug.log(Level.DEBUG,
+                         "SSLSubscriberWrapper (reader) %s",
+                         "processPendingSubscriber: no subscription yet");
+                return;
+            }
+            delegateWrapper = pendingDelegate.getAndSet(null);
+            if (delegateWrapper == null) return;
+            if (previous != null) {
+                previous.dropSubscription();
+            }
+            onNewSubscription(delegateWrapper, subscription);
+        }
+
+        @Override
+        public void dropSubscription() {
+            DelegateWrapper subscriberImpl = subscribed;
+            if (subscriberImpl != null) {
+                subscriberImpl.dropSubscription();
+            }
+        }
+
+        @Override
+        public void onSubscribe(Flow.Subscription subscription) {
+            debug.log(Level.DEBUG,
+                      "SSLSubscriberWrapper (reader) onSubscribe(%s)",
+                      subscription);
+            onSubscribeImpl(subscription);
+        }
+
+        // called in the reader flow, from onSubscribe.
+        private void onSubscribeImpl(Flow.Subscription subscription) {
+            assert subscription != null;
+            DelegateWrapper subscriberImpl, pending;
+            synchronized (this) {
+                readSubscription = subscription;
+                subscriberImpl = subscribed;
+                pending = pendingDelegate.get();
+            }
+
+            if (subscriberImpl == null && pending == null) {
+                debug.log(Level.DEBUG,
+                      "SSLSubscriberWrapper (reader) onSubscribeImpl: %s",
+                      "no delegate yet");
+                return;
+            }
+
+            if (pending == null) {
+                // There is no pending delegate, but we have a previously
+                // subscribed delegate. This is obviously a re-subscribe.
+                // We are in the downstream reader flow, so we should call
+                // onSubscribe directly.
+                debug.log(Level.DEBUG,
+                      "SSLSubscriberWrapper (reader) onSubscribeImpl: %s",
+                      "resubscribing");
+                onNewSubscription(subscriberImpl, subscription);
+            } else {
+                // We have some pending subscriber: subscribe it now that we have
+                // a subscription. If we already had a previous delegate then
+                // it will get a dropSubscription().
+                debug.log(Level.DEBUG,
+                      "SSLSubscriberWrapper (reader) onSubscribeImpl: %s",
+                      "subscribing pending");
+                processPendingSubscriber();
+            }
+        }
+
+        private void onNewSubscription(DelegateWrapper subscriberImpl,
+                                       Flow.Subscription subscription) {
+            assert subscriberImpl != null;
+            assert subscription != null;
+
+            Throwable failed;
+            boolean completed;
+            // reset any demand that may have been made by the previous
+            // subscriber
+            sslDelegate.resetReaderDemand();
+            // send the subscription to the subscriber.
+            subscriberImpl.onSubscribe(subscription);
+
+            // The following twisted logic is just here that we don't invoke
+            // onError before onSubscribe. It also prevents race conditions
+            // if onError is invoked concurrently with setDelegate.
+            synchronized (this) {
+                failed = this.errorRef.get();
+                completed = finished;
+                subscribed = subscriberImpl;
+            }
+            if (failed != null) {
+                subscriberImpl.onError(failed);
+            } else if (completed) {
+                subscriberImpl.onComplete();
+            }
+        }
+
+        @Override
+        public void onNext(List<ByteBuffer> item) {
+            subscribed.onNext(item);
+        }
+
+        public void onErrorImpl(Throwable throwable) {
+            // The following twisted logic is just here that we don't invoke
+            // onError before onSubscribe. It also prevents race conditions
+            // if onError is invoked concurrently with setDelegate.
+            // See setDelegate.
+
+            errorRef.compareAndSet(null, throwable);
+            Throwable failed = errorRef.get();
+            finished = true;
+            debug.log(Level.DEBUG, "%s: onErrorImpl: %s", this, throwable);
+            DelegateWrapper subscriberImpl;
+            synchronized (this) {
+                subscriberImpl = subscribed;
+            }
+            if (subscriberImpl != null) {
+                subscriberImpl.onError(failed);
+            } else {
+                debug.log(Level.DEBUG, "%s: delegate null, stored %s", this, failed);
+            }
+            // now if we have any pending subscriber, we should forward
+            // the error to them immediately as the read scheduler will
+            // already be stopped.
+            processPendingSubscriber();
+        }
+
+        @Override
+        public void onError(Throwable throwable) {
+            assert !finished && !onCompleteReceived;
+            onErrorImpl(throwable);
+        }
+
+        private boolean handshaking() {
+            HandshakeStatus hs = engine.getHandshakeStatus();
+            return !(hs == NOT_HANDSHAKING || hs == FINISHED);
+        }
+
+        private boolean handshakeFailed() {
+            // sslDelegate can be null if we reach here
+            // during the initial handshake, as that happens
+            // within the SSLFlowDelegate constructor.
+            // In that case we will want to raise an exception.
+            return handshaking()
+                    && (sslDelegate == null
+                    || !sslDelegate.closeNotifyReceived());
+        }
+
+        @Override
+        public void onComplete() {
+            assert !finished && !onCompleteReceived;
+            onCompleteReceived = true;
+            DelegateWrapper subscriberImpl;
+            synchronized(this) {
+                subscriberImpl = subscribed;
+            }
+
+            if (handshakeFailed()) {
+                debug.log(Level.DEBUG,
+                        "handshake: %s, inbound done: %s outbound done: %s",
+                        engine.getHandshakeStatus(),
+                        engine.isInboundDone(),
+                        engine.isOutboundDone());
+                onErrorImpl(new SSLHandshakeException(
+                        "Remote host terminated the handshake"));
+            } else if (subscriberImpl != null) {
+                finished = true;
+                subscriberImpl.onComplete();
+            }
+            // now if we have any pending subscriber, we should complete
+            // them immediately as the read scheduler will already be stopped.
+            processPendingSubscriber();
+        }
+    }
+
+    @Override
+    public void connectFlows(TubePublisher writePub,
+                             TubeSubscriber readSub) {
+        debug.log(Level.DEBUG, "connecting flows");
+        readSubscriber.setDelegate(readSub);
+        writePub.subscribe(this);
+    }
+
+    /** Outstanding write demand from the SSL Flow Delegate. */
+    private final Demand writeDemand = new Demand();
+
+    final class SSLSubscriptionWrapper implements Flow.Subscription {
+
+        volatile Flow.Subscription delegate;
+
+        void setSubscription(Flow.Subscription sub) {
+            long demand = writeDemand.get(); // FIXME: isn't it a racy way of passing the demand?
+            delegate = sub;
+            debug.log(Level.DEBUG, "setSubscription: demand=%d", demand);
+            if (demand > 0)
+                sub.request(demand);
+        }
+
+        @Override
+        public void request(long n) {
+            writeDemand.increase(n);
+            debug.log(Level.DEBUG, "request: n=%d", n);
+            Flow.Subscription sub = delegate;
+            if (sub != null && n > 0) {
+                sub.request(n);
+            }
+        }
+
+        @Override
+        public void cancel() {
+            // TODO:  no-op or error?
+        }
+    }
+
+    /* Subscriber - writing side */
+    @Override
+    public void onSubscribe(Flow.Subscription subscription) {
+        Objects.requireNonNull(subscription);
+        Flow.Subscription x = writeSubscription.delegate;
+        if (x != null)
+            x.cancel();
+
+        writeSubscription.setSubscription(subscription);
+    }
+
+    @Override
+    public void onNext(List<ByteBuffer> item) {
+        Objects.requireNonNull(item);
+        boolean decremented = writeDemand.tryDecrement();
+        assert decremented : "Unexpected writeDemand: ";
+        debug.log(Level.DEBUG,
+                "sending %d  buffers to SSL flow delegate", item.size());
+        sslDelegate.upstreamWriter().onNext(item);
+    }
+
+    @Override
+    public void onError(Throwable throwable) {
+        Objects.requireNonNull(throwable);
+        sslDelegate.upstreamWriter().onError(throwable);
+    }
+
+    @Override
+    public void onComplete() {
+        sslDelegate.upstreamWriter().onComplete();
+    }
+
+    @Override
+    public String toString() {
+        return dbgString();
+    }
+
+    final String dbgString() {
+        return "SSLTube(" + tube + ")";
+    }
+
+}
diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/common/SequentialScheduler.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/common/SequentialScheduler.java
new file mode 100644
index 00000000000..d3c409d26be
--- /dev/null
+++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/common/SequentialScheduler.java
@@ -0,0 +1,364 @@
+/*
+ * Copyright (c) 2016, 2017, 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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 jdk.incubator.http.internal.common;
+
+import java.util.concurrent.Executor;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import static java.util.Objects.requireNonNull;
+
+/**
+ * A scheduler of ( repeatable ) tasks that MUST be run sequentially.
+ *
+ * <p> This class can be used as a synchronization aid that assists a number of
+ * parties in running a task in a mutually exclusive fashion.
+ *
+ * <p> To run the task, a party invokes {@code runOrSchedule}. To permanently
+ * prevent the task from subsequent runs, the party invokes {@code stop}.
+ *
+ * <p> The parties can, but do not have to, operate in different threads.
+ *
+ * <p> The task can be either synchronous ( completes when its {@code run}
+ * method returns ), or asynchronous ( completed when its
+ * {@code DeferredCompleter} is explicitly completed ).
+ *
+ * <p> The next run of the task will not begin until the previous run has
+ * finished.
+ *
+ * <p> The task may invoke {@code runOrSchedule} itself, which may be a normal
+ * situation.
+ */
+public final class SequentialScheduler {
+
+    /*
+       Since the task is fixed and known beforehand, no blocking synchronization
+       (locks, queues, etc.) is required. The job can be done solely using
+       nonblocking primitives.
+
+       The machinery below addresses two problems:
+
+         1. Running the task in a sequential order (no concurrent runs):
+
+                begin, end, begin, end...
+
+         2. Avoiding indefinite recursion:
+
+                begin
+                  end
+                    begin
+                      end
+                        ...
+
+       Problem #1 is solved with a finite state machine with 4 states:
+
+           BEGIN, AGAIN, END, and STOP.
+
+       Problem #2 is solved with a "state modifier" OFFLOAD.
+
+       Parties invoke `runOrSchedule()` to signal the task must run. A party
+       that has invoked `runOrSchedule()` either begins the task or exploits the
+       party that is either beginning the task or ending it.
+
+       The party that is trying to end the task either ends it or begins it
+       again.
+
+       To avoid indefinite recursion, before re-running the task the
+       TryEndDeferredCompleter sets the OFFLOAD bit, signalling to its "child"
+       TryEndDeferredCompleter that this ("parent") TryEndDeferredCompleter is
+       available and the "child" must offload the task on to the "parent". Then
+       a race begins. Whichever invocation of TryEndDeferredCompleter.complete
+       manages to unset OFFLOAD bit first does not do the work.
+
+       There is at most 1 thread that is beginning the task and at most 2
+       threads that are trying to end it: "parent" and "child". In case of a
+       synchronous task "parent" and "child" are the same thread.
+     */
+
+    /**
+     * An interface to signal the completion of a {@link RestartableTask}.
+     *
+     * <p> The invocation of {@code complete} completes the task. The invocation
+     * of {@code complete} may restart the task, if an attempt has previously
+     * been made to run the task while it was already running.
+     *
+     * @apiNote {@code DeferredCompleter} is useful when a task is not necessary
+     * complete when its {@code run} method returns, but will complete at a
+     * later time, and maybe in different thread. This type exists for
+     * readability purposes at use-sites only.
+     */
+    public static abstract class DeferredCompleter {
+
+        /** Extensible from this (outer) class ONLY. */
+        private DeferredCompleter() { }
+
+        /** Completes the task. Must be called once, and once only. */
+        public abstract void complete();
+    }
+
+    /**
+     * A restartable task.
+     */
+    @FunctionalInterface
+    public interface RestartableTask {
+
+        /**
+         * The body of the task.
+         *
+         * @param taskCompleter
+         *         A completer that must be invoked once, and only once,
+         *         when this task is logically finished
+         */
+        void run(DeferredCompleter taskCompleter);
+    }
+
+    /**
+     * A complete restartable task is one which is simple and self-contained.
+     * It completes once its {@code run} method returns.
+     */
+    public static abstract class CompleteRestartableTask
+        implements RestartableTask
+    {
+        @Override
+        public final void run(DeferredCompleter taskCompleter) {
+            try {
+                run();
+            } finally {
+                taskCompleter.complete();
+            }
+        }
+
+        /** The body of the task. */
+        protected abstract void run();
+    }
+
+    /**
+     * A RestartableTask that runs its main loop within a
+     * synchronized block to place a memory barrier around it.
+     * Because the main loop can't run concurrently in two treads,
+     * then the lock shouldn't be contended and no deadlock should
+     * ever be possible.
+     */
+    public static final class SynchronizedRestartableTask
+            extends CompleteRestartableTask {
+        private final Runnable mainLoop;
+        private final Object lock = new Object();
+        public SynchronizedRestartableTask(Runnable mainLoop) {
+            this.mainLoop = mainLoop;
+        }
+
+        @Override
+        protected void run() {
+            synchronized(lock) {
+                mainLoop.run();
+            }
+        }
+    }
+
+    private static final int OFFLOAD =  1;
+    private static final int AGAIN   =  2;
+    private static final int BEGIN   =  4;
+    private static final int STOP    =  8;
+    private static final int END     = 16;
+
+    private final AtomicInteger state = new AtomicInteger(END);
+    private final RestartableTask restartableTask;
+    private final DeferredCompleter completer;
+    private final SchedulableTask schedulableTask;
+
+    /**
+     * A simple task that can be pushed on an executor to execute
+     * {@code restartableTask.run(completer)}.
+     */
+    private final class SchedulableTask implements Runnable {
+        @Override
+        public void run() {
+            restartableTask.run(completer);
+        }
+    }
+
+    public SequentialScheduler(RestartableTask restartableTask) {
+        this.restartableTask = requireNonNull(restartableTask);
+        this.completer = new TryEndDeferredCompleter();
+        this.schedulableTask = new SchedulableTask();
+    }
+
+    /**
+     * Runs or schedules the task to be run.
+     *
+     * @implSpec The recursion which is possible here must be bounded:
+     *
+     *  <pre>{@code
+     *     this.runOrSchedule()
+     *         completer.complete()
+     *             this.runOrSchedule()
+     *                 ...
+     * }</pre>
+     *
+     * @implNote The recursion in this implementation has the maximum
+     * depth of 1.
+     */
+    public void runOrSchedule() {
+        runOrSchedule(schedulableTask, null);
+    }
+
+    /**
+     * Runs or schedules the task to be run in the provided executor.
+     *
+     * <p> This method can be used when potential executing from a calling
+     * thread is not desirable.
+     *
+     * @param executor
+     *         An executor in which to execute the task, if the task needs
+     *         to be executed.
+     *
+     * @apiNote The given executor can be {@code null} in which case calling
+     * {@code deferOrSchedule(null)} is strictly equivalent to calling
+     * {@code runOrSchedule()}.
+     */
+    public void deferOrSchedule(Executor executor) { // TODO: why this name? why not runOrSchedule?
+        runOrSchedule(schedulableTask, executor);
+    }
+
+    private void runOrSchedule(SchedulableTask task, Executor executor) {
+        while (true) {
+            int s = state.get();
+            if (s == END) {
+                if (state.compareAndSet(END, BEGIN)) {
+                    break;
+                }
+            } else if ((s & BEGIN) != 0) {
+                // Tries to change the state to AGAIN, preserving OFFLOAD bit
+                if (state.compareAndSet(s, AGAIN | (s & OFFLOAD))) {
+                    return;
+                }
+            } else if ((s & AGAIN) != 0 || s == STOP) {
+                /* In the case of AGAIN the scheduler does not provide
+                   happens-before relationship between actions prior to
+                   runOrSchedule() and actions that happen in task.run().
+                   The reason is that no volatile write is done in this case,
+                   and the call piggybacks on the call that has actually set
+                   AGAIN state. */
+                return;
+            } else {
+                // Non-existent state, or the one that cannot be offloaded
+                throw new InternalError(String.valueOf(s));
+            }
+        }
+        if (executor == null) {
+            task.run();
+        } else {
+            executor.execute(task);
+        }
+    }
+
+    /** The only concrete {@code DeferredCompleter} implementation. */
+    private class TryEndDeferredCompleter extends DeferredCompleter {
+
+        @Override
+        public void complete() {
+            while (true) {
+                int s;
+                while (((s = state.get()) & OFFLOAD) != 0) {
+                    // Tries to offload ending of the task to the parent
+                    if (state.compareAndSet(s, s & ~OFFLOAD)) {
+                        return;
+                    }
+                }
+                while (true) {
+                    if ((s & OFFLOAD) != 0) {
+                        /* OFFLOAD bit can never be observed here. Otherwise
+                           it would mean there is another invocation of
+                           "complete" that can run the task. */
+                        throw new InternalError(String.valueOf(s));
+                    }
+                    if (s == BEGIN) {
+                        if (state.compareAndSet(BEGIN, END)) {
+                            return;
+                        }
+                    } else if (s == AGAIN) {
+                        if (state.compareAndSet(AGAIN, BEGIN | OFFLOAD)) {
+                            break;
+                        }
+                    } else if (s == STOP) {
+                        return;
+                    } else if (s == END) {
+                        throw new IllegalStateException("Duplicate completion");
+                    } else {
+                        // Non-existent state
+                        throw new InternalError(String.valueOf(s));
+                    }
+                    s = state.get();
+                }
+                restartableTask.run(completer);
+            }
+        }
+    }
+
+    /**
+     * Tells whether, or not, this scheduler has been permanently stopped.
+     *
+     * <p> Should be used from inside the task to poll the status of the
+     * scheduler, pretty much the same way as it is done for threads:
+     * <pre>{@code
+     *     if (!Thread.currentThread().isInterrupted()) {
+     *         ...
+     *     }
+     * }</pre>
+     */
+    public boolean isStopped() {
+        return state.get() == STOP;
+    }
+
+    /**
+     * Stops this scheduler.  Subsequent invocations of {@code runOrSchedule}
+     * are effectively no-ops.
+     *
+     * <p> If the task has already begun, this invocation will not affect it,
+     * unless the task itself uses {@code isStopped()} method to check the state
+     * of the handler.
+     */
+    public void stop() {
+        state.set(STOP);
+    }
+
+    /**
+     * Returns a new {@code SequentialScheduler} that executes the provided
+     * {@code mainLoop} from within a {@link SynchronizedRestartableTask}.
+     *
+     * @apiNote
+     * This is equivalent to calling
+     * {@code new SequentialScheduler(new SynchronizedRestartableTask(mainloop));}
+     * The main loop must not do any blocking operation.
+     *
+     * @param mainloop The main loop of the new sequential scheduler.
+     * @return a new {@code SequentialScheduler} that executes the provided
+     * {@code mainLoop} from within a {@link SynchronizedRestartableTask}.
+     */
+    public static SequentialScheduler synchronizedScheduler(Runnable mainloop) {
+        return new SequentialScheduler(new SynchronizedRestartableTask(mainloop));
+    }
+
+}
diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/common/SubscriberWrapper.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/common/SubscriberWrapper.java
new file mode 100644
index 00000000000..e51cb7380e4
--- /dev/null
+++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/common/SubscriberWrapper.java
@@ -0,0 +1,460 @@
+/*
+ * Copyright (c) 2017, 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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 jdk.incubator.http.internal.common;
+
+import java.io.Closeable;
+import java.lang.System.Logger.Level;
+import java.nio.ByteBuffer;
+import java.util.List;
+import java.util.Objects;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ConcurrentLinkedQueue;
+import java.util.concurrent.Flow;
+import java.util.concurrent.Flow.Subscriber;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.concurrent.atomic.AtomicReference;
+
+/**
+ * A wrapper for a Flow.Subscriber. This wrapper delivers data to the wrapped
+ * Subscriber which is supplied to the constructor. This class takes care of
+ * downstream flow control automatically and upstream flow control automatically
+ * by default.
+ * <p>
+ * Processing is done by implementing the {@link #incoming(List, boolean)} method
+ * which supplies buffers from upstream. This method (or any other method)
+ * can then call the outgoing() method to deliver processed buffers downstream.
+ * <p>
+ * Upstream error signals are delivered downstream directly. Cancellation from
+ * downstream is also propagated upstream immediately.
+ * <p>
+ * Each SubscriberWrapper has a {@link java.util.concurrent.CompletableFuture}{@code <Void>}
+ * which propagates completion/errors from downstream to upstream. Normal completion
+ * can only occur after onComplete() is called, but errors can be propagated upwards
+ * at any time.
+ */
+public abstract class SubscriberWrapper
+    implements FlowTube.TubeSubscriber, Closeable, Flow.Processor<List<ByteBuffer>,List<ByteBuffer>>
+                // TODO: SSLTube Subscriber will never change? Does this really need to be a TS?
+{
+    static final boolean DEBUG = Utils.DEBUG; // Revisit: temporary dev flag.
+    final System.Logger logger =
+            Utils.getDebugLogger(this::dbgString, DEBUG);
+
+    public enum SchedulingAction { CONTINUE, RETURN, RESCHEDULE }
+
+    volatile Flow.Subscription upstreamSubscription;
+    final SubscriptionBase downstreamSubscription;
+    volatile boolean upstreamCompleted;
+    volatile boolean downstreamCompleted;
+    volatile boolean completionAcknowledged;
+    private volatile Subscriber<? super List<ByteBuffer>> downstreamSubscriber;
+    // processed byte to send to the downstream subscriber.
+    private final ConcurrentLinkedQueue<List<ByteBuffer>> outputQ;
+    private final CompletableFuture<Void> cf;
+    private final SequentialScheduler pushScheduler;
+    private final AtomicReference<Throwable> errorRef = new AtomicReference<>();
+    final AtomicLong upstreamWindow = new AtomicLong(0);
+
+    /**
+     * Wraps the given downstream subscriber. For each call to {@link
+     * #onNext(List<ByteBuffer>) } the given filter function is invoked
+     * and the list (if not empty) returned is passed downstream.
+     *
+     * A {@code CompletableFuture} is supplied which can be used to signal an
+     * error from downstream and which terminates the wrapper or which signals
+     * completion of downstream activity which can be propagated upstream. Error
+     * completion can be signaled at any time, but normal completion must not be
+     * signaled before onComplete() is called.
+     */
+    public SubscriberWrapper()
+    {
+        this.outputQ = new ConcurrentLinkedQueue<>();
+        this.cf = new MinimalFuture<>();
+        this.pushScheduler =
+                SequentialScheduler.synchronizedScheduler(new DownstreamPusher());
+        this.downstreamSubscription = new SubscriptionBase(pushScheduler,
+                                                           this::downstreamCompletion);
+    }
+
+    @Override
+    public final void subscribe(Subscriber<?  super List<ByteBuffer>> downstreamSubscriber) {
+        Objects.requireNonNull(downstreamSubscriber);
+        this.downstreamSubscriber = downstreamSubscriber;
+    }
+
+    /**
+     * Wraps the given downstream wrapper in this. For each call to
+     * {@link #onNext(List<ByteBuffer>) } the incoming() method is called.
+     *
+     * The {@code downstreamCF} from the downstream wrapper is linked to this
+     * wrappers notifier.
+     *
+     * @param downstreamWrapper downstream destination
+     */
+    public SubscriberWrapper(Subscriber<? super List<ByteBuffer>> downstreamWrapper)
+    {
+        this();
+        subscribe(downstreamWrapper);
+    }
+
+    /**
+     * Delivers data to be processed by this wrapper. Generated data to be sent
+     * downstream, must be provided to the {@link #outgoing(List, boolean)}}
+     * method.
+     *
+     * @param buffers a List of ByteBuffers.
+     * @param complete if true then no more data will be added to the list
+     */
+    protected abstract void incoming(List<ByteBuffer> buffers, boolean complete);
+
+    /**
+     * This method is called to determine the window size to use at any time. The
+     * current window is supplied together with the current downstream queue size.
+     * {@code 0} should be returned if no change is
+     * required or a positive integer which will be added to the current window.
+     * The default implementation maintains a downstream queue size of no greater
+     * than 5. The method can be overridden if required.
+     *
+     * @param currentWindow the current upstream subscription window
+     * @param downstreamQsize the current number of buffers waiting to be sent
+     *                        downstream
+     *
+     * @return value to add to currentWindow
+     */
+    protected long upstreamWindowUpdate(long currentWindow, long downstreamQsize) {
+        if (downstreamQsize > 5) {
+            return 0;
+        }
+
+        if (currentWindow == 0) {
+            return 1;
+        } else {
+            return 0;
+        }
+    }
+
+    /**
+     * Override this if anything needs to be done after the upstream subscriber
+     * has subscribed
+     */
+    protected void onSubscribe() {
+    }
+
+    /**
+     * Override this if anything needs to be done before checking for error
+     * and processing the input queue.
+     * @return
+     */
+    protected SchedulingAction enterScheduling() {
+        return SchedulingAction.CONTINUE;
+    }
+
+    protected boolean signalScheduling() {
+        if (downstreamCompleted || pushScheduler.isStopped()) {
+            return false;
+        }
+        pushScheduler.runOrSchedule();
+        return true;
+    }
+
+    /**
+     * Delivers buffers of data downstream. After incoming()
+     * has been called complete == true signifying completion of the upstream
+     * subscription, data may continue to be delivered, up to when outgoing() is
+     * called complete == true, after which, the downstream subscription is
+     * completed.
+     *
+     * It's an error to call outgoing() with complete = true if incoming() has
+     * not previously been called with it.
+     */
+    public void outgoing(ByteBuffer buffer, boolean complete) {
+        Objects.requireNonNull(buffer);
+        assert !complete || !buffer.hasRemaining();
+        outgoing(List.of(buffer), complete);
+    }
+
+    /**
+     * Sometime it might be necessary to complete the downstream subscriber
+     * before the upstream completes. For instance, when an SSL server
+     * sends a notify_close. In that case we should let the outgoing
+     * complete before upstream us completed.
+     * @return true, may be overridden by subclasses.
+     */
+    public boolean closing() {
+        return false;
+    }
+
+    public void outgoing(List<ByteBuffer> buffers, boolean complete) {
+        Objects.requireNonNull(buffers);
+        if (complete) {
+            assert Utils.remaining(buffers) == 0;
+            boolean closing = closing();
+            logger.log(Level.DEBUG,
+                    "completionAcknowledged upstreamCompleted:%s, downstreamCompleted:%s, closing:%s",
+                    upstreamCompleted, downstreamCompleted, closing);
+            if (!upstreamCompleted && !closing)
+                throw new IllegalStateException("upstream not completed");
+            completionAcknowledged = true;
+        } else {
+            logger.log(Level.DEBUG, () -> "Adding "
+                                   + Utils.remaining(buffers)
+                                   + " to outputQ queue");
+            outputQ.add(buffers);
+        }
+        logger.log(Level.DEBUG, () -> "pushScheduler "
+                   + (pushScheduler.isStopped() ? " is stopped!" : " is alive"));
+        pushScheduler.runOrSchedule();
+    }
+
+    /**
+     * Returns a CompletableFuture which completes when this wrapper completes.
+     * Normal completion happens with the following steps (in order):
+     *   1. onComplete() is called
+     *   2. incoming() called with complete = true
+     *   3. outgoing() may continue to be called normally
+     *   4. outgoing called with complete = true
+     *   5. downstream subscriber is called onComplete()
+     *
+     * If the subscription is canceled or onComplete() is invoked the
+     * CompletableFuture completes exceptionally. Exceptional completion
+     * also occurs if downstreamCF completes exceptionally.
+     */
+    public CompletableFuture<Void> completion() {
+        return cf;
+    }
+
+    /**
+     * Invoked whenever it 'may' be possible to push buffers downstream.
+     */
+    class DownstreamPusher implements Runnable {
+        @Override
+        public void run() {
+            try {
+                run1();
+            } catch (Throwable t) {
+                errorCommon(t);
+            }
+        }
+
+        private void run1() {
+            if (downstreamCompleted) {
+                logger.log(Level.DEBUG, "DownstreamPusher: downstream is already completed");
+                return;
+            }
+            switch (enterScheduling()) {
+                case CONTINUE: break;
+                case RESCHEDULE: pushScheduler.runOrSchedule(); return;
+                case RETURN: return;
+                default:
+                    errorRef.compareAndSet(null,
+                            new InternalError("unknown scheduling command"));
+                    break;
+            }
+            // If there was an error, send it downstream.
+            Throwable error = errorRef.get();
+            if (error != null) {
+                synchronized(this) {
+                    if (downstreamCompleted) return;
+                    downstreamCompleted = true;
+                }
+                logger.log(Level.DEBUG,
+                        () -> "DownstreamPusher: forwarding error downstream: " + error);
+                pushScheduler.stop();
+                outputQ.clear();
+                downstreamSubscriber.onError(error);
+                return;
+            }
+
+            // OK - no error, let's proceed
+            if (!outputQ.isEmpty()) {
+                logger.log(Level.DEBUG,
+                    "DownstreamPusher: queue not empty, downstreamSubscription: %s",
+                     downstreamSubscription);
+            } else {
+                logger.log(Level.DEBUG,
+                       "DownstreamPusher: queue empty, downstreamSubscription: %s",
+                       downstreamSubscription);
+            }
+
+            final boolean dbgOn = logger.isLoggable(Level.DEBUG);
+            while (!outputQ.isEmpty() && downstreamSubscription.tryDecrement()) {
+                List<ByteBuffer> b = outputQ.poll();
+                if (dbgOn) logger.log(Level.DEBUG,
+                                            "DownstreamPusher: Pushing "
+                                            + Utils.remaining(b)
+                                            + " bytes downstream");
+                downstreamSubscriber.onNext(b);
+            }
+            upstreamWindowUpdate();
+            checkCompletion();
+        }
+    }
+
+    void upstreamWindowUpdate() {
+        long downstreamQueueSize = outputQ.size();
+        long n = upstreamWindowUpdate(upstreamWindow.get(), downstreamQueueSize);
+        if (n > 0)
+            upstreamRequest(n);
+    }
+
+    @Override
+    public void onSubscribe(Flow.Subscription subscription) {
+        if (upstreamSubscription != null) {
+            throw new IllegalStateException("Single shot publisher");
+        }
+        this.upstreamSubscription = subscription;
+        upstreamRequest(upstreamWindowUpdate(0, 0));
+        logger.log(Level.DEBUG,
+               "calling downstreamSubscriber::onSubscribe on %s",
+               downstreamSubscriber);
+        downstreamSubscriber.onSubscribe(downstreamSubscription);
+        onSubscribe();
+    }
+
+    @Override
+    public void onNext(List<ByteBuffer> item) {
+        logger.log(Level.DEBUG, "onNext");
+        long prev = upstreamWindow.getAndDecrement();
+        if (prev <= 0)
+            throw new IllegalStateException("invalid onNext call");
+        incomingCaller(item, false);
+        upstreamWindowUpdate();
+    }
+
+    private void upstreamRequest(long n) {
+        logger.log(Level.DEBUG, "requesting %d", n);
+        upstreamWindow.getAndAdd(n);
+        upstreamSubscription.request(n);
+    }
+
+    protected void requestMore() {
+        if (upstreamWindow.get() == 0) {
+            upstreamRequest(1);
+        }
+    }
+
+    public long upstreamWindow() {
+        return upstreamWindow.get();
+    }
+
+    @Override
+    public void onError(Throwable throwable) {
+        logger.log(Level.DEBUG, () -> "onError: " + throwable);
+        errorCommon(Objects.requireNonNull(throwable));
+    }
+
+    protected boolean errorCommon(Throwable throwable) {
+        assert throwable != null;
+        if (errorRef.compareAndSet(null, throwable)) {
+            logger.log(Level.DEBUG, "error", throwable);
+            pushScheduler.runOrSchedule();
+            upstreamCompleted = true;
+            cf.completeExceptionally(throwable);
+            return true;
+        }
+        return false;
+    }
+
+    @Override
+    public void close() {
+        errorCommon(new RuntimeException("wrapper closed"));
+    }
+
+    private void incomingCaller(List<ByteBuffer> l, boolean complete) {
+        try {
+            incoming(l, complete);
+        } catch(Throwable t) {
+            errorCommon(t);
+        }
+    }
+
+    @Override
+    public void onComplete() {
+        logger.log(Level.DEBUG, () -> "upstream completed: " + toString());
+        upstreamCompleted = true;
+        incomingCaller(Utils.EMPTY_BB_LIST, true);
+        // pushScheduler will call checkCompletion()
+        pushScheduler.runOrSchedule();
+    }
+
+    /** Adds the given data to the input queue. */
+    public void addData(ByteBuffer l) {
+        if (upstreamSubscription == null) {
+            throw new IllegalStateException("can't add data before upstream subscriber subscribes");
+        }
+        incomingCaller(List.of(l), false);
+    }
+
+    void checkCompletion() {
+        if (downstreamCompleted || !upstreamCompleted) {
+            return;
+        }
+        if (!outputQ.isEmpty()) {
+            return;
+        }
+        if (errorRef.get() != null) {
+            pushScheduler.runOrSchedule();
+            return;
+        }
+        if (completionAcknowledged) {
+            logger.log(Level.DEBUG, "calling downstreamSubscriber.onComplete()");
+            downstreamSubscriber.onComplete();
+            // Fix me subscriber.onComplete.run();
+            downstreamCompleted = true;
+            cf.complete(null);
+        }
+    }
+
+    // called from the downstream Subscription.cancel()
+    void downstreamCompletion() {
+        upstreamSubscription.cancel();
+        cf.complete(null);
+    }
+
+    public void resetDownstreamDemand() {
+        downstreamSubscription.demand.reset();
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+        sb.append("SubscriberWrapper:")
+          .append(" upstreamCompleted: ").append(Boolean.toString(upstreamCompleted))
+          .append(" upstreamWindow: ").append(upstreamWindow.toString())
+          .append(" downstreamCompleted: ").append(Boolean.toString(downstreamCompleted))
+          .append(" completionAcknowledged: ").append(Boolean.toString(completionAcknowledged))
+          .append(" outputQ size: ").append(Integer.toString(outputQ.size()))
+          //.append(" outputQ: ").append(outputQ.toString())
+          .append(" cf: ").append(cf.toString())
+          .append(" downstreamSubscription: ").append(downstreamSubscription.toString());
+
+        return sb.toString();
+    }
+
+    public String dbgString() {
+        return "SubscriberWrapper";
+    }
+}
diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/common/SubscriptionBase.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/common/SubscriptionBase.java
new file mode 100644
index 00000000000..62b6f6d9bc7
--- /dev/null
+++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/common/SubscriptionBase.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (c) 2017, 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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 jdk.incubator.http.internal.common;
+
+import java.util.concurrent.Flow;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+/**
+ * Maintains subscription counter and provides primitives for
+ * - accessing window
+ * - reducing window when delivering items externally
+ * - resume delivery when window was zero previously
+ *
+ * @author mimcmah
+ */
+public class SubscriptionBase implements Flow.Subscription {
+
+    final Demand demand = new Demand();
+
+    final SequentialScheduler scheduler; // when window was zero and is opened, run this
+    final Runnable cancelAction; // when subscription cancelled, run this
+    final AtomicBoolean cancelled;
+
+    public SubscriptionBase(SequentialScheduler scheduler, Runnable cancelAction) {
+        this.scheduler = scheduler;
+        this.cancelAction = cancelAction;
+        this.cancelled = new AtomicBoolean(false);
+    }
+
+    @Override
+    public void request(long n) {
+        if (demand.increase(n))
+            scheduler.runOrSchedule();
+    }
+
+
+
+    @Override
+    public synchronized String toString() {
+        return "SubscriptionBase: window = " + demand.get() +
+                " cancelled = " + cancelled.toString();
+    }
+
+    /**
+     * Returns true if the window was reduced by 1. In that case
+     * items must be supplied to subscribers and the scheduler run
+     * externally. If the window could not be reduced by 1, then false
+     * is returned and the scheduler will run later when the window is updated.
+     */
+    public boolean tryDecrement() {
+        return demand.tryDecrement();
+    }
+
+    public long window() {
+        return demand.get();
+    }
+
+    @Override
+    public void cancel() {
+        if (cancelled.getAndSet(true))
+            return;
+        scheduler.stop();
+        cancelAction.run();
+    }
+}
diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/common/Utils.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/common/Utils.java
index 516c683a89f..ec0c4a65e14 100644
--- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/common/Utils.java
+++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/common/Utils.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2017, 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
@@ -25,7 +25,7 @@
 
 package jdk.incubator.http.internal.common;
 
-import jdk.internal.misc.InnocuousThread;
+import jdk.incubator.http.HttpHeaders;
 import sun.net.NetProperties;
 import sun.net.util.IPAddressUtil;
 
@@ -33,11 +33,12 @@ import javax.net.ssl.SSLParameters;
 import java.io.ByteArrayOutputStream;
 import java.io.Closeable;
 import java.io.IOException;
-import java.io.UncheckedIOException;
 import java.io.PrintStream;
+import java.io.UncheckedIOException;
 import java.io.UnsupportedEncodingException;
+import java.lang.System.Logger;
+import java.lang.System.Logger.Level;
 import java.net.InetSocketAddress;
-import java.net.NetPermission;
 import java.net.URI;
 import java.net.URLPermission;
 import java.nio.ByteBuffer;
@@ -48,29 +49,45 @@ import java.security.PrivilegedAction;
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.List;
-import java.util.Map;
-import java.util.Optional;
 import java.util.Set;
-import java.util.concurrent.BlockingQueue;
-import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.Executor;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.CompletionException;
+import java.util.concurrent.ExecutionException;
 import java.util.function.Predicate;
-import jdk.incubator.http.HttpHeaders;
+import java.util.function.Supplier;
+import java.util.stream.Stream;
+
+import static java.util.stream.Collectors.joining;
 
 /**
  * Miscellaneous utilities
  */
 public final class Utils {
 
+    public static final boolean ASSERTIONSENABLED;
+    static {
+        boolean enabled = false;
+        assert enabled = true;
+        ASSERTIONSENABLED = enabled;
+    }
+//    public static final boolean TESTING;
+//    static {
+//        if (ASSERTIONSENABLED) {
+//            PrivilegedAction<String> action = () -> System.getProperty("test.src");
+//            TESTING = AccessController.doPrivileged(action) != null;
+//        } else TESTING = false;
+//    }
+    public static final boolean DEBUG = // Revisit: temporary dev flag.
+            getBooleanProperty(DebugLogger.HTTP_NAME, false);
+    public static final boolean DEBUG_HPACK = // Revisit: temporary dev flag.
+            getBooleanProperty(DebugLogger.HPACK_NAME, false);
+    public static final boolean TESTING = DEBUG;
+
     /**
      * Allocated buffer size. Must never be higher than 16K. But can be lower
      * if smaller allocation units preferred. HTTP/2 mandates that all
      * implementations support frame payloads of at least 16K.
      */
-    public static final int DEFAULT_BUFSIZE = 16 * 1024;
+    private static final int DEFAULT_BUFSIZE = 16 * 1024;
 
     public static final int BUFSIZE = getIntegerNetProperty(
             "jdk.httpclient.bufsize", DEFAULT_BUFSIZE
@@ -84,13 +101,17 @@ public final class Utils {
     public static final Predicate<String>
         ALLOWED_HEADERS = header -> !Utils.DISALLOWED_HEADERS_SET.contains(header);
 
-    public static final Predicate<String>
-        ALL_HEADERS = header -> true;
-
     public static ByteBuffer getBuffer() {
         return ByteBuffer.allocate(BUFSIZE);
     }
 
+    public static Throwable getCompletionCause(Throwable x) {
+        if (!(x instanceof CompletionException)
+                && !(x instanceof ExecutionException)) return x;
+        final Throwable cause = x.getCause();
+        return cause == null ? x : cause;
+    }
+
     public static IOException getIOException(Throwable t) {
         if (t instanceof IOException) {
             return (IOException) t;
@@ -102,39 +123,44 @@ public final class Utils {
         return new IOException(t);
     }
 
-    /**
-     * We use the same buffer for reading all headers and dummy bodies in an Exchange.
-     */
-    public static ByteBuffer getExchangeBuffer() {
-        ByteBuffer buf = getBuffer();
-        // Force a read the first time it is used
-        buf.limit(0);
-        return buf;
-    }
-
-    /**
-     * Puts position to limit and limit to capacity so we can resume reading
-     * into this buffer, but if required > 0 then limit may be reduced so that
-     * no more than required bytes are read next time.
-     */
-    static void resumeChannelRead(ByteBuffer buf, int required) {
-        int limit = buf.limit();
-        buf.position(limit);
-        int capacity = buf.capacity() - limit;
-        if (required > 0 && required < capacity) {
-            buf.limit(limit + required);
-        } else {
-            buf.limit(buf.capacity());
-        }
-    }
-
     private Utils() { }
 
-    public static ExecutorService innocuousThreadPool() {
-        return Executors.newCachedThreadPool(
-                (r) -> InnocuousThread.newThread("DefaultHttpClient", r));
+    /**
+     * Returns the security permissions required to connect to the proxy, or
+     * {@code null} if none is required or applicable.
+     */
+    public static URLPermission permissionForProxy(InetSocketAddress proxyAddress) {
+        if (proxyAddress == null)
+            return null;
+
+        StringBuilder sb = new StringBuilder();
+        sb.append("socket://")
+          .append(proxyAddress.getHostString()).append(":")
+          .append(proxyAddress.getPort());
+        String urlString = sb.toString();
+        return new URLPermission(urlString, "CONNECT");
     }
 
+    /**
+     * Returns the security permission required for the given details.
+     */
+    public static URLPermission permissionForServer(URI uri,
+                                                    String method,
+                                                    Stream<String> headers) {
+        String urlString = new StringBuilder()
+                .append(uri.getScheme()).append("://")
+                .append(uri.getAuthority())
+                .append(uri.getPath()).toString();
+
+        StringBuilder actionStringBuilder = new StringBuilder(method);
+        String collected = headers.collect(joining(","));
+        if (!collected.isEmpty()) {
+            actionStringBuilder.append(":").append(collected);
+        }
+        return new URLPermission(urlString, actionStringBuilder.toString());
+    }
+
+
     // ABNF primitives defined in RFC 7230
     private static final boolean[] tchar      = new boolean[256];
     private static final boolean[] fieldvchar = new boolean[256];
@@ -217,55 +243,6 @@ public final class Utils {
         return accepted;
     }
 
-    /**
-     * Returns the security permission required for the given details.
-     * If method is CONNECT, then uri must be of form "scheme://host:port"
-     */
-    public static URLPermission getPermission(URI uri,
-                                              String method,
-                                              Map<String, List<String>> headers) {
-        StringBuilder sb = new StringBuilder();
-
-        String urlstring, actionstring;
-
-        if (method.equals("CONNECT")) {
-            urlstring = uri.toString();
-            actionstring = "CONNECT";
-        } else {
-            sb.append(uri.getScheme())
-                    .append("://")
-                    .append(uri.getAuthority())
-                    .append(uri.getPath());
-            urlstring = sb.toString();
-
-            sb = new StringBuilder();
-            sb.append(method);
-            if (headers != null && !headers.isEmpty()) {
-                sb.append(':');
-                Set<String> keys = headers.keySet();
-                boolean first = true;
-                for (String key : keys) {
-                    if (!first) {
-                        sb.append(',');
-                    }
-                    sb.append(key);
-                    first = false;
-                }
-            }
-            actionstring = sb.toString();
-        }
-        return new URLPermission(urlstring, actionstring);
-    }
-
-    public static void checkNetPermission(String target) {
-        SecurityManager sm = System.getSecurityManager();
-        if (sm == null) {
-            return;
-        }
-        NetPermission np = new NetPermission(target);
-        sm.checkPermission(np);
-    }
-
     public static int getIntegerNetProperty(String name, int defaultValue) {
         return AccessController.doPrivileged((PrivilegedAction<Integer>) () ->
                 NetProperties.getInteger(name, defaultValue));
@@ -276,6 +253,11 @@ public final class Utils {
                 NetProperties.get(name));
     }
 
+    static boolean getBooleanProperty(String name, boolean def) {
+        return AccessController.doPrivileged((PrivilegedAction<Boolean>) () ->
+                Boolean.parseBoolean(System.getProperty(name, String.valueOf(def))));
+    }
+
     public static SSLParameters copySSLParameters(SSLParameters p) {
         SSLParameters p1 = new SSLParameters();
         p1.setAlgorithmConstraints(p.getAlgorithmConstraints());
@@ -313,7 +295,7 @@ public final class Utils {
             t.printStackTrace(p);
             s = bos.toString("US-ASCII");
         } catch (UnsupportedEncodingException ex) {
-            // can't happen
+            throw new InternalError(ex); // Can't happen
         }
         return s;
     }
@@ -337,28 +319,47 @@ public final class Utils {
         return srcLen - src.remaining();
     }
 
-    // copy up to amount from src to dst, but no more
-    public static int copyUpTo(ByteBuffer src, ByteBuffer dst, int amount) {
-        int toCopy = Math.min(src.remaining(), Math.min(dst.remaining(), amount));
-        copy(src, dst, toCopy);
-        return toCopy;
-    }
+    /** Threshold beyond which data is no longer copied into the current
+     * buffer, if that buffer has enough unused space. */
+    private static final int COPY_THRESHOLD = 8192;
 
     /**
-     * Copy amount bytes from src to dst. at least amount must be
-     * available in both dst and in src
+     * Adds the data from buffersToAdd to currentList. Either 1) appends the
+     * data from a particular buffer to the last buffer in the list ( if
+     * there is enough unused space ), or 2) adds it to the list.
+     *
+     * @return the number of bytes added
      */
-    public static void copy(ByteBuffer src, ByteBuffer dst, int amount) {
-        int excess = src.remaining() - amount;
-        assert excess >= 0;
-        if (excess > 0) {
-            int srclimit = src.limit();
-            src.limit(srclimit - excess);
-            dst.put(src);
-            src.limit(srclimit);
-        } else {
-            dst.put(src);
+    public static long accumulateBuffers(List<ByteBuffer> currentList,
+                                         List<ByteBuffer> buffersToAdd) {
+        long accumulatedBytes = 0;
+        for (ByteBuffer bufferToAdd : buffersToAdd) {
+            int remaining = bufferToAdd.remaining();
+            if (remaining <= 0)
+                continue;
+            int listSize = currentList.size();
+            if (listSize == 0) {
+                currentList.add(bufferToAdd);
+                accumulatedBytes = remaining;
+                continue;
+            }
+
+            ByteBuffer lastBuffer = currentList.get(listSize - 1);
+            int freeSpace = lastBuffer.capacity() - lastBuffer.limit();
+            if (remaining <= COPY_THRESHOLD && freeSpace >= remaining) {
+                // append the new data to the unused space in the last buffer
+                int position = lastBuffer.position();
+                int limit = lastBuffer.limit();
+                lastBuffer.position(limit);
+                lastBuffer.limit(limit + remaining);
+                lastBuffer.put(bufferToAdd);
+                lastBuffer.position(position);
+            } else {
+                currentList.add(bufferToAdd);
+            }
+            accumulatedBytes += remaining;
         }
+        return accumulatedBytes;
     }
 
     public static ByteBuffer copy(ByteBuffer src) {
@@ -378,34 +379,75 @@ public final class Utils {
         return Arrays.toString(source.toArray());
     }
 
-    public static int remaining(ByteBuffer[] bufs) {
-        int remain = 0;
+    public static long remaining(ByteBuffer[] bufs) {
+        long remain = 0;
         for (ByteBuffer buf : bufs) {
             remain += buf.remaining();
         }
         return remain;
     }
 
-    public static int remaining(List<ByteBuffer> bufs) {
-        int remain = 0;
-        for (ByteBuffer buf : bufs) {
-            remain += buf.remaining();
+    public static boolean hasRemaining(List<ByteBuffer> bufs) {
+        synchronized (bufs) {
+            for (ByteBuffer buf : bufs) {
+                if (buf.hasRemaining())
+                    return true;
+            }
+        }
+        return false;
+    }
+
+    public static long remaining(List<ByteBuffer> bufs) {
+        long remain = 0;
+        synchronized (bufs) {
+            for (ByteBuffer buf : bufs) {
+                remain += buf.remaining();
+            }
         }
         return remain;
     }
 
-    public static int remaining(ByteBufferReference[] refs) {
-        int remain = 0;
+    public static int remaining(List<ByteBuffer> bufs, int max) {
+        long remain = 0;
+        synchronized (bufs) {
+            for (ByteBuffer buf : bufs) {
+                remain += buf.remaining();
+                if (remain > max) {
+                    throw new IllegalArgumentException("too many bytes");
+                }
+            }
+        }
+        return (int) remain;
+    }
+
+    public static long remaining(ByteBufferReference[] refs) {
+        long remain = 0;
         for (ByteBufferReference ref : refs) {
             remain += ref.get().remaining();
         }
         return remain;
     }
 
-    // assumes buffer was written into starting at position zero
-    static void unflip(ByteBuffer buf) {
-        buf.position(buf.limit());
-        buf.limit(buf.capacity());
+    public static int remaining(ByteBufferReference[] refs, int max) {
+        long remain = 0;
+        for (ByteBufferReference ref : refs) {
+            remain += ref.get().remaining();
+            if (remain > max) {
+                throw new IllegalArgumentException("too many bytes");
+            }
+        }
+        return (int) remain;
+    }
+
+    public static int remaining(ByteBuffer[] refs, int max) {
+        long remain = 0;
+        for (ByteBuffer b : refs) {
+            remain += b.remaining();
+            if (remain > max) {
+                throw new IllegalArgumentException("too many bytes");
+            }
+        }
+        return (int) remain;
     }
 
     public static void close(Closeable... closeables) {
@@ -416,80 +458,11 @@ public final class Utils {
         }
     }
 
-    public static void close(Throwable t, Closeable... closeables) {
-        for (Closeable c : closeables) {
-            try {
-                ExceptionallyCloseable.close(t, c);
-            } catch (IOException ignored) { }
-        }
-    }
-
-    /**
-     * Returns an array with the same buffers, but starting at position zero
-     * in the array.
-     */
-    public static ByteBuffer[] reduce(ByteBuffer[] bufs, int start, int number) {
-        if (start == 0 && number == bufs.length) {
-            return bufs;
-        }
-        ByteBuffer[] nbufs = new ByteBuffer[number];
-        int j = 0;
-        for (int i=start; i<start+number; i++) {
-            nbufs[j++] = bufs[i];
-        }
-        return nbufs;
-    }
-
-    static String asString(ByteBuffer buf) {
-        byte[] b = new byte[buf.remaining()];
-        buf.get(b);
-        return new String(b, StandardCharsets.US_ASCII);
-    }
-
-    /**
-     * Returns a single threaded executor which uses one invocation
-     * of the parent executor to execute tasks (in sequence).
-     *
-     * Use a null valued Runnable to terminate.
-     */
-    // TODO: this is a blocking way of doing this;
-    public static Executor singleThreadExecutor(Executor parent) {
-        BlockingQueue<Optional<Runnable>> queue = new LinkedBlockingQueue<>();
-        parent.execute(() -> {
-            while (true) {
-                try {
-                    Optional<Runnable> o = queue.take();
-                    if (!o.isPresent()) {
-                        return;
-                    }
-                    o.get().run();
-                } catch (InterruptedException ex) {
-                    return;
-                }
-            }
-        });
-        return new Executor() {
-            @Override
-            public void execute(Runnable command) {
-                queue.offer(Optional.ofNullable(command));
-            }
-        };
-    }
-
-    private static void executeInline(Runnable r) {
-        r.run();
-    }
-
-    static Executor callingThreadExecutor() {
-        return Utils::executeInline;
-    }
-
     // Put all these static 'empty' singletons here
-    @SuppressWarnings("rawtypes")
-    public static final CompletableFuture[] EMPTY_CFARRAY = new CompletableFuture[0];
-
     public static final ByteBuffer EMPTY_BYTEBUFFER = ByteBuffer.allocate(0);
     public static final ByteBuffer[] EMPTY_BB_ARRAY = new ByteBuffer[0];
+    public static final List<ByteBuffer> EMPTY_BB_LIST = List.of();
+    public static final ByteBufferReference[] EMPTY_BBR_ARRAY = new ByteBufferReference[0];
 
     public static ByteBuffer slice(ByteBuffer buffer, int amount) {
         ByteBuffer newb = buffer.slice();
@@ -515,4 +488,155 @@ public final class Utils {
     public static UncheckedIOException unchecked(IOException e) {
         return new UncheckedIOException(e);
     }
+
+    /**
+     * Get a logger for debug HTTP traces.
+     *
+     * The logger should only be used with levels whose severity is
+     * {@code <= DEBUG}. By default, this logger will forward all messages
+     * logged to an internal logger named "jdk.internal.httpclient.debug".
+     * In addition, if the property -Djdk.internal.httpclient.debug=true is set,
+     * it will print the messages on stderr.
+     * The logger will add some decoration to the printed message, in the form of
+     * {@code <Level>:[<thread-name>] [<elapsed-time>] <dbgTag>: <formatted message>}
+     *
+     * @param dbgTag A lambda that returns a string that identifies the caller
+     *               (e.g: "SocketTube(3)", or "Http2Connection(SocketTube(3))")
+     *
+     * @return A logger for HTTP internal debug traces
+     */
+    public static Logger getDebugLogger(Supplier<String> dbgTag) {
+        return getDebugLogger(dbgTag, DEBUG);
+    }
+
+    /**
+     * Get a logger for debug HTTP traces.The logger should only be used
+     * with levels whose severity is {@code <= DEBUG}.
+     *
+     * By default, this logger will forward all messages logged to an internal
+     * logger named "jdk.internal.httpclient.debug".
+     * In addition, if the message severity level is >= to
+     * the provided {@code errLevel} it will print the messages on stderr.
+     * The logger will add some decoration to the printed message, in the form of
+     * {@code <Level>:[<thread-name>] [<elapsed-time>] <dbgTag>: <formatted message>}
+     *
+     * @apiNote To obtain a logger that will always print things on stderr in
+     *          addition to forwarding to the internal logger, use
+     *          {@code getDebugLogger(this::dbgTag, Level.ALL);}.
+     *          This is also equivalent to calling
+     *          {@code getDebugLogger(this::dbgTag, true);}.
+     *          To obtain a logger that will only forward to the internal logger,
+     *          use {@code getDebugLogger(this::dbgTag, Level.OFF);}.
+     *          This is also equivalent to calling
+     *          {@code getDebugLogger(this::dbgTag, false);}.
+     *
+     * @param dbgTag A lambda that returns a string that identifies the caller
+     *               (e.g: "SocketTube(3)", or "Http2Connection(SocketTube(3))")
+     * @param errLevel The level above which messages will be also printed on
+     *               stderr (in addition to be forwarded to the internal logger).
+     *
+     * @return A logger for HTTP internal debug traces
+     */
+    static Logger getDebugLogger(Supplier<String> dbgTag, Level errLevel) {
+        return DebugLogger.createHttpLogger(dbgTag, Level.OFF, errLevel);
+    }
+
+    /**
+     * Get a logger for debug HTTP traces.The logger should only be used
+     * with levels whose severity is {@code <= DEBUG}.
+     *
+     * By default, this logger will forward all messages logged to an internal
+     * logger named "jdk.internal.httpclient.debug".
+     * In addition, the provided boolean {@code on==true}, it will print the
+     * messages on stderr.
+     * The logger will add some decoration to the printed message, in the form of
+     * {@code <Level>:[<thread-name>] [<elapsed-time>] <dbgTag>: <formatted message>}
+     *
+     * @apiNote To obtain a logger that will always print things on stderr in
+     *          addition to forwarding to the internal logger, use
+     *          {@code getDebugLogger(this::dbgTag, true);}.
+     *          This is also equivalent to calling
+     *          {@code getDebugLogger(this::dbgTag, Level.ALL);}.
+     *          To obtain a logger that will only forward to the internal logger,
+     *          use {@code getDebugLogger(this::dbgTag, false);}.
+     *          This is also equivalent to calling
+     *          {@code getDebugLogger(this::dbgTag, Level.OFF);}.
+     *
+     * @param dbgTag A lambda that returns a string that identifies the caller
+     *               (e.g: "SocketTube(3)", or "Http2Connection(SocketTube(3))")
+     * @param on  Whether messages should also be printed on
+     *               stderr (in addition to be forwarded to the internal logger).
+     *
+     * @return A logger for HTTP internal debug traces
+     */
+    public static Logger getDebugLogger(Supplier<String> dbgTag, boolean on) {
+        Level errLevel = on ? Level.ALL : Level.OFF;
+        return getDebugLogger(dbgTag, errLevel);
+    }
+
+    /**
+     * Get a logger for debug HPACK traces.The logger should only be used
+     * with levels whose severity is {@code <= DEBUG}.
+     *
+     * By default, this logger will forward all messages logged to an internal
+     * logger named "jdk.internal.httpclient.hpack.debug".
+     * In addition, if the message severity level is >= to
+     * the provided {@code outLevel} it will print the messages on stdout.
+     * The logger will add some decoration to the printed message, in the form of
+     * {@code <Level>:[<thread-name>] [<elapsed-time>] <dbgTag>: <formatted message>}
+     *
+     * @apiNote To obtain a logger that will always print things on stdout in
+     *          addition to forwarding to the internal logger, use
+     *          {@code getHpackLogger(this::dbgTag, Level.ALL);}.
+     *          This is also equivalent to calling
+     *          {@code getHpackLogger(this::dbgTag, true);}.
+     *          To obtain a logger that will only forward to the internal logger,
+     *          use {@code getHpackLogger(this::dbgTag, Level.OFF);}.
+     *          This is also equivalent to calling
+     *          {@code getHpackLogger(this::dbgTag, false);}.
+     *
+     * @param dbgTag A lambda that returns a string that identifies the caller
+     *               (e.g: "Http2Connection(SocketTube(3))/hpack.Decoder(3)")
+     * @param outLevel The level above which messages will be also printed on
+     *               stdout (in addition to be forwarded to the internal logger).
+     *
+     * @return A logger for HPACK internal debug traces
+     */
+    public static Logger getHpackLogger(Supplier<String> dbgTag, Level outLevel) {
+        Level errLevel = Level.OFF;
+        return DebugLogger.createHpackLogger(dbgTag, outLevel, errLevel);
+    }
+
+    /**
+     * Get a logger for debug HPACK traces.The logger should only be used
+     * with levels whose severity is {@code <= DEBUG}.
+     *
+     * By default, this logger will forward all messages logged to an internal
+     * logger named "jdk.internal.httpclient.hpack.debug".
+     * In addition, the provided boolean {@code on==true}, it will print the
+     * messages on stdout.
+     * The logger will add some decoration to the printed message, in the form of
+     * {@code <Level>:[<thread-name>] [<elapsed-time>] <dbgTag>: <formatted message>}
+     *
+     * @apiNote To obtain a logger that will always print things on stdout in
+     *          addition to forwarding to the internal logger, use
+     *          {@code getHpackLogger(this::dbgTag, true);}.
+     *          This is also equivalent to calling
+     *          {@code getHpackLogger(this::dbgTag, Level.ALL);}.
+     *          To obtain a logger that will only forward to the internal logger,
+     *          use {@code getHpackLogger(this::dbgTag, false);}.
+     *          This is also equivalent to calling
+     *          {@code getHpackLogger(this::dbgTag, Level.OFF);}.
+     *
+     * @param dbgTag A lambda that returns a string that identifies the caller
+     *               (e.g: "Http2Connection(SocketTube(3))/hpack.Decoder(3)")
+     * @param on  Whether messages should also be printed on
+     *            stdout (in addition to be forwarded to the internal logger).
+     *
+     * @return A logger for HPACK internal debug traces
+     */
+    public static Logger getHpackLogger(Supplier<String> dbgTag, boolean on) {
+        Level outLevel = on ? Level.ALL : Level.OFF;
+        return getHpackLogger(dbgTag, outLevel);
+    }
 }
diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/frame/ContinuationFrame.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/frame/ContinuationFrame.java
index 8e4fbb89889..3c8c8158596 100644
--- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/frame/ContinuationFrame.java
+++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/frame/ContinuationFrame.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2017, 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
@@ -25,19 +25,19 @@
 
 package jdk.incubator.http.internal.frame;
 
-import jdk.incubator.http.internal.common.ByteBufferReference;
-import jdk.incubator.http.internal.common.Utils;
+import java.nio.ByteBuffer;
+import java.util.List;
 
 public class ContinuationFrame extends HeaderFrame {
 
     public static final int TYPE = 0x9;
 
-    public ContinuationFrame(int streamid, int flags, ByteBufferReference[] headerBlocks) {
+    public ContinuationFrame(int streamid, int flags, List<ByteBuffer> headerBlocks) {
         super(streamid, flags, headerBlocks);
     }
 
-    public ContinuationFrame(int streamid, ByteBufferReference headersBlock) {
-        this(streamid, 0, new ByteBufferReference[]{headersBlock});
+    public ContinuationFrame(int streamid, ByteBuffer headersBlock) {
+        this(streamid, 0, List.of(headersBlock));
     }
 
     @Override
diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/frame/DataFrame.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/frame/DataFrame.java
index 70d390a0c53..e5af442a784 100644
--- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/frame/DataFrame.java
+++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/frame/DataFrame.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2017, 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
@@ -25,9 +25,11 @@
 
 package jdk.incubator.http.internal.frame;
 
-import jdk.incubator.http.internal.common.ByteBufferReference;
 import jdk.incubator.http.internal.common.Utils;
 
+import java.nio.ByteBuffer;
+import java.util.List;
+
 public class DataFrame extends Http2Frame {
 
     public static final int TYPE = 0x0;
@@ -37,20 +39,20 @@ public class DataFrame extends Http2Frame {
     public static final int PADDED = 0x8;
 
     private int padLength;
-    private final ByteBufferReference[] data;
+    private final List<ByteBuffer> data;
     private final int dataLength;
 
-    public DataFrame(int streamid, int flags, ByteBufferReference data) {
-        this(streamid, flags, new ByteBufferReference[]{data});
+    public DataFrame(int streamid, int flags, ByteBuffer data) {
+        this(streamid, flags, List.of(data));
     }
 
-    public DataFrame(int streamid, int flags, ByteBufferReference[] data) {
+    public DataFrame(int streamid, int flags, List<ByteBuffer> data) {
         super(streamid, flags);
         this.data = data;
-        this.dataLength = Utils.remaining(data);
+        this.dataLength = Utils.remaining(data, Integer.MAX_VALUE);
     }
 
-    public DataFrame(int streamid, int flags, ByteBufferReference[] data, int padLength) {
+    public DataFrame(int streamid, int flags, List<ByteBuffer> data, int padLength) {
         this(streamid, flags, data);
         if (padLength > 0) {
             setPadLength(padLength);
@@ -78,7 +80,7 @@ public class DataFrame extends Http2Frame {
         return super.flagAsString(flag);
     }
 
-    public ByteBufferReference[] getData() {
+    public List<ByteBuffer> getData() {
         return data;
     }
 
diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/frame/ErrorFrame.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/frame/ErrorFrame.java
index e6a247cb5d8..22634fc6145 100644
--- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/frame/ErrorFrame.java
+++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/frame/ErrorFrame.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2017, 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
@@ -88,8 +88,4 @@ public abstract class ErrorFrame extends Http2Frame {
     public int getErrorCode() {
         return this.errorCode;
     }
-
-    public void setErrorCode(int errorCode) {
-        this.errorCode = errorCode;
-    }
 }
diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/frame/FramesDecoder.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/frame/FramesDecoder.java
index bc465110d66..1b8ff4ac0b1 100644
--- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/frame/FramesDecoder.java
+++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/frame/FramesDecoder.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2017, 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
@@ -25,15 +25,16 @@
 
 package jdk.incubator.http.internal.frame;
 
-import jdk.incubator.http.internal.common.ByteBufferReference;
 import jdk.incubator.http.internal.common.Log;
 import jdk.incubator.http.internal.common.Utils;
 
 import java.io.IOException;
+import java.lang.System.Logger.Level;
 import java.nio.ByteBuffer;
 import java.util.ArrayDeque;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Queue;
 
 /**
  * Frames Decoder
@@ -46,7 +47,9 @@ import java.util.List;
  */
 public class FramesDecoder {
 
-
+    static final boolean DEBUG = Utils.DEBUG; // Revisit: temporary dev flag.
+    static final System.Logger DEBUG_LOGGER =
+            Utils.getDebugLogger("FramesDecoder"::toString, DEBUG);
 
     @FunctionalInterface
     public interface FrameProcessor {
@@ -56,14 +59,14 @@ public class FramesDecoder {
     private final FrameProcessor frameProcessor;
     private final int maxFrameSize;
 
-    private ByteBufferReference currentBuffer; // current buffer either null or hasRemaining
+    private ByteBuffer currentBuffer; // current buffer either null or hasRemaining
 
-    private final java.util.Queue<ByteBufferReference> tailBuffers = new ArrayDeque<>();
+    private final ArrayDeque<ByteBuffer> tailBuffers = new ArrayDeque<>();
     private int tailSize = 0;
 
     private boolean slicedToDataFrame = false;
 
-    private final List<ByteBufferReference> prepareToRelease = new ArrayList<>();
+    private final List<ByteBuffer> prepareToRelease = new ArrayList<>();
 
     // if true  - Frame Header was parsed (9 bytes consumed) and subsequent fields have meaning
     // otherwise - stopped at frames boundary
@@ -72,6 +75,7 @@ public class FramesDecoder {
     private int frameType;
     private int frameFlags;
     private int frameStreamid;
+    private boolean closed;
 
     /**
      * Creates Frame Decoder
@@ -92,25 +96,63 @@ public class FramesDecoder {
         this.maxFrameSize = Math.min(Math.max(16 * 1024, maxFrameSize), 16 * 1024 * 1024 - 1);
     }
 
+    /** Threshold beyond which data is no longer copied into the current buffer,
+     * if that buffer has enough unused space. */
+    private static final int COPY_THRESHOLD = 8192;
+
     /**
-     * put next buffer into queue,
-     * if frame decoding is possible - decode all buffers and invoke FrameProcessor
+     * Adds the data from the given buffer, and performs frame decoding if
+     * possible.   Either 1) appends the data from the given buffer to the
+     * current buffer ( if there is enough unused space ), or 2) adds it to the
+     * next buffer in the queue.
      *
-     * @param buffer
-     * @throws IOException
+     * If there is enough data to perform frame decoding then, all buffers are
+     * decoded and the FrameProcessor is invoked.
      */
-    public void decode(ByteBufferReference buffer) throws IOException {
-        int remaining = buffer.get().remaining();
+    public void decode(ByteBuffer inBoundBuffer) throws IOException {
+        if (closed) {
+            DEBUG_LOGGER.log(Level.DEBUG, "closed: ignoring buffer (%s bytes)",
+                    inBoundBuffer.remaining());
+            inBoundBuffer.position(inBoundBuffer.limit());
+            return;
+        }
+        int remaining = inBoundBuffer.remaining();
+        DEBUG_LOGGER.log(Level.DEBUG, "decodes: %d", remaining);
         if (remaining > 0) {
             if (currentBuffer == null) {
-                currentBuffer = buffer;
+                currentBuffer = inBoundBuffer;
             } else {
-                tailBuffers.add(buffer);
-                tailSize += remaining;
+                ByteBuffer b = currentBuffer;
+                if (!tailBuffers.isEmpty()) {
+                    b = tailBuffers.getLast();
+                }
+
+                int limit = b.limit();
+                int freeSpace = b.capacity() - limit;
+                if (remaining <= COPY_THRESHOLD && freeSpace >= remaining) {
+                    // append the new data to the unused space in the current buffer
+                    int position = b.position();
+                    b.position(limit);
+                    b.limit(limit + inBoundBuffer.remaining());
+                    b.put(inBoundBuffer);
+                    b.position(position);
+                    if (b != currentBuffer)
+                        tailSize += remaining;
+                    DEBUG_LOGGER.log(Level.DEBUG, "copied: %d", remaining);
+                } else {
+                    DEBUG_LOGGER.log(Level.DEBUG, "added: %d", remaining);
+                    tailBuffers.add(inBoundBuffer);
+                    tailSize += remaining;
+                }
             }
         }
+        DEBUG_LOGGER.log(Level.DEBUG, "Tail size is now: %d, current=",
+                tailSize,
+                (currentBuffer == null ? 0 :
+                   currentBuffer.remaining()));
         Http2Frame frame;
         while ((frame = nextFrame()) != null) {
+            DEBUG_LOGGER.log(Level.DEBUG, "Got frame: %s", frame);
             frameProcessor.processFrame(frame);
             frameProcessed();
         }
@@ -121,21 +163,29 @@ public class FramesDecoder {
             if (currentBuffer == null) {
                 return null; // no data at all
             }
+            long available = currentBuffer.remaining() + tailSize;
             if (!frameHeaderParsed) {
-                if (currentBuffer.get().remaining() + tailSize >= Http2Frame.FRAME_HEADER_SIZE) {
+                if (available >= Http2Frame.FRAME_HEADER_SIZE) {
                     parseFrameHeader();
                     if (frameLength > maxFrameSize) {
                         // connection error
                         return new MalformedFrame(ErrorFrame.FRAME_SIZE_ERROR,
-                                "Frame type("+frameType+") " +"length("+frameLength+") exceeds MAX_FRAME_SIZE("+ maxFrameSize+")");
+                                "Frame type("+frameType+") "
+                                +"length("+frameLength
+                                +") exceeds MAX_FRAME_SIZE("
+                                + maxFrameSize+")");
                     }
                     frameHeaderParsed = true;
                 } else {
-                    return null; // no data for frame header
+                    DEBUG_LOGGER.log(Level.DEBUG,
+                            "Not enough data to parse header, needs: %d, has: %d",
+                            Http2Frame.FRAME_HEADER_SIZE, available);
+                    return null;
                 }
             }
+            available = currentBuffer == null ? 0 : currentBuffer.remaining() + tailSize;
             if ((frameLength == 0) ||
-                    (currentBuffer != null && currentBuffer.get().remaining() + tailSize >= frameLength)) {
+                    (currentBuffer != null && available >= frameLength)) {
                 Http2Frame frame = parseFrameBody();
                 frameHeaderParsed = false;
                 // frame == null means we have to skip this frame and try parse next
@@ -143,19 +193,21 @@ public class FramesDecoder {
                     return frame;
                 }
             } else {
+                DEBUG_LOGGER.log(Level.DEBUG,
+                        "Not enough data to parse frame body, needs: %d,  has: %d",
+                        frameLength, available);
                 return null;  // no data for the whole frame header
             }
         }
     }
 
     private void frameProcessed() {
-        prepareToRelease.forEach(ByteBufferReference::clear);
         prepareToRelease.clear();
     }
 
     private void parseFrameHeader() throws IOException {
         int x = getInt();
-        this.frameLength = x >> 8;
+        this.frameLength = (x >>> 8) & 0x00ffffff;
         this.frameType = x & 0xff;
         this.frameFlags = getByte();
         this.frameStreamid = getInt() & 0x7fffffff;
@@ -165,29 +217,27 @@ public class FramesDecoder {
 
     // move next buffer from tailBuffers to currentBuffer if required
     private void nextBuffer() {
-        if (!currentBuffer.get().hasRemaining()) {
+        if (!currentBuffer.hasRemaining()) {
             if (!slicedToDataFrame) {
                 prepareToRelease.add(currentBuffer);
             }
             slicedToDataFrame = false;
             currentBuffer = tailBuffers.poll();
             if (currentBuffer != null) {
-                tailSize -= currentBuffer.get().remaining();
+                tailSize -= currentBuffer.remaining();
             }
         }
     }
 
     public int getByte() {
-        ByteBuffer buf = currentBuffer.get();
-        int res = buf.get() & 0xff;
+        int res = currentBuffer.get() & 0xff;
         nextBuffer();
         return res;
     }
 
     public int getShort() {
-        ByteBuffer buf = currentBuffer.get();
-        if (buf.remaining() >= 2) {
-            int res = buf.getShort() & 0xffff;
+        if (currentBuffer.remaining() >= 2) {
+            int res = currentBuffer.getShort() & 0xffff;
             nextBuffer();
             return res;
         }
@@ -197,9 +247,8 @@ public class FramesDecoder {
     }
 
     public int getInt() {
-        ByteBuffer buf = currentBuffer.get();
-        if (buf.remaining() >= 4) {
-            int res = buf.getInt();
+        if (currentBuffer.remaining() >= 4) {
+            int res = currentBuffer.getInt();
             nextBuffer();
             return res;
         }
@@ -215,9 +264,8 @@ public class FramesDecoder {
         byte[] bytes = new byte[n];
         int offset = 0;
         while (n > 0) {
-            ByteBuffer buf = currentBuffer.get();
-            int length = Math.min(n, buf.remaining());
-            buf.get(bytes, offset, length);
+            int length = Math.min(n, currentBuffer.remaining());
+            currentBuffer.get(bytes, offset, length);
             offset += length;
             n -= length;
             nextBuffer();
@@ -226,36 +274,48 @@ public class FramesDecoder {
 
     }
 
-    private ByteBufferReference[] getBuffers(boolean isDataFrame, int bytecount) {
-        List<ByteBufferReference> res = new ArrayList<>();
+    private List<ByteBuffer> getBuffers(boolean isDataFrame, int bytecount) {
+        List<ByteBuffer> res = new ArrayList<>();
         while (bytecount > 0) {
-            ByteBuffer buf = currentBuffer.get();
-            int remaining = buf.remaining();
+            int remaining = currentBuffer.remaining();
             int extract = Math.min(remaining, bytecount);
             ByteBuffer extractedBuf;
             if (isDataFrame) {
-                extractedBuf = Utils.slice(buf, extract);
+                extractedBuf = Utils.slice(currentBuffer, extract);
                 slicedToDataFrame = true;
             } else {
                 // Header frames here
                 // HPACK decoding should performed under lock and immediately after frame decoding.
                 // in that case it is safe to release original buffer,
                 // because of sliced buffer has a very short life
-                extractedBuf = Utils.slice(buf, extract);
+                extractedBuf = Utils.slice(currentBuffer, extract);
             }
-            res.add(ByteBufferReference.of(extractedBuf));
+            res.add(extractedBuf);
             bytecount -= extract;
             nextBuffer();
         }
-        return res.toArray(new ByteBufferReference[0]);
+        return res;
+    }
+
+    public void close(String msg) {
+        closed = true;
+        tailBuffers.clear();
+        int bytes = tailSize;
+        ByteBuffer b = currentBuffer;
+        if (b != null) {
+            bytes += b.remaining();
+            b.position(b.limit());
+        }
+        tailSize = 0;
+        currentBuffer = null;
+        DEBUG_LOGGER.log(Level.DEBUG, "closed %s, ignoring %d bytes", msg, bytes);
     }
 
     public void skipBytes(int bytecount) {
         while (bytecount > 0) {
-            ByteBuffer buf = currentBuffer.get();
-            int remaining = buf.remaining();
+            int remaining = currentBuffer.remaining();
             int extract = Math.min(remaining, bytecount);
-            buf.position(buf.position() + extract);
+            currentBuffer.position(currentBuffer.position() + extract);
             bytecount -= remaining;
             nextBuffer();
         }
@@ -296,12 +356,13 @@ public class FramesDecoder {
     private Http2Frame parseDataFrame(int frameLength, int streamid, int flags) {
         // non-zero stream
         if (streamid == 0) {
-            return new MalformedFrame(ErrorFrame.PROTOCOL_ERROR, "zero streamId for DataFrame");
+            return new MalformedFrame(ErrorFrame.PROTOCOL_ERROR,
+                                      "zero streamId for DataFrame");
         }
         int padLength = 0;
         if ((flags & DataFrame.PADDED) != 0) {
             padLength = getByte();
-            if(padLength >= frameLength) {
+            if (padLength >= frameLength) {
                 return new MalformedFrame(ErrorFrame.PROTOCOL_ERROR,
                         "the length of the padding is the length of the frame payload or greater");
             }
@@ -317,7 +378,8 @@ public class FramesDecoder {
     private Http2Frame parseHeadersFrame(int frameLength, int streamid, int flags) {
         // non-zero stream
         if (streamid == 0) {
-            return new MalformedFrame(ErrorFrame.PROTOCOL_ERROR, "zero streamId for HeadersFrame");
+            return new MalformedFrame(ErrorFrame.PROTOCOL_ERROR,
+                                      "zero streamId for HeadersFrame");
         }
         int padLength = 0;
         if ((flags & HeadersFrame.PADDED) != 0) {
diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/frame/FramesEncoder.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/frame/FramesEncoder.java
index dd7b1f03560..85efbfe1cd0 100644
--- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/frame/FramesEncoder.java
+++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/frame/FramesEncoder.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2017, 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
@@ -25,12 +25,8 @@
 
 package jdk.incubator.http.internal.frame;
 
-import jdk.incubator.http.internal.common.ByteBufferReference;
-import jdk.incubator.http.internal.common.Utils;
-
 import java.nio.ByteBuffer;
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.List;
 
 /**
@@ -45,25 +41,24 @@ public class FramesEncoder {
     public FramesEncoder() {
     }
 
-    public ByteBufferReference[] encodeFrames(List<HeaderFrame> frames) {
-        List<ByteBufferReference> refs = new ArrayList<>(frames.size() * 2);
+    public List<ByteBuffer> encodeFrames(List<HeaderFrame> frames) {
+        List<ByteBuffer> bufs = new ArrayList<>(frames.size() * 2);
         for (HeaderFrame f : frames) {
-            refs.addAll(Arrays.asList(encodeFrame(f)));
+            bufs.addAll(encodeFrame(f));
         }
-        return refs.toArray(new ByteBufferReference[0]);
+        return bufs;
     }
 
-    public ByteBufferReference encodeConnectionPreface(byte[] preface, SettingsFrame frame) {
+    public ByteBuffer encodeConnectionPreface(byte[] preface, SettingsFrame frame) {
         final int length = frame.length();
-        ByteBufferReference ref = getBuffer(Http2Frame.FRAME_HEADER_SIZE + length + preface.length);
-        ByteBuffer buf = ref.get();
+        ByteBuffer buf = getBuffer(Http2Frame.FRAME_HEADER_SIZE + length + preface.length);
         buf.put(preface);
         putSettingsFrame(buf, frame, length);
         buf.flip();
-        return ref;
+        return buf;
     }
 
-    public ByteBufferReference[] encodeFrame(Http2Frame frame) {
+    public List<ByteBuffer> encodeFrame(Http2Frame frame) {
         switch (frame.type()) {
             case DataFrame.TYPE:
                 return encodeDataFrame((DataFrame) frame);
@@ -93,47 +88,45 @@ public class FramesEncoder {
     private static final int NO_FLAGS = 0;
     private static final int ZERO_STREAM = 0;
 
-    private ByteBufferReference[] encodeDataFrame(DataFrame frame) {
+    private List<ByteBuffer> encodeDataFrame(DataFrame frame) {
         // non-zero stream
         assert frame.streamid() != 0;
-        ByteBufferReference ref = encodeDataFrameStart(frame);
+        ByteBuffer buf = encodeDataFrameStart(frame);
         if (frame.getFlag(DataFrame.PADDED)) {
-            return joinWithPadding(ref, frame.getData(), frame.getPadLength());
+            return joinWithPadding(buf, frame.getData(), frame.getPadLength());
         } else {
-            return join(ref, frame.getData());
+            return join(buf, frame.getData());
         }
     }
 
-    private ByteBufferReference encodeDataFrameStart(DataFrame frame) {
+    private ByteBuffer encodeDataFrameStart(DataFrame frame) {
         boolean isPadded = frame.getFlag(DataFrame.PADDED);
         final int length = frame.getDataLength() + (isPadded ? (frame.getPadLength() + 1) : 0);
-        ByteBufferReference ref = getBuffer(Http2Frame.FRAME_HEADER_SIZE + (isPadded ? 1 : 0));
-        ByteBuffer buf = ref.get();
+        ByteBuffer buf = getBuffer(Http2Frame.FRAME_HEADER_SIZE + (isPadded ? 1 : 0));
         putHeader(buf, length, DataFrame.TYPE, frame.getFlags(), frame.streamid());
         if (isPadded) {
             buf.put((byte) frame.getPadLength());
         }
         buf.flip();
-        return ref;
+        return buf;
     }
 
-    private ByteBufferReference[] encodeHeadersFrame(HeadersFrame frame) {
+    private List<ByteBuffer> encodeHeadersFrame(HeadersFrame frame) {
         // non-zero stream
         assert frame.streamid() != 0;
-        ByteBufferReference ref = encodeHeadersFrameStart(frame);
+        ByteBuffer buf = encodeHeadersFrameStart(frame);
         if (frame.getFlag(HeadersFrame.PADDED)) {
-            return joinWithPadding(ref, frame.getHeaderBlock(), frame.getPadLength());
+            return joinWithPadding(buf, frame.getHeaderBlock(), frame.getPadLength());
         } else {
-            return join(ref, frame.getHeaderBlock());
+            return join(buf, frame.getHeaderBlock());
         }
     }
 
-    private ByteBufferReference encodeHeadersFrameStart(HeadersFrame frame) {
+    private ByteBuffer encodeHeadersFrameStart(HeadersFrame frame) {
         boolean isPadded = frame.getFlag(HeadersFrame.PADDED);
         boolean hasPriority = frame.getFlag(HeadersFrame.PRIORITY);
         final int length = frame.getHeaderLength() + (isPadded ? (frame.getPadLength() + 1) : 0) + (hasPriority ? 5 : 0);
-        ByteBufferReference ref = getBuffer(Http2Frame.FRAME_HEADER_SIZE + (isPadded ? 1 : 0) + (hasPriority ? 5 : 0));
-        ByteBuffer buf = ref.get();
+        ByteBuffer buf = getBuffer(Http2Frame.FRAME_HEADER_SIZE + (isPadded ? 1 : 0) + (hasPriority ? 5 : 0));
         putHeader(buf, length, HeadersFrame.TYPE, frame.getFlags(), frame.streamid());
         if (isPadded) {
             buf.put((byte) frame.getPadLength());
@@ -142,51 +135,47 @@ public class FramesEncoder {
             putPriority(buf, frame.getExclusive(), frame.getStreamDependency(), frame.getWeight());
         }
         buf.flip();
-        return ref;
+        return buf;
     }
 
-    private ByteBufferReference[] encodePriorityFrame(PriorityFrame frame) {
+    private List<ByteBuffer> encodePriorityFrame(PriorityFrame frame) {
         // non-zero stream; no flags
         assert frame.streamid() != 0;
         final int length = 5;
-        ByteBufferReference ref = getBuffer(Http2Frame.FRAME_HEADER_SIZE + length);
-        ByteBuffer buf = ref.get();
+        ByteBuffer buf = getBuffer(Http2Frame.FRAME_HEADER_SIZE + length);
         putHeader(buf, length, PriorityFrame.TYPE, NO_FLAGS, frame.streamid());
         putPriority(buf, frame.exclusive(), frame.streamDependency(), frame.weight());
         buf.flip();
-        return new ByteBufferReference[]{ref};
+        return List.of(buf);
     }
 
-    private ByteBufferReference[] encodeResetFrame(ResetFrame frame) {
+    private List<ByteBuffer> encodeResetFrame(ResetFrame frame) {
         // non-zero stream; no flags
         assert frame.streamid() != 0;
         final int length = 4;
-        ByteBufferReference ref = getBuffer(Http2Frame.FRAME_HEADER_SIZE + length);
-        ByteBuffer buf = ref.get();
+        ByteBuffer buf = getBuffer(Http2Frame.FRAME_HEADER_SIZE + length);
         putHeader(buf, length, ResetFrame.TYPE, NO_FLAGS, frame.streamid());
         buf.putInt(frame.getErrorCode());
         buf.flip();
-        return new ByteBufferReference[]{ref};
+        return List.of(buf);
     }
 
-    private ByteBufferReference[] encodeSettingsFrame(SettingsFrame frame) {
+    private List<ByteBuffer> encodeSettingsFrame(SettingsFrame frame) {
         // only zero stream
         assert frame.streamid() == 0;
         final int length = frame.length();
-        ByteBufferReference ref = getBuffer(Http2Frame.FRAME_HEADER_SIZE + length);
-        ByteBuffer buf = ref.get();
+        ByteBuffer buf = getBuffer(Http2Frame.FRAME_HEADER_SIZE + length);
         putSettingsFrame(buf, frame, length);
         buf.flip();
-        return new ByteBufferReference[]{ref};
+        return List.of(buf);
     }
 
-    private ByteBufferReference[] encodePushPromiseFrame(PushPromiseFrame frame) {
+    private List<ByteBuffer> encodePushPromiseFrame(PushPromiseFrame frame) {
         // non-zero stream
         assert frame.streamid() != 0;
         boolean isPadded = frame.getFlag(PushPromiseFrame.PADDED);
         final int length = frame.getHeaderLength() + (isPadded ? 5 : 4);
-        ByteBufferReference ref = getBuffer(Http2Frame.FRAME_HEADER_SIZE + (isPadded ? 5 : 4));
-        ByteBuffer buf = ref.get();
+        ByteBuffer buf = getBuffer(Http2Frame.FRAME_HEADER_SIZE + (isPadded ? 5 : 4));
         putHeader(buf, length, PushPromiseFrame.TYPE, frame.getFlags(), frame.streamid());
         if (isPadded) {
             buf.put((byte) frame.getPadLength());
@@ -195,31 +184,29 @@ public class FramesEncoder {
         buf.flip();
 
         if (frame.getFlag(PushPromiseFrame.PADDED)) {
-            return joinWithPadding(ref, frame.getHeaderBlock(), frame.getPadLength());
+            return joinWithPadding(buf, frame.getHeaderBlock(), frame.getPadLength());
         } else {
-            return join(ref, frame.getHeaderBlock());
+            return join(buf, frame.getHeaderBlock());
         }
     }
 
-    private ByteBufferReference[] encodePingFrame(PingFrame frame) {
+    private List<ByteBuffer> encodePingFrame(PingFrame frame) {
         // only zero stream
         assert frame.streamid() == 0;
         final int length = 8;
-        ByteBufferReference ref = getBuffer(Http2Frame.FRAME_HEADER_SIZE + length);
-        ByteBuffer buf = ref.get();
+        ByteBuffer buf = getBuffer(Http2Frame.FRAME_HEADER_SIZE + length);
         putHeader(buf, length, PingFrame.TYPE, frame.getFlags(), ZERO_STREAM);
         buf.put(frame.getData());
         buf.flip();
-        return new ByteBufferReference[]{ref};
+        return List.of(buf);
     }
 
-    private ByteBufferReference[] encodeGoAwayFrame(GoAwayFrame frame) {
+    private List<ByteBuffer> encodeGoAwayFrame(GoAwayFrame frame) {
         // only zero stream; no flags
         assert frame.streamid() == 0;
         byte[] debugData = frame.getDebugData();
         final int length = 8 + debugData.length;
-        ByteBufferReference ref = getBuffer(Http2Frame.FRAME_HEADER_SIZE + length);
-        ByteBuffer buf = ref.get();
+        ByteBuffer buf = getBuffer(Http2Frame.FRAME_HEADER_SIZE + length);
         putHeader(buf, length, GoAwayFrame.TYPE, NO_FLAGS, ZERO_STREAM);
         buf.putInt(frame.getLastStream());
         buf.putInt(frame.getErrorCode());
@@ -227,45 +214,50 @@ public class FramesEncoder {
             buf.put(debugData);
         }
         buf.flip();
-        return new ByteBufferReference[]{ref};
+        return List.of(buf);
     }
 
-    private ByteBufferReference[] encodeWindowUpdateFrame(WindowUpdateFrame frame) {
+    private List<ByteBuffer> encodeWindowUpdateFrame(WindowUpdateFrame frame) {
         // any stream; no flags
         final int length = 4;
-        ByteBufferReference ref = getBuffer(Http2Frame.FRAME_HEADER_SIZE + length);
-        ByteBuffer buf = ref.get();
+        ByteBuffer buf = getBuffer(Http2Frame.FRAME_HEADER_SIZE + length);
         putHeader(buf, length, WindowUpdateFrame.TYPE, NO_FLAGS, frame.streamid);
         buf.putInt(frame.getUpdate());
         buf.flip();
-        return new ByteBufferReference[]{ref};
+        return List.of(buf);
     }
 
-    private ByteBufferReference[] encodeContinuationFrame(ContinuationFrame frame) {
+    private List<ByteBuffer> encodeContinuationFrame(ContinuationFrame frame) {
         // non-zero stream;
         assert frame.streamid() != 0;
         final int length = frame.getHeaderLength();
-        ByteBufferReference ref = getBuffer(Http2Frame.FRAME_HEADER_SIZE);
-        ByteBuffer buf = ref.get();
+        ByteBuffer buf = getBuffer(Http2Frame.FRAME_HEADER_SIZE);
         putHeader(buf, length, ContinuationFrame.TYPE, frame.getFlags(), frame.streamid());
         buf.flip();
-        return join(ref, frame.getHeaderBlock());
+        return join(buf, frame.getHeaderBlock());
     }
 
-    private ByteBufferReference[] joinWithPadding(ByteBufferReference ref, ByteBufferReference[] data, int padLength) {
-        ByteBufferReference[] references = new ByteBufferReference[2 + data.length];
-        references[0] = ref;
-        System.arraycopy(data, 0, references, 1, data.length);
-        assert references[references.length - 1] == null;
-        references[references.length - 1] = getPadding(padLength);
-        return references;
+    private List<ByteBuffer> joinWithPadding(ByteBuffer buf, List<ByteBuffer> data, int padLength) {
+        int len = data.size();
+        if (len == 0) return List.of(buf, getPadding(padLength));
+        else if (len == 1) return List.of(buf, data.get(0), getPadding(padLength));
+        else if (len == 2) return List.of(buf, data.get(0), data.get(1), getPadding(padLength));
+        List<ByteBuffer> res = new ArrayList<>(len+2);
+        res.add(buf);
+        res.addAll(data);
+        res.add(getPadding(padLength));
+        return res;
     }
 
-    private ByteBufferReference[] join(ByteBufferReference ref, ByteBufferReference[] data) {
-        ByteBufferReference[] references = new ByteBufferReference[1 + data.length];
-        references[0] = ref;
-        System.arraycopy(data, 0, references, 1, data.length);
-        return references;
+    private List<ByteBuffer> join(ByteBuffer buf, List<ByteBuffer> data) {
+        int len = data.size();
+        if (len == 0) return List.of(buf);
+        else if (len == 1) return List.of(buf, data.get(0));
+        else if (len == 2) return List.of(buf, data.get(0), data.get(1));
+        List<ByteBuffer> joined = new ArrayList<>(len + 1);
+        joined.add(buf);
+        joined.addAll(data);
+        return joined;
     }
 
     private void putSettingsFrame(ByteBuffer buf, SettingsFrame frame, int length) {
@@ -287,15 +279,15 @@ public class FramesEncoder {
         buf.put((byte) weight);
     }
 
-    private ByteBufferReference getBuffer(int capacity) {
-        return ByteBufferReference.of(ByteBuffer.allocate(capacity));
+    private ByteBuffer getBuffer(int capacity) {
+        return ByteBuffer.allocate(capacity);
     }
 
-    public ByteBufferReference getPadding(int length) {
+    public ByteBuffer getPadding(int length) {
         if (length > 255) {
             throw new IllegalArgumentException("Padding too big");
         }
-        return ByteBufferReference.of(ByteBuffer.allocate(length)); // zeroed!
+        return ByteBuffer.allocate(length); // zeroed!
     }
 
 }
diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/frame/GoAwayFrame.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/frame/GoAwayFrame.java
index b29fed84147..0dd10f2a6be 100644
--- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/frame/GoAwayFrame.java
+++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/frame/GoAwayFrame.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2017, 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
diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/frame/HeaderFrame.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/frame/HeaderFrame.java
index cc243a7d14b..628e43e6aa7 100644
--- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/frame/HeaderFrame.java
+++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/frame/HeaderFrame.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2017, 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
@@ -25,10 +25,10 @@
 
 package jdk.incubator.http.internal.frame;
 
-import jdk.incubator.http.internal.common.ByteBufferReference;
 import jdk.incubator.http.internal.common.Utils;
 
 import java.nio.ByteBuffer;
+import java.util.List;
 
 /**
  * Either a HeadersFrame or a ContinuationFrame
@@ -36,19 +36,15 @@ import java.nio.ByteBuffer;
 public abstract class HeaderFrame extends Http2Frame {
 
     final int headerLength;
-    final ByteBufferReference[] headerBlocks;
+    final List<ByteBuffer> headerBlocks;
 
     public static final int END_STREAM = 0x1;
     public static final int END_HEADERS = 0x4;
 
-    public HeaderFrame(int streamid, int flags, ByteBufferReference headerBlock) {
-        this(streamid, flags, new ByteBufferReference[]{headerBlock});
-    }
-
-    public HeaderFrame(int streamid, int flags, ByteBufferReference[] headerBlocks) {
+    public HeaderFrame(int streamid, int flags, List<ByteBuffer> headerBlocks) {
         super(streamid, flags);
         this.headerBlocks = headerBlocks;
-        this.headerLength = Utils.remaining(headerBlocks);
+        this.headerLength = Utils.remaining(headerBlocks, Integer.MAX_VALUE);
     }
 
     @Override
@@ -63,7 +59,7 @@ public abstract class HeaderFrame extends Http2Frame {
     }
 
 
-    public ByteBufferReference[] getHeaderBlock() {
+    public List<ByteBuffer> getHeaderBlock() {
         return headerBlocks;
     }
 
diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/frame/HeadersFrame.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/frame/HeadersFrame.java
index 2efc5791d29..8c59edcfb18 100644
--- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/frame/HeadersFrame.java
+++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/frame/HeadersFrame.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2017, 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
@@ -25,8 +25,8 @@
 
 package jdk.incubator.http.internal.frame;
 
-import jdk.incubator.http.internal.common.ByteBufferReference;
-import jdk.incubator.http.internal.common.Utils;
+import java.nio.ByteBuffer;
+import java.util.List;
 
 public class HeadersFrame extends HeaderFrame {
 
@@ -43,19 +43,19 @@ public class HeadersFrame extends HeaderFrame {
     private int weight;
     private boolean exclusive;
 
-    public HeadersFrame(int streamid, int flags, ByteBufferReference[] headerBlocks, int padLength) {
+    public HeadersFrame(int streamid, int flags, List<ByteBuffer> headerBlocks, int padLength) {
         super(streamid, flags, headerBlocks);
         if (padLength > 0) {
             setPadLength(padLength);
         }
     }
 
-    public HeadersFrame(int streamid, int flags, ByteBufferReference[] headerBlocks) {
+    public HeadersFrame(int streamid, int flags, List<ByteBuffer> headerBlocks) {
         super(streamid, flags, headerBlocks);
     }
 
-    public HeadersFrame(int streamid, int flags, ByteBufferReference headerBlock) {
-        this(streamid, flags, new ByteBufferReference[]{headerBlock});
+    public HeadersFrame(int streamid, int flags, ByteBuffer headerBlock) {
+        this(streamid, flags, List.of(headerBlock));
     }
 
     @Override
diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/frame/Http2Frame.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/frame/Http2Frame.java
index 423edbade5a..f4930342a0f 100644
--- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/frame/Http2Frame.java
+++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/frame/Http2Frame.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2017, 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,10 +49,6 @@ public abstract class Http2Frame {
         flags |= flag;
     }
 
-    public void setFlags(int flags) {
-        this.flags = flags;
-    }
-
     public int getFlags() {
         return flags;
     }
@@ -61,16 +57,16 @@ public abstract class Http2Frame {
         return (flags & flag) != 0;
     }
 
-    public void clearFlag(int flag) {
-        flags &= 0xffffffff ^ flag;
-    }
+//    public void clearFlag(int flag) {
+//        flags &= 0xffffffff ^ flag;
+//    }
 
     public void streamid(int streamid) {
         this.streamid = streamid;
     }
 
 
-    public String typeAsString() {
+    private String typeAsString() {
         return asString(type());
     }
 
diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/frame/MalformedFrame.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/frame/MalformedFrame.java
index 3e0ac828149..f365dc7f0e4 100644
--- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/frame/MalformedFrame.java
+++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/frame/MalformedFrame.java
@@ -1,3 +1,28 @@
+/*
+ * Copyright (c) 2015, 2017, 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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 jdk.incubator.http.internal.frame;
 
 public class MalformedFrame extends Http2Frame {
diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/frame/OutgoingHeaders.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/frame/OutgoingHeaders.java
index 90daa44ca9a..984ee492cb6 100644
--- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/frame/OutgoingHeaders.java
+++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/frame/OutgoingHeaders.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2017, 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
diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/frame/PingFrame.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/frame/PingFrame.java
index b966b79ebab..640adb4883c 100644
--- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/frame/PingFrame.java
+++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/frame/PingFrame.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2017, 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
diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/frame/PriorityFrame.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/frame/PriorityFrame.java
index 9231865e138..c7f1b7fb7ac 100644
--- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/frame/PriorityFrame.java
+++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/frame/PriorityFrame.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2017, 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
diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/frame/PushPromiseFrame.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/frame/PushPromiseFrame.java
index 95c11955856..b5d53966bee 100644
--- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/frame/PushPromiseFrame.java
+++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/frame/PushPromiseFrame.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2017, 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
@@ -25,8 +25,8 @@
 
 package jdk.incubator.http.internal.frame;
 
-import jdk.incubator.http.internal.common.ByteBufferReference;
-import jdk.incubator.http.internal.common.Utils;
+import java.nio.ByteBuffer;
+import java.util.List;
 
 public class PushPromiseFrame extends HeaderFrame {
 
@@ -39,7 +39,7 @@ public class PushPromiseFrame extends HeaderFrame {
     public static final int END_HEADERS = 0x4;
     public static final int PADDED = 0x8;
 
-    public PushPromiseFrame(int streamid, int flags, int promisedStream, ByteBufferReference[] buffers, int padLength) {
+    public PushPromiseFrame(int streamid, int flags, int promisedStream, List<ByteBuffer> buffers, int padLength) {
         super(streamid, flags, buffers);
         this.promisedStream = promisedStream;
         if(padLength > 0 ) {
diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/frame/ResetFrame.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/frame/ResetFrame.java
index 63a9ee8a96a..e5d2dfc49ef 100644
--- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/frame/ResetFrame.java
+++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/frame/ResetFrame.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2017, 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
diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/frame/SettingsFrame.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/frame/SettingsFrame.java
index 4cf32c03a65..12b237d8ad8 100644
--- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/frame/SettingsFrame.java
+++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/frame/SettingsFrame.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2017, 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
diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/frame/WindowUpdateFrame.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/frame/WindowUpdateFrame.java
index ed9547b428d..b6ab8e142de 100644
--- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/frame/WindowUpdateFrame.java
+++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/frame/WindowUpdateFrame.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2017, 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
diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/hpack/BinaryRepresentationWriter.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/hpack/BinaryRepresentationWriter.java
index c2f30f01f4b..7b42cb7205b 100644
--- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/hpack/BinaryRepresentationWriter.java
+++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/hpack/BinaryRepresentationWriter.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2017, 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
diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/hpack/BulkSizeUpdateWriter.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/hpack/BulkSizeUpdateWriter.java
index df89d2f7037..f8e46fd434d 100644
--- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/hpack/BulkSizeUpdateWriter.java
+++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/hpack/BulkSizeUpdateWriter.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2017, 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
diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/hpack/Decoder.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/hpack/Decoder.java
index 9841204e50c..bbf7001ead3 100644
--- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/hpack/Decoder.java
+++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/hpack/Decoder.java
@@ -24,20 +24,22 @@
  */
 package jdk.incubator.http.internal.hpack;
 
+import jdk.incubator.http.internal.hpack.HPACK.Logger;
 import jdk.internal.vm.annotation.Stable;
 
 import java.io.IOException;
-import java.io.UncheckedIOException;
-import java.net.ProtocolException;
 import java.nio.ByteBuffer;
+import java.util.concurrent.atomic.AtomicLong;
 
+import static jdk.incubator.http.internal.hpack.HPACK.Logger.Level.EXTRA;
+import static jdk.incubator.http.internal.hpack.HPACK.Logger.Level.NORMAL;
 import static java.lang.String.format;
 import static java.util.Objects.requireNonNull;
 
 /**
  * Decodes headers from their binary representation.
  *
- * <p>Typical lifecycle looks like this:
+ * <p> Typical lifecycle looks like this:
  *
  * <p> {@link #Decoder(int) new Decoder}
  * ({@link #setMaxCapacity(int) setMaxCapacity}?
@@ -62,6 +64,9 @@ import static java.util.Objects.requireNonNull;
  */
 public final class Decoder {
 
+    private final Logger logger;
+    private static final AtomicLong DECODERS_IDS = new AtomicLong();
+
     @Stable
     private static final State[] states = new State[256];
 
@@ -92,6 +97,7 @@ public final class Decoder {
         }
     }
 
+    private final long id;
     private final HeaderTable table;
 
     private State state = State.READY;
@@ -111,9 +117,8 @@ public final class Decoder {
      * header table.
      *
      * <p> The value has to be agreed between decoder and encoder out-of-band,
-     * e.g. by a protocol that uses HPACK (see <a
-     * href="https://tools.ietf.org/html/rfc7541#section-4.2">4.2. Maximum Table
-     * Size</a>).
+     * e.g. by a protocol that uses HPACK
+     * (see <a href="https://tools.ietf.org/html/rfc7541#section-4.2">4.2. Maximum Table Size</a>).
      *
      * @param capacity
      *         a non-negative integer
@@ -122,8 +127,24 @@ public final class Decoder {
      *         if capacity is negative
      */
     public Decoder(int capacity) {
-        setMaxCapacity(capacity);
-        table = new HeaderTable(capacity);
+        id = DECODERS_IDS.incrementAndGet();
+        logger = HPACK.getLogger().subLogger("Decoder#" + id);
+        if (logger.isLoggable(NORMAL)) {
+            logger.log(NORMAL, () -> format("new decoder with maximum table size %s",
+                                            capacity));
+        }
+        if (logger.isLoggable(NORMAL)) {
+            /* To correlate with logging outside HPACK, knowing
+               hashCode/toString is important */
+            logger.log(NORMAL, () -> {
+                String hashCode = Integer.toHexString(
+                        System.identityHashCode(this));
+                return format("toString='%s', identityHashCode=%s",
+                              toString(), hashCode);
+            });
+        }
+        setMaxCapacity0(capacity);
+        table = new HeaderTable(capacity, logger.subLogger("HeaderTable"));
         integerReader = new IntegerReader();
         stringReader = new StringReader();
         name = new StringBuilder(512);
@@ -134,9 +155,8 @@ public final class Decoder {
      * Sets a maximum capacity of the header table.
      *
      * <p> The value has to be agreed between decoder and encoder out-of-band,
-     * e.g. by a protocol that uses HPACK (see <a
-     * href="https://tools.ietf.org/html/rfc7541#section-4.2">4.2. Maximum Table
-     * Size</a>).
+     * e.g. by a protocol that uses HPACK
+     * (see <a href="https://tools.ietf.org/html/rfc7541#section-4.2">4.2. Maximum Table Size</a>).
      *
      * @param capacity
      *         a non-negative integer
@@ -145,6 +165,14 @@ public final class Decoder {
      *         if capacity is negative
      */
     public void setMaxCapacity(int capacity) {
+        if (logger.isLoggable(NORMAL)) {
+            logger.log(NORMAL, () -> format("setting maximum table size to %s",
+                                            capacity));
+        }
+        setMaxCapacity0(capacity);
+    }
+
+    private void setMaxCapacity0(int capacity) {
         if (capacity < 0) {
             throw new IllegalArgumentException("capacity >= 0: " + capacity);
         }
@@ -155,8 +183,8 @@ public final class Decoder {
     /**
      * Decodes a header block from the given buffer to the given callback.
      *
-     * <p> Suppose a header block is represented by a sequence of {@code
-     * ByteBuffer}s in the form of {@code Iterator<ByteBuffer>}. And the
+     * <p> Suppose a header block is represented by a sequence of
+     * {@code ByteBuffer}s in the form of {@code Iterator<ByteBuffer>}. And the
      * consumer of decoded headers is represented by the callback. Then to
      * decode the header block, the following approach might be used:
      *
@@ -174,7 +202,7 @@ public final class Decoder {
      *
      * <p> Once the method is invoked with {@code endOfHeaderBlock == true}, the
      * current header block is deemed ended, and inconsistencies, if any, are
-     * reported immediately by throwing an {@code UncheckedIOException}.
+     * reported immediately by throwing an {@code IOException}.
      *
      * <p> Each callback method is called only after the implementation has
      * processed the corresponding bytes. If the bytes revealed a decoding
@@ -200,25 +228,32 @@ public final class Decoder {
      *
      * @param consumer
      *         the callback
-     * @throws UncheckedIOException
+     * @throws IOException
      *         in case of a decoding error
      * @throws NullPointerException
      *         if either headerBlock or consumer are null
      */
-    public void decode(ByteBuffer headerBlock, boolean endOfHeaderBlock,
-                       DecodingCallback consumer) {
+    public void decode(ByteBuffer headerBlock,
+                       boolean endOfHeaderBlock,
+                       DecodingCallback consumer) throws IOException {
         requireNonNull(headerBlock, "headerBlock");
         requireNonNull(consumer, "consumer");
+        if (logger.isLoggable(NORMAL)) {
+            logger.log(NORMAL, () -> format("reading %s, end of header block? %s",
+                                            headerBlock, endOfHeaderBlock));
+        }
         while (headerBlock.hasRemaining()) {
             proceed(headerBlock, consumer);
         }
         if (endOfHeaderBlock && state != State.READY) {
-            throw new UncheckedIOException(
-                    new ProtocolException("Unexpected end of header block"));
+            logger.log(NORMAL, () -> format("unexpected end of %s representation",
+                                            state));
+            throw new IOException("Unexpected end of header block");
         }
     }
 
-    private void proceed(ByteBuffer input, DecodingCallback action) {
+    private void proceed(ByteBuffer input, DecodingCallback action)
+            throws IOException {
         switch (state) {
             case READY:
                 resumeReady(input);
@@ -239,14 +274,17 @@ public final class Decoder {
                 resumeSizeUpdate(input, action);
                 break;
             default:
-                throw new InternalError(
-                        "Unexpected decoder state: " + String.valueOf(state));
+                throw new InternalError("Unexpected decoder state: " + state);
         }
     }
 
     private void resumeReady(ByteBuffer input) {
         int b = input.get(input.position()) & 0xff; // absolute read
         State s = states[b];
+        if (logger.isLoggable(EXTRA)) {
+            logger.log(EXTRA, () -> format("next binary representation %s (first byte 0x%02x)",
+                                           s, b));
+        }
         switch (s) {
             case INDEXED:
                 integerReader.configure(7);
@@ -292,20 +330,36 @@ public final class Decoder {
     //            | 1 |        Index (7+)         |
     //            +---+---------------------------+
     //
-    private void resumeIndexed(ByteBuffer input, DecodingCallback action) {
+    private void resumeIndexed(ByteBuffer input, DecodingCallback action)
+            throws IOException {
         if (!integerReader.read(input)) {
             return;
         }
         intValue = integerReader.get();
         integerReader.reset();
+        if (logger.isLoggable(NORMAL)) {
+            logger.log(NORMAL, () -> format("indexed %s", intValue));
+        }
         try {
-            HeaderTable.HeaderField f = table.get(intValue);
+            HeaderTable.HeaderField f = getHeaderFieldAt(intValue);
             action.onIndexed(intValue, f.name, f.value);
         } finally {
             state = State.READY;
         }
     }
 
+    private HeaderTable.HeaderField getHeaderFieldAt(int index)
+            throws IOException
+    {
+        HeaderTable.HeaderField f;
+        try {
+            f = table.get(index);
+        } catch (IndexOutOfBoundsException e) {
+            throw new IOException("header fields table index", e);
+        }
+        return f;
+    }
+
     //              0   1   2   3   4   5   6   7
     //            +---+---+---+---+---+---+---+---+
     //            | 0 | 0 | 0 | 0 |  Index (4+)   |
@@ -328,15 +382,24 @@ public final class Decoder {
     //            | Value String (Length octets)  |
     //            +-------------------------------+
     //
-    private void resumeLiteral(ByteBuffer input, DecodingCallback action) {
+    private void resumeLiteral(ByteBuffer input, DecodingCallback action)
+            throws IOException {
         if (!completeReading(input)) {
             return;
         }
         try {
             if (firstValueIndex) {
-                HeaderTable.HeaderField f = table.get(intValue);
+                if (logger.isLoggable(NORMAL)) {
+                    logger.log(NORMAL, () -> format("literal without indexing ('%s', '%s')",
+                                                    intValue, value));
+                }
+                HeaderTable.HeaderField f = getHeaderFieldAt(intValue);
                 action.onLiteral(intValue, f.name, value, valueHuffmanEncoded);
             } else {
+                if (logger.isLoggable(NORMAL)) {
+                    logger.log(NORMAL, () -> format("literal without indexing ('%s', '%s')",
+                                                    name, value));
+                }
                 action.onLiteral(name, nameHuffmanEncoded, value, valueHuffmanEncoded);
             }
         } finally {
@@ -367,7 +430,9 @@ public final class Decoder {
     //            | Value String (Length octets)  |
     //            +-------------------------------+
     //
-    private void resumeLiteralWithIndexing(ByteBuffer input, DecodingCallback action) {
+    private void resumeLiteralWithIndexing(ByteBuffer input,
+                                           DecodingCallback action)
+            throws IOException {
         if (!completeReading(input)) {
             return;
         }
@@ -381,17 +446,22 @@ public final class Decoder {
             String n;
             String v = value.toString();
             if (firstValueIndex) {
-                HeaderTable.HeaderField f = table.get(intValue);
+                if (logger.isLoggable(NORMAL)) {
+                    logger.log(NORMAL, () -> format("literal with incremental indexing ('%s', '%s')",
+                                                    intValue, value));
+                }
+                HeaderTable.HeaderField f = getHeaderFieldAt(intValue);
                 n = f.name;
                 action.onLiteralWithIndexing(intValue, n, v, valueHuffmanEncoded);
             } else {
                 n = name.toString();
+                if (logger.isLoggable(NORMAL)) {
+                    logger.log(NORMAL, () -> format("literal with incremental indexing ('%s', '%s')",
+                                                    n, value));
+                }
                 action.onLiteralWithIndexing(n, nameHuffmanEncoded, v, valueHuffmanEncoded);
             }
             table.put(n, v);
-        } catch (IllegalArgumentException | IllegalStateException e) {
-            throw new UncheckedIOException(
-                    (IOException) new ProtocolException().initCause(e));
         } finally {
             cleanUpAfterReading();
         }
@@ -419,15 +489,25 @@ public final class Decoder {
     //            | Value String (Length octets)  |
     //            +-------------------------------+
     //
-    private void resumeLiteralNeverIndexed(ByteBuffer input, DecodingCallback action) {
+    private void resumeLiteralNeverIndexed(ByteBuffer input,
+                                           DecodingCallback action)
+            throws IOException {
         if (!completeReading(input)) {
             return;
         }
         try {
             if (firstValueIndex) {
-                HeaderTable.HeaderField f = table.get(intValue);
+                if (logger.isLoggable(NORMAL)) {
+                    logger.log(NORMAL, () -> format("literal never indexed ('%s', '%s')",
+                                                    intValue, value));
+                }
+                HeaderTable.HeaderField f = getHeaderFieldAt(intValue);
                 action.onLiteralNeverIndexed(intValue, f.name, value, valueHuffmanEncoded);
             } else {
+                if (logger.isLoggable(NORMAL)) {
+                    logger.log(NORMAL, () -> format("literal never indexed ('%s', '%s')",
+                                                    name, value));
+                }
                 action.onLiteralNeverIndexed(name, nameHuffmanEncoded, value, valueHuffmanEncoded);
             }
         } finally {
@@ -440,16 +520,21 @@ public final class Decoder {
     //            | 0 | 0 | 1 |   Max size (5+)   |
     //            +---+---------------------------+
     //
-    private void resumeSizeUpdate(ByteBuffer input, DecodingCallback action) {
+    private void resumeSizeUpdate(ByteBuffer input,
+                                  DecodingCallback action) throws IOException {
         if (!integerReader.read(input)) {
             return;
         }
         intValue = integerReader.get();
+        if (logger.isLoggable(NORMAL)) {
+            logger.log(NORMAL, () -> format("dynamic table size update %s",
+                                            intValue));
+        }
         assert intValue >= 0;
         if (intValue > capacity) {
-            throw new UncheckedIOException(new ProtocolException(
-                    format("Received capacity exceeds expected: " +
-                            "capacity=%s, expected=%s", intValue, capacity)));
+            throw new IOException(
+                    format("Received capacity exceeds expected: capacity=%s, expected=%s",
+                           intValue, capacity));
         }
         integerReader.reset();
         try {
@@ -460,7 +545,7 @@ public final class Decoder {
         }
     }
 
-    private boolean completeReading(ByteBuffer input) {
+    private boolean completeReading(ByteBuffer input) throws IOException {
         if (!firstValueRead) {
             if (firstValueIndex) {
                 if (!integerReader.read(input)) {
diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/hpack/DecodingCallback.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/hpack/DecodingCallback.java
index 55b6270d3fc..3d1395ebe24 100644
--- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/hpack/DecodingCallback.java
+++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/hpack/DecodingCallback.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2017, 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
@@ -35,10 +35,10 @@ import java.nio.ByteBuffer;
  *
  * @apiNote
  *
- * <p> The callback provides methods for all possible <a
- * href="https://tools.ietf.org/html/rfc7541#section-6">binary
- * representations</a>. This could be useful for implementing an intermediary,
- * logging, debugging, etc.
+ * <p> The callback provides methods for all possible
+ * <a href="https://tools.ietf.org/html/rfc7541#section-6">binary representations</a>.
+ * This could be useful for implementing an intermediary, logging, debugging,
+ * etc.
  *
  * <p> The callback is an interface in order to interoperate with lambdas (in
  * the most common use case):
@@ -98,7 +98,8 @@ public interface DecodingCallback {
      * @see #onLiteralNeverIndexed(int, CharSequence, CharSequence, boolean)
      * @see #onLiteralNeverIndexed(CharSequence, boolean, CharSequence, boolean)
      */
-    default void onDecoded(CharSequence name, CharSequence value,
+    default void onDecoded(CharSequence name,
+                           CharSequence value,
                            boolean sensitive) {
         onDecoded(name, value);
     }
@@ -142,8 +143,10 @@ public interface DecodingCallback {
      * @param valueHuffman
      *         if the {@code value} was Huffman encoded
      */
-    default void onLiteral(int index, CharSequence name,
-                           CharSequence value, boolean valueHuffman) {
+    default void onLiteral(int index,
+                           CharSequence name,
+                           CharSequence value,
+                           boolean valueHuffman) {
         onDecoded(name, value, false);
     }
 
@@ -166,8 +169,10 @@ public interface DecodingCallback {
      * @param valueHuffman
      *         if the {@code value} was Huffman encoded
      */
-    default void onLiteral(CharSequence name, boolean nameHuffman,
-                           CharSequence value, boolean valueHuffman) {
+    default void onLiteral(CharSequence name,
+                           boolean nameHuffman,
+                           CharSequence value,
+                           boolean valueHuffman) {
         onDecoded(name, value, false);
     }
 
@@ -190,7 +195,8 @@ public interface DecodingCallback {
      * @param valueHuffman
      *         if the {@code value} was Huffman encoded
      */
-    default void onLiteralNeverIndexed(int index, CharSequence name,
+    default void onLiteralNeverIndexed(int index,
+                                       CharSequence name,
                                        CharSequence value,
                                        boolean valueHuffman) {
         onDecoded(name, value, true);
@@ -215,8 +221,10 @@ public interface DecodingCallback {
      * @param valueHuffman
      *         if the {@code value} was Huffman encoded
      */
-    default void onLiteralNeverIndexed(CharSequence name, boolean nameHuffman,
-                                       CharSequence value, boolean valueHuffman) {
+    default void onLiteralNeverIndexed(CharSequence name,
+                                       boolean nameHuffman,
+                                       CharSequence value,
+                                       boolean valueHuffman) {
         onDecoded(name, value, true);
     }
 
@@ -241,7 +249,8 @@ public interface DecodingCallback {
      */
     default void onLiteralWithIndexing(int index,
                                        CharSequence name,
-                                       CharSequence value, boolean valueHuffman) {
+                                       CharSequence value,
+                                       boolean valueHuffman) {
         onDecoded(name, value, false);
     }
 
@@ -264,8 +273,10 @@ public interface DecodingCallback {
      * @param valueHuffman
      *         if the {@code value} was Huffman encoded
      */
-    default void onLiteralWithIndexing(CharSequence name, boolean nameHuffman,
-                                       CharSequence value, boolean valueHuffman) {
+    default void onLiteralWithIndexing(CharSequence name,
+                                       boolean nameHuffman,
+                                       CharSequence value,
+                                       boolean valueHuffman) {
         onDecoded(name, value, false);
     }
 
diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/hpack/Encoder.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/hpack/Encoder.java
index cbb4a3546b9..cd91af4c4f2 100644
--- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/hpack/Encoder.java
+++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/hpack/Encoder.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2014, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2014, 2017, 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
@@ -24,27 +24,32 @@
  */
 package jdk.incubator.http.internal.hpack;
 
+import jdk.incubator.http.internal.hpack.HPACK.Logger;
+
 import java.nio.ByteBuffer;
 import java.nio.ReadOnlyBufferException;
 import java.util.LinkedList;
 import java.util.List;
+import java.util.concurrent.atomic.AtomicLong;
 
 import static java.lang.String.format;
 import static java.util.Objects.requireNonNull;
+import static jdk.incubator.http.internal.hpack.HPACK.Logger.Level.EXTRA;
+import static jdk.incubator.http.internal.hpack.HPACK.Logger.Level.NORMAL;
 
 /**
  * Encodes headers to their binary representation.
  *
- * <p>Typical lifecycle looks like this:
+ * <p> Typical lifecycle looks like this:
  *
  * <p> {@link #Encoder(int) new Encoder}
  * ({@link #setMaxCapacity(int) setMaxCapacity}?
  * {@link #encode(ByteBuffer) encode})*
  *
- * <p> Suppose headers are represented by {@code Map<String, List<String>>}. A
- * supplier and a consumer of {@link ByteBuffer}s in forms of {@code
- * Supplier<ByteBuffer>} and {@code Consumer<ByteBuffer>} respectively. Then to
- * encode headers, the following approach might be used:
+ * <p> Suppose headers are represented by {@code Map<String, List<String>>}.
+ * A supplier and a consumer of {@link ByteBuffer}s in forms of
+ * {@code Supplier<ByteBuffer>} and {@code Consumer<ByteBuffer>} respectively.
+ * Then to encode headers, the following approach might be used:
  *
  * <pre>{@code
  *     for (Map.Entry<String, List<String>> h : headers.entrySet()) {
@@ -61,10 +66,9 @@ import static java.util.Objects.requireNonNull;
  *     }
  * }</pre>
  *
- * <p> Though the specification <a
- * href="https://tools.ietf.org/html/rfc7541#section-2"> does not define</a> how
- * an encoder is to be implemented, a default implementation is provided by the
- * method {@link #header(CharSequence, CharSequence, boolean)}.
+ * <p> Though the specification <a href="https://tools.ietf.org/html/rfc7541#section-2">does not define</a>
+ * how an encoder is to be implemented, a default implementation is provided by
+ * the method {@link #header(CharSequence, CharSequence, boolean)}.
  *
  * <p> To provide a custom encoding implementation, {@code Encoder} has to be
  * extended. A subclass then can access methods for encoding using specific
@@ -85,8 +89,8 @@ import static java.util.Objects.requireNonNull;
  * the resulting header block afterwards.
  *
  * <p> Splitting the encoding operation into header set up and header encoding,
- * separates long lived arguments ({@code name}, {@code value}, {@code
- * sensitivity}, etc.) from the short lived ones (e.g. {@code buffer}),
+ * separates long lived arguments ({@code name}, {@code value},
+ * {@code sensitivity}, etc.) from the short lived ones (e.g. {@code buffer}),
  * simplifying each operation itself.
  *
  * @implNote
@@ -99,9 +103,13 @@ import static java.util.Objects.requireNonNull;
  */
 public class Encoder {
 
+    private static final AtomicLong ENCODERS_IDS = new AtomicLong();
+
     // TODO: enum: no huffman/smart huffman/always huffman
     private static final boolean DEFAULT_HUFFMAN = true;
 
+    private final Logger logger;
+    private final long id;
     private final IndexedWriter indexedWriter = new IndexedWriter();
     private final LiteralWriter literalWriter = new LiteralWriter();
     private final LiteralNeverIndexedWriter literalNeverIndexedWriter
@@ -129,9 +137,8 @@ public class Encoder {
      * header table.
      *
      * <p> The value has to be agreed between decoder and encoder out-of-band,
-     * e.g. by a protocol that uses HPACK (see <a
-     * href="https://tools.ietf.org/html/rfc7541#section-4.2">4.2. Maximum Table
-     * Size</a>).
+     * e.g. by a protocol that uses HPACK
+     * (see <a href="https://tools.ietf.org/html/rfc7541#section-4.2">4.2. Maximum Table Size</a>).
      *
      * @param maxCapacity
      *         a non-negative integer
@@ -140,14 +147,33 @@ public class Encoder {
      *         if maxCapacity is negative
      */
     public Encoder(int maxCapacity) {
+        id = ENCODERS_IDS.incrementAndGet();
+        this.logger = HPACK.getLogger().subLogger("Encoder#" + id);
+        if (logger.isLoggable(NORMAL)) {
+            logger.log(NORMAL, () -> format("new encoder with maximum table size %s",
+                                            maxCapacity));
+        }
+        if (logger.isLoggable(EXTRA)) {
+            /* To correlate with logging outside HPACK, knowing
+               hashCode/toString is important */
+            logger.log(EXTRA, () -> {
+                String hashCode = Integer.toHexString(
+                        System.identityHashCode(this));
+                /* Since Encoder can be subclassed hashCode AND identity
+                   hashCode might be different. So let's print both. */
+                return format("toString='%s', hashCode=%s, identityHashCode=%s",
+                              toString(), hashCode(), hashCode);
+            });
+        }
         if (maxCapacity < 0) {
-            throw new IllegalArgumentException("maxCapacity >= 0: " + maxCapacity);
+            throw new IllegalArgumentException(
+                    "maxCapacity >= 0: " + maxCapacity);
         }
         // Initial maximum capacity update mechanics
         minCapacity = Long.MAX_VALUE;
         currCapacity = -1;
-        setMaxCapacity(maxCapacity);
-        headerTable = new HeaderTable(lastCapacity);
+        setMaxCapacity0(maxCapacity);
+        headerTable = new HeaderTable(lastCapacity, logger.subLogger("HeaderTable"));
     }
 
     /**
@@ -176,6 +202,10 @@ public class Encoder {
      * Sets up the given header {@code (name, value)} with possibly sensitive
      * value.
      *
+     * <p> If the {@code value} is sensitive (think security, secrecy, etc.)
+     * this encoder will compress it using a special representation
+     * (see <a href="https://tools.ietf.org/html/rfc7541#section-6.2.3">6.2.3.  Literal Header Field Never Indexed</a>).
+     *
      * <p> Fixates {@code name} and {@code value} for the duration of encoding.
      *
      * @param name
@@ -193,8 +223,13 @@ public class Encoder {
      * @see #header(CharSequence, CharSequence)
      * @see DecodingCallback#onDecoded(CharSequence, CharSequence, boolean)
      */
-    public void header(CharSequence name, CharSequence value,
+    public void header(CharSequence name,
+                       CharSequence value,
                        boolean sensitive) throws IllegalStateException {
+        if (logger.isLoggable(NORMAL)) {
+            logger.log(NORMAL, () -> format("encoding ('%s', '%s'), sensitive: %s",
+                                            name, value, sensitive));
+        }
         // Arguably a good balance between complexity of implementation and
         // efficiency of encoding
         requireNonNull(name, "name");
@@ -222,9 +257,8 @@ public class Encoder {
      * Sets a maximum capacity of the header table.
      *
      * <p> The value has to be agreed between decoder and encoder out-of-band,
-     * e.g. by a protocol that uses HPACK (see <a
-     * href="https://tools.ietf.org/html/rfc7541#section-4.2">4.2. Maximum Table
-     * Size</a>).
+     * e.g. by a protocol that uses HPACK
+     * (see <a href="https://tools.ietf.org/html/rfc7541#section-4.2">4.2. Maximum Table Size</a>).
      *
      * <p> May be called any number of times after or before a complete header
      * has been encoded.
@@ -242,11 +276,23 @@ public class Encoder {
      *         hasn't yet started to encode it
      */
     public void setMaxCapacity(int capacity) {
+        if (logger.isLoggable(NORMAL)) {
+            logger.log(NORMAL, () -> format("setting maximum table size to %s",
+                                            capacity));
+        }
+        setMaxCapacity0(capacity);
+    }
+
+    private void setMaxCapacity0(int capacity) {
         checkEncoding();
         if (capacity < 0) {
             throw new IllegalArgumentException("capacity >= 0: " + capacity);
         }
         int calculated = calculateCapacity(capacity);
+        if (logger.isLoggable(NORMAL)) {
+            logger.log(NORMAL, () -> format("actual maximum table size will be %s",
+                                            calculated));
+        }
         if (calculated < 0 || calculated > capacity) {
             throw new IllegalArgumentException(
                     format("0 <= calculated <= capacity: calculated=%s, capacity=%s",
@@ -263,9 +309,22 @@ public class Encoder {
         minCapacity = Math.min(minCapacity, lastCapacity);
     }
 
+    /**
+     * Calculates actual capacity to be used by this encoder in response to
+     * a request to update maximum table size.
+     *
+     * <p> Default implementation does not add anything to the headers table,
+     * hence this method returns {@code 0}.
+     *
+     * <p> It is an error to return a value {@code c}, where {@code c < 0} or
+     * {@code c > maxCapacity}.
+     *
+     * @param maxCapacity
+     *         upper bound
+     *
+     * @return actual capacity
+     */
     protected int calculateCapacity(int maxCapacity) {
-        // Default implementation of the Encoder won't add anything to the
-        // table, therefore no need for a table space
         return 0;
     }
 
@@ -298,7 +357,10 @@ public class Encoder {
         if (!encoding) {
             throw new IllegalStateException("A header hasn't been set up");
         }
-        if (!prependWithCapacityUpdate(headerBlock)) {
+        if (logger.isLoggable(EXTRA)) {
+            logger.log(EXTRA, () -> format("writing to %s", headerBlock));
+        }
+        if (!prependWithCapacityUpdate(headerBlock)) { // TODO: log
             return false;
         }
         boolean done = writer.write(headerTable, headerBlock);
@@ -339,21 +401,35 @@ public class Encoder {
 
     protected final void indexed(int index) throws IndexOutOfBoundsException {
         checkEncoding();
+        if (logger.isLoggable(EXTRA)) {
+            logger.log(EXTRA, () -> format("indexed %s", index));
+        }
         encoding = true;
         writer = indexedWriter.index(index);
     }
 
-    protected final void literal(int index, CharSequence value,
+    protected final void literal(int index,
+                                 CharSequence value,
                                  boolean useHuffman)
             throws IndexOutOfBoundsException {
+        if (logger.isLoggable(EXTRA)) {
+            logger.log(EXTRA, () -> format("literal without indexing ('%s', '%s')",
+                                           index, value));
+        }
         checkEncoding();
         encoding = true;
         writer = literalWriter
                 .index(index).value(value, useHuffman);
     }
 
-    protected final void literal(CharSequence name, boolean nameHuffman,
-                                 CharSequence value, boolean valueHuffman) {
+    protected final void literal(CharSequence name,
+                                 boolean nameHuffman,
+                                 CharSequence value,
+                                 boolean valueHuffman) {
+        if (logger.isLoggable(EXTRA)) {
+            logger.log(EXTRA, () -> format("literal without indexing ('%s', '%s')",
+                                           name, value));
+        }
         checkEncoding();
         encoding = true;
         writer = literalWriter
@@ -364,6 +440,10 @@ public class Encoder {
                                              CharSequence value,
                                              boolean valueHuffman)
             throws IndexOutOfBoundsException {
+        if (logger.isLoggable(EXTRA)) {
+            logger.log(EXTRA, () -> format("literal never indexed ('%s', '%s')",
+                                           index, value));
+        }
         checkEncoding();
         encoding = true;
         writer = literalNeverIndexedWriter
@@ -374,6 +454,10 @@ public class Encoder {
                                              boolean nameHuffman,
                                              CharSequence value,
                                              boolean valueHuffman) {
+        if (logger.isLoggable(EXTRA)) {
+            logger.log(EXTRA, () -> format("literal never indexed ('%s', '%s')",
+                                           name, value));
+        }
         checkEncoding();
         encoding = true;
         writer = literalNeverIndexedWriter
@@ -384,6 +468,10 @@ public class Encoder {
                                              CharSequence value,
                                              boolean valueHuffman)
             throws IndexOutOfBoundsException {
+        if (logger.isLoggable(EXTRA)) {
+            logger.log(EXTRA, () -> format("literal with incremental indexing ('%s', '%s')",
+                                           index, value));
+        }
         checkEncoding();
         encoding = true;
         writer = literalWithIndexingWriter
@@ -394,6 +482,10 @@ public class Encoder {
                                              boolean nameHuffman,
                                              CharSequence value,
                                              boolean valueHuffman) {
+        if (logger.isLoggable(EXTRA)) { // TODO: include huffman info?
+            logger.log(EXTRA, () -> format("literal with incremental indexing ('%s', '%s')",
+                                           name, value));
+        }
         checkEncoding();
         encoding = true;
         writer = literalWithIndexingWriter
@@ -402,6 +494,10 @@ public class Encoder {
 
     protected final void sizeUpdate(int capacity)
             throws IllegalArgumentException {
+        if (logger.isLoggable(EXTRA)) {
+            logger.log(EXTRA, () -> format("dynamic table size update %s",
+                                           capacity));
+        }
         checkEncoding();
         // Ensure subclass follows the contract
         if (capacity > this.maxCapacity) {
@@ -420,7 +516,7 @@ public class Encoder {
         return headerTable;
     }
 
-    protected final void checkEncoding() {
+    protected final void checkEncoding() { // TODO: better name e.g. checkIfEncodingInProgress()
         if (encoding) {
             throw new IllegalStateException(
                     "Previous encoding operation hasn't finished yet");
diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/hpack/HPACK.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/hpack/HPACK.java
new file mode 100644
index 00000000000..00e5c654d6a
--- /dev/null
+++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/hpack/HPACK.java
@@ -0,0 +1,174 @@
+/*
+ * Copyright (c) 2017, 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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 jdk.incubator.http.internal.hpack;
+
+import jdk.incubator.http.internal.common.Utils;
+import jdk.incubator.http.internal.hpack.HPACK.Logger.Level;
+
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.Map;
+import java.util.ResourceBundle;
+import java.util.function.Supplier;
+
+import static java.lang.String.format;
+import static java.util.stream.Collectors.joining;
+import static jdk.incubator.http.internal.hpack.HPACK.Logger.Level.EXTRA;
+import static jdk.incubator.http.internal.hpack.HPACK.Logger.Level.NONE;
+import static jdk.incubator.http.internal.hpack.HPACK.Logger.Level.NORMAL;
+
+/**
+ * Internal utilities and stuff.
+ */
+public final class HPACK {
+
+    private static final RootLogger LOGGER;
+    private static final Map<String, Level> logLevels =
+            Map.of("NORMAL", NORMAL, "EXTRA", EXTRA);
+
+    static {
+        String PROPERTY = "jdk.internal.httpclient.hpack.log.level";
+
+        String value = AccessController.doPrivileged(
+                (PrivilegedAction<String>) () -> System.getProperty(PROPERTY));
+
+        if (value == null) {
+            LOGGER = new RootLogger(NONE);
+        } else {
+            String upperCasedValue = value.toUpperCase();
+            Level l = logLevels.get(upperCasedValue);
+            if (l == null) {
+                LOGGER = new RootLogger(NONE);
+                LOGGER.log(System.Logger.Level.INFO,
+                        () -> format("%s value '%s' not recognized (use %s); logging disabled",
+                                     PROPERTY, value, logLevels.keySet().stream().collect(joining(", "))));
+            } else {
+                LOGGER = new RootLogger(l);
+                LOGGER.log(System.Logger.Level.DEBUG,
+                        () -> format("logging level %s", l));
+            }
+        }
+    }
+
+    public static Logger getLogger() {
+        return LOGGER;
+    }
+
+    private HPACK() { }
+
+    /**
+     * The purpose of this logger is to provide means of diagnosing issues _in
+     * the HPACK implementation_. It's not a general purpose logger.
+     */
+    // implements System.Logger to make it possible to skip this class
+    // when looking for the Caller.
+    public static class Logger implements System.Logger {
+
+        /**
+         * Log detail level.
+         */
+        public enum Level {
+
+            NONE(0, System.Logger.Level.OFF),
+            NORMAL(1, System.Logger.Level.DEBUG),
+            EXTRA(2, System.Logger.Level.TRACE);
+
+            private final int level;
+            final System.Logger.Level systemLevel;
+
+            Level(int i, System.Logger.Level system) {
+                level = i;
+                systemLevel = system;
+            }
+
+            public final boolean implies(Level other) {
+                return this.level >= other.level;
+            }
+        }
+
+        private final String name;
+        private final Level level;
+        private final String path;
+        private final System.Logger logger;
+
+        private Logger(String path, String name, Level level) {
+            this(path, name, level, null);
+        }
+
+        private Logger(String p, String name, Level level, System.Logger logger) {
+            this.path = p;
+            this.name = name;
+            this.level = level;
+            this.logger = Utils.getHpackLogger(path::toString, level.systemLevel);
+        }
+
+        public final String getName() {
+            return name;
+        }
+
+        @Override
+        public boolean isLoggable(System.Logger.Level level) {
+            return logger.isLoggable(level);
+        }
+
+        @Override
+        public void log(System.Logger.Level level, ResourceBundle bundle, String msg, Throwable thrown) {
+            logger.log(level, bundle, msg,thrown);
+        }
+
+        @Override
+        public void log(System.Logger.Level level, ResourceBundle bundle, String format, Object... params) {
+            logger.log(level, bundle, format, params);
+        }
+
+        /*
+         * Usual performance trick for logging, reducing performance overhead in
+         * the case where logging with the specified level is a NOP.
+         */
+
+        public boolean isLoggable(Level level) {
+            return this.level.implies(level);
+        }
+
+        public void log(Level level, Supplier<String> s) {
+            if (this.level.implies(level)) {
+                logger.log(level.systemLevel, s);
+            }
+        }
+
+        public Logger subLogger(String name) {
+            return new Logger(path + "/" + name, name, level);
+        }
+
+    }
+
+    private static final class RootLogger extends Logger {
+
+        protected RootLogger(Level level) {
+            super("hpack", "hpack", level);
+        }
+
+    }
+}
diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/hpack/HeaderTable.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/hpack/HeaderTable.java
index 970ad42b54d..30f4304c48e 100644
--- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/hpack/HeaderTable.java
+++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/hpack/HeaderTable.java
@@ -24,6 +24,7 @@
  */
 package jdk.incubator.http.internal.hpack;
 
+import jdk.incubator.http.internal.hpack.HPACK.Logger;
 import jdk.internal.vm.annotation.Stable;
 
 import java.util.HashMap;
@@ -32,6 +33,8 @@ import java.util.Map;
 import java.util.NoSuchElementException;
 
 import static java.lang.String.format;
+import static jdk.incubator.http.internal.hpack.HPACK.Logger.Level.EXTRA;
+import static jdk.incubator.http.internal.hpack.HPACK.Logger.Level.NORMAL;
 
 //
 // Header Table combined from two tables: static and dynamic.
@@ -122,11 +125,13 @@ final class HeaderTable {
         }
     }
 
+    private final Logger logger;
     private final Table dynamicTable = new Table(0);
     private int maxSize;
     private int size;
 
-    public HeaderTable(int maxSize) {
+    public HeaderTable(int maxSize, Logger logger) {
+        this.logger = logger;
         setMaxSize(maxSize);
     }
 
@@ -211,21 +216,41 @@ final class HeaderTable {
     }
 
     private void put(HeaderField h) {
+        if (logger.isLoggable(NORMAL)) {
+            logger.log(NORMAL, () -> format("adding ('%s', '%s')",
+                                            h.name, h.value));
+        }
         int entrySize = sizeOf(h);
+        if (logger.isLoggable(EXTRA)) {
+            logger.log(EXTRA, () -> format("size of ('%s', '%s') is %s",
+                                           h.name, h.value, entrySize));
+        }
         while (entrySize > maxSize - size && size != 0) {
+            if (logger.isLoggable(EXTRA)) {
+                logger.log(EXTRA, () -> format("insufficient space %s, must evict entry",
+                                               (maxSize - size)));
+            }
             evictEntry();
         }
         if (entrySize > maxSize - size) {
+            if (logger.isLoggable(EXTRA)) {
+                logger.log(EXTRA, () -> format("not adding ('%s, '%s'), too big",
+                                               h.name, h.value));
+            }
             return;
         }
         size += entrySize;
         dynamicTable.add(h);
+        if (logger.isLoggable(EXTRA)) {
+            logger.log(EXTRA, () -> format("('%s, '%s') added", h.name, h.value));
+            logger.log(EXTRA, this::toString);
+        }
     }
 
     void setMaxSize(int maxSize) {
         if (maxSize < 0) {
-            throw new IllegalArgumentException
-                    ("maxSize >= 0: maxSize=" + maxSize);
+            throw new IllegalArgumentException(
+                    "maxSize >= 0: maxSize=" + maxSize);
         }
         while (maxSize < size && size != 0) {
             evictEntry();
@@ -237,22 +262,29 @@ final class HeaderTable {
 
     HeaderField evictEntry() {
         HeaderField f = dynamicTable.remove();
-        size -= sizeOf(f);
+        int s = sizeOf(f);
+        this.size -= s;
+        if (logger.isLoggable(EXTRA)) {
+            logger.log(EXTRA, () -> format("evicted entry ('%s', '%s') of size %s",
+                                           f.name, f.value, s));
+            logger.log(EXTRA, this::toString);
+        }
         return f;
     }
 
     @Override
     public String toString() {
         double used = maxSize == 0 ? 0 : 100 * (((double) size) / maxSize);
-        return format("entries: %d; used %s/%s (%.1f%%)", dynamicTable.size(),
-                size, maxSize, used);
+        return format("dynamic length: %d, full length: %s, used space: %s/%s (%.1f%%)",
+                      dynamicTable.size(), length(), size, maxSize, used);
     }
 
-    int checkIndex(int index) {
-        if (index < 1 || index > STATIC_TABLE_LENGTH + dynamicTable.size()) {
-            throw new IllegalArgumentException(
+    private int checkIndex(int index) {
+        int len = length();
+        if (index < 1 || index > len) {
+            throw new IndexOutOfBoundsException(
                     format("1 <= index <= length(): index=%s, length()=%s",
-                            index, length()));
+                           index, len));
         }
         return index;
     }
diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/hpack/Huffman.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/hpack/Huffman.java
index c9c572027cc..ee43b81bf90 100644
--- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/hpack/Huffman.java
+++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/hpack/Huffman.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2014, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2014, 2017, 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
@@ -25,7 +25,6 @@
 package jdk.incubator.http.internal.hpack;
 
 import java.io.IOException;
-import java.io.UncheckedIOException;
 import java.nio.ByteBuffer;
 
 import static java.lang.String.format;
@@ -51,16 +50,18 @@ public final class Huffman {
             reset();
         }
 
-        public void read(ByteBuffer source, Appendable destination,
-                         boolean isLast) {
+        public void read(ByteBuffer source,
+                         Appendable destination,
+                         boolean isLast) throws IOException {
             read(source, destination, true, isLast);
         }
 
         // Takes 'isLast' rather than returns whether the reading is done or
         // not, for more informative exceptions.
-        void read(ByteBuffer source, Appendable destination, boolean reportEOS,
-                  boolean isLast) {
-
+        void read(ByteBuffer source,
+                  Appendable destination,
+                  boolean reportEOS, /* reportEOS is exposed for tests */
+                  boolean isLast) throws IOException {
             Node c = curr;
             int l = len;
             /*
@@ -77,16 +78,20 @@ public final class Huffman {
                     l++;
                     if (c.isLeaf()) {
                         if (reportEOS && c.isEOSPath) {
-                            throw new IllegalArgumentException("Encountered EOS");
+                            throw new IOException("Encountered EOS");
+                        }
+                        char ch;
+                        try {
+                            ch = c.getChar();
+                        } catch (IllegalStateException e) {
+                            source.position(pos); // do we need this?
+                            throw new IOException(e);
                         }
                         try {
-                            destination.append(c.getChar());
-                        } catch (RuntimeException | Error e) {
-                            source.position(pos);
-                            throw e;
+                            destination.append(ch);
                         } catch (IOException e) {
-                            source.position(pos);
-                            throw new UncheckedIOException(e);
+                            source.position(pos); // do we need this?
+                            throw e;
                         }
                         c = INSTANCE.root;
                         l = 0;
@@ -107,11 +112,11 @@ public final class Huffman {
                 return; // it's ok, some extra padding bits
             }
             if (c.isEOSPath) {
-                throw new IllegalArgumentException(
+                throw new IOException(
                         "Padding is too long (len=" + len + ") " +
                                 "or unexpected end of data");
             }
-            throw new IllegalArgumentException(
+            throw new IOException(
                     "Not a EOS prefix padding or unexpected end of data");
         }
 
@@ -509,8 +514,8 @@ public final class Huffman {
      * @throws NullPointerException
      *         if the value is null
      * @throws IndexOutOfBoundsException
-     *         if any invocation of {@code value.charAt(i)}, where {@code start
-     *         <= i < end} would throw an IndexOutOfBoundsException
+     *         if any invocation of {@code value.charAt(i)}, where
+     *         {@code start <= i < end} would throw an IndexOutOfBoundsException
      */
     public int lengthOf(CharSequence value, int start, int end) {
         int len = 0;
diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/hpack/ISO_8859_1.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/hpack/ISO_8859_1.java
index c2ddc54062d..dbbd22a5762 100644
--- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/hpack/ISO_8859_1.java
+++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/hpack/ISO_8859_1.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2017, 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
@@ -25,7 +25,6 @@
 package jdk.incubator.http.internal.hpack;
 
 import java.io.IOException;
-import java.io.UncheckedIOException;
 import java.nio.ByteBuffer;
 
 //
@@ -47,14 +46,15 @@ final class ISO_8859_1 {
 
     public static final class Reader {
 
-        public void read(ByteBuffer source, Appendable destination) {
+        public void read(ByteBuffer source, Appendable destination)
+                throws IOException {
             for (int i = 0, len = source.remaining(); i < len; i++) {
                 char c = (char) (source.get() & 0xff);
                 try {
                     destination.append(c);
                 } catch (IOException e) {
-                    throw new UncheckedIOException
-                            ("Error appending to the destination", e);
+                    throw new IOException(
+                            "Error appending to the destination", e);
                 }
             }
         }
diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/hpack/IndexNameValueWriter.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/hpack/IndexNameValueWriter.java
index 1f9062824f0..7369df25f77 100644
--- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/hpack/IndexNameValueWriter.java
+++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/hpack/IndexNameValueWriter.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2017, 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,7 +73,8 @@ abstract class IndexNameValueWriter implements BinaryRepresentationWriter {
                     return false;
                 }
             } else {
-                if (!intWriter.write(destination) || !nameWriter.write(destination)) {
+                if (!intWriter.write(destination) ||
+                        !nameWriter.write(destination)) {
                     return false;
                 }
             }
diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/hpack/IndexedWriter.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/hpack/IndexedWriter.java
index 2605355a31a..e1a8df59376 100644
--- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/hpack/IndexedWriter.java
+++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/hpack/IndexedWriter.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2017, 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
diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/hpack/IntegerReader.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/hpack/IntegerReader.java
index ff59963a8ff..ff0e9a1a773 100644
--- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/hpack/IntegerReader.java
+++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/hpack/IntegerReader.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2014, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2014, 2017, 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
@@ -24,6 +24,7 @@
  */
 package jdk.incubator.http.internal.hpack;
 
+import java.io.IOException;
 import java.nio.ByteBuffer;
 import java.util.Arrays;
 
@@ -73,7 +74,7 @@ final class IntegerReader {
         return this;
     }
 
-    public boolean read(ByteBuffer input) {
+    public boolean read(ByteBuffer input) throws IOException {
         if (state == NEW) {
             throw new IllegalStateException("Configure first");
         }
@@ -105,7 +106,7 @@ final class IntegerReader {
                 i = input.get();
                 long increment = b * (i & 127);
                 if (r + increment > maxValue) {
-                    throw new IllegalArgumentException(format(
+                    throw new IOException(format(
                             "Integer overflow: maxValue=%,d, value=%,d",
                             maxValue, r + increment));
                 }
diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/hpack/IntegerWriter.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/hpack/IntegerWriter.java
index 783a31b88af..33253730a5a 100644
--- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/hpack/IntegerWriter.java
+++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/hpack/IntegerWriter.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2017, 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
diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/hpack/LiteralNeverIndexedWriter.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/hpack/LiteralNeverIndexedWriter.java
index 245df4807e9..b2a7e15b39d 100644
--- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/hpack/LiteralNeverIndexedWriter.java
+++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/hpack/LiteralNeverIndexedWriter.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2017, 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
diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/hpack/LiteralWithIndexingWriter.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/hpack/LiteralWithIndexingWriter.java
index 518eb69c461..d1145eda792 100644
--- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/hpack/LiteralWithIndexingWriter.java
+++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/hpack/LiteralWithIndexingWriter.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2017, 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
diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/hpack/LiteralWriter.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/hpack/LiteralWriter.java
index 1a7b31bf538..f04866b99a2 100644
--- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/hpack/LiteralWriter.java
+++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/hpack/LiteralWriter.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2017, 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
diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/hpack/SizeUpdateWriter.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/hpack/SizeUpdateWriter.java
index a61bc1cc169..0c7895c40e7 100644
--- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/hpack/SizeUpdateWriter.java
+++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/hpack/SizeUpdateWriter.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2017, 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
diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/hpack/StringReader.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/hpack/StringReader.java
index 61cb392d022..68d3bfb3efb 100644
--- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/hpack/StringReader.java
+++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/hpack/StringReader.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2017, 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
@@ -24,6 +24,7 @@
  */
 package jdk.incubator.http.internal.hpack;
 
+import java.io.IOException;
 import java.nio.ByteBuffer;
 import java.util.Arrays;
 
@@ -51,7 +52,7 @@ final class StringReader {
     private boolean huffman;
     private int remainingLength;
 
-    boolean read(ByteBuffer input, Appendable output) {
+    boolean read(ByteBuffer input, Appendable output) throws IOException {
         if (state == DONE) {
             return true;
         }
diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/hpack/StringWriter.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/hpack/StringWriter.java
index f18eb5dc81d..a9c1421fdd9 100644
--- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/hpack/StringWriter.java
+++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/hpack/StringWriter.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2017, 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
@@ -62,7 +62,9 @@ final class StringWriter {
         return configure(input, 0, input.length(), huffman);
     }
 
-    StringWriter configure(CharSequence input, int start, int end,
+    StringWriter configure(CharSequence input,
+                           int start,
+                           int end,
                            boolean huffman) {
         if (start < 0 || end < 0 || end > input.length() || start > end) {
             throw new IndexOutOfBoundsException(
diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/hpack/package-info.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/hpack/package-info.java
index c6a2cf1c9e1..094904aa581 100644
--- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/hpack/package-info.java
+++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/hpack/package-info.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2017, 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
diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/websocket/BuilderImpl.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/websocket/BuilderImpl.java
index 7f662922bc0..07a9406c15f 100644
--- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/websocket/BuilderImpl.java
+++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/websocket/BuilderImpl.java
@@ -31,11 +31,13 @@ import jdk.incubator.http.WebSocket.Builder;
 import jdk.incubator.http.WebSocket.Listener;
 import jdk.incubator.http.internal.common.Pair;
 
+import java.net.ProxySelector;
 import java.net.URI;
 import java.time.Duration;
 import java.util.Collection;
 import java.util.LinkedList;
 import java.util.List;
+import java.util.Optional;
 import java.util.concurrent.CompletableFuture;
 
 import static java.util.Objects.requireNonNull;
@@ -44,16 +46,37 @@ import static jdk.incubator.http.internal.common.Pair.pair;
 public final class BuilderImpl implements Builder {
 
     private final HttpClient client;
-    private final URI uri;
-    private final Listener listener;
-    private final Collection<Pair<String, String>> headers = new LinkedList<>();
-    private final Collection<String> subprotocols = new LinkedList<>();
+    private URI uri;
+    private Listener listener;
+    private final Optional<ProxySelector> proxySelector;
+    private final Collection<Pair<String, String>> headers;
+    private final Collection<String> subprotocols;
     private Duration timeout;
 
-    public BuilderImpl(HttpClient client, URI uri, Listener listener) {
-        this.client = requireNonNull(client, "client");
-        this.uri = requireNonNull(uri, "uri");
-        this.listener = requireNonNull(listener, "listener");
+    public BuilderImpl(HttpClient client, ProxySelector proxySelector)
+    {
+        this(client, null, null, Optional.ofNullable(proxySelector),
+             new LinkedList<>(), new LinkedList<>(), null);
+    }
+
+    private BuilderImpl(HttpClient client,
+                        URI uri,
+                        Listener listener,
+                        Optional<ProxySelector> proxySelector,
+                        Collection<Pair<String, String>> headers,
+                        Collection<String> subprotocols,
+                        Duration timeout) {
+        this.client = client;
+        this.uri = uri;
+        this.listener = listener;
+        this.proxySelector = proxySelector;
+        // If a proxy selector was supplied by the user, it should be present
+        // on the client and should be the same that what we got as an argument
+        assert !client.proxy().isPresent()
+                || client.proxy().equals(proxySelector);
+        this.headers = headers;
+        this.subprotocols = subprotocols;
+        this.timeout = timeout;
     }
 
     @Override
@@ -65,8 +88,7 @@ public final class BuilderImpl implements Builder {
     }
 
     @Override
-    public Builder subprotocols(String mostPreferred,
-                                String... lesserPreferred)
+    public Builder subprotocols(String mostPreferred, String... lesserPreferred)
     {
         requireNonNull(mostPreferred, "mostPreferred");
         requireNonNull(lesserPreferred, "lesserPreferred");
@@ -89,8 +111,13 @@ public final class BuilderImpl implements Builder {
     }
 
     @Override
-    public CompletableFuture<WebSocket> buildAsync() {
-        return WebSocketImpl.newInstanceAsync(this);
+    public CompletableFuture<WebSocket> buildAsync(URI uri, Listener listener) {
+        this.uri = requireNonNull(uri, "uri");
+        this.listener = requireNonNull(listener, "listener");
+        // A snapshot of builder inaccessible for further modification
+        // from the outside
+        BuilderImpl copy = immutableCopy();
+        return WebSocketImpl.newInstanceAsync(copy);
     }
 
     HttpClient getClient() { return client; }
@@ -104,4 +131,19 @@ public final class BuilderImpl implements Builder {
     Collection<String> getSubprotocols() { return subprotocols; }
 
     Duration getConnectTimeout() { return timeout; }
+
+    Optional<ProxySelector> getProxySelector() { return proxySelector; }
+
+    private BuilderImpl immutableCopy() {
+        @SuppressWarnings({"unchecked", "rawtypes"})
+        BuilderImpl copy = new BuilderImpl(
+                client,
+                uri,
+                listener,
+                proxySelector,
+                List.of(this.headers.toArray(new Pair[0])),
+                List.of(this.subprotocols.toArray(new String[0])),
+                timeout);
+        return copy;
+    }
 }
diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/websocket/CheckFailedException.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/websocket/CheckFailedException.java
index cec8fcc331c..ff1fa2e23d0 100644
--- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/websocket/CheckFailedException.java
+++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/websocket/CheckFailedException.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2017, 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
diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/websocket/CooperativeHandler.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/websocket/CooperativeHandler.java
deleted file mode 100644
index 1ccec2d4e8a..00000000000
--- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/websocket/CooperativeHandler.java
+++ /dev/null
@@ -1,208 +0,0 @@
-/*
- * Copyright (c) 2016, 2017, 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.  Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * 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 jdk.incubator.http.internal.websocket;
-
-import java.util.concurrent.atomic.AtomicInteger;
-import java.util.function.Consumer;
-
-import static java.util.Objects.requireNonNull;
-
-/*
- * A synchronization aid that assists a number of parties in running a task
- * in a mutually exclusive fashion.
- *
- * To run the task, a party invokes `handle`. To permanently prevent the task
- * from subsequent runs, the party invokes `stop`.
- *
- * The parties do not have to operate in different threads.
- *
- * The task can be either synchronous or asynchronous.
- *
- * If the task is synchronous, it is represented with `Runnable`.
- * The handler invokes `Runnable.run` to run the task.
- *
- * If the task is asynchronous, it is represented with `Consumer<Runnable>`.
- * The handler invokes `Consumer.accept(end)` to begin the task. The task
- * invokes `end.run()` when it has ended.
- *
- * The next run of the task will not begin until the previous run has finished.
- *
- * The task may invoke `handle()` by itself, it's a normal situation.
- */
-public final class CooperativeHandler {
-
-    /*
-       Since the task is fixed and known beforehand, no blocking synchronization
-       (locks, queues, etc.) is required. The job can be done solely using
-       nonblocking primitives.
-
-       The machinery below addresses two problems:
-
-         1. Running the task in a sequential order (no concurrent runs):
-
-                begin, end, begin, end...
-
-         2. Avoiding indefinite recursion:
-
-                begin
-                  end
-                    begin
-                      end
-                        ...
-
-       Problem #1 is solved with a finite state machine with 4 states:
-
-           BEGIN, AGAIN, END, and STOP.
-
-       Problem #2 is solved with a "state modifier" OFFLOAD.
-
-       Parties invoke `handle()` to signal the task must run. A party that has
-       invoked `handle()` either begins the task or exploits the party that is
-       either beginning the task or ending it.
-
-       The party that is trying to end the task either ends it or begins it
-       again.
-
-       To avoid indefinite recursion, before re-running the task tryEnd() sets
-       OFFLOAD bit, signalling to its "child" tryEnd() that this ("parent")
-       tryEnd() is available and the "child" must offload the task on to the
-       "parent". Then a race begins. Whichever invocation of tryEnd() manages
-       to unset OFFLOAD bit first does not do the work.
-
-       There is at most 1 thread that is beginning the task and at most 2
-       threads that are trying to end it: "parent" and "child". In case of a
-       synchronous task "parent" and "child" are the same thread.
-     */
-
-    private static final int OFFLOAD =  1;
-    private static final int AGAIN   =  2;
-    private static final int BEGIN   =  4;
-    private static final int STOP    =  8;
-    private static final int END     = 16;
-
-    private final AtomicInteger state = new AtomicInteger(END);
-    private final Consumer<Runnable> begin;
-
-    public CooperativeHandler(Runnable task) {
-        this(asyncOf(task));
-    }
-
-    public CooperativeHandler(Consumer<Runnable> begin) {
-        this.begin = requireNonNull(begin);
-    }
-
-    /*
-     * Runs the task (though maybe by a different party).
-     *
-     * The recursion which is possible here will have the maximum depth of 1:
-     *
-     *     this.handle()
-     *         begin.accept()
-     *             this.handle()
-     */
-    public void handle() {
-        while (true) {
-            int s = state.get();
-            if (s == END) {
-                if (state.compareAndSet(END, BEGIN)) {
-                    break;
-                }
-            } else if ((s & BEGIN) != 0) {
-                // Tries to change the state to AGAIN, preserving OFFLOAD bit
-                if (state.compareAndSet(s, AGAIN | (s & OFFLOAD))) {
-                    return;
-                }
-            } else if ((s & AGAIN) != 0 || s == STOP) {
-                return;
-            } else {
-                throw new InternalError(String.valueOf(s));
-            }
-        }
-        begin.accept(this::tryEnd);
-    }
-
-    private void tryEnd() {
-        while (true) {
-            int s;
-            while (((s = state.get()) & OFFLOAD) != 0) {
-                // Tries to offload ending of the task to the parent
-                if (state.compareAndSet(s, s & ~OFFLOAD)) {
-                    return;
-                }
-            }
-            while (true) {
-                if (s == BEGIN) {
-                    if (state.compareAndSet(BEGIN, END)) {
-                        return;
-                    }
-                } else if (s == AGAIN) {
-                    if (state.compareAndSet(AGAIN, BEGIN | OFFLOAD)) {
-                        break;
-                    }
-                } else if (s == STOP) {
-                    return;
-                } else {
-                    throw new InternalError(String.valueOf(s));
-                }
-                s = state.get();
-            }
-            begin.accept(this::tryEnd);
-        }
-    }
-
-    /*
-     * Checks whether or not this handler has been permanently stopped.
-     *
-     * Should be used from inside the task to poll the status of the handler,
-     * pretty much the same way as it is done for threads:
-     *
-     *     if (!Thread.currentThread().isInterrupted()) {
-     *         ...
-     *     }
-     */
-    public boolean isStopped() {
-        return state.get() == STOP;
-    }
-
-    /*
-     * Signals this handler to ignore subsequent invocations to `handle()`.
-     *
-     * If the task has already begun, this invocation will not affect it,
-     * unless the task itself uses `isStopped()` method to check the state
-     * of the handler.
-     */
-    public void stop() {
-        state.set(STOP);
-    }
-
-    private static Consumer<Runnable> asyncOf(Runnable task) {
-        requireNonNull(task);
-        return ender -> {
-            task.run();
-            ender.run();
-        };
-    }
-}
diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/websocket/FailWebSocketException.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/websocket/FailWebSocketException.java
index aa4f7cf6a03..a66bab23228 100644
--- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/websocket/FailWebSocketException.java
+++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/websocket/FailWebSocketException.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2017, 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
diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/websocket/FrameConsumer.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/websocket/FrameConsumer.java
index 1fe63664499..33f42622aad 100644
--- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/websocket/FrameConsumer.java
+++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/websocket/FrameConsumer.java
@@ -184,7 +184,7 @@ class FrameConsumer implements Frame.Consumer {
             part = determinePart(isLast);
             boolean text = opcode == Opcode.TEXT || originatingOpcode == Opcode.TEXT;
             if (!text) {
-                output.onBinary(part, data.slice());
+                output.onBinary(data.slice(), part);
                 data.position(data.limit()); // Consume
             } else {
                 boolean binaryNonEmpty = data.hasRemaining();
@@ -199,7 +199,7 @@ class FrameConsumer implements Frame.Consumer {
                 if (!(binaryNonEmpty && !textData.hasRemaining())) {
                     // If there's a binary data, that result in no text, then we
                     // don't deliver anything
-                    output.onText(part, textData);
+                    output.onText(textData, part);
                 }
             }
         }
diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/websocket/MessageStreamConsumer.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/websocket/MessageStreamConsumer.java
index e93740afa52..1015dd967a5 100644
--- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/websocket/MessageStreamConsumer.java
+++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/websocket/MessageStreamConsumer.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2017, 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
@@ -34,9 +34,9 @@ import java.nio.ByteBuffer;
  */
 interface MessageStreamConsumer {
 
-    void onText(MessagePart part, CharSequence data);
+    void onText(CharSequence data, MessagePart part);
 
-    void onBinary(MessagePart part, ByteBuffer data);
+    void onBinary(ByteBuffer data, MessagePart part);
 
     void onPing(ByteBuffer data);
 
@@ -44,11 +44,11 @@ interface MessageStreamConsumer {
 
     void onClose(int statusCode, CharSequence reason);
 
-    void onError(Exception e);
-
     /*
      * Indicates the end of stream has been reached and there will be no further
      * messages.
      */
     void onComplete();
+
+    void onError(Throwable e);
 }
diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/websocket/OpeningHandshake.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/websocket/OpeningHandshake.java
index 4af7771d8c2..183ba5ecb1d 100644
--- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/websocket/OpeningHandshake.java
+++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/websocket/OpeningHandshake.java
@@ -25,11 +25,6 @@
 
 package jdk.incubator.http.internal.websocket;
 
-import jdk.incubator.http.internal.common.MinimalFuture;
-
-import java.io.IOException;
-import java.net.URI;
-import java.net.URISyntaxException;
 import jdk.incubator.http.HttpClient;
 import jdk.incubator.http.HttpClient.Version;
 import jdk.incubator.http.HttpHeaders;
@@ -37,11 +32,22 @@ import jdk.incubator.http.HttpRequest;
 import jdk.incubator.http.HttpResponse;
 import jdk.incubator.http.HttpResponse.BodyHandler;
 import jdk.incubator.http.WebSocketHandshakeException;
+import jdk.incubator.http.internal.common.MinimalFuture;
 import jdk.incubator.http.internal.common.Pair;
+import jdk.incubator.http.internal.common.Utils;
 
+import java.io.IOException;
+import java.net.InetSocketAddress;
+import java.net.Proxy;
+import java.net.ProxySelector;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URLPermission;
 import java.nio.charset.StandardCharsets;
+import java.security.AccessController;
 import java.security.MessageDigest;
 import java.security.NoSuchAlgorithmException;
+import java.security.PrivilegedAction;
 import java.security.SecureRandom;
 import java.time.Duration;
 import java.util.Base64;
@@ -54,12 +60,14 @@ import java.util.Set;
 import java.util.TreeSet;
 import java.util.concurrent.CompletableFuture;
 import java.util.stream.Collectors;
+import java.util.stream.Stream;
 
 import static java.lang.String.format;
 import static jdk.incubator.http.internal.common.Utils.isValidName;
+import static jdk.incubator.http.internal.common.Utils.permissionForProxy;
 import static jdk.incubator.http.internal.common.Utils.stringOf;
 
-final class OpeningHandshake {
+public class OpeningHandshake {
 
     private static final String HEADER_CONNECTION = "Connection";
     private static final String HEADER_UPGRADE    = "Upgrade";
@@ -68,19 +76,20 @@ final class OpeningHandshake {
     private static final String HEADER_KEY        = "Sec-WebSocket-Key";
     private static final String HEADER_PROTOCOL   = "Sec-WebSocket-Protocol";
     private static final String HEADER_VERSION    = "Sec-WebSocket-Version";
+    private static final String VERSION           = "13";  // WebSocket's lucky number
 
-    private static final Set<String> FORBIDDEN_HEADERS;
+    private static final Set<String> ILLEGAL_HEADERS;
 
     static {
-        FORBIDDEN_HEADERS = new TreeSet<>(String.CASE_INSENSITIVE_ORDER);
-        FORBIDDEN_HEADERS.addAll(List.of(HEADER_ACCEPT,
-                                         HEADER_EXTENSIONS,
-                                         HEADER_KEY,
-                                         HEADER_PROTOCOL,
-                                         HEADER_VERSION));
+        ILLEGAL_HEADERS = new TreeSet<>(String.CASE_INSENSITIVE_ORDER);
+        ILLEGAL_HEADERS.addAll(List.of(HEADER_ACCEPT,
+                                       HEADER_EXTENSIONS,
+                                       HEADER_KEY,
+                                       HEADER_PROTOCOL,
+                                       HEADER_VERSION));
     }
 
-    private static final SecureRandom srandom = new SecureRandom();
+    private static final SecureRandom random = new SecureRandom();
 
     private final MessageDigest sha1;
     private final HttpClient client;
@@ -99,7 +108,10 @@ final class OpeningHandshake {
     private final Collection<String> subprotocols;
     private final String nonce;
 
-    OpeningHandshake(BuilderImpl b) {
+    public OpeningHandshake(BuilderImpl b) {
+        checkURI(b.getUri());
+        Proxy proxy = proxyFor(b.getProxySelector(), b.getUri());
+        checkPermissions(b, proxy);
         this.client = b.getClient();
         URI httpURI = createRequestURI(b.getUri());
         HttpRequest.Builder requestBuilder = HttpRequest.newBuilder(httpURI);
@@ -108,7 +120,7 @@ final class OpeningHandshake {
             requestBuilder.timeout(connectTimeout);
         }
         for (Pair<String, String> p : b.getHeaders()) {
-            if (FORBIDDEN_HEADERS.contains(p.first)) {
+            if (ILLEGAL_HEADERS.contains(p.first)) {
                 throw illegal("Illegal header: " + p.first);
             }
             requestBuilder.header(p.first, p.second);
@@ -118,7 +130,7 @@ final class OpeningHandshake {
             String p = this.subprotocols.stream().collect(Collectors.joining(", "));
             requestBuilder.header(HEADER_PROTOCOL, p);
         }
-        requestBuilder.header(HEADER_VERSION, "13"); // WebSocket's lucky number
+        requestBuilder.header(HEADER_VERSION, VERSION);
         this.nonce = createNonce();
         requestBuilder.header(HEADER_KEY, this.nonce);
         // Setting request version to HTTP/1.1 forcibly, since it's not possible
@@ -130,6 +142,7 @@ final class OpeningHandshake {
         r.isWebSocket(true);
         r.setSystemHeader(HEADER_UPGRADE, "websocket");
         r.setSystemHeader(HEADER_CONNECTION, "Upgrade");
+        r.setProxy(proxy);
     }
 
     private static Collection<String> createRequestSubprotocols(
@@ -153,15 +166,9 @@ final class OpeningHandshake {
      *
      * https://tools.ietf.org/html/rfc6455#section-3
      */
-    private static URI createRequestURI(URI uri) {
-        // TODO: check permission for WebSocket URI and translate it into
-        // http/https permission
-        String s = uri.getScheme(); // The scheme might be null (i.e. undefined)
-        if (!("ws".equalsIgnoreCase(s) || "wss".equalsIgnoreCase(s))
-                || uri.getFragment() != null)
-        {
-            throw illegal("Bad URI: " + uri);
-        }
+    static URI createRequestURI(URI uri) {
+        String s = uri.getScheme();
+        assert "ws".equalsIgnoreCase(s) || "wss".equalsIgnoreCase(s);
         String scheme = "ws".equalsIgnoreCase(s) ? "http" : "https";
         try {
             return new URI(scheme,
@@ -177,9 +184,11 @@ final class OpeningHandshake {
         }
     }
 
-    CompletableFuture<Result> send() {
-        return client.sendAsync(this.request, BodyHandler.<Void>discard(null))
-                .thenCompose(this::resultFrom);
+    public CompletableFuture<Result> send() {
+        PrivilegedAction<CompletableFuture<Result>> pa = () ->
+                client.sendAsync(this.request, BodyHandler.<Void>discard(null))
+                      .thenCompose(this::resultFrom);
+        return AccessController.doPrivileged(pa);
     }
 
     /*
@@ -188,11 +197,11 @@ final class OpeningHandshake {
     static final class Result {
 
         final String subprotocol;
-        final RawChannel channel;
+        final TransportSupplier transport;
 
-        private Result(String subprotocol, RawChannel channel) {
+        private Result(String subprotocol, TransportSupplier transport) {
             this.subprotocol = subprotocol;
-            this.channel = channel;
+            this.transport = transport;
         }
     }
 
@@ -240,7 +249,10 @@ final class OpeningHandshake {
         if (!connection.equalsIgnoreCase("Upgrade")) {
             throw checkFailed("Bad response field: " + HEADER_CONNECTION);
         }
-        requireAbsent(headers, HEADER_VERSION);
+        Optional<String> version = requireAtMostOne(headers, HEADER_VERSION);
+        if (version.isPresent() && !version.get().equals(VERSION)) {
+            throw checkFailed("Bad response field: " + HEADER_VERSION);
+        }
         requireAbsent(headers, HEADER_EXTENSIONS);
         String x = this.nonce + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
         this.sha1.update(x.getBytes(StandardCharsets.ISO_8859_1));
@@ -251,7 +263,7 @@ final class OpeningHandshake {
         }
         String subprotocol = checkAndReturnSubprotocol(headers);
         RawChannel channel = ((RawChannel.Provider) response).rawChannel();
-        return new Result(subprotocol, channel);
+        return new Result(subprotocol, new TransportSupplier(channel));
     }
 
     private String checkAndReturnSubprotocol(HttpHeaders responseHeaders)
@@ -284,6 +296,18 @@ final class OpeningHandshake {
         }
     }
 
+    private static Optional<String> requireAtMostOne(HttpHeaders responseHeaders,
+                                                     String headerName)
+    {
+        List<String> values = responseHeaders.allValues(headerName);
+        if (values.size() > 1) {
+            throw checkFailed(format("Response field '%s' multivalued: %s",
+                                     headerName,
+                                     stringOf(values)));
+        }
+        return values.stream().findFirst();
+    }
+
     private static String requireSingle(HttpHeaders responseHeaders,
                                         String headerName)
     {
@@ -300,15 +324,69 @@ final class OpeningHandshake {
 
     private static String createNonce() {
         byte[] bytes = new byte[16];
-        OpeningHandshake.srandom.nextBytes(bytes);
+        OpeningHandshake.random.nextBytes(bytes);
         return Base64.getEncoder().encodeToString(bytes);
     }
 
+    private static CheckFailedException checkFailed(String message) {
+        throw new CheckFailedException(message);
+    }
+
+    private static URI checkURI(URI uri) {
+        String scheme = uri.getScheme();
+        if (!("ws".equalsIgnoreCase(scheme) || "wss".equalsIgnoreCase(scheme)))
+            throw illegal("invalid URI scheme: " + scheme);
+        if (uri.getHost() == null)
+            throw illegal("URI must contain a host: " + uri);
+        if (uri.getFragment() != null)
+            throw illegal("URI must not contain a fragment: " + uri);
+        return uri;
+    }
+
     private static IllegalArgumentException illegal(String message) {
         return new IllegalArgumentException(message);
     }
 
-    private static CheckFailedException checkFailed(String message) {
-        throw new CheckFailedException(message);
+    /**
+     * Returns the proxy for the given URI when sent through the given client,
+     * or {@code null} if none is required or applicable.
+     */
+    private static Proxy proxyFor(Optional<ProxySelector> selector, URI uri) {
+        if (!selector.isPresent()) {
+            return null;
+        }
+        URI requestURI = createRequestURI(uri); // Based on the HTTP scheme
+        List<Proxy> pl = selector.get().select(requestURI);
+        if (pl.isEmpty()) {
+            return null;
+        }
+        Proxy proxy = pl.get(0);
+        if (proxy.type() != Proxy.Type.HTTP) {
+            return null;
+        }
+        return proxy;
+    }
+
+    /**
+     * Performs the necessary security permissions checks to connect ( possibly
+     * through a proxy ) to the builders WebSocket URI.
+     *
+     * @throws SecurityException if the security manager denies access
+     */
+    static void checkPermissions(BuilderImpl b, Proxy proxy) {
+        SecurityManager sm = System.getSecurityManager();
+        if (sm == null) {
+            return;
+        }
+        Stream<String> headers = b.getHeaders().stream().map(p -> p.first).distinct();
+        URLPermission perm1 = Utils.permissionForServer(b.getUri(), "", headers);
+        sm.checkPermission(perm1);
+        if (proxy == null) {
+            return;
+        }
+        URLPermission perm2 = permissionForProxy((InetSocketAddress) proxy.address());
+        if (perm2 != null) {
+            sm.checkPermission(perm2);
+        }
     }
 }
diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/websocket/OutgoingMessage.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/websocket/OutgoingMessage.java
index c1dc19f756e..ba717bca398 100644
--- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/websocket/OutgoingMessage.java
+++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/websocket/OutgoingMessage.java
@@ -71,12 +71,13 @@ abstract class OutgoingMessage {
      * so it would be possible to defer the work it does until the most
      * convenient moment (up to the point where sentTo is invoked).
      */
-    protected void contextualize(Context context) {
+    protected boolean contextualize(Context context) {
         // masking and charset decoding should be performed here rather than in
         // the constructor (as of today)
         if (context.isCloseSent()) {
             throw new IllegalStateException("Close sent");
         }
+        return true;
     }
 
     protected boolean sendTo(RawChannel channel) throws IOException {
@@ -115,7 +116,7 @@ abstract class OutgoingMessage {
         }
 
         @Override
-        protected void contextualize(Context context) {
+        protected boolean contextualize(Context context) {
             super.contextualize(context);
             if (context.isPreviousBinary() && !context.isPreviousLast()) {
                 throw new IllegalStateException("Unexpected text message");
@@ -125,6 +126,7 @@ abstract class OutgoingMessage {
             context.setPreviousBinary(false);
             context.setPreviousText(true);
             context.setPreviousLast(isLast);
+            return true;
         }
     }
 
@@ -139,7 +141,7 @@ abstract class OutgoingMessage {
         }
 
         @Override
-        protected void contextualize(Context context) {
+        protected boolean contextualize(Context context) {
             super.contextualize(context);
             if (context.isPreviousText() && !context.isPreviousLast()) {
                 throw new IllegalStateException("Unexpected binary message");
@@ -150,6 +152,7 @@ abstract class OutgoingMessage {
             context.setPreviousText(false);
             context.setPreviousBinary(true);
             context.setPreviousLast(isLast);
+            return true;
         }
     }
 
@@ -195,9 +198,13 @@ abstract class OutgoingMessage {
         }
 
         @Override
-        protected void contextualize(Context context) {
-            super.contextualize(context);
-            context.setCloseSent();
+        protected boolean contextualize(Context context) {
+            if (context.isCloseSent()) {
+                return false;
+            } else {
+                context.setCloseSent();
+                return true;
+            }
         }
     }
 
diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/websocket/RawChannel.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/websocket/RawChannel.java
index 7a749645242..d93106353cf 100644
--- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/websocket/RawChannel.java
+++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/websocket/RawChannel.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2017, 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
diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/websocket/Receiver.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/websocket/Receiver.java
index 8812d442794..d9371c58ac2 100644
--- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/websocket/Receiver.java
+++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/websocket/Receiver.java
@@ -25,10 +25,12 @@
 
 package jdk.incubator.http.internal.websocket;
 
+import jdk.incubator.http.internal.common.Demand;
+import jdk.incubator.http.internal.common.SequentialScheduler;
+
 import java.io.IOException;
 import java.nio.ByteBuffer;
 import java.nio.channels.SelectionKey;
-import java.util.concurrent.atomic.AtomicLong;
 
 /*
  * Receives incoming data from the channel on demand and converts it into a
@@ -49,15 +51,15 @@ import java.util.concurrent.atomic.AtomicLong;
  *
  * even if `request(long n)` is called from inside these invocations.
  */
-final class Receiver {
+public class Receiver {
 
     private final MessageStreamConsumer messageConsumer;
     private final RawChannel channel;
     private final FrameConsumer frameConsumer;
     private final Frame.Reader reader = new Frame.Reader();
     private final RawChannel.RawEvent event = createHandler();
-    private final AtomicLong demand = new AtomicLong();
-    private final CooperativeHandler handler;
+    protected final Demand demand = new Demand(); /* Exposed for testing purposes */
+    private final SequentialScheduler pushScheduler;
 
     private ByteBuffer data;
     private volatile int state;
@@ -66,7 +68,7 @@ final class Receiver {
     private static final int AVAILABLE    = 1;
     private static final int WAITING      = 2;
 
-    Receiver(MessageStreamConsumer messageConsumer, RawChannel channel) {
+    public Receiver(MessageStreamConsumer messageConsumer, RawChannel channel) {
         this.messageConsumer = messageConsumer;
         this.channel = channel;
         this.frameConsumer = new FrameConsumer(this.messageConsumer);
@@ -74,7 +76,12 @@ final class Receiver {
         // To ensure the initial non-final `data` will be visible
         // (happens-before) when `handler` invokes `pushContinuously`
         // the following assignment is done last:
-        handler = new CooperativeHandler(this::pushContinuously);
+        pushScheduler = createScheduler();
+    }
+
+    /* Exposed for testing purposes */
+    protected SequentialScheduler createScheduler() {
+        return new SequentialScheduler(new PushContinuouslyTask());
     }
 
     private RawChannel.RawEvent createHandler() {
@@ -88,21 +95,25 @@ final class Receiver {
             @Override
             public void handle() {
                 state = AVAILABLE;
-                handler.handle();
+                pushScheduler.runOrSchedule();
             }
         };
     }
 
-    void request(long n) {
-        if (n < 0L) {
-            throw new IllegalArgumentException("Negative: " + n);
+    public void request(long n) {
+        if (demand.increase(n)) {
+            pushScheduler.runOrSchedule();
         }
-        demand.accumulateAndGet(n, (p, i) -> p + i < 0 ? Long.MAX_VALUE : p + i);
-        handler.handle();
     }
 
+    /*
+     * Why is this method needed? Since Receiver operates through callbacks
+     * this method allows to abstract out what constitutes as a message being
+     * received (i.e. to decide outside this type when exactly one should
+     * decrement the demand).
+     */
     void acknowledge() {
-        long x = demand.decrementAndGet();
+        long x = demand.decreaseAndGet(1);
         if (x < 0) {
             throw new InternalError(String.valueOf(x));
         }
@@ -112,61 +123,66 @@ final class Receiver {
      * Stops the machinery from reading and delivering messages permanently,
      * regardless of the current demand and data availability.
      */
-    void close() {
-        handler.stop();
+    public void close() throws IOException {
+        pushScheduler.stop();
+        channel.shutdownInput();
     }
 
-    private void pushContinuously() {
-        while (!handler.isStopped()) {
-            if (data.hasRemaining()) {
-                if (demand.get() > 0) {
-                    try {
-                        int oldPos = data.position();
-                        reader.readFrame(data, frameConsumer);
-                        int newPos = data.position();
-                        assert oldPos != newPos : data; // reader always consumes bytes
-                    } catch (FailWebSocketException e) {
-                        handler.stop();
-                        messageConsumer.onError(e);
+    private class PushContinuouslyTask
+        extends SequentialScheduler.CompleteRestartableTask
+    {
+        @Override
+        public void run() {
+            while (!pushScheduler.isStopped()) {
+                if (data.hasRemaining()) {
+                    if (!demand.isFulfilled()) {
+                        try {
+                            int oldPos = data.position();
+                            reader.readFrame(data, frameConsumer);
+                            int newPos = data.position();
+                            assert oldPos != newPos : data; // reader always consumes bytes
+                        } catch (Throwable e) {
+                            pushScheduler.stop();
+                            messageConsumer.onError(e);
+                        }
+                        continue;
                     }
-                    continue;
+                    break;
                 }
-                break;
-            }
-            switch (state) {
-                case WAITING:
-                    return;
-                case UNREGISTERED:
-                    try {
-                        state = WAITING;
-                        channel.registerEvent(event);
-                    } catch (IOException e) {
-                        handler.stop();
-                        messageConsumer.onError(e);
-                    }
-                    return;
-                case AVAILABLE:
-                    try {
-                        data = channel.read();
-                    } catch (IOException e) {
-                        handler.stop();
-                        messageConsumer.onError(e);
+                switch (state) {
+                    case WAITING:
                         return;
-                    }
-                    if (data == null) { // EOF
-                        handler.stop();
-                        messageConsumer.onComplete();
+                    case UNREGISTERED:
+                        try {
+                            state = WAITING;
+                            channel.registerEvent(event);
+                        } catch (Throwable e) {
+                            pushScheduler.stop();
+                            messageConsumer.onError(e);
+                        }
                         return;
-                    } else if (!data.hasRemaining()) { // No data at the moment
-                        // Pretty much a "goto", reusing the existing code path
-                        // for registration
-                        state = UNREGISTERED;
-                    }
-                    continue;
-                default:
-                    throw new InternalError(String.valueOf(state));
+                    case AVAILABLE:
+                        try {
+                            data = channel.read();
+                        } catch (Throwable e) {
+                            pushScheduler.stop();
+                            messageConsumer.onError(e);
+                            return;
+                        }
+                        if (data == null) { // EOF
+                            pushScheduler.stop();
+                            messageConsumer.onComplete();
+                            return;
+                        } else if (!data.hasRemaining()) { // No data at the moment
+                            // Pretty much a "goto", reusing the existing code path
+                            // for registration
+                            state = UNREGISTERED;
+                        }
+                        continue;
+                    default:
+                        throw new InternalError(String.valueOf(state));
+                }
             }
         }
     }
 }
-
diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/websocket/Transmitter.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/websocket/Transmitter.java
index 4991515cbb6..e15374eb1f9 100644
--- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/websocket/Transmitter.java
+++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/websocket/Transmitter.java
@@ -40,7 +40,7 @@ import static java.util.Objects.requireNonNull;
  * to accept a new message. Until then, the transmitter is considered "busy" and
  * an IllegalStateException will be thrown on each attempt to invoke send.
  */
-final class Transmitter {
+public class Transmitter {
 
     /* This flag is used solely for assertions */
     private final AtomicBoolean busy = new AtomicBoolean();
@@ -49,8 +49,8 @@ final class Transmitter {
     private final RawChannel channel;
     private final RawChannel.RawEvent event;
 
-    Transmitter(RawChannel channel) {
-        this.channel = requireNonNull(channel);
+    public Transmitter(RawChannel channel) {
+        this.channel = channel;
         this.event = createHandler();
     }
 
@@ -59,7 +59,9 @@ final class Transmitter {
      * A {@code StackOverflowError} may thus occur if there's a possibility
      * that this method is called again by the supplied handler.
      */
-    void send(OutgoingMessage message, Consumer<Exception> completionHandler) {
+    public void send(OutgoingMessage message,
+                     Consumer<Exception> completionHandler)
+    {
         requireNonNull(message);
         requireNonNull(completionHandler);
         if (!busy.compareAndSet(false, true)) {
@@ -68,6 +70,10 @@ final class Transmitter {
         send0(message, completionHandler);
     }
 
+    public void close() throws IOException {
+        channel.shutdownOutput();
+    }
+
     private RawChannel.RawEvent createHandler() {
         return new RawChannel.RawEvent() {
 
diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/websocket/TransportSupplier.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/websocket/TransportSupplier.java
new file mode 100644
index 00000000000..d433a208d9a
--- /dev/null
+++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/websocket/TransportSupplier.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (c) 2017, 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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 jdk.incubator.http.internal.websocket;
+
+import java.io.IOException;
+
+/*
+ * Abstracts out I/O channel for the WebSocket implementation. The latter then
+ * deals with input and output streams of messages and does not have to
+ * understand the state machine of channels (e.g. how exactly they are closed).
+ * Mocking this type will allow testing WebSocket message exchange in isolation.
+ */
+public class TransportSupplier {
+
+    protected final RawChannel channel; /* Exposed for testing purposes */
+    private final Object lock = new Object();
+    private Transmitter transmitter;
+    private Receiver receiver;
+    private boolean receiverShutdown;
+    private boolean transmitterShutdown;
+    private boolean closed;
+
+    public TransportSupplier(RawChannel channel) {
+        this.channel = channel;
+    }
+
+    public Receiver receiver(MessageStreamConsumer consumer) {
+        synchronized (lock) {
+            if (receiver == null) {
+                receiver = newReceiver(consumer);
+            }
+            return receiver;
+        }
+    }
+
+    public Transmitter transmitter() {
+        synchronized (lock) {
+            if (transmitter == null) {
+                transmitter = newTransmitter();
+            }
+            return transmitter;
+        }
+    }
+
+    protected Receiver newReceiver(MessageStreamConsumer consumer) {
+        return new Receiver(consumer, channel) {
+            @Override
+            public void close() throws IOException {
+                synchronized (lock) {
+                    if (!closed) {
+                        try {
+                            super.close();
+                        } finally {
+                            receiverShutdown = true;
+                            if (transmitterShutdown) {
+                                closed = true;
+                                channel.close();
+                            }
+                        }
+                    }
+                }
+            }
+        };
+    }
+
+    protected Transmitter newTransmitter() {
+        return new Transmitter(channel) {
+            @Override
+            public void close() throws IOException {
+                synchronized (lock) {
+                    if (!closed) {
+                        try {
+                            super.close();
+                        } finally {
+                            transmitterShutdown = true;
+                            if (receiverShutdown) {
+                                closed = true;
+                                channel.close();
+                            }
+                        }
+                    }
+                }
+            }
+        };
+    }
+}
diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/websocket/UTF8AccumulatingDecoder.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/websocket/UTF8AccumulatingDecoder.java
index 0a166ccf288..81485783b2f 100644
--- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/websocket/UTF8AccumulatingDecoder.java
+++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/websocket/UTF8AccumulatingDecoder.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2017, 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
diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/websocket/WebSocketImpl.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/websocket/WebSocketImpl.java
index 74d1445fc29..043b97eff66 100644
--- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/websocket/WebSocketImpl.java
+++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/websocket/WebSocketImpl.java
@@ -26,8 +26,13 @@
 package jdk.incubator.http.internal.websocket;
 
 import jdk.incubator.http.WebSocket;
+import jdk.incubator.http.internal.common.Demand;
 import jdk.incubator.http.internal.common.Log;
+import jdk.incubator.http.internal.common.MinimalFuture;
 import jdk.incubator.http.internal.common.Pair;
+import jdk.incubator.http.internal.common.SequentialScheduler;
+import jdk.incubator.http.internal.common.SequentialScheduler.DeferredCompleter;
+import jdk.incubator.http.internal.common.Utils;
 import jdk.incubator.http.internal.websocket.OpeningHandshake.Result;
 import jdk.incubator.http.internal.websocket.OutgoingMessage.Binary;
 import jdk.incubator.http.internal.websocket.OutgoingMessage.Close;
@@ -37,6 +42,7 @@ import jdk.incubator.http.internal.websocket.OutgoingMessage.Pong;
 import jdk.incubator.http.internal.websocket.OutgoingMessage.Text;
 
 import java.io.IOException;
+import java.lang.ref.Reference;
 import java.net.ProtocolException;
 import java.net.URI;
 import java.nio.ByteBuffer;
@@ -44,229 +50,140 @@ import java.util.Queue;
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.CompletionStage;
 import java.util.concurrent.ConcurrentLinkedQueue;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
 import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicReference;
 import java.util.function.Consumer;
 import java.util.function.Function;
 
 import static java.util.Objects.requireNonNull;
-import static java.util.concurrent.CompletableFuture.failedFuture;
+import static jdk.incubator.http.internal.common.MinimalFuture.failedFuture;
 import static jdk.incubator.http.internal.common.Pair.pair;
 import static jdk.incubator.http.internal.websocket.StatusCodes.CLOSED_ABNORMALLY;
 import static jdk.incubator.http.internal.websocket.StatusCodes.NO_STATUS_CODE;
 import static jdk.incubator.http.internal.websocket.StatusCodes.isLegalToSendFromClient;
+import static jdk.incubator.http.internal.websocket.WebSocketImpl.State.BINARY;
+import static jdk.incubator.http.internal.websocket.WebSocketImpl.State.CLOSE;
+import static jdk.incubator.http.internal.websocket.WebSocketImpl.State.ERROR;
+import static jdk.incubator.http.internal.websocket.WebSocketImpl.State.IDLE;
+import static jdk.incubator.http.internal.websocket.WebSocketImpl.State.OPEN;
+import static jdk.incubator.http.internal.websocket.WebSocketImpl.State.PING;
+import static jdk.incubator.http.internal.websocket.WebSocketImpl.State.PONG;
+import static jdk.incubator.http.internal.websocket.WebSocketImpl.State.TEXT;
+import static jdk.incubator.http.internal.websocket.WebSocketImpl.State.WAITING;
 
 /*
  * A WebSocket client.
  */
-final class WebSocketImpl implements WebSocket {
+public final class WebSocketImpl implements WebSocket {
+
+    enum State {
+        OPEN,
+        IDLE,
+        WAITING,
+        TEXT,
+        BINARY,
+        PING,
+        PONG,
+        CLOSE,
+        ERROR;
+    }
+
+    private volatile boolean inputClosed;
+    private volatile boolean outputClosed;
+
+    private final AtomicReference<State> state = new AtomicReference<>(OPEN);
+
+    /* Components of calls to Listener's methods */
+    private MessagePart part;
+    private ByteBuffer binaryData;
+    private CharSequence text;
+    private int statusCode;
+    private String reason;
+    private final AtomicReference<Throwable> error = new AtomicReference<>();
 
     private final URI uri;
     private final String subprotocol;
-    private final RawChannel channel;
     private final Listener listener;
 
-    /*
-     * Whether or not Listener.onClose or Listener.onError has been already
-     * invoked. We keep track of this since only one of these methods is invoked
-     * and it is invoked at most once.
-     */
-    private boolean lastMethodInvoked;
     private final AtomicBoolean outstandingSend = new AtomicBoolean();
-    private final CooperativeHandler sendHandler =
-              new CooperativeHandler(this::sendFirst);
+    private final SequentialScheduler sendScheduler = new SequentialScheduler(new SendTask());
     private final Queue<Pair<OutgoingMessage, CompletableFuture<WebSocket>>>
             queue = new ConcurrentLinkedQueue<>();
     private final Context context = new OutgoingMessage.Context();
     private final Transmitter transmitter;
     private final Receiver receiver;
+    private final SequentialScheduler receiveScheduler = new SequentialScheduler(new ReceiveTask());
+    private final Demand demand = new Demand();
 
-    /*
-     * Whether or not the WebSocket has been closed. When a WebSocket has been
-     * closed it means that no further messages can be sent or received.
-     * A closure can be triggered by:
-     *
-     *   1. abort()
-     *   2. "Failing the WebSocket Connection" (i.e. a fatal error)
-     *   3. Completion of the Closing handshake
-     */
-    private final AtomicBoolean closed = new AtomicBoolean();
-
-    /*
-     * This lock is enforcing sequential ordering of invocations to listener's
-     * methods. It is supposed to be uncontended. The only contention that can
-     * happen is when onOpen, an asynchronous onError (not related to reading
-     * from the channel, e.g. an error from automatic Pong reply) or onClose
-     * (related to abort) happens. Since all of the above are one-shot actions,
-     * the said contention is insignificant.
-     */
-    private final Object lock = new Object();
-
-    private final CompletableFuture<?> closeReceived = new CompletableFuture<>();
-    private final CompletableFuture<?> closeSent = new CompletableFuture<>();
-
-    static CompletableFuture<WebSocket> newInstanceAsync(BuilderImpl b) {
+    public static CompletableFuture<WebSocket> newInstanceAsync(BuilderImpl b) {
         Function<Result, WebSocket> newWebSocket = r -> {
-            WebSocketImpl ws = new WebSocketImpl(b.getUri(),
-                                                 r.subprotocol,
-                                                 r.channel,
-                                                 b.getListener());
-            // The order of calls might cause a subtle effects, like CF will be
-            // returned from the buildAsync _after_ onOpen has been signalled.
-            // This means if onOpen is lengthy, it might cause some problems.
-            ws.signalOpen();
+            WebSocket ws = newInstance(b.getUri(),
+                                       r.subprotocol,
+                                       b.getListener(),
+                                       r.transport);
+            // Make sure we don't release the builder until this lambda
+            // has been executed. The builder has a strong reference to
+            // the HttpClientFacade, and we want to keep that live until
+            // after the raw channel is created and passed to WebSocketImpl.
+            Reference.reachabilityFence(b);
             return ws;
         };
         OpeningHandshake h;
         try {
             h = new OpeningHandshake(b);
-        } catch (IllegalArgumentException e) {
+        } catch (Throwable e) {
             return failedFuture(e);
         }
         return h.send().thenApply(newWebSocket);
     }
 
-    WebSocketImpl(URI uri,
-                  String subprotocol,
-                  RawChannel channel,
-                  Listener listener)
-    {
+    /* Exposed for testing purposes */
+    static WebSocket newInstance(URI uri,
+                                 String subprotocol,
+                                 Listener listener,
+                                 TransportSupplier transport) {
+        WebSocketImpl ws = new WebSocketImpl(uri, subprotocol, listener, transport);
+        // This initialisation is outside of the constructor for the sake of
+        // safe publication of WebSocketImpl.this
+        ws.signalOpen();
+        return ws;
+    }
+
+    private WebSocketImpl(URI uri,
+                          String subprotocol,
+                          Listener listener,
+                          TransportSupplier transport) {
         this.uri = requireNonNull(uri);
         this.subprotocol = requireNonNull(subprotocol);
-        this.channel = requireNonNull(channel);
         this.listener = requireNonNull(listener);
-        this.transmitter = new Transmitter(channel);
-        this.receiver = new Receiver(messageConsumerOf(listener), channel);
-
-        // Set up the Closing Handshake action
-        CompletableFuture.allOf(closeReceived, closeSent)
-                .whenComplete((result, error) -> {
-                    try {
-                        channel.close();
-                    } catch (IOException e) {
-                        Log.logError(e);
-                    } finally {
-                        closed.set(true);
-                    }
-                });
-    }
-
-    /*
-     * This initialisation is outside of the constructor for the sake of
-     * safe publication.
-     */
-    private void signalOpen() {
-        synchronized (lock) {
-            // TODO: might hold lock longer than needed causing prolonged
-            // contention? substitute lock for ConcurrentLinkedQueue<Runnable>?
-            try {
-                listener.onOpen(this);
-            } catch (Exception e) {
-                signalError(e);
-            }
-        }
-    }
-
-    private void signalError(Throwable error) {
-        synchronized (lock) {
-            if (lastMethodInvoked) {
-                Log.logError(error);
-            } else {
-                lastMethodInvoked = true;
-                receiver.close();
-                try {
-                    listener.onError(this, error);
-                } catch (Exception e) {
-                    Log.logError(e);
-                }
-            }
-        }
-    }
-
-    /*
-     * Processes a Close event that came from the channel. Invoked at most once.
-     */
-    private void processClose(int statusCode, String reason) {
-        receiver.close();
-        try {
-            channel.shutdownInput();
-        } catch (IOException e) {
-            Log.logError(e);
-        }
-        boolean alreadyCompleted = !closeReceived.complete(null);
-        if (alreadyCompleted) {
-            // This CF is supposed to be completed only once, the first time a
-            // Close message is received. No further messages are pulled from
-            // the socket.
-            throw new InternalError();
-        }
-        int code;
-        if (statusCode == NO_STATUS_CODE || statusCode == CLOSED_ABNORMALLY) {
-            code = NORMAL_CLOSURE;
-        } else {
-            code = statusCode;
-        }
-        CompletionStage<?> readyToClose = signalClose(statusCode, reason);
-        if (readyToClose == null) {
-            readyToClose = CompletableFuture.completedFuture(null);
-        }
-        readyToClose.whenComplete((r, error) -> {
-            enqueueClose(new Close(code, ""))
-                    .whenComplete((r1, error1) -> {
-                        if (error1 != null) {
-                            Log.logError(error1);
-                        }
-                    });
-        });
-    }
-
-    /*
-     * Signals a Close event (might not correspond to anything happened on the
-     * channel, e.g. `abort()`).
-     */
-    private CompletionStage<?> signalClose(int statusCode, String reason) {
-        synchronized (lock) {
-            if (lastMethodInvoked) {
-                Log.logTrace("Close: {0}, ''{1}''", statusCode, reason);
-            } else {
-                lastMethodInvoked = true;
-                receiver.close();
-                try {
-                    return listener.onClose(this, statusCode, reason);
-                } catch (Exception e) {
-                    Log.logError(e);
-                }
-            }
-        }
-        return null;
+        this.transmitter = transport.transmitter();
+        this.receiver = transport.receiver(new SignallingMessageConsumer());
     }
 
     @Override
-    public CompletableFuture<WebSocket> sendText(CharSequence message,
-                                                 boolean isLast)
-    {
+    public CompletableFuture<WebSocket> sendText(CharSequence message, boolean isLast) {
         return enqueueExclusively(new Text(message, isLast));
     }
 
     @Override
-    public CompletableFuture<WebSocket> sendBinary(ByteBuffer message,
-                                                   boolean isLast)
-    {
+    public CompletableFuture<WebSocket> sendBinary(ByteBuffer message, boolean isLast) {
         return enqueueExclusively(new Binary(message, isLast));
     }
 
     @Override
     public CompletableFuture<WebSocket> sendPing(ByteBuffer message) {
-        return enqueueExclusively(new Ping(message));
+        return enqueue(new Ping(message));
     }
 
     @Override
     public CompletableFuture<WebSocket> sendPong(ByteBuffer message) {
-        return enqueueExclusively(new Pong(message));
+        return enqueue(new Pong(message));
     }
 
     @Override
-    public CompletableFuture<WebSocket> sendClose(int statusCode,
-                                                  String reason) {
+    public CompletableFuture<WebSocket> sendClose(int statusCode, String reason) {
         if (!isLegalToSendFromClient(statusCode)) {
             return failedFuture(
                     new IllegalArgumentException("statusCode: " + statusCode));
@@ -277,27 +194,33 @@ final class WebSocketImpl implements WebSocket {
         } catch (IllegalArgumentException e) {
             return failedFuture(e);
         }
+        outputClosed = true;
         return enqueueClose(msg);
     }
 
     /*
-     * Sends a Close message with the given contents and then shuts down the
-     * channel for writing since no more messages are expected to be sent after
-     * this. Invoked at most once.
+     * Sends a Close message, then shuts down the transmitter since no more
+     * messages are expected to be sent after this.
      */
     private CompletableFuture<WebSocket> enqueueClose(Close m) {
-        return enqueue(m).whenComplete((r, error) -> {
-            try {
-                channel.shutdownOutput();
-            } catch (IOException e) {
-                Log.logError(e);
-            }
-            boolean alreadyCompleted = !closeSent.complete(null);
-            if (alreadyCompleted) {
-                // Shouldn't happen as this callback must run at most once
-                throw new InternalError();
-            }
-        });
+        // TODO: MUST be a CF created once and shared across sendClose, otherwise
+        // a second sendClose may prematurely close the channel
+        return enqueue(m)
+                .orTimeout(60, TimeUnit.SECONDS)
+                .whenComplete((r, error) -> {
+                    try {
+                        transmitter.close();
+                    } catch (IOException e) {
+                        Log.logError(e);
+                    }
+                    if (error instanceof TimeoutException) {
+                        try {
+                            receiver.close();
+                        } catch (IOException e) {
+                            Log.logError(e);
+                        }
+                    }
+                });
     }
 
     /*
@@ -307,60 +230,75 @@ final class WebSocketImpl implements WebSocket {
      * completes. This method is used to enforce "one outstanding send
      * operation" policy.
      */
-    private CompletableFuture<WebSocket> enqueueExclusively(OutgoingMessage m)
-    {
-        if (closed.get()) {
-            return failedFuture(new IllegalStateException("Closed"));
-        }
+    private CompletableFuture<WebSocket> enqueueExclusively(OutgoingMessage m) {
         if (!outstandingSend.compareAndSet(false, true)) {
-            return failedFuture(new IllegalStateException("Outstanding send"));
+            return failedFuture(new IllegalStateException("Send pending"));
         }
         return enqueue(m).whenComplete((r, e) -> outstandingSend.set(false));
     }
 
     private CompletableFuture<WebSocket> enqueue(OutgoingMessage m) {
-        CompletableFuture<WebSocket> cf = new CompletableFuture<>();
+        CompletableFuture<WebSocket> cf = new MinimalFuture<>();
         boolean added = queue.add(pair(m, cf));
         if (!added) {
             // The queue is supposed to be unbounded
             throw new InternalError();
         }
-        sendHandler.handle();
+        sendScheduler.runOrSchedule();
         return cf;
     }
 
     /*
-     * This is the main sending method. It may be run in different threads,
-     * but never concurrently.
+     * This is a message sending task. It pulls messages from the queue one by
+     * one and sends them. It may be run in different threads, but never
+     * concurrently.
      */
-    private void sendFirst(Runnable whenSent) {
-        Pair<OutgoingMessage, CompletableFuture<WebSocket>> p = queue.poll();
-        if (p == null) {
-            whenSent.run();
-            return;
-        }
-        OutgoingMessage message = p.first;
-        CompletableFuture<WebSocket> cf = p.second;
-        try {
-            message.contextualize(context);
-            Consumer<Exception> h = e -> {
-                if (e == null) {
-                    cf.complete(WebSocketImpl.this);
-                } else {
-                    cf.completeExceptionally(e);
+    private class SendTask implements SequentialScheduler.RestartableTask {
+
+        @Override
+        public void run(DeferredCompleter taskCompleter) {
+            Pair<OutgoingMessage, CompletableFuture<WebSocket>> p = queue.poll();
+            if (p == null) {
+                taskCompleter.complete();
+                return;
+            }
+            OutgoingMessage message = p.first;
+            CompletableFuture<WebSocket> cf = p.second;
+            try {
+                if (!message.contextualize(context)) { // Do not send the message
+                    cf.complete(null);
+                    repeat(taskCompleter);
+                    return;
                 }
-                sendHandler.handle();
-                whenSent.run();
-            };
-            transmitter.send(message, h);
-        } catch (Exception t) {
-            cf.completeExceptionally(t);
+                Consumer<Exception> h = e -> {
+                    if (e == null) {
+                        cf.complete(WebSocketImpl.this);
+                    } else {
+                        cf.completeExceptionally(e);
+                    }
+                    repeat(taskCompleter);
+                };
+                transmitter.send(message, h);
+            } catch (Throwable t) {
+                cf.completeExceptionally(t);
+                repeat(taskCompleter);
+            }
+        }
+
+        private void repeat(DeferredCompleter taskCompleter) {
+            taskCompleter.complete();
+            // More than a single message may have been enqueued while
+            // the task has been busy with the current message, but
+            // there is only a single signal recorded
+            sendScheduler.runOrSchedule();
         }
     }
 
     @Override
     public void request(long n) {
-        receiver.request(n);
+        if (demand.increase(n)) {
+            receiveScheduler.runOrSchedule();
+        }
     }
 
     @Override
@@ -369,137 +307,299 @@ final class WebSocketImpl implements WebSocket {
     }
 
     @Override
-    public boolean isClosed() {
-        return closed.get();
+    public boolean isOutputClosed() {
+        return outputClosed;
     }
 
     @Override
-    public void abort() throws IOException {
-        try {
-            channel.close();
-        } finally {
-            closed.set(true);
-            signalClose(CLOSED_ABNORMALLY, "");
-        }
+    public boolean isInputClosed() {
+        return inputClosed;
+    }
+
+    @Override
+    public void abort() {
+        inputClosed = true;
+        outputClosed = true;
+        receiveScheduler.stop();
+        close();
     }
 
     @Override
     public String toString() {
         return super.toString()
-                + "[" + (closed.get() ? "CLOSED" : "OPEN") + "]: " + uri
-                + (!subprotocol.isEmpty() ? ", subprotocol=" + subprotocol : "");
+                + "[uri=" + uri
+                + (!subprotocol.isEmpty() ? ", subprotocol=" + subprotocol : "")
+                + "]";
     }
 
-    private MessageStreamConsumer messageConsumerOf(Listener listener) {
-        // Synchronization performed here in every method is not for the sake of
-        // ordering invocations to this consumer, after all they are naturally
-        // ordered in the channel. The reason is to avoid an interference with
-        // any unrelated to the channel calls to onOpen, onClose and onError.
-        return new MessageStreamConsumer() {
+    /*
+     * The assumptions about order is as follows:
+     *
+     *     - state is never changed more than twice inside the `run` method:
+     *       x --(1)--> IDLE --(2)--> y (otherwise we're loosing events, or
+     *       overwriting parts of messages creating a mess since there's no
+     *       queueing)
+     *     - OPEN is always the first state
+     *     - no messages are requested/delivered before onOpen is called (this
+     *       is implemented by making WebSocket instance accessible first in
+     *       onOpen)
+     *     - after the state has been observed as CLOSE/ERROR, the scheduler
+     *       is stopped
+     */
+    private class ReceiveTask extends SequentialScheduler.CompleteRestartableTask {
 
-            @Override
-            public void onText(MessagePart part, CharSequence data) {
-                receiver.acknowledge();
-                synchronized (WebSocketImpl.this.lock) {
-                    try {
-                        listener.onText(WebSocketImpl.this, data, part);
-                    } catch (Exception e) {
-                        signalError(e);
-                    }
-                }
-            }
+        // Receiver only asked here and nowhere else because we must make sure
+        // onOpen is invoked first and no messages become pending before onOpen
+        // finishes
 
-            @Override
-            public void onBinary(MessagePart part, ByteBuffer data) {
-                receiver.acknowledge();
-                synchronized (WebSocketImpl.this.lock) {
-                    try {
-                        listener.onBinary(WebSocketImpl.this, data.slice(), part);
-                    } catch (Exception e) {
-                        signalError(e);
-                    }
-                }
-            }
-
-            @Override
-            public void onPing(ByteBuffer data) {
-                receiver.acknowledge();
-                // Let's make a full copy of this tiny data. What we want here
-                // is to rule out a possibility the shared data we send might be
-                // corrupted the by processing in the listener.
-                ByteBuffer slice = data.slice();
-                ByteBuffer copy = ByteBuffer.allocate(data.remaining())
-                        .put(data)
-                        .flip();
-                // Non-exclusive send;
-                CompletableFuture<WebSocket> pongSent = enqueue(new Pong(copy));
-                pongSent.whenComplete(
-                        (r, error) -> {
-                            if (error != null) {
-                                WebSocketImpl.this.signalError(error);
+        @Override
+        public void run() {
+            while (true) {
+                State s = state.get();
+                try {
+                    switch (s) {
+                        case OPEN:
+                            processOpen();
+                            tryChangeState(OPEN, IDLE);
+                            break;
+                        case TEXT:
+                            processText();
+                            tryChangeState(TEXT, IDLE);
+                            break;
+                        case BINARY:
+                            processBinary();
+                            tryChangeState(BINARY, IDLE);
+                            break;
+                        case PING:
+                            processPing();
+                            tryChangeState(PING, IDLE);
+                            break;
+                        case PONG:
+                            processPong();
+                            tryChangeState(PONG, IDLE);
+                            break;
+                        case CLOSE:
+                            processClose();
+                            return;
+                        case ERROR:
+                            processError();
+                            return;
+                        case IDLE:
+                            if (demand.tryDecrement()
+                                    && tryChangeState(IDLE, WAITING)) {
+                                receiver.request(1);
                             }
+                            return;
+                        case WAITING:
+                            // For debugging spurious signalling: when there was a
+                            // signal, but apparently nothing has changed
+                            return;
+                        default:
+                            throw new InternalError(String.valueOf(s));
+                    }
+                } catch (Throwable t) {
+                    signalError(t);
+                }
+            }
+        }
+
+        private void processError() throws IOException {
+            receiver.close();
+            receiveScheduler.stop();
+            Throwable err = error.get();
+            if (err instanceof FailWebSocketException) {
+                int code1 = ((FailWebSocketException) err).getStatusCode();
+                err = new ProtocolException().initCause(err);
+                enqueueClose(new Close(code1, ""))
+                        .whenComplete(
+                                (r, e) -> {
+                                    if (e != null) {
+                                        Log.logError(e);
+                                    }
+                                });
+            }
+            listener.onError(WebSocketImpl.this, err);
+        }
+
+        private void processClose() throws IOException {
+            receiver.close();
+            receiveScheduler.stop();
+            CompletionStage<?> readyToClose;
+            readyToClose = listener.onClose(WebSocketImpl.this, statusCode, reason);
+            if (readyToClose == null) {
+                readyToClose = MinimalFuture.completedFuture(null);
+            }
+            int code;
+            if (statusCode == NO_STATUS_CODE || statusCode == CLOSED_ABNORMALLY) {
+                code = NORMAL_CLOSURE;
+            } else {
+                code = statusCode;
+            }
+            readyToClose.whenComplete((r, e) -> {
+                enqueueClose(new Close(code, ""))
+                        .whenComplete((r1, e1) -> {
+                            if (e1 != null) {
+                                Log.logError(e1);
+                            }
+                        });
+            });
+        }
+
+        private void processPong() {
+            listener.onPong(WebSocketImpl.this, binaryData);
+        }
+
+        private void processPing() {
+            // Let's make a full copy of this tiny data. What we want here
+            // is to rule out a possibility the shared data we send might be
+            // corrupted by processing in the listener.
+            ByteBuffer slice = binaryData.slice();
+            ByteBuffer copy = ByteBuffer.allocate(binaryData.remaining())
+                    .put(binaryData)
+                    .flip();
+            // Non-exclusive send;
+            CompletableFuture<WebSocket> pongSent = enqueue(new Pong(copy));
+            pongSent.whenComplete(
+                    (r, e) -> {
+                        if (e != null) {
+                            signalError(Utils.getCompletionCause(e));
                         }
-                );
-                synchronized (WebSocketImpl.this.lock) {
-                    try {
-                        listener.onPing(WebSocketImpl.this, slice);
-                    } catch (Exception e) {
-                        signalError(e);
                     }
-                }
-            }
+            );
+            listener.onPing(WebSocketImpl.this, slice);
+        }
 
-            @Override
-            public void onPong(ByteBuffer data) {
-                receiver.acknowledge();
-                synchronized (WebSocketImpl.this.lock) {
-                    try {
-                        listener.onPong(WebSocketImpl.this, data.slice());
-                    } catch (Exception e) {
-                        signalError(e);
-                    }
-                }
-            }
+        private void processBinary() {
+            listener.onBinary(WebSocketImpl.this, binaryData, part);
+        }
 
-            @Override
-            public void onClose(int statusCode, CharSequence reason) {
-                receiver.acknowledge();
-                processClose(statusCode, reason.toString());
-            }
+        private void processText() {
+            listener.onText(WebSocketImpl.this, text, part);
+        }
 
-            @Override
-            public void onError(Exception error) {
-                // An signalError doesn't necessarily mean we must signalClose
-                // the WebSocket. However, if it's something the WebSocket
-                // Specification recognizes as a reason for "Failing the
-                // WebSocket Connection", then we must do so, but BEFORE
-                // notifying the Listener.
-                if (!(error instanceof FailWebSocketException)) {
-                    signalError(error);
-                } else {
-                    Exception ex = (Exception) new ProtocolException().initCause(error);
-                    int code = ((FailWebSocketException) error).getStatusCode();
-                    enqueueClose(new Close(code, ""))
-                            .whenComplete((r, e) -> {
-                                if (e != null) {
-                                    ex.addSuppressed(e);
-                                }
-                                try {
-                                    channel.close();
-                                } catch (IOException e1) {
-                                    ex.addSuppressed(e1);
-                                } finally {
-                                    closed.set(true);
-                                }
-                                signalError(ex);
-                            });
-                }
-            }
+        private void processOpen() {
+            listener.onOpen(WebSocketImpl.this);
+        }
+    }
 
-            @Override
-            public void onComplete() {
-                processClose(CLOSED_ABNORMALLY, "");
+    private void signalOpen() {
+        receiveScheduler.runOrSchedule();
+    }
+
+    private void signalError(Throwable error) {
+        inputClosed = true;
+        outputClosed = true;
+        if (!this.error.compareAndSet(null, error) || !trySetState(ERROR)) {
+            Log.logError(error);
+        } else {
+            close();
+        }
+    }
+
+    private void close() {
+        try {
+            try {
+                receiver.close();
+            } finally {
+                transmitter.close();
             }
-        };
+        } catch (Throwable t) {
+            Log.logError(t);
+        }
+    }
+
+    /*
+     * Signals a Close event (might not correspond to anything happened on the
+     * channel, i.e. might be synthetic).
+     */
+    private void signalClose(int statusCode, String reason) {
+        inputClosed = true;
+        this.statusCode = statusCode;
+        this.reason = reason;
+        if (!trySetState(CLOSE)) {
+            Log.logTrace("Close: {0}, ''{1}''", statusCode, reason);
+        } else {
+            try {
+                receiver.close();
+            } catch (Throwable t) {
+                Log.logError(t);
+            }
+        }
+    }
+
+    private class SignallingMessageConsumer implements MessageStreamConsumer {
+
+        @Override
+        public void onText(CharSequence data, MessagePart part) {
+            receiver.acknowledge();
+            text = data;
+            WebSocketImpl.this.part = part;
+            tryChangeState(WAITING, TEXT);
+        }
+
+        @Override
+        public void onBinary(ByteBuffer data, MessagePart part) {
+            receiver.acknowledge();
+            binaryData = data;
+            WebSocketImpl.this.part = part;
+            tryChangeState(WAITING, BINARY);
+        }
+
+        @Override
+        public void onPing(ByteBuffer data) {
+            receiver.acknowledge();
+            binaryData = data;
+            tryChangeState(WAITING, PING);
+        }
+
+        @Override
+        public void onPong(ByteBuffer data) {
+            receiver.acknowledge();
+            binaryData = data;
+            tryChangeState(WAITING, PONG);
+        }
+
+        @Override
+        public void onClose(int statusCode, CharSequence reason) {
+            receiver.acknowledge();
+            signalClose(statusCode, reason.toString());
+        }
+
+        @Override
+        public void onComplete() {
+            receiver.acknowledge();
+            signalClose(CLOSED_ABNORMALLY, "");
+        }
+
+        @Override
+        public void onError(Throwable error) {
+            signalError(error);
+        }
+    }
+
+    private boolean trySetState(State newState) {
+        while (true) {
+            State currentState = state.get();
+            if (currentState == ERROR || currentState == CLOSE) {
+                return false;
+            } else if (state.compareAndSet(currentState, newState)) {
+                receiveScheduler.runOrSchedule();
+                return true;
+            }
+        }
+    }
+
+    private boolean tryChangeState(State expectedState, State newState) {
+        State witness = state.compareAndExchange(expectedState, newState);
+        if (witness == expectedState) {
+            receiveScheduler.runOrSchedule();
+            return true;
+        }
+        // This should be the only reason for inability to change the state from
+        // IDLE to WAITING: the state has changed to terminal
+        if (witness != ERROR && witness != CLOSE) {
+            throw new InternalError();
+        }
+        return false;
     }
 }
diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/websocket/WebSocketRequest.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/websocket/WebSocketRequest.java
index 33c45195f21..799f00250ce 100644
--- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/websocket/WebSocketRequest.java
+++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/websocket/WebSocketRequest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2017, 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
@@ -25,6 +25,8 @@
 
 package jdk.incubator.http.internal.websocket;
 
+import java.net.Proxy;
+
 /*
  * https://tools.ietf.org/html/rfc6455#section-4.1
  */
@@ -41,4 +43,9 @@ public interface WebSocketRequest {
      * WebSocket specification.
      */
     void setSystemHeader(String name, String value);
+
+    /*
+     * Sets the proxy for this request.
+     */
+    void setProxy(Proxy proxy);
 }
diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/package-info.java b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/package-info.java
index 312e74923be..15b99d35cee 100644
--- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/package-info.java
+++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/package-info.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2017, 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
diff --git a/src/jdk.incubator.httpclient/share/classes/module-info.java b/src/jdk.incubator.httpclient/share/classes/module-info.java
index 49e8f69f33c..471bc39f067 100644
--- a/src/jdk.incubator.httpclient/share/classes/module-info.java
+++ b/src/jdk.incubator.httpclient/share/classes/module-info.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2017, 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
@@ -33,4 +33,3 @@
 module jdk.incubator.httpclient {
     exports jdk.incubator.http;
 }
-
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.asm.amd64/src/org/graalvm/compiler/asm/amd64/AMD64Assembler.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.asm.amd64/src/org/graalvm/compiler/asm/amd64/AMD64Assembler.java
index 04e0e22182d..089cb3f4b0e 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.asm.amd64/src/org/graalvm/compiler/asm/amd64/AMD64Assembler.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.asm.amd64/src/org/graalvm/compiler/asm/amd64/AMD64Assembler.java
@@ -352,17 +352,18 @@ public class AMD64Assembler extends Assembler {
      */
     private enum OpAssertion {
         ByteAssertion(CPU, CPU, BYTE),
-        IntegerAssertion(CPU, CPU, WORD, DWORD, QWORD),
-        No16BitAssertion(CPU, CPU, DWORD, QWORD),
-        No32BitAssertion(CPU, CPU, WORD, QWORD),
-        QwordOnlyAssertion(CPU, CPU, QWORD),
-        FloatingAssertion(XMM, XMM, SS, SD, PS, PD),
-        PackedFloatingAssertion(XMM, XMM, PS, PD),
+        ByteOrLargerAssertion(CPU, CPU, BYTE, WORD, DWORD, QWORD),
+        WordOrLargerAssertion(CPU, CPU, WORD, DWORD, QWORD),
+        DwordOrLargerAssertion(CPU, CPU, DWORD, QWORD),
+        WordOrDwordAssertion(CPU, CPU, WORD, QWORD),
+        QwordAssertion(CPU, CPU, QWORD),
+        FloatAssertion(XMM, XMM, SS, SD, PS, PD),
+        PackedFloatAssertion(XMM, XMM, PS, PD),
         SingleAssertion(XMM, XMM, SS),
         DoubleAssertion(XMM, XMM, SD),
         PackedDoubleAssertion(XMM, XMM, PD),
-        IntToFloatingAssertion(XMM, CPU, DWORD, QWORD),
-        FloatingToIntAssertion(CPU, XMM, DWORD, QWORD);
+        IntToFloatAssertion(XMM, CPU, DWORD, QWORD),
+        FloatToIntAssertion(CPU, XMM, DWORD, QWORD);
 
         private final RegisterCategory resultCategory;
         private final RegisterCategory inputCategory;
@@ -772,25 +773,25 @@ public class AMD64Assembler extends Assembler {
      */
     public static class AMD64RMOp extends AMD64RROp {
         // @formatter:off
-        public static final AMD64RMOp IMUL   = new AMD64RMOp("IMUL",         P_0F, 0xAF);
+        public static final AMD64RMOp IMUL   = new AMD64RMOp("IMUL",         P_0F, 0xAF, OpAssertion.ByteOrLargerAssertion);
         public static final AMD64RMOp BSF    = new AMD64RMOp("BSF",          P_0F, 0xBC);
         public static final AMD64RMOp BSR    = new AMD64RMOp("BSR",          P_0F, 0xBD);
         public static final AMD64RMOp POPCNT = new AMD64RMOp("POPCNT", 0xF3, P_0F, 0xB8, CPUFeature.POPCNT);
         public static final AMD64RMOp TZCNT  = new AMD64RMOp("TZCNT",  0xF3, P_0F, 0xBC, CPUFeature.BMI1);
         public static final AMD64RMOp LZCNT  = new AMD64RMOp("LZCNT",  0xF3, P_0F, 0xBD, CPUFeature.LZCNT);
-        public static final AMD64RMOp MOVZXB = new AMD64RMOp("MOVZXB",       P_0F, 0xB6, false, true, OpAssertion.IntegerAssertion);
-        public static final AMD64RMOp MOVZX  = new AMD64RMOp("MOVZX",        P_0F, 0xB7, OpAssertion.No16BitAssertion);
-        public static final AMD64RMOp MOVSXB = new AMD64RMOp("MOVSXB",       P_0F, 0xBE, false, true, OpAssertion.IntegerAssertion);
-        public static final AMD64RMOp MOVSX  = new AMD64RMOp("MOVSX",        P_0F, 0xBF, OpAssertion.No16BitAssertion);
-        public static final AMD64RMOp MOVSXD = new AMD64RMOp("MOVSXD",             0x63, OpAssertion.QwordOnlyAssertion);
+        public static final AMD64RMOp MOVZXB = new AMD64RMOp("MOVZXB",       P_0F, 0xB6, false, true, OpAssertion.WordOrLargerAssertion);
+        public static final AMD64RMOp MOVZX  = new AMD64RMOp("MOVZX",        P_0F, 0xB7, OpAssertion.DwordOrLargerAssertion);
+        public static final AMD64RMOp MOVSXB = new AMD64RMOp("MOVSXB",       P_0F, 0xBE, false, true, OpAssertion.WordOrLargerAssertion);
+        public static final AMD64RMOp MOVSX  = new AMD64RMOp("MOVSX",        P_0F, 0xBF, OpAssertion.DwordOrLargerAssertion);
+        public static final AMD64RMOp MOVSXD = new AMD64RMOp("MOVSXD",             0x63, OpAssertion.QwordAssertion);
         public static final AMD64RMOp MOVB   = new AMD64RMOp("MOVB",               0x8A, OpAssertion.ByteAssertion);
         public static final AMD64RMOp MOV    = new AMD64RMOp("MOV",                0x8B);
 
         // MOVD/MOVQ and MOVSS/MOVSD are the same opcode, just with different operand size prefix
-        public static final AMD64RMOp MOVD   = new AMD64RMOp("MOVD",   0x66, P_0F, 0x6E, OpAssertion.IntToFloatingAssertion, CPUFeature.SSE2);
-        public static final AMD64RMOp MOVQ   = new AMD64RMOp("MOVQ",   0x66, P_0F, 0x6E, OpAssertion.IntToFloatingAssertion, CPUFeature.SSE2);
-        public static final AMD64RMOp MOVSS  = new AMD64RMOp("MOVSS",        P_0F, 0x10, OpAssertion.FloatingAssertion, CPUFeature.SSE);
-        public static final AMD64RMOp MOVSD  = new AMD64RMOp("MOVSD",        P_0F, 0x10, OpAssertion.FloatingAssertion, CPUFeature.SSE);
+        public static final AMD64RMOp MOVD   = new AMD64RMOp("MOVD",   0x66, P_0F, 0x6E, OpAssertion.IntToFloatAssertion, CPUFeature.SSE2);
+        public static final AMD64RMOp MOVQ   = new AMD64RMOp("MOVQ",   0x66, P_0F, 0x6E, OpAssertion.IntToFloatAssertion, CPUFeature.SSE2);
+        public static final AMD64RMOp MOVSS  = new AMD64RMOp("MOVSS",        P_0F, 0x10, OpAssertion.FloatAssertion, CPUFeature.SSE);
+        public static final AMD64RMOp MOVSD  = new AMD64RMOp("MOVSD",        P_0F, 0x10, OpAssertion.FloatAssertion, CPUFeature.SSE);
 
         // TEST is documented as MR operation, but it's symmetric, and using it as RM operation is more convenient.
         public static final AMD64RMOp TESTB  = new AMD64RMOp("TEST",               0x84, OpAssertion.ByteAssertion);
@@ -822,7 +823,7 @@ public class AMD64Assembler extends Assembler {
         }
 
         protected AMD64RMOp(String opcode, int prefix1, int prefix2, int op, CPUFeature feature) {
-            this(opcode, prefix1, prefix2, op, OpAssertion.IntegerAssertion, feature);
+            this(opcode, prefix1, prefix2, op, OpAssertion.WordOrLargerAssertion, feature);
         }
 
         protected AMD64RMOp(String opcode, int prefix1, int prefix2, int op, OpAssertion assertion, CPUFeature feature) {
@@ -1014,7 +1015,7 @@ public class AMD64Assembler extends Assembler {
         }
 
         protected AMD64RRMOp(String opcode, int prefix1, int prefix2, int op, CPUFeature feature) {
-            this(opcode, prefix1, prefix2, op, OpAssertion.IntegerAssertion, feature);
+            this(opcode, prefix1, prefix2, op, OpAssertion.WordOrLargerAssertion, feature);
         }
 
         protected AMD64RRMOp(String opcode, int prefix1, int prefix2, int op, OpAssertion assertion, CPUFeature feature) {
@@ -1114,12 +1115,12 @@ public class AMD64Assembler extends Assembler {
 
         // MOVD and MOVQ are the same opcode, just with different operand size prefix
         // Note that as MR opcodes, they have reverse operand order, so the IntToFloatingAssertion must be used.
-        public static final AMD64MROp MOVD   = new AMD64MROp("MOVD",   0x66, P_0F, 0x7E, OpAssertion.IntToFloatingAssertion, CPUFeature.SSE2);
-        public static final AMD64MROp MOVQ   = new AMD64MROp("MOVQ",   0x66, P_0F, 0x7E, OpAssertion.IntToFloatingAssertion, CPUFeature.SSE2);
+        public static final AMD64MROp MOVD   = new AMD64MROp("MOVD",   0x66, P_0F, 0x7E, OpAssertion.IntToFloatAssertion, CPUFeature.SSE2);
+        public static final AMD64MROp MOVQ   = new AMD64MROp("MOVQ",   0x66, P_0F, 0x7E, OpAssertion.IntToFloatAssertion, CPUFeature.SSE2);
 
         // MOVSS and MOVSD are the same opcode, just with different operand size prefix
-        public static final AMD64MROp MOVSS  = new AMD64MROp("MOVSS",        P_0F, 0x11, OpAssertion.FloatingAssertion, CPUFeature.SSE);
-        public static final AMD64MROp MOVSD  = new AMD64MROp("MOVSD",        P_0F, 0x11, OpAssertion.FloatingAssertion, CPUFeature.SSE);
+        public static final AMD64MROp MOVSS  = new AMD64MROp("MOVSS",        P_0F, 0x11, OpAssertion.FloatAssertion, CPUFeature.SSE);
+        public static final AMD64MROp MOVSD  = new AMD64MROp("MOVSD",        P_0F, 0x11, OpAssertion.FloatAssertion, CPUFeature.SSE);
         // @formatter:on
 
         protected AMD64MROp(String opcode, int op) {
@@ -1131,7 +1132,7 @@ public class AMD64Assembler extends Assembler {
         }
 
         protected AMD64MROp(String opcode, int prefix, int op) {
-            this(opcode, prefix, op, OpAssertion.IntegerAssertion);
+            this(opcode, prefix, op, OpAssertion.WordOrLargerAssertion);
         }
 
         protected AMD64MROp(String opcode, int prefix, int op, OpAssertion assertion) {
@@ -1279,7 +1280,7 @@ public class AMD64Assembler extends Assembler {
         public static final AMD64MOp INC  = new AMD64MOp("INC",  0xFF, 0);
         public static final AMD64MOp DEC  = new AMD64MOp("DEC",  0xFF, 1);
         public static final AMD64MOp PUSH = new AMD64MOp("PUSH", 0xFF, 6);
-        public static final AMD64MOp POP  = new AMD64MOp("POP",  0x8F, 0, OpAssertion.No32BitAssertion);
+        public static final AMD64MOp POP  = new AMD64MOp("POP",  0x8F, 0, OpAssertion.WordOrDwordAssertion);
         // @formatter:on
 
         private final int ext;
@@ -1289,7 +1290,7 @@ public class AMD64Assembler extends Assembler {
         }
 
         protected AMD64MOp(String opcode, int prefix, int op, int ext) {
-            this(opcode, prefix, op, ext, OpAssertion.IntegerAssertion);
+            this(opcode, prefix, op, ext, OpAssertion.WordOrLargerAssertion);
         }
 
         protected AMD64MOp(String opcode, int op, int ext, OpAssertion assertion) {
@@ -1327,7 +1328,7 @@ public class AMD64Assembler extends Assembler {
         private final int ext;
 
         protected AMD64MIOp(String opcode, boolean immIsByte, int op, int ext) {
-            this(opcode, immIsByte, op, ext, OpAssertion.IntegerAssertion);
+            this(opcode, immIsByte, op, ext, OpAssertion.WordOrLargerAssertion);
         }
 
         protected AMD64MIOp(String opcode, boolean immIsByte, int op, int ext, OpAssertion assertion) {
@@ -1369,7 +1370,7 @@ public class AMD64Assembler extends Assembler {
         // @formatter:on
 
         protected AMD64RMIOp(String opcode, boolean immIsByte, int op) {
-            this(opcode, immIsByte, 0, op, OpAssertion.IntegerAssertion);
+            this(opcode, immIsByte, 0, op, OpAssertion.WordOrLargerAssertion);
         }
 
         protected AMD64RMIOp(String opcode, boolean immIsByte, int prefix, int op, OpAssertion assertion) {
@@ -1504,16 +1505,16 @@ public class AMD64Assembler extends Assembler {
 
     public static class SSEOp extends AMD64RMOp {
         // @formatter:off
-        public static final SSEOp CVTSI2SS  = new SSEOp("CVTSI2SS",  0xF3, P_0F, 0x2A, OpAssertion.IntToFloatingAssertion);
-        public static final SSEOp CVTSI2SD  = new SSEOp("CVTSI2SS",  0xF2, P_0F, 0x2A, OpAssertion.IntToFloatingAssertion);
-        public static final SSEOp CVTTSS2SI = new SSEOp("CVTTSS2SI", 0xF3, P_0F, 0x2C, OpAssertion.FloatingToIntAssertion);
-        public static final SSEOp CVTTSD2SI = new SSEOp("CVTTSD2SI", 0xF2, P_0F, 0x2C, OpAssertion.FloatingToIntAssertion);
-        public static final SSEOp UCOMIS    = new SSEOp("UCOMIS",          P_0F, 0x2E, OpAssertion.PackedFloatingAssertion);
+        public static final SSEOp CVTSI2SS  = new SSEOp("CVTSI2SS",  0xF3, P_0F, 0x2A, OpAssertion.IntToFloatAssertion);
+        public static final SSEOp CVTSI2SD  = new SSEOp("CVTSI2SS",  0xF2, P_0F, 0x2A, OpAssertion.IntToFloatAssertion);
+        public static final SSEOp CVTTSS2SI = new SSEOp("CVTTSS2SI", 0xF3, P_0F, 0x2C, OpAssertion.FloatToIntAssertion);
+        public static final SSEOp CVTTSD2SI = new SSEOp("CVTTSD2SI", 0xF2, P_0F, 0x2C, OpAssertion.FloatToIntAssertion);
+        public static final SSEOp UCOMIS    = new SSEOp("UCOMIS",          P_0F, 0x2E, OpAssertion.PackedFloatAssertion);
         public static final SSEOp SQRT      = new SSEOp("SQRT",            P_0F, 0x51);
-        public static final SSEOp AND       = new SSEOp("AND",             P_0F, 0x54, OpAssertion.PackedFloatingAssertion);
-        public static final SSEOp ANDN      = new SSEOp("ANDN",            P_0F, 0x55, OpAssertion.PackedFloatingAssertion);
-        public static final SSEOp OR        = new SSEOp("OR",              P_0F, 0x56, OpAssertion.PackedFloatingAssertion);
-        public static final SSEOp XOR       = new SSEOp("XOR",             P_0F, 0x57, OpAssertion.PackedFloatingAssertion);
+        public static final SSEOp AND       = new SSEOp("AND",             P_0F, 0x54, OpAssertion.PackedFloatAssertion);
+        public static final SSEOp ANDN      = new SSEOp("ANDN",            P_0F, 0x55, OpAssertion.PackedFloatAssertion);
+        public static final SSEOp OR        = new SSEOp("OR",              P_0F, 0x56, OpAssertion.PackedFloatAssertion);
+        public static final SSEOp XOR       = new SSEOp("XOR",             P_0F, 0x57, OpAssertion.PackedFloatAssertion);
         public static final SSEOp ADD       = new SSEOp("ADD",             P_0F, 0x58);
         public static final SSEOp MUL       = new SSEOp("MUL",             P_0F, 0x59);
         public static final SSEOp CVTSS2SD  = new SSEOp("CVTSS2SD",        P_0F, 0x5A, OpAssertion.SingleAssertion);
@@ -1525,7 +1526,7 @@ public class AMD64Assembler extends Assembler {
         // @formatter:on
 
         protected SSEOp(String opcode, int prefix, int op) {
-            this(opcode, prefix, op, OpAssertion.FloatingAssertion);
+            this(opcode, prefix, op, OpAssertion.FloatAssertion);
         }
 
         protected SSEOp(String opcode, int prefix, int op, OpAssertion assertion) {
@@ -1539,10 +1540,10 @@ public class AMD64Assembler extends Assembler {
 
     public static class AVXOp extends AMD64RRMOp {
         // @formatter:off
-        public static final AVXOp AND       = new AVXOp("AND",             P_0F, 0x54, OpAssertion.PackedFloatingAssertion);
-        public static final AVXOp ANDN      = new AVXOp("ANDN",            P_0F, 0x55, OpAssertion.PackedFloatingAssertion);
-        public static final AVXOp OR        = new AVXOp("OR",              P_0F, 0x56, OpAssertion.PackedFloatingAssertion);
-        public static final AVXOp XOR       = new AVXOp("XOR",             P_0F, 0x57, OpAssertion.PackedFloatingAssertion);
+        public static final AVXOp AND       = new AVXOp("AND",             P_0F, 0x54, OpAssertion.PackedFloatAssertion);
+        public static final AVXOp ANDN      = new AVXOp("ANDN",            P_0F, 0x55, OpAssertion.PackedFloatAssertion);
+        public static final AVXOp OR        = new AVXOp("OR",              P_0F, 0x56, OpAssertion.PackedFloatAssertion);
+        public static final AVXOp XOR       = new AVXOp("XOR",             P_0F, 0x57, OpAssertion.PackedFloatAssertion);
         public static final AVXOp ADD       = new AVXOp("ADD",             P_0F, 0x58);
         public static final AVXOp MUL       = new AVXOp("MUL",             P_0F, 0x59);
         public static final AVXOp SUB       = new AVXOp("SUB",             P_0F, 0x5C);
@@ -1552,7 +1553,7 @@ public class AMD64Assembler extends Assembler {
         // @formatter:on
 
         protected AVXOp(String opcode, int prefix, int op) {
-            this(opcode, prefix, op, OpAssertion.FloatingAssertion);
+            this(opcode, prefix, op, OpAssertion.FloatAssertion);
         }
 
         protected AVXOp(String opcode, int prefix, int op, OpAssertion assertion) {
@@ -1595,10 +1596,10 @@ public class AMD64Assembler extends Assembler {
             byteMrOp = new AMD64MROp(opcode, 0, baseOp, OpAssertion.ByteAssertion);
             byteRmOp = new AMD64RMOp(opcode, 0, baseOp | 0x02, OpAssertion.ByteAssertion);
 
-            immOp = new AMD64MIOp(opcode, false, 0, 0x81, code, OpAssertion.IntegerAssertion);
-            immSxOp = new AMD64MIOp(opcode, true, 0, 0x83, code, OpAssertion.IntegerAssertion);
-            mrOp = new AMD64MROp(opcode, 0, baseOp | 0x01, OpAssertion.IntegerAssertion);
-            rmOp = new AMD64RMOp(opcode, 0, baseOp | 0x03, OpAssertion.IntegerAssertion);
+            immOp = new AMD64MIOp(opcode, false, 0, 0x81, code, OpAssertion.WordOrLargerAssertion);
+            immSxOp = new AMD64MIOp(opcode, true, 0, 0x83, code, OpAssertion.WordOrLargerAssertion);
+            mrOp = new AMD64MROp(opcode, 0, baseOp | 0x01, OpAssertion.WordOrLargerAssertion);
+            rmOp = new AMD64RMOp(opcode, 0, baseOp | 0x03, OpAssertion.WordOrLargerAssertion);
         }
 
         public AMD64MIOp getMIOpcode(OperandSize size, boolean sx) {
@@ -1647,9 +1648,9 @@ public class AMD64Assembler extends Assembler {
         public final AMD64MIOp miOp;
 
         private AMD64Shift(String opcode, int code) {
-            m1Op = new AMD64MOp(opcode, 0, 0xD1, code, OpAssertion.IntegerAssertion);
-            mcOp = new AMD64MOp(opcode, 0, 0xD3, code, OpAssertion.IntegerAssertion);
-            miOp = new AMD64MIOp(opcode, true, 0, 0xC1, code, OpAssertion.IntegerAssertion);
+            m1Op = new AMD64MOp(opcode, 0, 0xD1, code, OpAssertion.WordOrLargerAssertion);
+            mcOp = new AMD64MOp(opcode, 0, 0xD3, code, OpAssertion.WordOrLargerAssertion);
+            miOp = new AMD64MIOp(opcode, true, 0, 0xC1, code, OpAssertion.WordOrLargerAssertion);
         }
     }
 
@@ -1967,6 +1968,12 @@ public class AMD64Assembler extends Assembler {
         }
     }
 
+    public final void lead(Register dst, AMD64Address src) {
+        prefix(src, dst);
+        emitByte(0x8D);
+        emitOperandHelper(dst, src, 0);
+    }
+
     public final void leaq(Register dst, AMD64Address src) {
         prefixq(src, dst);
         emitByte(0x8D);
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.aarch64/src/org/graalvm/compiler/core/aarch64/AArch64AddressLoweringByUse.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.aarch64/src/org/graalvm/compiler/core/aarch64/AArch64AddressLoweringByUse.java
index f584d35d65a..95abe6f0e70 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.aarch64/src/org/graalvm/compiler/core/aarch64/AArch64AddressLoweringByUse.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.aarch64/src/org/graalvm/compiler/core/aarch64/AArch64AddressLoweringByUse.java
@@ -24,8 +24,6 @@
 
 package org.graalvm.compiler.core.aarch64;
 
-import jdk.vm.ci.aarch64.AArch64Kind;
-import jdk.vm.ci.meta.JavaConstant;
 import org.graalvm.compiler.asm.aarch64.AArch64Address;
 import org.graalvm.compiler.core.common.LIRKind;
 import org.graalvm.compiler.core.common.NumUtil;
@@ -34,9 +32,11 @@ import org.graalvm.compiler.nodes.ValueNode;
 import org.graalvm.compiler.nodes.calc.AddNode;
 import org.graalvm.compiler.nodes.memory.address.AddressNode;
 import org.graalvm.compiler.nodes.memory.address.OffsetAddressNode;
-import org.graalvm.compiler.nodes.memory.address.RawAddressNode;
 import org.graalvm.compiler.phases.common.AddressLoweringByUsePhase;
 
+import jdk.vm.ci.aarch64.AArch64Kind;
+import jdk.vm.ci.meta.JavaConstant;
+
 public class AArch64AddressLoweringByUse extends AddressLoweringByUsePhase.AddressLoweringByUse {
     private AArch64LIRKindTool kindtool;
 
@@ -46,9 +46,7 @@ public class AArch64AddressLoweringByUse extends AddressLoweringByUsePhase.Addre
 
     @Override
     public AddressNode lower(ValueNode use, Stamp stamp, AddressNode address) {
-        if (address instanceof RawAddressNode) {
-            return doLower(stamp, address.getBase(), null);
-        } else if (address instanceof OffsetAddressNode) {
+        if (address instanceof OffsetAddressNode) {
             OffsetAddressNode offsetAddress = (OffsetAddressNode) address;
             return doLower(stamp, offsetAddress.getBase(), offsetAddress.getOffset());
         } else {
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.aarch64/src/org/graalvm/compiler/core/aarch64/AArch64AddressNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.aarch64/src/org/graalvm/compiler/core/aarch64/AArch64AddressNode.java
index 3fddb853424..2737f18b574 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.aarch64/src/org/graalvm/compiler/core/aarch64/AArch64AddressNode.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.aarch64/src/org/graalvm/compiler/core/aarch64/AArch64AddressNode.java
@@ -30,6 +30,7 @@ import org.graalvm.compiler.graph.NodeClass;
 import org.graalvm.compiler.lir.aarch64.AArch64AddressValue;
 import org.graalvm.compiler.lir.gen.LIRGeneratorTool;
 import org.graalvm.compiler.nodeinfo.NodeInfo;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.ValueNode;
 import org.graalvm.compiler.nodes.memory.address.AddressNode;
 import org.graalvm.compiler.nodes.spi.LIRLowerable;
@@ -88,7 +89,7 @@ public class AArch64AddressNode extends AddressNode implements LIRLowerable {
             }
         }
 
-        LIRKind kind = LIRKind.combineDerived(tool.getLIRKind(stamp()), baseReference, indexReference);
+        LIRKind kind = LIRKind.combineDerived(tool.getLIRKind(stamp(NodeView.DEFAULT)), baseReference, indexReference);
         gen.setResult(this, new AArch64AddressValue(kind, baseValue, indexValue, (int) displacement, scaleFactor, addressingMode));
     }
 
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.aarch64/src/org/graalvm/compiler/core/aarch64/AArch64NodeMatchRules.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.aarch64/src/org/graalvm/compiler/core/aarch64/AArch64NodeMatchRules.java
index b99cd62bdad..3e98252c3e1 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.aarch64/src/org/graalvm/compiler/core/aarch64/AArch64NodeMatchRules.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.aarch64/src/org/graalvm/compiler/core/aarch64/AArch64NodeMatchRules.java
@@ -27,6 +27,7 @@ import org.graalvm.compiler.core.gen.NodeMatchRules;
 import org.graalvm.compiler.lir.LIRFrameState;
 import org.graalvm.compiler.lir.gen.LIRGeneratorTool;
 import org.graalvm.compiler.nodes.DeoptimizingNode;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.memory.Access;
 
 import jdk.vm.ci.aarch64.AArch64Kind;
@@ -45,7 +46,7 @@ public class AArch64NodeMatchRules extends NodeMatchRules {
     }
 
     protected AArch64Kind getMemoryKind(Access access) {
-        return (AArch64Kind) gen.getLIRKind(access.asNode().stamp()).getPlatformKind();
+        return (AArch64Kind) gen.getLIRKind(access.asNode().stamp(NodeView.DEFAULT)).getPlatformKind();
     }
 
     @Override
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.amd64.test/src/org/graalvm/compiler/core/amd64/test/AMD64AllocatorTest.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.amd64.test/src/org/graalvm/compiler/core/amd64/test/AMD64AllocatorTest.java
index c95707da994..e3fb54825b0 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.amd64.test/src/org/graalvm/compiler/core/amd64/test/AMD64AllocatorTest.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.amd64.test/src/org/graalvm/compiler/core/amd64/test/AMD64AllocatorTest.java
@@ -44,7 +44,7 @@ public class AMD64AllocatorTest extends AllocatorTest {
 
     @Test
     public void test1() {
-        testAllocation("test1snippet", 3, 1, 0);
+        testAllocation("test1snippet", 3, 0, 0);
     }
 
     public static long test1snippet(long x) {
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.amd64/src/org/graalvm/compiler/core/amd64/AMD64AddressLowering.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.amd64/src/org/graalvm/compiler/core/amd64/AMD64AddressLowering.java
index 63e535bc853..595e7174ed2 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.amd64/src/org/graalvm/compiler/core/amd64/AMD64AddressLowering.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.amd64/src/org/graalvm/compiler/core/amd64/AMD64AddressLowering.java
@@ -25,24 +25,23 @@ package org.graalvm.compiler.core.amd64;
 
 import org.graalvm.compiler.asm.amd64.AMD64Address.Scale;
 import org.graalvm.compiler.core.common.NumUtil;
+import org.graalvm.compiler.core.common.type.AbstractPointerStamp;
 import org.graalvm.compiler.core.common.type.IntegerStamp;
+import org.graalvm.compiler.core.common.type.PrimitiveStamp;
 import org.graalvm.compiler.debug.DebugContext;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.StructuredGraph;
 import org.graalvm.compiler.nodes.ValueNode;
 import org.graalvm.compiler.nodes.calc.AddNode;
 import org.graalvm.compiler.nodes.calc.LeftShiftNode;
 import org.graalvm.compiler.nodes.calc.NegateNode;
-import org.graalvm.compiler.nodes.calc.ZeroExtendNode;
 import org.graalvm.compiler.nodes.memory.address.AddressNode;
 import org.graalvm.compiler.phases.common.AddressLoweringPhase.AddressLowering;
 
 import jdk.vm.ci.meta.JavaConstant;
 
 public class AMD64AddressLowering extends AddressLowering {
-    @Override
-    public AddressNode lower(ValueNode address) {
-        return lower(address, null);
-    }
+    private static final int ADDRESS_BITS = 64;
 
     @Override
     public AddressNode lower(ValueNode base, ValueNode offset) {
@@ -54,9 +53,15 @@ public class AMD64AddressLowering extends AddressLowering {
             changed = improve(graph, base.getDebug(), ret, false, false);
         } while (changed);
 
+        assert checkAddressBitWidth(ret.getBase());
+        assert checkAddressBitWidth(ret.getIndex());
         return graph.unique(ret);
     }
 
+    private static boolean checkAddressBitWidth(ValueNode value) {
+        return value == null || value.stamp(NodeView.DEFAULT) instanceof AbstractPointerStamp || IntegerStamp.getBits(value.stamp(NodeView.DEFAULT)) == ADDRESS_BITS;
+    }
+
     /**
      * Tries to optimize addresses so that they match the AMD64-specific addressing mode better
      * (base + index * scale + displacement).
@@ -148,7 +153,7 @@ public class AMD64AddressLowering extends AddressLowering {
                 if (base == ret.getBase()) {
                     ret.setBase(originalBase);
                 } else if (ret.getBase() != null) {
-                    ret.setBase(graph.maybeAddOrUnique(NegateNode.create(ret.getBase())));
+                    ret.setBase(graph.maybeAddOrUnique(NegateNode.create(ret.getBase(), NodeView.DEFAULT)));
                 }
             }
 
@@ -156,7 +161,7 @@ public class AMD64AddressLowering extends AddressLowering {
                 if (index == ret.getIndex()) {
                     ret.setIndex(originalIndex);
                 } else if (ret.getIndex() != null) {
-                    ret.setIndex(graph.maybeAddOrUnique(NegateNode.create(ret.getIndex())));
+                    ret.setIndex(graph.maybeAddOrUnique(NegateNode.create(ret.getIndex(), NodeView.DEFAULT)));
                 }
             }
             return improved;
@@ -168,12 +173,12 @@ public class AMD64AddressLowering extends AddressLowering {
 
     private static ValueNode considerNegation(StructuredGraph graph, ValueNode value, boolean negate) {
         if (negate && value != null) {
-            return graph.maybeAddOrUnique(NegateNode.create(value));
+            return graph.maybeAddOrUnique(NegateNode.create(value, NodeView.DEFAULT));
         }
         return value;
     }
 
-    private ValueNode improveInput(AMD64AddressNode address, ValueNode node, int shift, boolean negateExtractedDisplacement) {
+    private static ValueNode improveInput(AMD64AddressNode address, ValueNode node, int shift, boolean negateExtractedDisplacement) {
         if (node == null) {
             return null;
         }
@@ -182,30 +187,26 @@ public class AMD64AddressLowering extends AddressLowering {
         if (c != null) {
             return improveConstDisp(address, node, c, null, shift, negateExtractedDisplacement);
         } else {
-            if (node.stamp() instanceof IntegerStamp) {
-                if (node instanceof ZeroExtendNode && (((ZeroExtendNode) node).getInputBits() == 32)) {
-                    /*
-                     * we can't just swallow all zero-extends as we might encounter something like
-                     * the following: ZeroExtend(Add(negativeValue, positiveValue)).
-                     *
-                     * if we swallow the zero-extend in this case and subsequently optimize the add,
-                     * we might end up with a negative value that has less than 64 bits in base or
-                     * index. such a value would require sign extension instead of zero-extension
-                     * but the backend can only do zero-extension. if we ever want to optimize that
-                     * further, we would also need to be careful about over-/underflows.
-                     *
-                     * furthermore, we also can't swallow zero-extends with less than 32 bits as
-                     * most of these values are immediately sign-extended to 32 bit by the backend
-                     * (therefore, the subsequent implicit zero-extension to 64 bit won't do what we
-                     * expect).
-                     */
-                    ValueNode value = ((ZeroExtendNode) node).getValue();
-                    if (!mightBeOptimized(value)) {
-                        // if the value is not optimized further by the address lowering, then we
-                        // can safely rely on the backend doing the implicitly zero-extension.
-                        return value;
-                    }
-                }
+            if (node.stamp(NodeView.DEFAULT) instanceof IntegerStamp) {
+                assert PrimitiveStamp.getBits(node.stamp(NodeView.DEFAULT)) == ADDRESS_BITS;
+
+                /*
+                 * we can't swallow zero-extends because of multiple reasons:
+                 *
+                 * a) we might encounter something like the following: ZeroExtend(Add(negativeValue,
+                 * positiveValue)). if we swallow the zero-extend in this case and subsequently
+                 * optimize the add, we might end up with a negative value that has less than 64
+                 * bits in base or index. such a value would require sign extension instead of
+                 * zero-extension but the backend can only do (implicit) zero-extension by using a
+                 * larger register (e.g., rax instead of eax).
+                 *
+                 * b) our backend does not guarantee that the upper half of a 64-bit register equals
+                 * 0 if a 32-bit value is stored in there.
+                 *
+                 * c) we also can't swallow zero-extends with less than 32 bits as most of these
+                 * values are immediately sign-extended to 32 bit by the backend (therefore, the
+                 * subsequent implicit zero-extension to 64 bit won't do what we expect).
+                 */
 
                 if (node instanceof AddNode) {
                     AddNode add = (AddNode) node;
@@ -221,13 +222,6 @@ public class AMD64AddressLowering extends AddressLowering {
         return node;
     }
 
-    /**
-     * This method returns true for all nodes that might be optimized by the address lowering.
-     */
-    protected boolean mightBeOptimized(ValueNode value) {
-        return value instanceof AddNode || value instanceof LeftShiftNode || value instanceof NegateNode || value instanceof ZeroExtendNode;
-    }
-
     private static ValueNode improveConstDisp(AMD64AddressNode address, ValueNode original, JavaConstant c, ValueNode other, int shift, boolean negateExtractedDisplacement) {
         if (c.getJavaKind().isNumericInteger()) {
             long delta = c.asLong() << shift;
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.amd64/src/org/graalvm/compiler/core/amd64/AMD64AddressNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.amd64/src/org/graalvm/compiler/core/amd64/AMD64AddressNode.java
index 90a8258d41c..bd27ef756d3 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.amd64/src/org/graalvm/compiler/core/amd64/AMD64AddressNode.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.amd64/src/org/graalvm/compiler/core/amd64/AMD64AddressNode.java
@@ -33,6 +33,7 @@ import org.graalvm.compiler.lir.gen.LIRGeneratorTool;
 import org.graalvm.compiler.nodeinfo.NodeInfo;
 import org.graalvm.compiler.nodes.ConstantNode;
 import org.graalvm.compiler.nodes.LoopBeginNode;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.PhiNode;
 import org.graalvm.compiler.nodes.ValueNode;
 import org.graalvm.compiler.nodes.calc.AddNode;
@@ -113,7 +114,7 @@ public class AMD64AddressNode extends AddressNode implements Simplifiable, LIRLo
             }
         }
 
-        LIRKind kind = LIRKind.combineDerived(tool.getLIRKind(stamp()), baseReference, indexReference);
+        LIRKind kind = LIRKind.combineDerived(tool.getLIRKind(stamp(NodeView.DEFAULT)), baseReference, indexReference);
         gen.setResult(this, new AMD64AddressValue(kind, baseValue, indexValue, scale, displacement));
     }
 
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.amd64/src/org/graalvm/compiler/core/amd64/AMD64ArithmeticLIRGenerator.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.amd64/src/org/graalvm/compiler/core/amd64/AMD64ArithmeticLIRGenerator.java
index 11e770b6e50..5b442b1e3bf 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.amd64/src/org/graalvm/compiler/core/amd64/AMD64ArithmeticLIRGenerator.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.amd64/src/org/graalvm/compiler/core/amd64/AMD64ArithmeticLIRGenerator.java
@@ -99,8 +99,9 @@ import org.graalvm.compiler.lir.amd64.AMD64ArithmeticLIRGeneratorTool;
 import org.graalvm.compiler.lir.amd64.AMD64Binary;
 import org.graalvm.compiler.lir.amd64.AMD64BinaryConsumer;
 import org.graalvm.compiler.lir.amd64.AMD64ClearRegisterOp;
-import org.graalvm.compiler.lir.amd64.AMD64MathIntrinsicUnaryOp;
 import org.graalvm.compiler.lir.amd64.AMD64MathIntrinsicBinaryOp;
+import org.graalvm.compiler.lir.amd64.AMD64MathIntrinsicUnaryOp;
+import org.graalvm.compiler.lir.amd64.AMD64Move;
 import org.graalvm.compiler.lir.amd64.AMD64MulDivOp;
 import org.graalvm.compiler.lir.amd64.AMD64ShiftOp;
 import org.graalvm.compiler.lir.amd64.AMD64SignExtendOp;
@@ -287,14 +288,33 @@ public class AMD64ArithmeticLIRGenerator extends ArithmeticLIRGenerator implemen
         return ((AMD64Kind) kind).isInteger();
     }
 
+    private Variable emitBaseOffsetLea(LIRKind resultKind, Value base, int offset, OperandSize size) {
+        Variable result = getLIRGen().newVariable(resultKind);
+        AMD64AddressValue address = new AMD64AddressValue(resultKind, getLIRGen().asAllocatable(base), offset);
+        getLIRGen().append(new AMD64Move.LeaOp(result, address, size));
+        return result;
+    }
+
     @Override
     public Variable emitAdd(LIRKind resultKind, Value a, Value b, boolean setFlags) {
         TargetDescription target = getLIRGen().target();
         boolean isAvx = ((AMD64) target.arch).getFeatures().contains(CPUFeature.AVX);
         switch ((AMD64Kind) a.getPlatformKind()) {
             case DWORD:
+                if (isJavaConstant(b) && !setFlags) {
+                    long displacement = asJavaConstant(b).asLong();
+                    if (NumUtil.isInt(displacement) && displacement != 1 && displacement != -1) {
+                        return emitBaseOffsetLea(resultKind, a, (int) displacement, OperandSize.DWORD);
+                    }
+                }
                 return emitBinary(resultKind, ADD, DWORD, true, a, b, setFlags);
             case QWORD:
+                if (isJavaConstant(b) && !setFlags) {
+                    long displacement = asJavaConstant(b).asLong();
+                    if (NumUtil.isInt(displacement) && displacement != 1 && displacement != -1) {
+                        return emitBaseOffsetLea(resultKind, a, (int) displacement, OperandSize.QWORD);
+                    }
+                }
                 return emitBinary(resultKind, ADD, QWORD, true, a, b, setFlags);
             case SINGLE:
                 if (isAvx) {
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.amd64/src/org/graalvm/compiler/core/amd64/AMD64MoveFactory.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.amd64/src/org/graalvm/compiler/core/amd64/AMD64MoveFactory.java
index 2e07cd3a23c..c72bf39b999 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.amd64/src/org/graalvm/compiler/core/amd64/AMD64MoveFactory.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.amd64/src/org/graalvm/compiler/core/amd64/AMD64MoveFactory.java
@@ -28,6 +28,7 @@ import static org.graalvm.compiler.lir.LIRValueUtil.asConstant;
 import static org.graalvm.compiler.lir.LIRValueUtil.isConstantValue;
 import static org.graalvm.compiler.lir.LIRValueUtil.isStackSlotValue;
 
+import org.graalvm.compiler.asm.amd64.AMD64Assembler;
 import org.graalvm.compiler.core.common.NumUtil;
 import org.graalvm.compiler.core.common.type.DataPointerConstant;
 import org.graalvm.compiler.debug.GraalError;
@@ -85,7 +86,7 @@ public abstract class AMD64MoveFactory extends AMD64MoveFactoryBase {
     @Override
     public AMD64LIRInstruction createMove(AllocatableValue dst, Value src) {
         if (src instanceof AMD64AddressValue) {
-            return new LeaOp(dst, (AMD64AddressValue) src);
+            return new LeaOp(dst, (AMD64AddressValue) src, AMD64Assembler.OperandSize.QWORD);
         } else if (isConstantValue(src)) {
             return createLoad(dst, asConstant(src));
         } else if (isRegister(src) || isStackSlotValue(dst)) {
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.amd64/src/org/graalvm/compiler/core/amd64/AMD64NodeMatchRules.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.amd64/src/org/graalvm/compiler/core/amd64/AMD64NodeMatchRules.java
index 760f599d08c..7d6a4c7840f 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.amd64/src/org/graalvm/compiler/core/amd64/AMD64NodeMatchRules.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.amd64/src/org/graalvm/compiler/core/amd64/AMD64NodeMatchRules.java
@@ -60,6 +60,7 @@ import org.graalvm.compiler.lir.gen.LIRGeneratorTool;
 import org.graalvm.compiler.nodes.ConstantNode;
 import org.graalvm.compiler.nodes.DeoptimizingNode;
 import org.graalvm.compiler.nodes.IfNode;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.ValueNode;
 import org.graalvm.compiler.nodes.calc.CompareNode;
 import org.graalvm.compiler.nodes.calc.FloatConvertNode;
@@ -478,7 +479,7 @@ public class AMD64NodeMatchRules extends NodeMatchRules {
     @MatchRule("(Write object Narrow=narrow)")
     public ComplexMatchResult writeNarrow(WriteNode root, NarrowNode narrow) {
         return builder -> {
-            LIRKind writeKind = getLIRGeneratorTool().getLIRKind(root.value().stamp());
+            LIRKind writeKind = getLIRGeneratorTool().getLIRKind(root.value().stamp(NodeView.DEFAULT));
             getArithmeticLIRGenerator().emitStore(writeKind, operand(root.getAddress()), operand(narrow.getValue()), state(root));
             return null;
         };
@@ -504,10 +505,10 @@ public class AMD64NodeMatchRules extends NodeMatchRules {
             @Override
             public Value evaluate(NodeLIRBuilder builder) {
                 AMD64AddressValue address = (AMD64AddressValue) operand(access.getAddress());
-                LIRKind addressKind = LIRKind.combineDerived(getLIRGeneratorTool().getLIRKind(root.asNode().stamp()),
+                LIRKind addressKind = LIRKind.combineDerived(getLIRGeneratorTool().getLIRKind(root.asNode().stamp(NodeView.DEFAULT)),
                                 address.getBase(), address.getIndex());
                 AMD64AddressValue newAddress = address.withKind(addressKind);
-                LIRKind readKind = getLIRGeneratorTool().getLIRKind(root.stamp());
+                LIRKind readKind = getLIRGeneratorTool().getLIRKind(root.stamp(NodeView.DEFAULT));
                 return getArithmeticLIRGenerator().emitZeroExtendMemory((AMD64Kind) readKind.getPlatformKind(),
                                 root.getResultBits(), newAddress, getState(access));
             }
@@ -517,7 +518,7 @@ public class AMD64NodeMatchRules extends NodeMatchRules {
     @MatchRule("(SignExtend (Narrow=narrow Read=access))")
     @MatchRule("(SignExtend (Narrow=narrow FloatingRead=access))")
     public ComplexMatchResult signExtendNarrowRead(SignExtendNode root, NarrowNode narrow, LIRLowerableAccess access) {
-        LIRKind kind = getLIRGeneratorTool().getLIRKind(narrow.stamp());
+        LIRKind kind = getLIRGeneratorTool().getLIRKind(narrow.stamp(NodeView.DEFAULT));
         return emitSignExtendMemory(access, narrow.getResultBits(), root.getResultBits(), kind);
     }
 
@@ -554,7 +555,7 @@ public class AMD64NodeMatchRules extends NodeMatchRules {
     @MatchRule("(Reinterpret FloatingRead=access)")
     public ComplexMatchResult reinterpret(ReinterpretNode root, LIRLowerableAccess access) {
         return builder -> {
-            LIRKind kind = getLIRGeneratorTool().getLIRKind(root.stamp());
+            LIRKind kind = getLIRGeneratorTool().getLIRKind(root.stamp(NodeView.DEFAULT));
             return emitReinterpretMemory(kind, access);
         };
 
@@ -563,7 +564,7 @@ public class AMD64NodeMatchRules extends NodeMatchRules {
     @MatchRule("(Write object Reinterpret=reinterpret)")
     public ComplexMatchResult writeReinterpret(WriteNode root, ReinterpretNode reinterpret) {
         return builder -> {
-            LIRKind kind = getLIRGeneratorTool().getLIRKind(reinterpret.getValue().stamp());
+            LIRKind kind = getLIRGeneratorTool().getLIRKind(reinterpret.getValue().stamp(NodeView.DEFAULT));
             AllocatableValue value = getLIRGeneratorTool().asAllocatable(operand(reinterpret.getValue()));
 
             AMD64AddressValue address = (AMD64AddressValue) operand(root.getAddress());
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.common/src/org/graalvm/compiler/core/common/type/StampFactory.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.common/src/org/graalvm/compiler/core/common/type/StampFactory.java
index 2d94012cf75..d726673a6cd 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.common/src/org/graalvm/compiler/core/common/type/StampFactory.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.common/src/org/graalvm/compiler/core/common/type/StampFactory.java
@@ -251,22 +251,34 @@ public class StampFactory {
     }
 
     public static Stamp[] createParameterStamps(Assumptions assumptions, ResolvedJavaMethod method) {
-        Signature sig = method.getSignature();
-        Stamp[] result = new Stamp[sig.getParameterCount(!method.isStatic())];
-        int index = 0;
+        return createParameterStamps(assumptions, method, false);
+    }
 
-        if (!method.isStatic()) {
-            result[index++] = StampFactory.objectNonNull(TypeReference.create(assumptions, method.getDeclaringClass()));
+    public static Stamp[] createParameterStamps(Assumptions assumptions, ResolvedJavaMethod method, boolean trustInterfaceTypes) {
+        Signature signature = method.getSignature();
+        Stamp[] result = new Stamp[signature.getParameterCount(method.hasReceiver())];
+
+        int index = 0;
+        ResolvedJavaType accessingClass = method.getDeclaringClass();
+        if (method.hasReceiver()) {
+            if (trustInterfaceTypes) {
+                result[index++] = StampFactory.objectNonNull(TypeReference.createTrusted(assumptions, accessingClass));
+            } else {
+                result[index++] = StampFactory.objectNonNull(TypeReference.create(assumptions, accessingClass));
+            }
         }
 
-        int max = sig.getParameterCount(false);
-        ResolvedJavaType accessingClass = method.getDeclaringClass();
-        for (int i = 0; i < max; i++) {
-            JavaType type = sig.getParameterType(i, accessingClass);
+        for (int i = 0; i < signature.getParameterCount(false); i++) {
+            JavaType type = signature.getParameterType(i, accessingClass);
             JavaKind kind = type.getJavaKind();
+
             Stamp stamp;
             if (kind == JavaKind.Object && type instanceof ResolvedJavaType) {
-                stamp = StampFactory.object(TypeReference.create(assumptions, (ResolvedJavaType) type));
+                if (trustInterfaceTypes) {
+                    stamp = StampFactory.object(TypeReference.createTrusted(assumptions, (ResolvedJavaType) type));
+                } else {
+                    stamp = StampFactory.object(TypeReference.create(assumptions, (ResolvedJavaType) type));
+                }
             } else {
                 stamp = StampFactory.forKind(kind);
             }
@@ -284,16 +296,28 @@ public class StampFactory {
         if (returnType.getJavaKind() == JavaKind.Object && returnType instanceof ResolvedJavaType) {
             ResolvedJavaType resolvedJavaType = (ResolvedJavaType) returnType;
             TypeReference reference = TypeReference.create(assumptions, resolvedJavaType);
-            if (resolvedJavaType.isInterface()) {
-                ResolvedJavaType implementor = resolvedJavaType.getSingleImplementor();
-                if (implementor != null && !resolvedJavaType.equals(implementor)) {
-                    TypeReference uncheckedType = TypeReference.createTrusted(assumptions, implementor);
-                    return StampPair.create(StampFactory.object(reference, nonNull), StampFactory.object(uncheckedType, nonNull));
+            ResolvedJavaType elementalType = resolvedJavaType.getElementalType();
+            if (elementalType.isInterface()) {
+                assert reference == null || !reference.getType().equals(resolvedJavaType);
+                TypeReference uncheckedType;
+                ResolvedJavaType elementalImplementor = elementalType.getSingleImplementor();
+                if (elementalImplementor != null && !elementalType.equals(elementalImplementor)) {
+                    ResolvedJavaType implementor = elementalImplementor;
+                    ResolvedJavaType t = resolvedJavaType;
+                    while (t.isArray()) {
+                        implementor = implementor.getArrayClass();
+                        t = t.getComponentType();
+                    }
+                    uncheckedType = TypeReference.createTrusted(assumptions, implementor);
+                } else {
+                    uncheckedType = TypeReference.createTrusted(assumptions, resolvedJavaType);
                 }
+                return StampPair.create(StampFactory.object(reference, nonNull), StampFactory.object(uncheckedType, nonNull));
             }
             return StampPair.createSingle(StampFactory.object(reference, nonNull));
         } else {
             return StampPair.createSingle(StampFactory.forKind(returnType.getJavaKind()));
         }
     }
+
 }
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.sparc/src/org/graalvm/compiler/core/sparc/SPARCAddressLowering.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.sparc/src/org/graalvm/compiler/core/sparc/SPARCAddressLowering.java
index 14725bf4982..13edb612d34 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.sparc/src/org/graalvm/compiler/core/sparc/SPARCAddressLowering.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.sparc/src/org/graalvm/compiler/core/sparc/SPARCAddressLowering.java
@@ -33,11 +33,6 @@ import jdk.vm.ci.meta.JavaConstant;
 
 public class SPARCAddressLowering extends AddressLowering {
 
-    @Override
-    public AddressNode lower(ValueNode address) {
-        return lower(address, 0);
-    }
-
     @Override
     public AddressNode lower(ValueNode base, ValueNode offset) {
         JavaConstant immBase = asImmediate(base);
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.sparc/src/org/graalvm/compiler/core/sparc/SPARCImmediateAddressNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.sparc/src/org/graalvm/compiler/core/sparc/SPARCImmediateAddressNode.java
index abdef18c51e..1dee79bd5a4 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.sparc/src/org/graalvm/compiler/core/sparc/SPARCImmediateAddressNode.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.sparc/src/org/graalvm/compiler/core/sparc/SPARCImmediateAddressNode.java
@@ -28,6 +28,7 @@ import org.graalvm.compiler.core.common.LIRKind;
 import org.graalvm.compiler.graph.NodeClass;
 import org.graalvm.compiler.lir.sparc.SPARCImmediateAddressValue;
 import org.graalvm.compiler.nodeinfo.NodeInfo;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.ValueNode;
 import org.graalvm.compiler.nodes.memory.address.AddressNode;
 import org.graalvm.compiler.nodes.spi.LIRLowerable;
@@ -59,7 +60,7 @@ public class SPARCImmediateAddressNode extends AddressNode implements LIRLowerab
 
         AllocatableValue baseValue = tool.asAllocatable(gen.operand(base));
 
-        LIRKind kind = tool.getLIRKind(stamp());
+        LIRKind kind = tool.getLIRKind(stamp(NodeView.DEFAULT));
         AllocatableValue baseReference = LIRKind.derivedBaseFromValue(baseValue);
         if (baseReference != null) {
             kind = kind.makeDerivedReference(baseReference);
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.sparc/src/org/graalvm/compiler/core/sparc/SPARCIndexedAddressNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.sparc/src/org/graalvm/compiler/core/sparc/SPARCIndexedAddressNode.java
index 3fee3e82ea1..456f17aa215 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.sparc/src/org/graalvm/compiler/core/sparc/SPARCIndexedAddressNode.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.sparc/src/org/graalvm/compiler/core/sparc/SPARCIndexedAddressNode.java
@@ -27,6 +27,7 @@ import org.graalvm.compiler.core.common.LIRKind;
 import org.graalvm.compiler.graph.NodeClass;
 import org.graalvm.compiler.lir.sparc.SPARCIndexedAddressValue;
 import org.graalvm.compiler.nodeinfo.NodeInfo;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.ValueNode;
 import org.graalvm.compiler.nodes.memory.address.AddressNode;
 import org.graalvm.compiler.nodes.spi.LIRLowerable;
@@ -61,7 +62,7 @@ public class SPARCIndexedAddressNode extends AddressNode implements LIRLowerable
         AllocatableValue baseReference = LIRKind.derivedBaseFromValue(baseValue);
         AllocatableValue indexReference = LIRKind.derivedBaseFromValue(indexValue);
 
-        LIRKind kind = LIRKind.combineDerived(tool.getLIRKind(stamp()), baseReference, indexReference);
+        LIRKind kind = LIRKind.combineDerived(tool.getLIRKind(stamp(NodeView.DEFAULT)), baseReference, indexReference);
         gen.setResult(this, new SPARCIndexedAddressValue(kind, baseValue, indexValue));
     }
 
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.sparc/src/org/graalvm/compiler/core/sparc/SPARCIntegerCompareCanonicalizationPhase.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.sparc/src/org/graalvm/compiler/core/sparc/SPARCIntegerCompareCanonicalizationPhase.java
index 9f0cd9f3cdc..7c69a738408 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.sparc/src/org/graalvm/compiler/core/sparc/SPARCIntegerCompareCanonicalizationPhase.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.sparc/src/org/graalvm/compiler/core/sparc/SPARCIntegerCompareCanonicalizationPhase.java
@@ -27,6 +27,7 @@ import org.graalvm.compiler.core.common.type.Stamp;
 import org.graalvm.compiler.debug.GraalError;
 import org.graalvm.compiler.graph.Node;
 import org.graalvm.compiler.nodes.ConstantNode;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.StructuredGraph;
 import org.graalvm.compiler.nodes.ValueNode;
 import org.graalvm.compiler.nodes.calc.CompareNode;
@@ -59,7 +60,7 @@ public class SPARCIntegerCompareCanonicalizationPhase extends Phase {
     }
 
     private static void min32(CompareNode enode, ValueNode v) {
-        Stamp s = v.stamp();
+        Stamp s = v.stamp(NodeView.DEFAULT);
         if (s instanceof IntegerStamp) {
             int bits = ((IntegerStamp) s).getBits();
             if (bits != 32 && bits != 64) {
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/CountedLoopTest.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/CountedLoopTest.java
index 8c1e5733a9d..877e98aae84 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/CountedLoopTest.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/CountedLoopTest.java
@@ -25,6 +25,7 @@ package org.graalvm.compiler.core.test;
 import static org.graalvm.compiler.nodeinfo.NodeCycles.CYCLES_IGNORED;
 import static org.graalvm.compiler.nodeinfo.NodeSize.SIZE_IGNORED;
 
+import org.graalvm.compiler.nodes.NodeView;
 import org.junit.Test;
 
 import org.graalvm.compiler.api.directives.GraalDirectives;
@@ -223,7 +224,7 @@ public class CountedLoopTest extends GraalCompilerTest {
         @Input private ValueNode iv;
 
         protected IVPropertyNode(IVProperty property, ValueNode iv) {
-            super(TYPE, iv.stamp().unrestricted());
+            super(TYPE, iv.stamp(NodeView.DEFAULT).unrestricted());
             this.property = property;
             this.iv = iv;
         }
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/NodePropertiesTest.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/NodePropertiesTest.java
index 51fdf9426d3..a364aae2f8f 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/NodePropertiesTest.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/NodePropertiesTest.java
@@ -166,7 +166,8 @@ public class NodePropertiesTest extends GraalCompilerTest {
         ImprovementSavingCanonicalizer c2 = new ImprovementSavingCanonicalizer();
         StructuredGraph g2 = parseForCompile(getResolvedJavaMethod("test2Snippet"));
         new CanonicalizerPhase(c2).apply(g2, htc);
-        Assert.assertTrue(c1.savedCycles > c2.savedCycles);
+        Assert.assertEquals(0, c1.savedCycles);
+        Assert.assertEquals(0, c2.savedCycles);
     }
 
     private static void prepareGraphForLoopFrequencies(StructuredGraph g, HighTierContext htc) {
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/UncheckedInterfaceProviderTest.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/UncheckedInterfaceProviderTest.java
new file mode 100644
index 00000000000..bcb0fddc0ed
--- /dev/null
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/UncheckedInterfaceProviderTest.java
@@ -0,0 +1,146 @@
+/*
+ * Copyright (c) 2017, 2017, 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 org.graalvm.compiler.core.test;
+
+import static org.hamcrest.CoreMatchers.instanceOf;
+import static org.hamcrest.CoreMatchers.is;
+
+import jdk.vm.ci.meta.ResolvedJavaType;
+import org.graalvm.compiler.api.directives.GraalDirectives;
+import org.graalvm.compiler.core.common.type.Stamp;
+import org.graalvm.compiler.nodeinfo.Verbosity;
+import org.graalvm.compiler.nodes.StructuredGraph;
+import org.graalvm.compiler.nodes.ValueNode;
+import org.graalvm.compiler.nodes.debug.BlackholeNode;
+import org.graalvm.compiler.nodes.graphbuilderconf.GraphBuilderContext;
+import org.graalvm.compiler.nodes.graphbuilderconf.InlineInvokePlugin;
+import org.graalvm.compiler.nodes.spi.UncheckedInterfaceProvider;
+import org.graalvm.compiler.nodes.type.StampTool;
+import org.junit.Assert;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import jdk.vm.ci.meta.ResolvedJavaMethod;
+
+public class UncheckedInterfaceProviderTest extends GraalCompilerTest {
+    private Runnable interfaceField;
+    private Runnable[] interfaceArrayField;
+
+    public void snippet(Runnable interfaceParameter, Runnable[] interfaceArrayParameter) {
+        GraalDirectives.blackhole(interfaceParameter);
+        GraalDirectives.blackhole(interfaceArrayParameter);
+        GraalDirectives.blackhole(interfaceField);
+        GraalDirectives.blackhole(interfaceArrayField);
+        GraalDirectives.blackhole(interfaceReturn());
+        GraalDirectives.blackhole(interfaceArrayReturn());
+        GraalDirectives.blackhole(interfaceReturnException());
+        GraalDirectives.blackhole(interfaceArrayReturnException());
+    }
+
+    public static Runnable interfaceReturn() {
+        return new A();
+    }
+
+    public static Runnable[] interfaceArrayReturn() {
+        return new Runnable[]{new A(), new B(), new C(), new D()};
+    }
+
+    public static Runnable interfaceReturnException() {
+        return new A();
+    }
+
+    public static Runnable[] interfaceArrayReturnException() {
+        return new Runnable[]{new A(), new B(), new C(), new D()};
+    }
+
+    @Override
+    protected InlineInvokePlugin.InlineInfo bytecodeParserShouldInlineInvoke(GraphBuilderContext b, ResolvedJavaMethod method, ValueNode[] args) {
+        if (method.getName().startsWith("interfaceReturn") || method.getName().startsWith("interfaceArrayReturn")) {
+            if (method.getName().equals("Exception")) {
+                return InlineInvokePlugin.InlineInfo.DO_NOT_INLINE_WITH_EXCEPTION;
+            }
+            return InlineInvokePlugin.InlineInfo.DO_NOT_INLINE_NO_EXCEPTION;
+        }
+        return super.bytecodeParserShouldInlineInvoke(b, method, args);
+    }
+
+    @BeforeClass
+    public static void setup() {
+        interfaceArrayReturn();
+    }
+
+    @Test
+    public void test() {
+        StructuredGraph graph = parseEager("snippet", StructuredGraph.AllowAssumptions.YES);
+        for (BlackholeNode b : graph.getNodes().filter(BlackholeNode.class)) {
+            Assert.assertThat(b.getValue(), is(instanceOf(UncheckedInterfaceProvider.class)));
+            Stamp uncheckedStamp = ((UncheckedInterfaceProvider) b.getValue()).uncheckedStamp();
+            String context = b.getValue().toString(Verbosity.Debugger);
+            Assert.assertNotNull(context, uncheckedStamp);
+            ResolvedJavaType uncheckedType = StampTool.typeOrNull(uncheckedStamp);
+            ResolvedJavaType type = StampTool.typeOrNull(b.getValue());
+            Assert.assertEquals(context, arrayDepth(type), arrayDepth(uncheckedType));
+            Assert.assertTrue(context + ": " + type, type == null || type.getElementalType().isJavaLangObject());
+            Assert.assertNotNull(context, uncheckedType);
+            Assert.assertTrue(context, uncheckedType.getElementalType().isInterface());
+        }
+    }
+
+    private static int arrayDepth(ResolvedJavaType type) {
+        int depth = 0;
+        ResolvedJavaType t = type;
+        while (t != null && t.isArray()) {
+            depth += 1;
+            t = t.getComponentType();
+        }
+        return depth;
+    }
+
+    public static class A implements Runnable {
+        @Override
+        public void run() {
+            // nop
+        }
+    }
+
+    public static class B implements Runnable {
+        @Override
+        public void run() {
+            // nop
+        }
+    }
+
+    public static class C implements Runnable {
+        @Override
+        public void run() {
+            // nop
+        }
+    }
+
+    public static class D implements Runnable {
+        @Override
+        public void run() {
+            // nop
+        }
+    }
+}
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/deopt/CompiledMethodTest.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/deopt/CompiledMethodTest.java
index f7e0c928304..26a0f899620 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/deopt/CompiledMethodTest.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/deopt/CompiledMethodTest.java
@@ -22,14 +22,7 @@
  */
 package org.graalvm.compiler.core.test.deopt;
 
-import jdk.vm.ci.code.InstalledCode;
-import jdk.vm.ci.code.InvalidInstalledCodeException;
-import jdk.vm.ci.meta.JavaKind;
-import jdk.vm.ci.meta.ResolvedJavaMethod;
-
-import org.junit.Assert;
-import org.junit.Test;
-
+import org.graalvm.compiler.api.directives.GraalDirectives;
 import org.graalvm.compiler.core.test.GraalCompilerTest;
 import org.graalvm.compiler.nodes.ConstantNode;
 import org.graalvm.compiler.nodes.StructuredGraph;
@@ -37,16 +30,19 @@ import org.graalvm.compiler.nodes.StructuredGraph.AllowAssumptions;
 import org.graalvm.compiler.phases.common.CanonicalizerPhase;
 import org.graalvm.compiler.phases.common.DeadCodeEliminationPhase;
 import org.graalvm.compiler.phases.tiers.PhaseContext;
+import org.junit.Assert;
+import org.junit.Test;
+
+import jdk.vm.ci.code.InstalledCode;
+import jdk.vm.ci.code.InvalidInstalledCodeException;
+import jdk.vm.ci.meta.JavaKind;
+import jdk.vm.ci.meta.ResolvedJavaMethod;
 
-/**
- * In the following tests, the usages of local variable "a" are replaced with the integer constant
- * 0. Then canonicalization is applied and it is verified that the resulting graph is equal to the
- * graph of the method that just has a "return 1" statement in it.
- */
 public class CompiledMethodTest extends GraalCompilerTest {
 
     public static Object testMethod(Object arg1, Object arg2, Object arg3) {
-        return arg1 + " " + arg2 + " " + arg3;
+        String res = arg1 + " " + arg2 + " " + arg3;
+        return GraalDirectives.inCompiledCode() ? res : "interpreter";
     }
 
     Object f1;
@@ -55,6 +51,12 @@ public class CompiledMethodTest extends GraalCompilerTest {
         return f1 + " " + arg1 + " " + arg2 + " " + arg3;
     }
 
+    /**
+     * Usages of the constant {@code " "} are replaced with the constant {@code "-"} and it is
+     * verified that executing the compiled code produces a result that the preserves the node
+     * replacement unless deoptimization occurs (e.g., due to -Xcomp causing profiles to be
+     * missing).
+     */
     @Test
     public void test1() {
         final ResolvedJavaMethod javaMethod = getResolvedJavaMethod("testMethod");
@@ -71,7 +73,10 @@ public class CompiledMethodTest extends GraalCompilerTest {
         InstalledCode compiledMethod = getCode(javaMethod, graph);
         try {
             Object result = compiledMethod.executeVarargs("1", "2", "3");
-            Assert.assertEquals("1-2-3", result);
+            if (!"1-2-3".equals(result)) {
+                // Deoptimization probably occurred
+                Assert.assertEquals("interpreter", result);
+            }
         } catch (InvalidInstalledCodeException t) {
             Assert.fail("method invalidated");
         }
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/ea/PartialEscapeUnsafeStoreTest.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/ea/PartialEscapeUnsafeStoreTest.java
new file mode 100644
index 00000000000..0792e3ca08d
--- /dev/null
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/ea/PartialEscapeUnsafeStoreTest.java
@@ -0,0 +1,1001 @@
+/*
+ * Copyright (c) 2016, 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 org.graalvm.compiler.core.test.ea;
+
+import java.lang.reflect.Field;
+
+import org.graalvm.compiler.core.test.GraalCompilerTest;
+import org.junit.Test;
+
+import sun.misc.Unsafe;
+
+/**
+ * Exercise a mix of unsafe and normal reads ands writes in situations where EA might attempt to
+ * fold the operations.
+ */
+public class PartialEscapeUnsafeStoreTest extends GraalCompilerTest {
+
+    private static final Unsafe unsafe = initUnsafe();
+
+    private static Unsafe initUnsafe() {
+        try {
+            Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
+            theUnsafe.setAccessible(true);
+            return (Unsafe) theUnsafe.get(Unsafe.class);
+        } catch (Exception e) {
+            throw new RuntimeException("exception while trying to get Unsafe", e);
+        }
+    }
+
+    private static final long byteArrayBaseOffset = unsafe.arrayBaseOffset(byte[].class);
+    private static byte byteValue = 0x61;
+
+    public static byte[] testByteArrayWithCharStoreSnippet(char v) {
+        byte[] b = new byte[8];
+        unsafe.putChar(b, byteArrayBaseOffset, v);
+        return b;
+    }
+
+    @Test
+    public void testByteArrayWithCharStore() {
+        test("testByteArrayWithCharStoreSnippet", charValue);
+    }
+
+    public static byte[] testByteArrayWithShortStoreSnippet(short v) {
+        byte[] b = new byte[8];
+        unsafe.putShort(b, byteArrayBaseOffset, v);
+        return b;
+    }
+
+    @Test
+    public void testByteArrayWithShortStore() {
+        test("testByteArrayWithShortStoreSnippet", shortValue);
+    }
+
+    public static byte[] testByteArrayWithIntStoreSnippet(int v) {
+        byte[] b = new byte[8];
+        unsafe.putInt(b, byteArrayBaseOffset, v);
+        return b;
+    }
+
+    @Test
+    public void testByteArrayWithIntStore() {
+        test("testByteArrayWithIntStoreSnippet", intValue);
+    }
+
+    public static byte[] testByteArrayWithLongStoreSnippet(long v) {
+        byte[] b = new byte[8];
+        unsafe.putLong(b, byteArrayBaseOffset, v);
+        return b;
+    }
+
+    @Test
+    public void testByteArrayWithLongStore() {
+        test("testByteArrayWithLongStoreSnippet", longValue);
+    }
+
+    public static byte[] testByteArrayWithFloatStoreSnippet(float v) {
+        byte[] b = new byte[8];
+        unsafe.putFloat(b, byteArrayBaseOffset, v);
+        return b;
+    }
+
+    @Test
+    public void testByteArrayWithFloatStore() {
+        test("testByteArrayWithFloatStoreSnippet", floatValue);
+    }
+
+    public static byte[] testByteArrayWithDoubleStoreSnippet(double v) {
+        byte[] b = new byte[8];
+        unsafe.putDouble(b, byteArrayBaseOffset, v);
+        return b;
+    }
+
+    @Test
+    public void testByteArrayWithDoubleStore() {
+        test("testByteArrayWithDoubleStoreSnippet", doubleValue);
+    }
+
+    private static final long charArrayBaseOffset = unsafe.arrayBaseOffset(char[].class);
+    private static char charValue = 0x4142;
+
+    public static char[] testCharArrayWithByteStoreSnippet(byte v) {
+        char[] b = new char[4];
+        unsafe.putByte(b, charArrayBaseOffset, v);
+        return b;
+    }
+
+    @Test
+    public void testCharArrayWithByteStore() {
+        test("testCharArrayWithByteStoreSnippet", byteValue);
+    }
+
+    public static char[] testCharArrayWithShortStoreSnippet(short v) {
+        char[] b = new char[4];
+        unsafe.putShort(b, charArrayBaseOffset, v);
+        return b;
+    }
+
+    @Test
+    public void testCharArrayWithShortStore() {
+        test("testCharArrayWithShortStoreSnippet", shortValue);
+    }
+
+    public static char[] testCharArrayWithIntStoreSnippet(int v) {
+        char[] b = new char[4];
+        unsafe.putInt(b, charArrayBaseOffset, v);
+        return b;
+    }
+
+    @Test
+    public void testCharArrayWithIntStore() {
+        test("testCharArrayWithIntStoreSnippet", intValue);
+    }
+
+    public static char[] testCharArrayWithLongStoreSnippet(long v) {
+        char[] b = new char[4];
+        unsafe.putLong(b, charArrayBaseOffset, v);
+        return b;
+    }
+
+    @Test
+    public void testCharArrayWithLongStore() {
+        test("testCharArrayWithLongStoreSnippet", longValue);
+    }
+
+    public static char[] testCharArrayWithFloatStoreSnippet(float v) {
+        char[] b = new char[4];
+        unsafe.putFloat(b, charArrayBaseOffset, v);
+        return b;
+    }
+
+    @Test
+    public void testCharArrayWithFloatStore() {
+        test("testCharArrayWithFloatStoreSnippet", floatValue);
+    }
+
+    public static char[] testCharArrayWithDoubleStoreSnippet(double v) {
+        char[] b = new char[4];
+        unsafe.putDouble(b, charArrayBaseOffset, v);
+        return b;
+    }
+
+    @Test
+    public void testCharArrayWithDoubleStore() {
+        test("testCharArrayWithDoubleStoreSnippet", doubleValue);
+    }
+
+    private static final long shortArrayBaseOffset = unsafe.arrayBaseOffset(short[].class);
+    private static short shortValue = 0x1112;
+
+    public static short[] testShortArrayWithByteStoreSnippet(byte v) {
+        short[] b = new short[4];
+        unsafe.putByte(b, shortArrayBaseOffset, v);
+        return b;
+    }
+
+    @Test
+    public void testShortArrayWithByteStore() {
+        test("testShortArrayWithByteStoreSnippet", byteValue);
+    }
+
+    public static short[] testShortArrayWithCharStoreSnippet(char v) {
+        short[] b = new short[4];
+        unsafe.putChar(b, shortArrayBaseOffset, v);
+        return b;
+    }
+
+    @Test
+    public void testShortArrayWithCharStore() {
+        test("testShortArrayWithCharStoreSnippet", charValue);
+    }
+
+    public static short[] testShortArrayWithIntStoreSnippet(int v) {
+        short[] b = new short[4];
+        unsafe.putInt(b, shortArrayBaseOffset, v);
+        return b;
+    }
+
+    @Test
+    public void testShortArrayWithIntStore() {
+        test("testShortArrayWithIntStoreSnippet", intValue);
+    }
+
+    public static short[] testShortArrayWithLongStoreSnippet(long v) {
+        short[] b = new short[4];
+        unsafe.putLong(b, shortArrayBaseOffset, v);
+        return b;
+    }
+
+    @Test
+    public void testShortArrayWithLongStore() {
+        test("testShortArrayWithLongStoreSnippet", longValue);
+    }
+
+    public static short[] testShortArrayWithFloatStoreSnippet(float v) {
+        short[] b = new short[4];
+        unsafe.putFloat(b, shortArrayBaseOffset, v);
+        return b;
+    }
+
+    @Test
+    public void testShortArrayWithFloatStore() {
+        test("testShortArrayWithFloatStoreSnippet", floatValue);
+    }
+
+    public static short[] testShortArrayWithDoubleStoreSnippet(double v) {
+        short[] b = new short[4];
+        unsafe.putDouble(b, shortArrayBaseOffset, v);
+        return b;
+    }
+
+    @Test
+    public void testShortArrayWithDoubleStore() {
+        test("testShortArrayWithDoubleStoreSnippet", doubleValue);
+    }
+
+    private static final long intArrayBaseOffset = unsafe.arrayBaseOffset(int[].class);
+    private static int intValue = 0x01020304;
+
+    public static int[] testIntArrayWithByteStoreSnippet(byte v) {
+        int[] b = new int[4];
+        unsafe.putByte(b, intArrayBaseOffset, v);
+        return b;
+    }
+
+    @Test
+    public void testIntArrayWithByteStore() {
+        test("testIntArrayWithByteStoreSnippet", byteValue);
+    }
+
+    public static int[] testIntArrayWithCharStoreSnippet(char v) {
+        int[] b = new int[4];
+        unsafe.putChar(b, intArrayBaseOffset, v);
+        return b;
+    }
+
+    @Test
+    public void testIntArrayWithCharStore() {
+        test("testIntArrayWithCharStoreSnippet", charValue);
+    }
+
+    public static int[] testIntArrayWithShortStoreSnippet(short v) {
+        int[] b = new int[4];
+        unsafe.putShort(b, intArrayBaseOffset, v);
+        return b;
+    }
+
+    @Test
+    public void testIntArrayWithShortStore() {
+        test("testIntArrayWithShortStoreSnippet", shortValue);
+    }
+
+    public static int[] testIntArrayWithLongStoreSnippet(long v) {
+        int[] b = new int[4];
+        unsafe.putLong(b, intArrayBaseOffset, v);
+        return b;
+    }
+
+    @Test
+    public void testIntArrayWithLongStore() {
+        test("testIntArrayWithLongStoreSnippet", longValue);
+    }
+
+    public static int[] testIntArrayWithFloatStoreSnippet(float v) {
+        int[] b = new int[4];
+        unsafe.putFloat(b, intArrayBaseOffset, v);
+        return b;
+    }
+
+    @Test
+    public void testIntArrayWithFloatStore() {
+        test("testIntArrayWithFloatStoreSnippet", floatValue);
+    }
+
+    public static int[] testIntArrayWithDoubleStoreSnippet(double v) {
+        int[] b = new int[4];
+        unsafe.putDouble(b, intArrayBaseOffset, v);
+        return b;
+    }
+
+    @Test
+    public void testIntArrayWithDoubleStore() {
+        test("testIntArrayWithDoubleStoreSnippet", doubleValue);
+    }
+
+    private static final long longArrayBaseOffset = unsafe.arrayBaseOffset(long[].class);
+    private static long longValue = 0x31323334353637L;
+
+    public static long[] testLongArrayWithByteStoreSnippet(byte v) {
+        long[] b = new long[4];
+        unsafe.putByte(b, longArrayBaseOffset, v);
+        return b;
+    }
+
+    @Test
+    public void testLongArrayWithByteStore() {
+        test("testLongArrayWithByteStoreSnippet", byteValue);
+    }
+
+    public static long[] testLongArrayWithCharStoreSnippet(char v) {
+        long[] b = new long[4];
+        unsafe.putChar(b, longArrayBaseOffset, v);
+        return b;
+    }
+
+    @Test
+    public void testLongArrayWithCharStore() {
+        test("testLongArrayWithCharStoreSnippet", charValue);
+    }
+
+    public static long[] testLongArrayWithShortStoreSnippet(short v) {
+        long[] b = new long[4];
+        unsafe.putShort(b, longArrayBaseOffset, v);
+        return b;
+    }
+
+    @Test
+    public void testLongArrayWithShortStore() {
+        test("testLongArrayWithShortStoreSnippet", shortValue);
+    }
+
+    public static long[] testLongArrayWithIntStoreSnippet(int v) {
+        long[] b = new long[4];
+        unsafe.putInt(b, longArrayBaseOffset, v);
+        return b;
+    }
+
+    @Test
+    public void testLongArrayWithIntStore() {
+        test("testLongArrayWithIntStoreSnippet", intValue);
+    }
+
+    public static long[] testLongArrayWithFloatStoreSnippet(float v) {
+        long[] b = new long[4];
+        unsafe.putFloat(b, longArrayBaseOffset, v);
+        return b;
+    }
+
+    @Test
+    public void testLongArrayWithFloatStore() {
+        test("testLongArrayWithFloatStoreSnippet", floatValue);
+    }
+
+    public static long[] testLongArrayWithDoubleStoreSnippet(double v) {
+        long[] b = new long[4];
+        unsafe.putDouble(b, longArrayBaseOffset, v);
+        return b;
+    }
+
+    @Test
+    public void testLongArrayWithDoubleStore() {
+        test("testLongArrayWithDoubleStoreSnippet", doubleValue);
+    }
+
+    private static final long floatArrayBaseOffset = unsafe.arrayBaseOffset(float[].class);
+    private static float floatValue = Float.NaN;
+
+    public static float[] testFloatArrayWithByteStoreSnippet(byte v) {
+        float[] b = new float[4];
+        unsafe.putByte(b, floatArrayBaseOffset, v);
+        return b;
+    }
+
+    @Test
+    public void testFloatArrayWithByteStore() {
+        test("testFloatArrayWithByteStoreSnippet", byteValue);
+    }
+
+    public static float[] testFloatArrayWithCharStoreSnippet(char v) {
+        float[] b = new float[4];
+        unsafe.putChar(b, floatArrayBaseOffset, v);
+        return b;
+    }
+
+    @Test
+    public void testFloatArrayWithCharStore() {
+        test("testFloatArrayWithCharStoreSnippet", charValue);
+    }
+
+    public static float[] testFloatArrayWithShortStoreSnippet(short v) {
+        float[] b = new float[4];
+        unsafe.putShort(b, floatArrayBaseOffset, v);
+        return b;
+    }
+
+    @Test
+    public void testFloatArrayWithShortStore() {
+        test("testFloatArrayWithShortStoreSnippet", shortValue);
+    }
+
+    public static float[] testFloatArrayWithIntStoreSnippet(int v) {
+        float[] b = new float[4];
+        unsafe.putInt(b, floatArrayBaseOffset, v);
+        return b;
+    }
+
+    @Test
+    public void testFloatArrayWithIntStore() {
+        test("testFloatArrayWithIntStoreSnippet", intValue);
+    }
+
+    public static float[] testFloatArrayWithLongStoreSnippet(long v) {
+        float[] b = new float[4];
+        unsafe.putLong(b, floatArrayBaseOffset, v);
+        return b;
+    }
+
+    @Test
+    public void testFloatArrayWithLongStore() {
+        test("testFloatArrayWithLongStoreSnippet", longValue);
+    }
+
+    public static float[] testFloatArrayWithDoubleStoreSnippet(double v) {
+        float[] b = new float[4];
+        unsafe.putDouble(b, floatArrayBaseOffset, v);
+        return b;
+    }
+
+    @Test
+    public void testFloatArrayWithDoubleStore() {
+        test("testFloatArrayWithDoubleStoreSnippet", doubleValue);
+    }
+
+    private static final long doubleArrayBaseOffset = unsafe.arrayBaseOffset(double[].class);
+    private static double doubleValue = Double.NaN;
+    private static final int byteSize = 1;
+    private static final int charSize = 2;
+    private static final int shortSize = 2;
+    private static final int intSize = 4;
+    private static final int floatSize = 4;
+    private static final int longSize = 8;
+    private static final int doubleSize = 8;
+
+    public static double[] testDoubleArrayWithByteStoreSnippet(byte v) {
+        double[] b = new double[4];
+        unsafe.putByte(b, doubleArrayBaseOffset, v);
+        return b;
+    }
+
+    @Test
+    public void testDoubleArrayWithByteStore() {
+        test("testDoubleArrayWithByteStoreSnippet", byteValue);
+    }
+
+    public static double[] testDoubleArrayWithCharStoreSnippet(char v) {
+        double[] b = new double[4];
+        unsafe.putChar(b, doubleArrayBaseOffset, v);
+        return b;
+    }
+
+    @Test
+    public void testDoubleArrayWithCharStore() {
+        test("testDoubleArrayWithCharStoreSnippet", charValue);
+    }
+
+    public static double[] testDoubleArrayWithShortStoreSnippet(short v) {
+        double[] b = new double[4];
+        unsafe.putShort(b, doubleArrayBaseOffset, v);
+        return b;
+    }
+
+    @Test
+    public void testDoubleArrayWithShortStore() {
+        test("testDoubleArrayWithShortStoreSnippet", shortValue);
+    }
+
+    public static double[] testDoubleArrayWithIntStoreSnippet(int v) {
+        double[] b = new double[4];
+        unsafe.putInt(b, doubleArrayBaseOffset, v);
+        return b;
+    }
+
+    @Test
+    public void testDoubleArrayWithIntStore() {
+        test("testDoubleArrayWithIntStoreSnippet", intValue);
+    }
+
+    public static double[] testDoubleArrayWithLongStoreSnippet(long v) {
+        double[] b = new double[4];
+        unsafe.putLong(b, doubleArrayBaseOffset, v);
+        return b;
+    }
+
+    @Test
+    public void testDoubleArrayWithLongStore() {
+        test("testDoubleArrayWithLongStoreSnippet", longValue);
+    }
+
+    public static double[] testDoubleArrayWithFloatStoreSnippet(float v) {
+        double[] b = new double[4];
+        unsafe.putFloat(b, doubleArrayBaseOffset, v);
+        return b;
+    }
+
+    @Test
+    public void testDoubleArrayWithFloatStore() {
+        test("testDoubleArrayWithFloatStoreSnippet", floatValue);
+    }
+
+    public static byte testByteArrayWithCharStoreAndReadSnippet(char v) {
+        byte[] b = new byte[4];
+        unsafe.putChar(b, byteArrayBaseOffset, v);
+        return b[(byteSize / charSize) + 1];
+    }
+
+    @Test
+    public void testByteArrayWithCharStoreAndRead() {
+        test("testByteArrayWithCharStoreAndReadSnippet", charValue);
+    }
+
+    public static byte testByteArrayWithShortStoreAndReadSnippet(short v) {
+        byte[] b = new byte[4];
+        unsafe.putShort(b, byteArrayBaseOffset, v);
+        return b[(byteSize / shortSize) + 1];
+    }
+
+    @Test
+    public void testByteArrayWithShortStoreAndRead() {
+        test("testByteArrayWithShortStoreAndReadSnippet", shortValue);
+    }
+
+    public static byte testByteArrayWithIntStoreAndReadSnippet(int v) {
+        byte[] b = new byte[4];
+        unsafe.putInt(b, byteArrayBaseOffset, v);
+        return b[(byteSize / intSize) + 1];
+    }
+
+    @Test
+    public void testByteArrayWithIntStoreAndRead() {
+        test("testByteArrayWithIntStoreAndReadSnippet", intValue);
+    }
+
+    public static byte testByteArrayWithLongStoreAndReadSnippet(long v) {
+        byte[] b = new byte[4];
+        unsafe.putLong(b, byteArrayBaseOffset, v);
+        return b[(byteSize / longSize) + 1];
+    }
+
+    @Test
+    public void testByteArrayWithLongStoreAndRead() {
+        test("testByteArrayWithLongStoreAndReadSnippet", longValue);
+    }
+
+    public static byte testByteArrayWithFloatStoreAndReadSnippet(float v) {
+        byte[] b = new byte[4];
+        unsafe.putFloat(b, byteArrayBaseOffset, v);
+        return b[(byteSize / floatSize) + 1];
+    }
+
+    @Test
+    public void testByteArrayWithFloatStoreAndRead() {
+        test("testByteArrayWithFloatStoreAndReadSnippet", floatValue);
+    }
+
+    public static byte testByteArrayWithDoubleStoreAndReadSnippet(double v) {
+        byte[] b = new byte[4];
+        unsafe.putDouble(b, byteArrayBaseOffset, v);
+        return b[(byteSize / doubleSize) + 1];
+    }
+
+    @Test
+    public void testByteArrayWithDoubleStoreAndRead() {
+        test("testByteArrayWithDoubleStoreAndReadSnippet", doubleValue);
+    }
+
+    public static char testCharArrayWithByteStoreAndReadSnippet(byte v) {
+        char[] b = new char[4];
+        unsafe.putByte(b, charArrayBaseOffset, v);
+        return b[(charSize / byteSize) + 1];
+    }
+
+    @Test
+    public void testCharArrayWithByteStoreAndRead() {
+        test("testCharArrayWithByteStoreAndReadSnippet", byteValue);
+    }
+
+    public static char testCharArrayWithShortStoreAndReadSnippet(short v) {
+        char[] b = new char[4];
+        unsafe.putShort(b, charArrayBaseOffset, v);
+        return b[(charSize / shortSize) + 1];
+    }
+
+    @Test
+    public void testCharArrayWithShortStoreAndRead() {
+        test("testCharArrayWithShortStoreAndReadSnippet", shortValue);
+    }
+
+    public static char testCharArrayWithIntStoreAndReadSnippet(int v) {
+        char[] b = new char[4];
+        unsafe.putInt(b, charArrayBaseOffset, v);
+        return b[(charSize / intSize) + 1];
+    }
+
+    @Test
+    public void testCharArrayWithIntStoreAndRead() {
+        test("testCharArrayWithIntStoreAndReadSnippet", intValue);
+    }
+
+    public static char testCharArrayWithLongStoreAndReadSnippet(long v) {
+        char[] b = new char[4];
+        unsafe.putLong(b, charArrayBaseOffset, v);
+        return b[(charSize / longSize) + 1];
+    }
+
+    @Test
+    public void testCharArrayWithLongStoreAndRead() {
+        test("testCharArrayWithLongStoreAndReadSnippet", longValue);
+    }
+
+    public static char testCharArrayWithFloatStoreAndReadSnippet(float v) {
+        char[] b = new char[4];
+        unsafe.putFloat(b, charArrayBaseOffset, v);
+        return b[(charSize / floatSize) + 1];
+    }
+
+    @Test
+    public void testCharArrayWithFloatStoreAndRead() {
+        test("testCharArrayWithFloatStoreAndReadSnippet", floatValue);
+    }
+
+    public static char testCharArrayWithDoubleStoreAndReadSnippet(double v) {
+        char[] b = new char[4];
+        unsafe.putDouble(b, charArrayBaseOffset, v);
+        return b[(charSize / doubleSize) + 1];
+    }
+
+    @Test
+    public void testCharArrayWithDoubleStoreAndRead() {
+        test("testCharArrayWithDoubleStoreAndReadSnippet", doubleValue);
+    }
+
+    public static short testShortArrayWithByteStoreAndReadSnippet(byte v) {
+        short[] b = new short[4];
+        unsafe.putByte(b, shortArrayBaseOffset, v);
+        return b[(shortSize / byteSize) + 1];
+    }
+
+    @Test
+    public void testShortArrayWithByteStoreAndRead() {
+        test("testShortArrayWithByteStoreAndReadSnippet", byteValue);
+    }
+
+    public static short testShortArrayWithCharStoreAndReadSnippet(char v) {
+        short[] b = new short[4];
+        unsafe.putChar(b, shortArrayBaseOffset, v);
+        return b[(shortSize / charSize) + 1];
+    }
+
+    @Test
+    public void testShortArrayWithCharStoreAndRead() {
+        test("testShortArrayWithCharStoreAndReadSnippet", charValue);
+    }
+
+    public static short testShortArrayWithIntStoreAndReadSnippet(int v) {
+        short[] b = new short[4];
+        unsafe.putInt(b, shortArrayBaseOffset, v);
+        return b[(shortSize / intSize) + 1];
+    }
+
+    @Test
+    public void testShortArrayWithIntStoreAndRead() {
+        test("testShortArrayWithIntStoreAndReadSnippet", intValue);
+    }
+
+    public static short testShortArrayWithLongStoreAndReadSnippet(long v) {
+        short[] b = new short[4];
+        unsafe.putLong(b, shortArrayBaseOffset, v);
+        return b[(shortSize / longSize) + 1];
+    }
+
+    @Test
+    public void testShortArrayWithLongStoreAndRead() {
+        test("testShortArrayWithLongStoreAndReadSnippet", longValue);
+    }
+
+    public static short testShortArrayWithFloatStoreAndReadSnippet(float v) {
+        short[] b = new short[4];
+        unsafe.putFloat(b, shortArrayBaseOffset, v);
+        return b[(shortSize / floatSize) + 1];
+    }
+
+    @Test
+    public void testShortArrayWithFloatStoreAndRead() {
+        test("testShortArrayWithFloatStoreAndReadSnippet", floatValue);
+    }
+
+    public static short testShortArrayWithDoubleStoreAndReadSnippet(double v) {
+        short[] b = new short[4];
+        unsafe.putDouble(b, shortArrayBaseOffset, v);
+        return b[(shortSize / doubleSize) + 1];
+    }
+
+    @Test
+    public void testShortArrayWithDoubleStoreAndRead() {
+        test("testShortArrayWithDoubleStoreAndReadSnippet", doubleValue);
+    }
+
+    public static int testIntArrayWithByteStoreAndReadSnippet(byte v) {
+        int[] b = new int[4];
+        unsafe.putByte(b, intArrayBaseOffset, v);
+        return b[(intSize / byteSize) + 1];
+    }
+
+    @Test
+    public void testIntArrayWithByteStoreAndRead() {
+        test("testIntArrayWithByteStoreAndReadSnippet", byteValue);
+    }
+
+    public static int testIntArrayWithCharStoreAndReadSnippet(char v) {
+        int[] b = new int[4];
+        unsafe.putChar(b, intArrayBaseOffset, v);
+        return b[(intSize / charSize) + 1];
+    }
+
+    @Test
+    public void testIntArrayWithCharStoreAndRead() {
+        test("testIntArrayWithCharStoreAndReadSnippet", charValue);
+    }
+
+    public static int testIntArrayWithShortStoreAndReadSnippet(short v) {
+        int[] b = new int[4];
+        unsafe.putShort(b, intArrayBaseOffset, v);
+        return b[(intSize / shortSize) + 1];
+    }
+
+    @Test
+    public void testIntArrayWithShortStoreAndRead() {
+        test("testIntArrayWithShortStoreAndReadSnippet", shortValue);
+    }
+
+    public static int testIntArrayWithLongStoreAndReadSnippet(long v) {
+        int[] b = new int[4];
+        unsafe.putLong(b, intArrayBaseOffset, v);
+        return b[(intSize / longSize) + 1];
+    }
+
+    @Test
+    public void testIntArrayWithLongStoreAndRead() {
+        test("testIntArrayWithLongStoreAndReadSnippet", longValue);
+    }
+
+    public static int testIntArrayWithFloatStoreAndReadSnippet(float v) {
+        int[] b = new int[4];
+        unsafe.putFloat(b, intArrayBaseOffset, v);
+        return b[(intSize / floatSize) + 1];
+    }
+
+    @Test
+    public void testIntArrayWithFloatStoreAndRead() {
+        test("testIntArrayWithFloatStoreAndReadSnippet", floatValue);
+    }
+
+    public static int testIntArrayWithDoubleStoreAndReadSnippet(double v) {
+        int[] b = new int[4];
+        unsafe.putDouble(b, intArrayBaseOffset, v);
+        return b[(intSize / doubleSize) + 1];
+    }
+
+    @Test
+    public void testIntArrayWithDoubleStoreAndRead() {
+        test("testIntArrayWithDoubleStoreAndReadSnippet", doubleValue);
+    }
+
+    public static long testLongArrayWithByteStoreAndReadSnippet(byte v) {
+        long[] b = new long[4];
+        unsafe.putByte(b, longArrayBaseOffset, v);
+        return b[(longSize / byteSize) + 1];
+    }
+
+    @Test
+    public void testLongArrayWithByteStoreAndRead() {
+        test("testLongArrayWithByteStoreAndReadSnippet", byteValue);
+    }
+
+    public static long testLongArrayWithCharStoreAndReadSnippet(char v) {
+        long[] b = new long[4];
+        unsafe.putChar(b, longArrayBaseOffset, v);
+        return b[(longSize / charSize) + 1];
+    }
+
+    @Test
+    public void testLongArrayWithCharStoreAndRead() {
+        test("testLongArrayWithCharStoreAndReadSnippet", charValue);
+    }
+
+    public static long testLongArrayWithShortStoreAndReadSnippet(short v) {
+        long[] b = new long[4];
+        unsafe.putShort(b, longArrayBaseOffset, v);
+        return b[(longSize / shortSize) + 1];
+    }
+
+    @Test
+    public void testLongArrayWithShortStoreAndRead() {
+        test("testLongArrayWithShortStoreAndReadSnippet", shortValue);
+    }
+
+    public static long testLongArrayWithIntStoreAndReadSnippet(int v) {
+        long[] b = new long[4];
+        unsafe.putInt(b, longArrayBaseOffset, v);
+        return b[(longSize / intSize) + 1];
+    }
+
+    @Test
+    public void testLongArrayWithIntStoreAndRead() {
+        test("testLongArrayWithIntStoreAndReadSnippet", intValue);
+    }
+
+    public static long testLongArrayWithFloatStoreAndReadSnippet(float v) {
+        long[] b = new long[4];
+        unsafe.putFloat(b, longArrayBaseOffset, v);
+        return b[(longSize / floatSize) + 1];
+    }
+
+    @Test
+    public void testLongArrayWithFloatStoreAndRead() {
+        test("testLongArrayWithFloatStoreAndReadSnippet", floatValue);
+    }
+
+    public static long testLongArrayWithDoubleStoreAndReadSnippet(double v) {
+        long[] b = new long[4];
+        unsafe.putDouble(b, longArrayBaseOffset, v);
+        return b[(longSize / doubleSize) + 1];
+    }
+
+    @Test
+    public void testLongArrayWithDoubleStoreAndRead() {
+        test("testLongArrayWithDoubleStoreAndReadSnippet", doubleValue);
+    }
+
+    public static float testFloatArrayWithByteStoreAndReadSnippet(byte v) {
+        float[] b = new float[4];
+        unsafe.putByte(b, floatArrayBaseOffset, v);
+        return b[(floatSize / byteSize) + 1];
+    }
+
+    @Test
+    public void testFloatArrayWithByteStoreAndRead() {
+        test("testFloatArrayWithByteStoreAndReadSnippet", byteValue);
+    }
+
+    public static float testFloatArrayWithCharStoreAndReadSnippet(char v) {
+        float[] b = new float[4];
+        unsafe.putChar(b, floatArrayBaseOffset, v);
+        return b[(floatSize / charSize) + 1];
+    }
+
+    @Test
+    public void testFloatArrayWithCharStoreAndRead() {
+        test("testFloatArrayWithCharStoreAndReadSnippet", charValue);
+    }
+
+    public static float testFloatArrayWithShortStoreAndReadSnippet(short v) {
+        float[] b = new float[4];
+        unsafe.putShort(b, floatArrayBaseOffset, v);
+        return b[(floatSize / shortSize) + 1];
+    }
+
+    @Test
+    public void testFloatArrayWithShortStoreAndRead() {
+        test("testFloatArrayWithShortStoreAndReadSnippet", shortValue);
+    }
+
+    public static float testFloatArrayWithIntStoreAndReadSnippet(int v) {
+        float[] b = new float[4];
+        unsafe.putInt(b, floatArrayBaseOffset, v);
+        return b[(floatSize / intSize) + 1];
+    }
+
+    @Test
+    public void testFloatArrayWithIntStoreAndRead() {
+        test("testFloatArrayWithIntStoreAndReadSnippet", intValue);
+    }
+
+    public static float testFloatArrayWithLongStoreAndReadSnippet(long v) {
+        float[] b = new float[4];
+        unsafe.putLong(b, floatArrayBaseOffset, v);
+        return b[(floatSize / longSize) + 1];
+    }
+
+    @Test
+    public void testFloatArrayWithLongStoreAndRead() {
+        test("testFloatArrayWithLongStoreAndReadSnippet", longValue);
+    }
+
+    public static float testFloatArrayWithDoubleStoreAndReadSnippet(double v) {
+        float[] b = new float[4];
+        unsafe.putDouble(b, floatArrayBaseOffset, v);
+        return b[(floatSize / doubleSize) + 1];
+    }
+
+    @Test
+    public void testFloatArrayWithDoubleStoreAndRead() {
+        test("testFloatArrayWithDoubleStoreAndReadSnippet", doubleValue);
+    }
+
+    public static double testDoubleArrayWithByteStoreAndReadSnippet(byte v) {
+        double[] b = new double[4];
+        unsafe.putByte(b, doubleArrayBaseOffset, v);
+        return b[(doubleSize / byteSize) + 1];
+    }
+
+    @Test
+    public void testDoubleArrayWithByteStoreAndRead() {
+        test("testDoubleArrayWithByteStoreAndReadSnippet", byteValue);
+    }
+
+    public static double testDoubleArrayWithCharStoreAndReadSnippet(char v) {
+        double[] b = new double[4];
+        unsafe.putChar(b, doubleArrayBaseOffset, v);
+        return b[(doubleSize / charSize) + 1];
+    }
+
+    @Test
+    public void testDoubleArrayWithCharStoreAndRead() {
+        test("testDoubleArrayWithCharStoreAndReadSnippet", charValue);
+    }
+
+    public static double testDoubleArrayWithShortStoreAndReadSnippet(short v) {
+        double[] b = new double[4];
+        unsafe.putShort(b, doubleArrayBaseOffset, v);
+        return b[(doubleSize / shortSize) + 1];
+    }
+
+    @Test
+    public void testDoubleArrayWithShortStoreAndRead() {
+        test("testDoubleArrayWithShortStoreAndReadSnippet", shortValue);
+    }
+
+    public static double testDoubleArrayWithIntStoreAndReadSnippet(int v) {
+        double[] b = new double[4];
+        unsafe.putInt(b, doubleArrayBaseOffset, v);
+        return b[(doubleSize / intSize) + 1];
+    }
+
+    @Test
+    public void testDoubleArrayWithIntStoreAndRead() {
+        test("testDoubleArrayWithIntStoreAndReadSnippet", intValue);
+    }
+
+    public static double testDoubleArrayWithLongStoreAndReadSnippet(long v) {
+        double[] b = new double[4];
+        unsafe.putLong(b, doubleArrayBaseOffset, v);
+        return b[(doubleSize / longSize) + 1];
+    }
+
+    @Test
+    public void testDoubleArrayWithLongStoreAndRead() {
+        test("testDoubleArrayWithLongStoreAndReadSnippet", longValue);
+    }
+
+    public static double testDoubleArrayWithFloatStoreAndReadSnippet(float v) {
+        double[] b = new double[4];
+        unsafe.putFloat(b, doubleArrayBaseOffset, v);
+        return b[(doubleSize / floatSize) + 1];
+    }
+
+    @Test
+    public void testDoubleArrayWithFloatStoreAndRead() {
+        test("testDoubleArrayWithFloatStoreAndReadSnippet", floatValue);
+    }
+}
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/inlining/PolymorphicInliningTest.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/inlining/PolymorphicInliningTest.java
new file mode 100644
index 00000000000..015add57c38
--- /dev/null
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/inlining/PolymorphicInliningTest.java
@@ -0,0 +1,358 @@
+/*
+ * Copyright (c) 2012, 2016, 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 org.graalvm.compiler.core.test.inlining;
+
+import static org.graalvm.compiler.test.SubprocessUtil.getVMCommandLine;
+import static org.graalvm.compiler.test.SubprocessUtil.java;
+import static org.graalvm.compiler.test.SubprocessUtil.withoutDebuggerArguments;
+
+import jdk.vm.ci.meta.ResolvedJavaMethod;
+import org.graalvm.compiler.core.test.GraalCompilerTest;
+import org.graalvm.compiler.debug.DebugContext;
+import org.graalvm.compiler.debug.DebugDumpScope;
+import org.graalvm.compiler.graph.Node;
+import org.graalvm.compiler.nodes.DeoptimizeNode;
+import org.graalvm.compiler.nodes.InvokeNode;
+import org.graalvm.compiler.nodes.StructuredGraph;
+import org.graalvm.compiler.nodes.StructuredGraph.AllowAssumptions;
+import org.graalvm.compiler.nodes.StructuredGraph.Builder;
+import org.graalvm.compiler.nodes.graphbuilderconf.GraphBuilderConfiguration;
+import org.graalvm.compiler.nodes.java.TypeSwitchNode;
+import org.graalvm.compiler.phases.OptimisticOptimizations;
+import org.graalvm.compiler.phases.PhaseSuite;
+import org.graalvm.compiler.phases.common.CanonicalizerPhase;
+import org.graalvm.compiler.phases.common.DeadCodeEliminationPhase;
+import org.graalvm.compiler.phases.common.inlining.InliningPhase;
+import org.graalvm.compiler.phases.tiers.HighTierContext;
+import org.graalvm.compiler.test.SubprocessUtil;
+import org.junit.Assert;
+import org.junit.Test;
+
+import java.io.IOException;
+import java.util.List;
+
+public class PolymorphicInliningTest extends GraalCompilerTest {
+
+    @Test
+    public void testInSubprocess() throws InterruptedException, IOException {
+        String recursionPropName = getClass().getName() + ".recursion";
+        if (Boolean.getBoolean(recursionPropName)) {
+            testPolymorphicInlining();
+            testPolymorphicNotInlining();
+            testMegamorphicInlining();
+            testMegamorphicNotInlining();
+        } else {
+            List<String> vmArgs = withoutDebuggerArguments(getVMCommandLine());
+            NotInlinableSubClass.class.getCanonicalName();
+            vmArgs.add("-XX:CompileCommand=dontinline,org/graalvm/compiler/core/test/inlining/PolymorphicInliningTest$NotInlinableSubClass.publicOverriddenMethod");
+            vmArgs.add("-D" + recursionPropName + "=true");
+            SubprocessUtil.Subprocess proc = java(vmArgs, "com.oracle.mxtool.junit.MxJUnitWrapper", getClass().getName());
+            if (proc.exitCode != 0) {
+                Assert.fail(String.format("non-zero exit code %d for command:%n%s", proc.exitCode, proc));
+            }
+        }
+    }
+
+    public int polymorphicCallsite(SuperClass receiver) {
+        return receiver.publicOverriddenMethod();
+    }
+
+    public void testPolymorphicInlining() {
+        for (int i = 0; i < 10000; i++) {
+            if (i % 2 == 0) {
+                polymorphicCallsite(Receivers.subClassA);
+            } else {
+                polymorphicCallsite(Receivers.subClassB);
+            }
+        }
+        StructuredGraph graph = getGraph("polymorphicCallsite", false);
+        // This callsite should be inlined with a TypeCheckedInliningViolated deoptimization.
+        assertTrue(getNodeCount(graph, InvokeNode.class) == 0);
+        assertTrue(getNodeCount(graph, TypeSwitchNode.class) == 1);
+        assertTrue(getNodeCount(graph, DeoptimizeNode.class) >= 1);
+    }
+
+    /**
+     * This snippet is identical to {@link #polymorphicCallsite(SuperClass)}, and is for avoiding
+     * interference of the receiver type profile from different unit tests.
+     */
+    public int polymorphicCallsite1(SuperClass receiver) {
+        return receiver.publicOverriddenMethod();
+    }
+
+    public void testPolymorphicNotInlining() {
+        for (int i = 0; i < 10000; i++) {
+            if (i % 2 == 0) {
+                polymorphicCallsite1(Receivers.subClassA);
+            } else {
+                polymorphicCallsite1(Receivers.notInlinableSubClass);
+            }
+        }
+        StructuredGraph graph = getGraph("polymorphicCallsite1", false);
+        // This callsite should not be inlined due to one of the potential callee method is not
+        // inlinable.
+        assertTrue(getNodeCount(graph, InvokeNode.class) == 1);
+        assertTrue(getNodeCount(graph, TypeSwitchNode.class) == 0);
+    }
+
+    /**
+     * This snippet is identical to {@link #polymorphicCallsite(SuperClass)}, and is for avoiding
+     * interference of the receiver type profile from different unit tests.
+     */
+    public int polymorphicCallsite2(SuperClass receiver) {
+        return receiver.publicOverriddenMethod();
+    }
+
+    public void testMegamorphicInlining() {
+        // Construct a receiver type profile that exceeds the max type width (by default 8 in JVMCI,
+        // specified by -XX:TypeProfileWidth).
+        for (int i = 0; i < 2000; i++) {
+            // Ensure the following receiver type is within the type profile.
+            polymorphicCallsite2(Receivers.subClassA);
+        }
+        for (int i = 0; i < 10000; i++) {
+            switch (i % 20) {
+                case 0:
+                case 1:
+                case 2:
+                case 3:
+                case 4:
+                case 5:
+                case 6:
+                case 7:
+                    // Probability: 40%
+                    // Ensure the probability is greater than
+                    // GraalOptions.MegamorphicInliningMinMethodProbability (by default 0.33D);
+                    polymorphicCallsite2(Receivers.subClassA);
+                    break;
+                case 8:
+                    polymorphicCallsite2(Receivers.subClassB);
+                    break;
+                case 9:
+                    polymorphicCallsite2(Receivers.subClassC);
+                    break;
+                case 10:
+                    polymorphicCallsite2(Receivers.subClassD);
+                    break;
+                case 11:
+                    polymorphicCallsite2(Receivers.subClassE);
+                    break;
+                case 12:
+                    polymorphicCallsite2(Receivers.subClassF);
+                    break;
+                case 13:
+                    polymorphicCallsite2(Receivers.subClassG);
+                    break;
+                case 14:
+                    polymorphicCallsite2(Receivers.subClassH);
+                    break;
+                default:
+                    // Probability: 25%
+                    polymorphicCallsite2(Receivers.notInlinableSubClass);
+                    break;
+            }
+        }
+        StructuredGraph graph = getGraph("polymorphicCallsite2", false);
+        // This callsite should be inlined with a fallback invocation.
+        assertTrue(getNodeCount(graph, InvokeNode.class) == 1);
+        assertTrue(getNodeCount(graph, TypeSwitchNode.class) == 1);
+    }
+
+    /**
+     * This snippet is identical to {@link #polymorphicCallsite(SuperClass)}, and is for avoiding
+     * interference of the receiver type profile from different unit tests.
+     */
+    public int polymorphicCallsite3(SuperClass receiver) {
+        return receiver.publicOverriddenMethod();
+    }
+
+    public void testMegamorphicNotInlining() {
+        for (int i = 0; i < 10000; i++) {
+            switch (i % 10) {
+                case 0:
+                case 1:
+                    polymorphicCallsite3(Receivers.subClassA);
+                    break;
+                case 2:
+                    polymorphicCallsite3(Receivers.subClassB);
+                    break;
+                case 3:
+                    polymorphicCallsite3(Receivers.subClassC);
+                    break;
+                case 4:
+                    polymorphicCallsite3(Receivers.subClassD);
+                    break;
+                case 5:
+                    polymorphicCallsite3(Receivers.subClassE);
+                    break;
+                case 6:
+                    polymorphicCallsite3(Receivers.subClassF);
+                    break;
+                case 7:
+                    polymorphicCallsite3(Receivers.subClassG);
+                    break;
+                case 8:
+                    polymorphicCallsite3(Receivers.subClassH);
+                    break;
+                default:
+                    polymorphicCallsite3(Receivers.notInlinableSubClass);
+                    break;
+            }
+        }
+        StructuredGraph graph = getGraph("polymorphicCallsite3", false);
+        // This callsite should not be inlined due to non of the potential callee method exceeds the
+        // probability specified by GraalOptions.MegamorphicInliningMinMethodProbability.
+        assertTrue(getNodeCount(graph, InvokeNode.class) == 1);
+        assertTrue(getNodeCount(graph, TypeSwitchNode.class) == 0);
+    }
+
+    @SuppressWarnings("try")
+    private StructuredGraph getGraph(final String snippet, final boolean eagerInfopointMode) {
+        DebugContext debug = getDebugContext();
+        try (DebugContext.Scope s = debug.scope("InliningTest", new DebugDumpScope(snippet, true))) {
+            ResolvedJavaMethod method = getResolvedJavaMethod(snippet);
+            Builder builder = builder(method, AllowAssumptions.YES, debug);
+            StructuredGraph graph = eagerInfopointMode ? parse(builder, getDebugGraphBuilderSuite()) : parse(builder, getEagerGraphBuilderSuite());
+            try (DebugContext.Scope s2 = debug.scope("Inlining", graph)) {
+                PhaseSuite<HighTierContext> graphBuilderSuite = eagerInfopointMode
+                                ? getCustomGraphBuilderSuite(GraphBuilderConfiguration.getDefault(getDefaultGraphBuilderPlugins()).withFullInfopoints(true))
+                                : getDefaultGraphBuilderSuite();
+                HighTierContext context = new HighTierContext(getProviders(), graphBuilderSuite, OptimisticOptimizations.ALL);
+                debug.dump(DebugContext.BASIC_LEVEL, graph, "Graph");
+                new CanonicalizerPhase().apply(graph, context);
+                new InliningPhase(new CanonicalizerPhase()).apply(graph, context);
+                debug.dump(DebugContext.BASIC_LEVEL, graph, "Graph");
+                new CanonicalizerPhase().apply(graph, context);
+                new DeadCodeEliminationPhase().apply(graph);
+                return graph;
+            }
+        } catch (Throwable e) {
+            throw debug.handle(e);
+        }
+    }
+
+    private static int getNodeCount(StructuredGraph graph, Class<? extends Node> nodeClass) {
+        return graph.getNodes().filter(nodeClass).count();
+    }
+
+    private static final class Receivers {
+        static final SubClassA subClassA = new SubClassA();
+        static final SubClassB subClassB = new SubClassB();
+        static final SubClassC subClassC = new SubClassC();
+        static final SubClassD subClassD = new SubClassD();
+        static final SubClassE subClassE = new SubClassE();
+        static final SubClassF subClassF = new SubClassF();
+        static final SubClassG subClassG = new SubClassG();
+        static final SubClassH subClassH = new SubClassH();
+
+        static final NotInlinableSubClass notInlinableSubClass = new NotInlinableSubClass();
+    }
+
+    private abstract static class SuperClass {
+
+        public abstract int publicOverriddenMethod();
+
+    }
+
+    private static class SubClassA extends SuperClass {
+
+        @Override
+        public int publicOverriddenMethod() {
+            return 'A';
+        }
+
+    }
+
+    private static class SubClassB extends SuperClass {
+
+        @Override
+        public int publicOverriddenMethod() {
+            return 'B';
+        }
+
+    }
+
+    private static class SubClassC extends SuperClass {
+
+        @Override
+        public int publicOverriddenMethod() {
+            return 'C';
+        }
+
+    }
+
+    private static class SubClassD extends SuperClass {
+
+        @Override
+        public int publicOverriddenMethod() {
+            return 'D';
+        }
+
+    }
+
+    private static class SubClassE extends SuperClass {
+
+        @Override
+        public int publicOverriddenMethod() {
+            return 'E';
+        }
+
+    }
+
+    private static class SubClassF extends SuperClass {
+
+        @Override
+        public int publicOverriddenMethod() {
+            return 'F';
+        }
+
+    }
+
+    private static class SubClassG extends SuperClass {
+
+        @Override
+        public int publicOverriddenMethod() {
+            return 'G';
+        }
+
+    }
+
+    private static class SubClassH extends SuperClass {
+
+        @Override
+        public int publicOverriddenMethod() {
+            return 'H';
+        }
+
+    }
+
+    private static final class NotInlinableSubClass extends SuperClass {
+
+        @Override
+        public int publicOverriddenMethod() {
+            return 'X';
+        }
+
+    }
+
+}
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core/src/org/graalvm/compiler/core/gen/NodeLIRBuilder.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core/src/org/graalvm/compiler/core/gen/NodeLIRBuilder.java
index fffe121e077..a151bc46c28 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core/src/org/graalvm/compiler/core/gen/NodeLIRBuilder.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.core/src/org/graalvm/compiler/core/gen/NodeLIRBuilder.java
@@ -81,6 +81,7 @@ import org.graalvm.compiler.nodes.LogicConstantNode;
 import org.graalvm.compiler.nodes.LogicNode;
 import org.graalvm.compiler.nodes.LoopEndNode;
 import org.graalvm.compiler.nodes.LoweredCallTargetNode;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.ParameterNode;
 import org.graalvm.compiler.nodes.PhiNode;
 import org.graalvm.compiler.nodes.StructuredGraph;
@@ -243,7 +244,7 @@ public abstract class NodeLIRBuilder implements NodeLIRBuilderTool, LIRGeneratio
     }
 
     protected LIRKind getExactPhiKind(PhiNode phi) {
-        LIRKind derivedKind = gen.toRegisterKind(gen.getLIRKind(phi.stamp()));
+        LIRKind derivedKind = gen.toRegisterKind(gen.getLIRKind(phi.stamp(NodeView.DEFAULT)));
         /* Collect reference information. */
         for (int i = 0; i < phi.valueCount() && !derivedKind.isUnknownReference(); i++) {
             ValueNode node = phi.valueAt(i);
@@ -255,7 +256,7 @@ public abstract class NodeLIRBuilder implements NodeLIRBuilderTool, LIRGeneratio
                 valueKind = value.getValueKind(LIRKind.class);
             } else {
                 assert isPhiInputFromBackedge(phi, i) : String.format("Input %s to phi node %s is not yet available although it is not coming from a loop back edge", node, phi);
-                LIRKind kind = gen.getLIRKind(node.stamp());
+                LIRKind kind = gen.getLIRKind(node.stamp(NodeView.DEFAULT));
                 valueKind = gen.toRegisterKind(kind);
             }
             /* Merge the reference information of the derived kind and the input. */
@@ -448,7 +449,7 @@ public abstract class NodeLIRBuilder implements NodeLIRBuilderTool, LIRGeneratio
     }
 
     protected void emitNode(ValueNode node) {
-        if (node.getDebug().isLogEnabled() && node.stamp().isEmpty()) {
+        if (node.getDebug().isLogEnabled() && node.stamp(NodeView.DEFAULT).isEmpty()) {
             node.getDebug().log("This node has an empty stamp, we are emitting dead code(?): %s", node);
         }
         setSourcePosition(node.getNodeSourcePosition());
@@ -477,7 +478,8 @@ public abstract class NodeLIRBuilder implements NodeLIRBuilderTool, LIRGeneratio
 
         for (ParameterNode param : graph.getNodes(ParameterNode.TYPE)) {
             Value paramValue = params[param.index()];
-            assert paramValue.getValueKind().equals(getLIRGeneratorTool().getLIRKind(param.stamp())) : paramValue + " " + getLIRGeneratorTool().getLIRKind(param.stamp());
+            assert paramValue.getValueKind().equals(getLIRGeneratorTool().getLIRKind(param.stamp(NodeView.DEFAULT))) : paramValue + " " +
+                            getLIRGeneratorTool().getLIRKind(param.stamp(NodeView.DEFAULT));
             setResult(param, gen.emitMove(paramValue));
         }
     }
@@ -506,7 +508,7 @@ public abstract class NodeLIRBuilder implements NodeLIRBuilderTool, LIRGeneratio
     }
 
     protected LIRKind getPhiKind(PhiNode phi) {
-        return gen.getLIRKind(phi.stamp());
+        return gen.getLIRKind(phi.stamp(NodeView.DEFAULT));
     }
 
     @Override
@@ -529,13 +531,13 @@ public abstract class NodeLIRBuilder implements NodeLIRBuilderTool, LIRGeneratio
     }
 
     private void emitNullCheckBranch(IsNullNode node, LabelRef trueSuccessor, LabelRef falseSuccessor, double trueSuccessorProbability) {
-        LIRKind kind = gen.getLIRKind(node.getValue().stamp());
+        LIRKind kind = gen.getLIRKind(node.getValue().stamp(NodeView.DEFAULT));
         Value nullValue = gen.emitConstant(kind, JavaConstant.NULL_POINTER);
         gen.emitCompareBranch(kind.getPlatformKind(), operand(node.getValue()), nullValue, Condition.EQ, false, trueSuccessor, falseSuccessor, trueSuccessorProbability);
     }
 
     public void emitCompareBranch(CompareNode compare, LabelRef trueSuccessor, LabelRef falseSuccessor, double trueSuccessorProbability) {
-        PlatformKind kind = gen.getLIRKind(compare.getX().stamp()).getPlatformKind();
+        PlatformKind kind = gen.getLIRKind(compare.getX().stamp(NodeView.DEFAULT)).getPlatformKind();
         gen.emitCompareBranch(kind, operand(compare.getX()), operand(compare.getY()), compare.condition(), compare.unorderedIsTrue(), trueSuccessor, falseSuccessor, trueSuccessorProbability);
     }
 
@@ -558,12 +560,12 @@ public abstract class NodeLIRBuilder implements NodeLIRBuilderTool, LIRGeneratio
     public Variable emitConditional(LogicNode node, Value trueValue, Value falseValue) {
         if (node instanceof IsNullNode) {
             IsNullNode isNullNode = (IsNullNode) node;
-            LIRKind kind = gen.getLIRKind(isNullNode.getValue().stamp());
+            LIRKind kind = gen.getLIRKind(isNullNode.getValue().stamp(NodeView.DEFAULT));
             Value nullValue = gen.emitConstant(kind, JavaConstant.NULL_POINTER);
             return gen.emitConditionalMove(kind.getPlatformKind(), operand(isNullNode.getValue()), nullValue, Condition.EQ, false, trueValue, falseValue);
         } else if (node instanceof CompareNode) {
             CompareNode compare = (CompareNode) node;
-            PlatformKind kind = gen.getLIRKind(compare.getX().stamp()).getPlatformKind();
+            PlatformKind kind = gen.getLIRKind(compare.getX().stamp(NodeView.DEFAULT)).getPlatformKind();
             return gen.emitConditionalMove(kind, operand(compare.getX()), operand(compare.getY()), compare.condition(), compare.unorderedIsTrue(), trueValue, falseValue);
         } else if (node instanceof LogicConstantNode) {
             return gen.emitMove(((LogicConstantNode) node).getValue() ? trueValue : falseValue);
@@ -579,7 +581,8 @@ public abstract class NodeLIRBuilder implements NodeLIRBuilderTool, LIRGeneratio
     public void emitInvoke(Invoke x) {
         LoweredCallTargetNode callTarget = (LoweredCallTargetNode) x.callTarget();
         FrameMapBuilder frameMapBuilder = gen.getResult().getFrameMapBuilder();
-        CallingConvention invokeCc = frameMapBuilder.getRegisterConfig().getCallingConvention(callTarget.callType(), x.asNode().stamp().javaType(gen.getMetaAccess()), callTarget.signature(), gen);
+        CallingConvention invokeCc = frameMapBuilder.getRegisterConfig().getCallingConvention(callTarget.callType(), x.asNode().stamp(NodeView.DEFAULT).javaType(gen.getMetaAccess()),
+                        callTarget.signature(), gen);
         frameMapBuilder.callsMethod(invokeCc);
 
         Value[] parameters = visitInvokeArguments(invokeCc, callTarget.arguments());
@@ -650,7 +653,7 @@ public abstract class NodeLIRBuilder implements NodeLIRBuilderTool, LIRGeneratio
             if (keyCount == 1) {
                 assert defaultTarget != null;
                 double probability = x.probability(x.keySuccessor(0));
-                LIRKind kind = gen.getLIRKind(x.value().stamp());
+                LIRKind kind = gen.getLIRKind(x.value().stamp(NodeView.DEFAULT));
                 Value key = gen.emitConstant(kind, x.keyAt(0));
                 gen.emitCompareBranch(kind.getPlatformKind(), gen.load(operand(x.value())), key, Condition.EQ, false, getLIRBlock(x.keySuccessor(0)), defaultTarget, probability);
             } else if (x instanceof IntegerSwitchNode && x.isSorted()) {
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug.test/src/org/graalvm/compiler/debug/test/VersionsTest.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug.test/src/org/graalvm/compiler/debug/test/VersionsTest.java
new file mode 100644
index 00000000000..f9b562f0e21
--- /dev/null
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug.test/src/org/graalvm/compiler/debug/test/VersionsTest.java
@@ -0,0 +1,199 @@
+/*
+ * Copyright (c) 2013, 2017, 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 org.graalvm.compiler.debug.test;
+
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.nio.file.FileVisitResult;
+import java.nio.file.FileVisitor;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.nio.file.attribute.BasicFileAttributes;
+import java.util.HashMap;
+import java.util.Map;
+import org.graalvm.compiler.debug.Versions;
+import org.junit.After;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.junit.Assume.assumeTrue;
+import org.junit.Test;
+
+public class VersionsTest {
+    private File temporaryDirectory;
+
+    @After
+    public void cleanUp() throws IOException {
+        if (temporaryDirectory != null) {
+            Files.walkFileTree(temporaryDirectory.toPath(), new FileVisitor<Path>() {
+                @Override
+                public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
+                    return FileVisitResult.CONTINUE;
+                }
+
+                @Override
+                public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
+                    Files.delete(file);
+                    return FileVisitResult.CONTINUE;
+                }
+
+                @Override
+                public FileVisitResult visitFileFailed(Path file, IOException exc) throws IOException {
+                    Files.delete(file);
+                    return FileVisitResult.CONTINUE;
+                }
+
+                @Override
+                public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
+                    Files.delete(dir);
+                    return FileVisitResult.CONTINUE;
+                }
+            });
+        }
+        temporaryDirectory = null;
+    }
+
+    @Test
+    public void emptyProperties() {
+        Path root = Paths.get("file:/");
+        Versions v = new Versions(root);
+        assertEmpty(v.withVersions(null));
+    }
+
+    @Test
+    public void emptyWithNullProperties() {
+        Path root = Paths.get("file:/");
+        Versions v = new Versions(root);
+        assertEmpty(v.withVersions(null));
+    }
+
+    @Test
+    public void readFromSameDirNullProps() throws IOException {
+        File dir = prepareReleaseFile();
+
+        Versions v = new Versions(dir.toPath());
+        Map<Object, Object> map = v.withVersions(null);
+        assertNonModifiable(map);
+
+        assertEquals("16055f1ffaf736b7b86dcfaea53971983cd9ae0a", map.get("version.sdk"));
+        assertEquals("7930979c3b0af09a910accaaf3e73b2a55d2bade", map.get("version.truffleruby"));
+    }
+
+    @Test
+    public void readFromSameDir() throws IOException {
+        File dir = prepareReleaseFile();
+
+        Versions v = new Versions(dir.toPath());
+
+        Map<Object, Object> prepared = new HashMap<>();
+        prepared.put("test", "best");
+
+        Map<Object, Object> map = v.withVersions(prepared);
+        assertSame(prepared, map);
+
+        assertEquals("16055f1ffaf736b7b86dcfaea53971983cd9ae0a", map.get("version.sdk"));
+        assertEquals("7930979c3b0af09a910accaaf3e73b2a55d2bade", map.get("version.truffleruby"));
+        assertEquals("best", map.get("test"));
+    }
+
+    @Test
+    public void readFromSubDirNullProps() throws IOException {
+        File dir = prepareSubReleaseFile();
+
+        Versions v = new Versions(dir.toPath());
+        Map<Object, Object> map = v.withVersions(null);
+        assertNonModifiable(map);
+
+        assertEquals("16055f1ffaf736b7b86dcfaea53971983cd9ae0a", map.get("version.sdk"));
+        assertEquals("7930979c3b0af09a910accaaf3e73b2a55d2bade", map.get("version.truffleruby"));
+    }
+
+    @Test
+    public void readFromSubDir() throws IOException {
+        File dir = prepareSubReleaseFile();
+
+        Versions v = new Versions(dir.toPath());
+
+        Map<Object, Object> prepared = new HashMap<>();
+        prepared.put("test", "best");
+
+        Map<Object, Object> map = v.withVersions(prepared);
+        assertSame(prepared, map);
+
+        assertEquals("16055f1ffaf736b7b86dcfaea53971983cd9ae0a", map.get("version.sdk"));
+        assertEquals("7930979c3b0af09a910accaaf3e73b2a55d2bade", map.get("version.truffleruby"));
+        assertEquals("best", map.get("test"));
+    }
+
+    private File prepareReleaseFile() throws IOException {
+        if (temporaryDirectory == null) {
+            temporaryDirectory = File.createTempFile("versions", ".tmp");
+            temporaryDirectory.delete();
+            assumeTrue(temporaryDirectory.mkdirs());
+            try (FileWriter w = new FileWriter(new File(temporaryDirectory, "release"))) {
+// @formatter:off
+                w.write(
+"OS_NAME=linux\n" +
+"OS_ARCH=amd64\n" +
+"SOURCE=\" truffle:16055f1ffaf736b7b86dcfaea53971983cd9ae0a sdk:16055f1ffaf736b7b86dcfaea53971983cd9ae0a " +
+"tools-enterprise:fcc1292a05e807a63589e24ce6073aafdef45bb9 graal-js:d374a8fd2733487a9f7518be6a55eb6163a779d1 " +
+"graal-nodejs:3fcaf6874c9059d5ca5f0615edaa405d66cc1b02 truffleruby:7930979c3b0af09a910accaaf3e73b2a55d2bade " +
+"fastr:079c6513b46f36abc24bce8aa6022c90576b3eaf graalpython:4cbee7853d460930c4d693970a21b73f811a4703 " +
+"sulong:2c425f92caa004b12f60428a3e7e6e2715b51f87 substratevm:fcc1292a05e807a63589e24ce6073aafdef45bb9 " +
+"compiler:16055f1ffaf736b7b86dcfaea53971983cd9ae0a substratevm-enterprise:fcc1292a05e807a63589e24ce6073aafdef45bb9 " +
+"vm-enterprise:fcc1292a05e807a63589e24ce6073aafdef45bb9 graal-enterprise:fcc1292a05e807a63589e24ce6073aafdef45bb9 \"\n" +
+"COMMIT_INFO={\"vm-enterprise\":{\"commit.rev\":\"fcc1292a05e807a63589e24ce6073aafdef45bb9\"," +
+"\"commit.committer\":\"Vojin Jovanovic <vojin.jovanovic@oracle.com>\",}}\n" +
+"GRAALVM_VERSION=\"0.29-dev\""
+                );
+// @formatter:on
+            }
+        }
+        return temporaryDirectory;
+    }
+
+    private File prepareSubReleaseFile() throws IOException {
+        File subdir = new File(prepareReleaseFile(), "subdir");
+        assumeTrue(subdir.mkdirs());
+        return subdir;
+    }
+
+    private static void assertEmpty(Map<?, ?> map) {
+        assertNotNull(map);
+        assertTrue(map.isEmpty());
+        assertNonModifiable(map);
+    }
+
+    private static void assertNonModifiable(Map<?, ?> map) {
+        try {
+            map.put(null, null);
+            fail("Map shall not be modifiable: " + map);
+        } catch (UnsupportedOperationException ex) {
+            // ok
+        }
+    }
+}
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/DebugContext.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/DebugContext.java
index 96cd6cbf524..ae2285f019b 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/DebugContext.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/DebugContext.java
@@ -129,6 +129,20 @@ public final class DebugContext implements AutoCloseable {
         }
     }
 
+    /**
+     * Adds version properties to the provided map. The version properties are read at a start of
+     * the JVM from a JVM specific location. Each property identifiers a commit of a certain
+     * component in the system. The properties added to the {@code properties} map are prefixed with
+     * {@code "version."} prefix.
+     *
+     * @param properties map to add the version properties to or {@code null}
+     * @return {@code properties} with version properties added or an unmodifiable map containing
+     *         the version properties if {@code properties == null}
+     */
+    public static Map<Object, Object> addVersionProperties(Map<Object, Object> properties) {
+        return Versions.VERSIONS.withVersions(properties);
+    }
+
     /**
      * The immutable configuration that can be shared between {@link DebugContext} objects.
      */
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/Versions.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/Versions.java
new file mode 100644
index 00000000000..6de0bfe3020
--- /dev/null
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/Versions.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (c) 2012, 2017, 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 org.graalvm.compiler.debug;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+/** Avoid using directly. Only public for the needs of unit testing. */
+public final class Versions {
+    static final Versions VERSIONS;
+    static {
+        String home = System.getProperty("java.home");
+        VERSIONS = new Versions(home == null ? null : new File(home).toPath());
+    }
+
+    private final Map<Object, Object> versions;
+
+    public Versions(Path home) {
+        Map<Object, Object> map = new HashMap<>();
+        ASSIGN: try {
+            Path info = findReleaseInfo(home);
+            if (info == null) {
+                break ASSIGN;
+            }
+            for (String line : Files.readAllLines(info)) {
+                final String prefix = "SOURCE=";
+                if (line.startsWith(prefix)) {
+                    for (String versionInfo : line.substring(prefix.length()).replace('"', ' ').split(" ")) {
+                        String[] idVersion = versionInfo.split(":");
+                        if (idVersion != null && idVersion.length == 2) {
+                            map.put("version." + idVersion[0], idVersion[1]);
+                        }
+                    }
+                    break ASSIGN;
+                }
+            }
+        } catch (IOException ex) {
+            // no versions file found
+        }
+        versions = Collections.unmodifiableMap(map);
+    }
+
+    public Map<Object, Object> withVersions(Map<Object, Object> properties) {
+        if (properties == null) {
+            return versions;
+        } else {
+            properties.putAll(versions);
+            return properties;
+        }
+    }
+
+    private static Path findReleaseInfo(Path jreDir) {
+        if (jreDir == null) {
+            return null;
+        }
+        Path releaseInJre = jreDir.resolve("release");
+        if (Files.exists(releaseInJre)) {
+            return releaseInJre;
+        }
+        Path jdkDir = jreDir.getParent();
+        if (jdkDir == null) {
+            return null;
+        }
+        Path releaseInJdk = jdkDir.resolve("release");
+        return Files.exists(releaseInJdk) ? releaseInJdk : null;
+    }
+
+}
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.aarch64/src/org/graalvm/compiler/hotspot/aarch64/AArch64HotSpotEpilogueOp.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.aarch64/src/org/graalvm/compiler/hotspot/aarch64/AArch64HotSpotEpilogueOp.java
index 79be3d65623..182f6f4c5bd 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.aarch64/src/org/graalvm/compiler/hotspot/aarch64/AArch64HotSpotEpilogueOp.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.aarch64/src/org/graalvm/compiler/hotspot/aarch64/AArch64HotSpotEpilogueOp.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2017, 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
@@ -37,10 +37,18 @@ import jdk.vm.ci.code.Register;
 abstract class AArch64HotSpotEpilogueOp extends AArch64BlockEndOp {
 
     private final GraalHotSpotVMConfig config;
+    private final Register thread;
+
+    protected AArch64HotSpotEpilogueOp(LIRInstructionClass<? extends AArch64HotSpotEpilogueOp> c, GraalHotSpotVMConfig config, Register thread) {
+        super(c);
+        this.config = config;
+        this.thread = thread;
+    }
 
     protected AArch64HotSpotEpilogueOp(LIRInstructionClass<? extends AArch64HotSpotEpilogueOp> c, GraalHotSpotVMConfig config) {
         super(c);
         this.config = config;
+        this.thread = null; // no safepoint
     }
 
     protected void leaveFrame(CompilationResultBuilder crb, AArch64MacroAssembler masm, boolean emitSafepoint) {
@@ -49,7 +57,7 @@ abstract class AArch64HotSpotEpilogueOp extends AArch64BlockEndOp {
         if (emitSafepoint) {
             try (ScratchRegister sc = masm.getScratchRegister()) {
                 Register scratch = sc.getRegister();
-                AArch64HotSpotSafepointOp.emitCode(crb, masm, config, true, scratch, null);
+                AArch64HotSpotSafepointOp.emitCode(crb, masm, config, true, thread, scratch, null);
             }
         }
     }
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.aarch64/src/org/graalvm/compiler/hotspot/aarch64/AArch64HotSpotLIRGenerator.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.aarch64/src/org/graalvm/compiler/hotspot/aarch64/AArch64HotSpotLIRGenerator.java
index 1803ea3adbb..2ed6e3408c0 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.aarch64/src/org/graalvm/compiler/hotspot/aarch64/AArch64HotSpotLIRGenerator.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.aarch64/src/org/graalvm/compiler/hotspot/aarch64/AArch64HotSpotLIRGenerator.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2017, 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
@@ -342,7 +342,8 @@ public class AArch64HotSpotLIRGenerator extends AArch64LIRGenerator implements H
             operand = resultOperandFor(kind, input.getValueKind());
             emitMove(operand, input);
         }
-        append(new AArch64HotSpotReturnOp(operand, getStub() != null, config));
+        Register thread = getProviders().getRegisters().getThreadRegister();
+        append(new AArch64HotSpotReturnOp(operand, getStub() != null, config, thread));
     }
 
     /**
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.aarch64/src/org/graalvm/compiler/hotspot/aarch64/AArch64HotSpotNodeLIRBuilder.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.aarch64/src/org/graalvm/compiler/hotspot/aarch64/AArch64HotSpotNodeLIRBuilder.java
index 7865e886a87..e67915192cb 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.aarch64/src/org/graalvm/compiler/hotspot/aarch64/AArch64HotSpotNodeLIRBuilder.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.aarch64/src/org/graalvm/compiler/hotspot/aarch64/AArch64HotSpotNodeLIRBuilder.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2017, 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,6 +49,7 @@ import org.graalvm.compiler.nodes.CallTargetNode.InvokeKind;
 import org.graalvm.compiler.nodes.DirectCallTargetNode;
 import org.graalvm.compiler.nodes.FullInfopointNode;
 import org.graalvm.compiler.nodes.IndirectCallTargetNode;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.ParameterNode;
 import org.graalvm.compiler.nodes.SafepointNode;
 import org.graalvm.compiler.nodes.StructuredGraph;
@@ -110,7 +111,7 @@ public class AArch64HotSpotNodeLIRBuilder extends AArch64NodeLIRBuilder implemen
 
         for (ParameterNode param : graph.getNodes(ParameterNode.TYPE)) {
             Value paramValue = params[param.index()];
-            assert paramValue.getValueKind().equals(getLIRGeneratorTool().getLIRKind(param.stamp())) : paramValue.getValueKind() + " != " + param.stamp();
+            assert paramValue.getValueKind().equals(getLIRGeneratorTool().getLIRKind(param.stamp(NodeView.DEFAULT))) : paramValue.getValueKind() + " != " + param.stamp(NodeView.DEFAULT);
             setResult(param, gen.emitMove(paramValue));
         }
     }
@@ -118,8 +119,9 @@ public class AArch64HotSpotNodeLIRBuilder extends AArch64NodeLIRBuilder implemen
     @Override
     public void visitSafepointNode(SafepointNode i) {
         LIRFrameState info = state(i);
+        Register thread = getGen().getProviders().getRegisters().getThreadRegister();
         Variable scratch = gen.newVariable(LIRKind.value(getGen().target().arch.getWordKind()));
-        append(new AArch64HotSpotSafepointOp(info, getGen().config, scratch));
+        append(new AArch64HotSpotSafepointOp(info, getGen().config, thread, scratch));
     }
 
     @Override
@@ -180,7 +182,7 @@ public class AArch64HotSpotNodeLIRBuilder extends AArch64NodeLIRBuilder implemen
     public void visitBreakpointNode(BreakpointNode node) {
         JavaType[] sig = new JavaType[node.arguments().size()];
         for (int i = 0; i < sig.length; i++) {
-            sig[i] = node.arguments().get(i).stamp().javaType(gen.getMetaAccess());
+            sig[i] = node.arguments().get(i).stamp(NodeView.DEFAULT).javaType(gen.getMetaAccess());
         }
 
         Value[] parameters = visitInvokeArguments(gen.getRegisterConfig().getCallingConvention(HotSpotCallingConventionType.JavaCall, null, sig, gen), node.arguments());
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.aarch64/src/org/graalvm/compiler/hotspot/aarch64/AArch64HotSpotReturnOp.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.aarch64/src/org/graalvm/compiler/hotspot/aarch64/AArch64HotSpotReturnOp.java
index 283e3fa7cf2..3065f491c6d 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.aarch64/src/org/graalvm/compiler/hotspot/aarch64/AArch64HotSpotReturnOp.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.aarch64/src/org/graalvm/compiler/hotspot/aarch64/AArch64HotSpotReturnOp.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2017, 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
@@ -33,6 +33,7 @@ import org.graalvm.compiler.lir.LIRInstructionClass;
 import org.graalvm.compiler.lir.Opcode;
 import org.graalvm.compiler.lir.asm.CompilationResultBuilder;
 
+import jdk.vm.ci.code.Register;
 import jdk.vm.ci.meta.Value;
 
 /**
@@ -46,8 +47,8 @@ public final class AArch64HotSpotReturnOp extends AArch64HotSpotEpilogueOp {
     @Use({REG, ILLEGAL}) private Value result;
     private final boolean isStub;
 
-    public AArch64HotSpotReturnOp(Value result, boolean isStub, GraalHotSpotVMConfig config) {
-        super(TYPE, config);
+    public AArch64HotSpotReturnOp(Value result, boolean isStub, GraalHotSpotVMConfig config, Register thread) {
+        super(TYPE, config, thread);
         assert validReturnValue(result);
         this.result = result;
         this.isStub = isStub;
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.aarch64/src/org/graalvm/compiler/hotspot/aarch64/AArch64HotSpotSafepointOp.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.aarch64/src/org/graalvm/compiler/hotspot/aarch64/AArch64HotSpotSafepointOp.java
index a2023faf776..f075f28bf8b 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.aarch64/src/org/graalvm/compiler/hotspot/aarch64/AArch64HotSpotSafepointOp.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.aarch64/src/org/graalvm/compiler/hotspot/aarch64/AArch64HotSpotSafepointOp.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2017, 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
@@ -50,18 +50,20 @@ public class AArch64HotSpotSafepointOp extends AArch64LIRInstruction {
     @Temp protected AllocatableValue scratchValue;
 
     private final GraalHotSpotVMConfig config;
+    private final Register thread;
 
-    public AArch64HotSpotSafepointOp(LIRFrameState state, GraalHotSpotVMConfig config, AllocatableValue scratch) {
+    public AArch64HotSpotSafepointOp(LIRFrameState state, GraalHotSpotVMConfig config, Register thread, AllocatableValue scratch) {
         super(TYPE);
         this.state = state;
         this.config = config;
+        this.thread = thread;
         this.scratchValue = scratch;
     }
 
     @Override
     public void emitCode(CompilationResultBuilder crb, AArch64MacroAssembler masm) {
         Register scratch = asRegister(scratchValue);
-        emitCode(crb, masm, config, false, scratch, state);
+        emitCode(crb, masm, config, false, thread, scratch, state);
     }
 
     /**
@@ -76,7 +78,15 @@ public class AArch64HotSpotSafepointOp extends AArch64LIRInstruction {
         return !NumUtil.isSignedNbit(21, pollingPageAddress - config.codeCacheLowBound) || !NumUtil.isSignedNbit(21, pollingPageAddress - config.codeCacheHighBound);
     }
 
-    public static void emitCode(CompilationResultBuilder crb, AArch64MacroAssembler masm, GraalHotSpotVMConfig config, boolean onReturn, Register scratch, LIRFrameState state) {
+    public static void emitCode(CompilationResultBuilder crb, AArch64MacroAssembler masm, GraalHotSpotVMConfig config, boolean onReturn, Register thread, Register scratch, LIRFrameState state) {
+        if (config.threadLocalHandshakes) {
+            emitThreadLocalPoll(crb, masm, config, onReturn, thread, scratch, state);
+        } else {
+            emitGlobalPoll(crb, masm, config, onReturn, scratch, state);
+        }
+    }
+
+    private static void emitGlobalPoll(CompilationResultBuilder crb, AArch64MacroAssembler masm, GraalHotSpotVMConfig config, boolean onReturn, Register scratch, LIRFrameState state) {
         if (isPollingPageFar(config)) {
             crb.recordMark(onReturn ? config.MARKID_POLL_RETURN_FAR : config.MARKID_POLL_FAR);
             masm.movNativeAddress(scratch, config.safepointPollingAddress);
@@ -94,4 +104,15 @@ public class AArch64HotSpotSafepointOp extends AArch64LIRInstruction {
         }
     }
 
+    private static void emitThreadLocalPoll(CompilationResultBuilder crb, AArch64MacroAssembler masm, GraalHotSpotVMConfig config, boolean onReturn, Register thread, Register scratch,
+                    LIRFrameState state) {
+        assert config.threadPollingPageOffset >= 0;
+        masm.ldr(64, scratch, masm.makeAddress(thread, config.threadPollingPageOffset, 8));
+        crb.recordMark(onReturn ? config.MARKID_POLL_RETURN_FAR : config.MARKID_POLL_FAR);
+        if (state != null) {
+            crb.recordInfopoint(masm.position(), state, InfopointReason.SAFEPOINT);
+        }
+        masm.ldr(32, zr, AArch64Address.createBaseRegisterOnlyAddress(scratch));
+    }
+
 }
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.amd64.test/src/org/graalvm/compiler/hotspot/amd64/test/DataPatchInConstantsTest.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.amd64.test/src/org/graalvm/compiler/hotspot/amd64/test/DataPatchInConstantsTest.java
index 4d1adff918e..6585e2135da 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.amd64.test/src/org/graalvm/compiler/hotspot/amd64/test/DataPatchInConstantsTest.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.amd64.test/src/org/graalvm/compiler/hotspot/amd64/test/DataPatchInConstantsTest.java
@@ -40,6 +40,7 @@ import org.graalvm.compiler.lir.asm.CompilationResultBuilder;
 import org.graalvm.compiler.lir.gen.LIRGeneratorTool;
 import org.graalvm.compiler.nodeinfo.NodeInfo;
 import org.graalvm.compiler.nodes.FixedWithNextNode;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.ValueNode;
 import org.graalvm.compiler.nodes.graphbuilderconf.GraphBuilderContext;
 import org.graalvm.compiler.nodes.graphbuilderconf.InvocationPlugin;
@@ -170,7 +171,7 @@ public class DataPatchInConstantsTest extends HotSpotGraalCompilerTest {
         @Input protected ValueNode input;
 
         protected LoadThroughPatchNode(ValueNode input) {
-            super(TYPE, input.stamp());
+            super(TYPE, input.stamp(NodeView.DEFAULT));
             this.input = input;
         }
 
@@ -179,9 +180,9 @@ public class DataPatchInConstantsTest extends HotSpotGraalCompilerTest {
             assert input.isConstant();
 
             LIRGeneratorTool gen = generator.getLIRGeneratorTool();
-            Variable ret = gen.newVariable(gen.getLIRKind(stamp()));
+            Variable ret = gen.newVariable(gen.getLIRKind(stamp(NodeView.DEFAULT)));
 
-            gen.append(new LoadThroughPatchOp(input.asConstant(), stamp() instanceof NarrowOopStamp, ret));
+            gen.append(new LoadThroughPatchOp(input.asConstant(), stamp(NodeView.DEFAULT) instanceof NarrowOopStamp, ret));
             generator.setResult(this, ret);
         }
     }
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.amd64/src/org/graalvm/compiler/hotspot/amd64/AMD64HotSpotAddressLowering.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.amd64/src/org/graalvm/compiler/hotspot/amd64/AMD64HotSpotAddressLowering.java
index 70bf4dae8c9..e2be3f3bb26 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.amd64/src/org/graalvm/compiler/hotspot/amd64/AMD64HotSpotAddressLowering.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.amd64/src/org/graalvm/compiler/hotspot/amd64/AMD64HotSpotAddressLowering.java
@@ -43,6 +43,7 @@ import org.graalvm.compiler.hotspot.nodes.type.KlassPointerStamp;
 import org.graalvm.compiler.nodeinfo.NodeInfo;
 import org.graalvm.compiler.nodes.CompressionNode;
 import org.graalvm.compiler.nodes.CompressionNode.CompressionOp;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.StructuredGraph;
 import org.graalvm.compiler.nodes.ValueNode;
 import org.graalvm.compiler.nodes.calc.FloatingNode;
@@ -76,7 +77,7 @@ public class AMD64HotSpotAddressLowering extends AMD64AddressLowering {
 
         @Override
         public void generate(NodeLIRBuilderTool generator) {
-            LIRKind kind = generator.getLIRGeneratorTool().getLIRKind(stamp());
+            LIRKind kind = generator.getLIRGeneratorTool().getLIRKind(stamp(NodeView.DEFAULT));
             generator.setResult(this, heapBaseRegister.asValue(kind));
         }
     }
@@ -117,11 +118,6 @@ public class AMD64HotSpotAddressLowering extends AMD64AddressLowering {
         return false;
     }
 
-    @Override
-    protected boolean mightBeOptimized(ValueNode value) {
-        return super.mightBeOptimized(value) || value instanceof CompressionNode;
-    }
-
     private boolean improveUncompression(AMD64AddressNode addr, CompressionNode compression, ValueNode other, boolean isBaseNegated, boolean isIndexNegated) {
         if (isBaseNegated || isIndexNegated || compression.getOp() != CompressionOp.Uncompress) {
             return false;
@@ -134,7 +130,7 @@ public class AMD64HotSpotAddressLowering extends AMD64AddressLowering {
         }
 
         if (heapBaseRegister != null && encoding.getBase() == heapBase) {
-            if ((!generatePIC || compression.stamp() instanceof ObjectStamp) && other == null) {
+            if ((!generatePIC || compression.stamp(NodeView.DEFAULT) instanceof ObjectStamp) && other == null) {
                 // With PIC it is only legal to do for oops since the base value may be
                 // different at runtime.
                 ValueNode base = compression.graph().unique(new HeapBaseNode(heapBaseRegister));
@@ -142,7 +138,7 @@ public class AMD64HotSpotAddressLowering extends AMD64AddressLowering {
             } else {
                 return false;
             }
-        } else if (encoding.getBase() != 0 || (generatePIC && compression.stamp() instanceof KlassPointerStamp)) {
+        } else if (encoding.getBase() != 0 || (generatePIC && compression.stamp(NodeView.DEFAULT) instanceof KlassPointerStamp)) {
             if (generatePIC) {
                 if (other == null) {
                     ValueNode base = compression.graph().unique(new GraalHotSpotVMConfigNode(config, config.MARKID_NARROW_KLASS_BASE_ADDRESS, JavaKind.Long));
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.amd64/src/org/graalvm/compiler/hotspot/amd64/AMD64HotSpotLIRGenerator.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.amd64/src/org/graalvm/compiler/hotspot/amd64/AMD64HotSpotLIRGenerator.java
index 3a26ff997a7..9082a2bc874 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.amd64/src/org/graalvm/compiler/hotspot/amd64/AMD64HotSpotLIRGenerator.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.amd64/src/org/graalvm/compiler/hotspot/amd64/AMD64HotSpotLIRGenerator.java
@@ -264,7 +264,8 @@ public class AMD64HotSpotLIRGenerator extends AMD64LIRGenerator implements HotSp
         if (pollOnReturnScratchRegister == null) {
             pollOnReturnScratchRegister = findPollOnReturnScratchRegister();
         }
-        append(new AMD64HotSpotReturnOp(operand, getStub() != null, pollOnReturnScratchRegister, config));
+        Register thread = getProviders().getRegisters().getThreadRegister();
+        append(new AMD64HotSpotReturnOp(operand, getStub() != null, thread, pollOnReturnScratchRegister, config));
     }
 
     @Override
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.amd64/src/org/graalvm/compiler/hotspot/amd64/AMD64HotSpotNodeLIRBuilder.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.amd64/src/org/graalvm/compiler/hotspot/amd64/AMD64HotSpotNodeLIRBuilder.java
index 93a2c360fa8..7f71cac66ff 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.amd64/src/org/graalvm/compiler/hotspot/amd64/AMD64HotSpotNodeLIRBuilder.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.amd64/src/org/graalvm/compiler/hotspot/amd64/AMD64HotSpotNodeLIRBuilder.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2017, 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
@@ -46,6 +46,7 @@ import org.graalvm.compiler.nodes.CallTargetNode.InvokeKind;
 import org.graalvm.compiler.nodes.DirectCallTargetNode;
 import org.graalvm.compiler.nodes.FullInfopointNode;
 import org.graalvm.compiler.nodes.IndirectCallTargetNode;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.ParameterNode;
 import org.graalvm.compiler.nodes.SafepointNode;
 import org.graalvm.compiler.nodes.StructuredGraph;
@@ -113,7 +114,7 @@ public class AMD64HotSpotNodeLIRBuilder extends AMD64NodeLIRBuilder implements H
 
         for (ParameterNode param : graph.getNodes(ParameterNode.TYPE)) {
             Value paramValue = params[param.index()];
-            assert paramValue.getValueKind().equals(getLIRGeneratorTool().getLIRKind(param.stamp())) : paramValue.getValueKind() + " != " + param.stamp();
+            assert paramValue.getValueKind().equals(getLIRGeneratorTool().getLIRKind(param.stamp(NodeView.DEFAULT))) : paramValue.getValueKind() + " != " + param.stamp(NodeView.DEFAULT);
             setResult(param, gen.emitMove(paramValue));
         }
     }
@@ -121,7 +122,8 @@ public class AMD64HotSpotNodeLIRBuilder extends AMD64NodeLIRBuilder implements H
     @Override
     public void visitSafepointNode(SafepointNode i) {
         LIRFrameState info = state(i);
-        append(new AMD64HotSpotSafepointOp(info, getGen().config, this));
+        Register thread = getGen().getProviders().getRegisters().getThreadRegister();
+        append(new AMD64HotSpotSafepointOp(info, getGen().config, this, thread));
     }
 
     @Override
@@ -186,7 +188,7 @@ public class AMD64HotSpotNodeLIRBuilder extends AMD64NodeLIRBuilder implements H
     public void visitBreakpointNode(BreakpointNode node) {
         JavaType[] sig = new JavaType[node.arguments().size()];
         for (int i = 0; i < sig.length; i++) {
-            sig[i] = node.arguments().get(i).stamp().javaType(gen.getMetaAccess());
+            sig[i] = node.arguments().get(i).stamp(NodeView.DEFAULT).javaType(gen.getMetaAccess());
         }
 
         Value[] parameters = visitInvokeArguments(gen.getRegisterConfig().getCallingConvention(HotSpotCallingConventionType.JavaCall, null, sig, gen), node.arguments());
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.amd64/src/org/graalvm/compiler/hotspot/amd64/AMD64HotSpotReturnOp.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.amd64/src/org/graalvm/compiler/hotspot/amd64/AMD64HotSpotReturnOp.java
index ffcdc620df1..3428e278828 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.amd64/src/org/graalvm/compiler/hotspot/amd64/AMD64HotSpotReturnOp.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.amd64/src/org/graalvm/compiler/hotspot/amd64/AMD64HotSpotReturnOp.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2017, 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
@@ -45,13 +45,15 @@ final class AMD64HotSpotReturnOp extends AMD64HotSpotEpilogueBlockEndOp implemen
     public static final LIRInstructionClass<AMD64HotSpotReturnOp> TYPE = LIRInstructionClass.create(AMD64HotSpotReturnOp.class);
     @Use({REG, ILLEGAL}) protected Value value;
     private final boolean isStub;
+    private final Register thread;
     private final Register scratchForSafepointOnReturn;
     private final GraalHotSpotVMConfig config;
 
-    AMD64HotSpotReturnOp(Value value, boolean isStub, Register scratchForSafepointOnReturn, GraalHotSpotVMConfig config) {
+    AMD64HotSpotReturnOp(Value value, boolean isStub, Register thread, Register scratchForSafepointOnReturn, GraalHotSpotVMConfig config) {
         super(TYPE);
         this.value = value;
         this.isStub = isStub;
+        this.thread = thread;
         this.scratchForSafepointOnReturn = scratchForSafepointOnReturn;
         this.config = config;
     }
@@ -61,7 +63,7 @@ final class AMD64HotSpotReturnOp extends AMD64HotSpotEpilogueBlockEndOp implemen
         leaveFrameAndRestoreRbp(crb, masm);
         if (!isStub) {
             // Every non-stub compile method must have a poll before the return.
-            AMD64HotSpotSafepointOp.emitCode(crb, masm, config, true, null, scratchForSafepointOnReturn);
+            AMD64HotSpotSafepointOp.emitCode(crb, masm, config, true, null, thread, scratchForSafepointOnReturn);
 
             /*
              * We potentially return to the interpreter, and that's an AVX-SSE transition. The only
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.amd64/src/org/graalvm/compiler/hotspot/amd64/AMD64HotSpotSafepointOp.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.amd64/src/org/graalvm/compiler/hotspot/amd64/AMD64HotSpotSafepointOp.java
index 7a5ee813a89..59ae1546014 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.amd64/src/org/graalvm/compiler/hotspot/amd64/AMD64HotSpotSafepointOp.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.amd64/src/org/graalvm/compiler/hotspot/amd64/AMD64HotSpotSafepointOp.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2017, 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
@@ -58,12 +58,14 @@ public final class AMD64HotSpotSafepointOp extends AMD64LIRInstruction {
     @Temp({OperandFlag.REG, OperandFlag.ILLEGAL}) private AllocatableValue temp;
 
     private final GraalHotSpotVMConfig config;
+    private final Register thread;
 
-    public AMD64HotSpotSafepointOp(LIRFrameState state, GraalHotSpotVMConfig config, NodeLIRBuilderTool tool) {
+    public AMD64HotSpotSafepointOp(LIRFrameState state, GraalHotSpotVMConfig config, NodeLIRBuilderTool tool, Register thread) {
         super(TYPE);
         this.state = state;
         this.config = config;
-        if (isPollingPageFar(config) || ImmutableCode.getValue(tool.getOptions())) {
+        this.thread = thread;
+        if (config.threadLocalHandshakes || isPollingPageFar(config) || ImmutableCode.getValue(tool.getOptions())) {
             temp = tool.getLIRGeneratorTool().newVariable(LIRKind.value(tool.getLIRGeneratorTool().target().arch.getWordKind()));
         } else {
             // Don't waste a register if it's unneeded
@@ -73,7 +75,15 @@ public final class AMD64HotSpotSafepointOp extends AMD64LIRInstruction {
 
     @Override
     public void emitCode(CompilationResultBuilder crb, AMD64MacroAssembler asm) {
-        emitCode(crb, asm, config, false, state, temp instanceof RegisterValue ? ((RegisterValue) temp).getRegister() : null);
+        emitCode(crb, asm, config, false, state, thread, temp instanceof RegisterValue ? ((RegisterValue) temp).getRegister() : null);
+    }
+
+    public static void emitCode(CompilationResultBuilder crb, AMD64MacroAssembler asm, GraalHotSpotVMConfig config, boolean atReturn, LIRFrameState state, Register thread, Register scratch) {
+        if (config.threadLocalHandshakes) {
+            emitThreadLocalPoll(crb, asm, config, atReturn, state, thread, scratch);
+        } else {
+            emitGlobalPoll(crb, asm, config, atReturn, state, scratch);
+        }
     }
 
     /**
@@ -85,7 +95,7 @@ public final class AMD64HotSpotSafepointOp extends AMD64LIRInstruction {
         return config.forceUnreachable || !isInt(pollingPageAddress - config.codeCacheLowBound) || !isInt(pollingPageAddress - config.codeCacheHighBound);
     }
 
-    public static void emitCode(CompilationResultBuilder crb, AMD64MacroAssembler asm, GraalHotSpotVMConfig config, boolean atReturn, LIRFrameState state, Register scratch) {
+    private static void emitGlobalPoll(CompilationResultBuilder crb, AMD64MacroAssembler asm, GraalHotSpotVMConfig config, boolean atReturn, LIRFrameState state, Register scratch) {
         assert !atReturn || state == null : "state is unneeded at return";
         if (ImmutableCode.getValue(crb.getOptions())) {
             JavaKind hostWordKind = JavaKind.Long;
@@ -123,4 +133,18 @@ public final class AMD64HotSpotSafepointOp extends AMD64LIRInstruction {
             asm.testl(rax, new AMD64Address(rip, 0));
         }
     }
+
+    private static void emitThreadLocalPoll(CompilationResultBuilder crb, AMD64MacroAssembler asm, GraalHotSpotVMConfig config, boolean atReturn, LIRFrameState state, Register thread,
+                    Register scratch) {
+        assert !atReturn || state == null : "state is unneeded at return";
+
+        assert config.threadPollingPageOffset >= 0;
+        asm.movptr(scratch, new AMD64Address(thread, config.threadPollingPageOffset));
+        crb.recordMark(atReturn ? config.MARKID_POLL_RETURN_FAR : config.MARKID_POLL_FAR);
+        final int pos = asm.position();
+        if (state != null) {
+            crb.recordInfopoint(pos, state, InfopointReason.SAFEPOINT);
+        }
+        asm.testl(rax, new AMD64Address(scratch));
+    }
 }
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.sparc/src/org/graalvm/compiler/hotspot/sparc/SPARCHotSpotLIRGenerator.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.sparc/src/org/graalvm/compiler/hotspot/sparc/SPARCHotSpotLIRGenerator.java
index 883a91dd2fe..793935718a5 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.sparc/src/org/graalvm/compiler/hotspot/sparc/SPARCHotSpotLIRGenerator.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.sparc/src/org/graalvm/compiler/hotspot/sparc/SPARCHotSpotLIRGenerator.java
@@ -190,7 +190,8 @@ public class SPARCHotSpotLIRGenerator extends SPARCLIRGenerator implements HotSp
             operand = resultOperandFor(javaKind, input.getValueKind());
             emitMove(operand, input);
         }
-        append(new SPARCHotSpotReturnOp(operand, getStub() != null, config, getSafepointAddressValue()));
+        Register thread = getProviders().getRegisters().getThreadRegister();
+        append(new SPARCHotSpotReturnOp(operand, getStub() != null, config, thread, getSafepointAddressValue()));
     }
 
     @Override
@@ -383,7 +384,7 @@ public class SPARCHotSpotLIRGenerator extends SPARCLIRGenerator implements HotSp
 
     public AllocatableValue getSafepointAddressValue() {
         if (this.safepointAddressValue == null) {
-            this.safepointAddressValue = newVariable(LIRKind.value(target().arch.getWordKind()));
+            this.safepointAddressValue = SPARCHotSpotSafepointOp.getSafepointAddressValue(this);
         }
         return this.safepointAddressValue;
     }
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.sparc/src/org/graalvm/compiler/hotspot/sparc/SPARCHotSpotNodeLIRBuilder.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.sparc/src/org/graalvm/compiler/hotspot/sparc/SPARCHotSpotNodeLIRBuilder.java
index 09dc9cf5f42..afe79ed1132 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.sparc/src/org/graalvm/compiler/hotspot/sparc/SPARCHotSpotNodeLIRBuilder.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.sparc/src/org/graalvm/compiler/hotspot/sparc/SPARCHotSpotNodeLIRBuilder.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2017, 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
@@ -46,6 +46,7 @@ import org.graalvm.compiler.nodes.CallTargetNode.InvokeKind;
 import org.graalvm.compiler.nodes.DirectCallTargetNode;
 import org.graalvm.compiler.nodes.FullInfopointNode;
 import org.graalvm.compiler.nodes.IndirectCallTargetNode;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.SafepointNode;
 import org.graalvm.compiler.nodes.StructuredGraph;
 import org.graalvm.compiler.nodes.ValueNode;
@@ -84,7 +85,8 @@ public class SPARCHotSpotNodeLIRBuilder extends SPARCNodeLIRBuilder implements H
     @Override
     public void visitSafepointNode(SafepointNode i) {
         LIRFrameState info = state(i);
-        append(new SPARCHotSpotSafepointOp(info, getGen().config, gen));
+        Register thread = getGen().getProviders().getRegisters().getThreadRegister();
+        append(new SPARCHotSpotSafepointOp(info, getGen().config, thread, gen));
     }
 
     @Override
@@ -141,9 +143,7 @@ public class SPARCHotSpotNodeLIRBuilder extends SPARCNodeLIRBuilder implements H
     @Override
     protected void emitPrologue(StructuredGraph graph) {
         super.emitPrologue(graph);
-        AllocatableValue var = getGen().getSafepointAddressValue();
-        append(new SPARCHotSpotSafepointOp.SPARCLoadSafepointPollAddress(var, getGen().config));
-        getGen().append(((HotSpotDebugInfoBuilder) getDebugInfoBuilder()).lockStack());
+        SPARCHotSpotSafepointOp.emitPrologue(this, getGen());
     }
 
     @Override
@@ -159,7 +159,7 @@ public class SPARCHotSpotNodeLIRBuilder extends SPARCNodeLIRBuilder implements H
     public void visitBreakpointNode(BreakpointNode node) {
         JavaType[] sig = new JavaType[node.arguments().size()];
         for (int i = 0; i < sig.length; i++) {
-            sig[i] = node.arguments().get(i).stamp().javaType(gen.getMetaAccess());
+            sig[i] = node.arguments().get(i).stamp(NodeView.DEFAULT).javaType(gen.getMetaAccess());
         }
 
         Value[] parameters = visitInvokeArguments(gen.getRegisterConfig().getCallingConvention(HotSpotCallingConventionType.JavaCall, null, sig, gen), node.arguments());
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.sparc/src/org/graalvm/compiler/hotspot/sparc/SPARCHotSpotReturnOp.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.sparc/src/org/graalvm/compiler/hotspot/sparc/SPARCHotSpotReturnOp.java
index 134da9043f8..f27d72fd136 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.sparc/src/org/graalvm/compiler/hotspot/sparc/SPARCHotSpotReturnOp.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.sparc/src/org/graalvm/compiler/hotspot/sparc/SPARCHotSpotReturnOp.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2017, 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
@@ -24,7 +24,6 @@ package org.graalvm.compiler.hotspot.sparc;
 
 import static org.graalvm.compiler.lir.LIRInstruction.OperandFlag.ILLEGAL;
 import static org.graalvm.compiler.lir.LIRInstruction.OperandFlag.REG;
-import static jdk.vm.ci.code.ValueUtil.asRegister;
 
 import org.graalvm.compiler.asm.sparc.SPARCMacroAssembler;
 import org.graalvm.compiler.hotspot.GraalHotSpotVMConfig;
@@ -33,6 +32,7 @@ import org.graalvm.compiler.lir.Opcode;
 import org.graalvm.compiler.lir.asm.CompilationResultBuilder;
 import org.graalvm.compiler.lir.sparc.SPARCControlFlow.ReturnOp;
 
+import jdk.vm.ci.code.Register;
 import jdk.vm.ci.meta.Value;
 
 /**
@@ -44,15 +44,17 @@ final class SPARCHotSpotReturnOp extends SPARCHotSpotEpilogueOp {
     public static final SizeEstimate SIZE = SizeEstimate.create(2);
 
     @Use({REG, ILLEGAL}) protected Value value;
-    @Use({REG}) protected Value safepointPollAddress;
+    @Use({REG, ILLEGAL}) protected Value safepointPollAddress;
     private final boolean isStub;
     private final GraalHotSpotVMConfig config;
+    private final Register thread;
 
-    SPARCHotSpotReturnOp(Value value, boolean isStub, GraalHotSpotVMConfig config, Value safepointPoll) {
+    SPARCHotSpotReturnOp(Value value, boolean isStub, GraalHotSpotVMConfig config, Register thread, Value safepointPoll) {
         super(TYPE, SIZE);
         this.value = value;
         this.isStub = isStub;
         this.config = config;
+        this.thread = thread;
         this.safepointPollAddress = safepointPoll;
     }
 
@@ -60,7 +62,7 @@ final class SPARCHotSpotReturnOp extends SPARCHotSpotEpilogueOp {
     public void emitCode(CompilationResultBuilder crb, SPARCMacroAssembler masm) {
         if (!isStub) {
             // Every non-stub compile method must have a poll before the return.
-            SPARCHotSpotSafepointOp.emitCode(crb, masm, config, true, null, asRegister(safepointPollAddress));
+            SPARCHotSpotSafepointOp.emitCode(crb, masm, config, true, null, thread, safepointPollAddress);
         }
         ReturnOp.emitCodeHelper(crb, masm);
     }
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.sparc/src/org/graalvm/compiler/hotspot/sparc/SPARCHotSpotSafepointOp.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.sparc/src/org/graalvm/compiler/hotspot/sparc/SPARCHotSpotSafepointOp.java
index 4b0bf193314..b50dabf883e 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.sparc/src/org/graalvm/compiler/hotspot/sparc/SPARCHotSpotSafepointOp.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.sparc/src/org/graalvm/compiler/hotspot/sparc/SPARCHotSpotSafepointOp.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2017, 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
@@ -27,7 +27,10 @@ import static jdk.vm.ci.sparc.SPARC.g0;
 
 import org.graalvm.compiler.asm.sparc.SPARCAddress;
 import org.graalvm.compiler.asm.sparc.SPARCMacroAssembler;
+import org.graalvm.compiler.asm.sparc.SPARCMacroAssembler.ScratchRegister;
+import org.graalvm.compiler.core.common.LIRKind;
 import org.graalvm.compiler.hotspot.GraalHotSpotVMConfig;
+import org.graalvm.compiler.hotspot.HotSpotDebugInfoBuilder;
 import org.graalvm.compiler.lir.LIRFrameState;
 import org.graalvm.compiler.lir.LIRInstructionClass;
 import org.graalvm.compiler.lir.Opcode;
@@ -39,6 +42,7 @@ import jdk.vm.ci.code.Register;
 import jdk.vm.ci.code.ValueUtil;
 import jdk.vm.ci.code.site.InfopointReason;
 import jdk.vm.ci.meta.AllocatableValue;
+import jdk.vm.ci.meta.Value;
 
 /**
  * Emits a safepoint poll.
@@ -49,23 +53,37 @@ public class SPARCHotSpotSafepointOp extends SPARCLIRInstruction {
     public static final SizeEstimate SIZE = SizeEstimate.create(9);
 
     @State protected LIRFrameState state;
-    @Use({OperandFlag.REG}) AllocatableValue safepointPollAddress;
+    @Use({OperandFlag.REG, OperandFlag.ILLEGAL}) AllocatableValue safepointPollAddress;
     private final GraalHotSpotVMConfig config;
+    private final Register thread;
 
-    public SPARCHotSpotSafepointOp(LIRFrameState state, GraalHotSpotVMConfig config, LIRGeneratorTool tool) {
+    public SPARCHotSpotSafepointOp(LIRFrameState state, GraalHotSpotVMConfig config, Register thread, LIRGeneratorTool tool) {
         super(TYPE, SIZE);
         this.state = state;
         this.config = config;
+        this.thread = thread;
         SPARCHotSpotLIRGenerator lirGen = (SPARCHotSpotLIRGenerator) tool;
         safepointPollAddress = lirGen.getSafepointAddressValue();
     }
 
     @Override
     public void emitCode(CompilationResultBuilder crb, SPARCMacroAssembler masm) {
-        emitCode(crb, masm, config, false, state, asRegister(safepointPollAddress));
+        emitCode(crb, masm, config, false, state, thread, safepointPollAddress);
     }
 
-    public static void emitCode(CompilationResultBuilder crb, SPARCMacroAssembler masm, GraalHotSpotVMConfig config, boolean atReturn, LIRFrameState state, Register safepointPollAddress) {
+    public static void emitCode(CompilationResultBuilder crb, SPARCMacroAssembler masm, GraalHotSpotVMConfig config, boolean atReturn, LIRFrameState state, Register thread,
+                    Value safepointPollAddress) {
+        if (config.threadLocalHandshakes) {
+            emitThreadLocalPoll(crb, masm, config, atReturn, state, thread);
+        } else {
+            emitGlobalPoll(crb, masm, config, atReturn, state, asRegister(safepointPollAddress));
+        }
+    }
+
+    /**
+     * Emit a global safepoint poll.
+     */
+    private static void emitGlobalPoll(CompilationResultBuilder crb, SPARCMacroAssembler masm, GraalHotSpotVMConfig config, boolean atReturn, LIRFrameState state, Register safepointPollAddress) {
         crb.recordMark(atReturn ? config.MARKID_POLL_RETURN_FAR : config.MARKID_POLL_FAR);
         if (state != null) {
             final int pos = masm.position();
@@ -74,6 +92,44 @@ public class SPARCHotSpotSafepointOp extends SPARCLIRInstruction {
         masm.ldx(new SPARCAddress(safepointPollAddress, 0), g0);
     }
 
+    /**
+     * Emit a thread-local safepoint poll.
+     */
+    private static void emitThreadLocalPoll(CompilationResultBuilder crb, SPARCMacroAssembler masm, GraalHotSpotVMConfig config, boolean atReturn, LIRFrameState state, Register thread) {
+        assert !atReturn || state == null : "state is unneeded at return";
+
+        assert config.threadPollingPageOffset >= 0;
+
+        try (ScratchRegister scratchReg = masm.getScratchRegister()) {
+            Register scratch = scratchReg.getRegister();
+
+            masm.ldx(new SPARCAddress(thread, config.threadPollingPageOffset), scratch);
+
+            crb.recordMark(atReturn ? config.MARKID_POLL_RETURN_FAR : config.MARKID_POLL_FAR);
+            if (state != null) {
+                final int pos = masm.position();
+                crb.recordInfopoint(pos, state, InfopointReason.SAFEPOINT);
+            }
+            masm.ldx(new SPARCAddress(scratch, 0), g0);
+        }
+    }
+
+    static AllocatableValue getSafepointAddressValue(SPARCHotSpotLIRGenerator gen) {
+        if (gen.config.threadLocalHandshakes) {
+            return Value.ILLEGAL;
+        } else {
+            return gen.newVariable(LIRKind.value(gen.target().arch.getWordKind()));
+        }
+    }
+
+    static void emitPrologue(SPARCHotSpotNodeLIRBuilder lir, SPARCHotSpotLIRGenerator gen) {
+        if (!gen.config.threadLocalHandshakes) {
+            AllocatableValue var = gen.getSafepointAddressValue();
+            lir.append(new SPARCHotSpotSafepointOp.SPARCLoadSafepointPollAddress(var, gen.config));
+            gen.append(((HotSpotDebugInfoBuilder) lir.getDebugInfoBuilder()).lockStack());
+        }
+    }
+
     public static class SPARCLoadSafepointPollAddress extends SPARCLIRInstruction {
         public static final LIRInstructionClass<SPARCLoadSafepointPollAddress> TYPE = LIRInstructionClass.create(SPARCLoadSafepointPollAddress.class);
         public static final SizeEstimate SIZE = SizeEstimate.create(2);
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/AheadOfTimeCompilationTest.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/AheadOfTimeCompilationTest.java
index a08edd079c0..bd0c8693723 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/AheadOfTimeCompilationTest.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/AheadOfTimeCompilationTest.java
@@ -22,14 +22,15 @@
  */
 package org.graalvm.compiler.hotspot.test;
 
-import static org.graalvm.compiler.core.common.GraalOptions.ImmutableCode;
-import static org.graalvm.compiler.nodes.ConstantNode.getConstantNodes;
-
+import jdk.vm.ci.aarch64.AArch64;
+import jdk.vm.ci.hotspot.HotSpotResolvedObjectType;
+import jdk.vm.ci.meta.JavaConstant;
+import jdk.vm.ci.meta.JavaKind;
 import org.graalvm.compiler.core.common.type.Stamp;
-import org.graalvm.compiler.core.test.GraalCompilerTest;
 import org.graalvm.compiler.graph.iterators.NodeIterable;
 import org.graalvm.compiler.hotspot.nodes.type.KlassPointerStamp;
 import org.graalvm.compiler.nodes.ConstantNode;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.PiNode;
 import org.graalvm.compiler.nodes.StructuredGraph;
 import org.graalvm.compiler.nodes.StructuredGraph.AllowAssumptions;
@@ -41,10 +42,8 @@ import org.junit.Assume;
 import org.junit.Before;
 import org.junit.Test;
 
-import jdk.vm.ci.aarch64.AArch64;
-import jdk.vm.ci.hotspot.HotSpotResolvedObjectType;
-import jdk.vm.ci.meta.JavaConstant;
-import jdk.vm.ci.meta.JavaKind;
+import static org.graalvm.compiler.core.common.GraalOptions.ImmutableCode;
+import static org.graalvm.compiler.nodes.ConstantNode.getConstantNodes;
 
 /**
  * use
@@ -55,7 +54,7 @@ import jdk.vm.ci.meta.JavaKind;
  *
  * to print disassembly.
  */
-public class AheadOfTimeCompilationTest extends GraalCompilerTest {
+public class AheadOfTimeCompilationTest extends HotSpotGraalCompilerTest {
 
     public static final Object STATICFINALOBJECT = new Object();
     public static final String STATICFINALSTRING = "test string";
@@ -74,9 +73,10 @@ public class AheadOfTimeCompilationTest extends GraalCompilerTest {
     public void testStaticFinalObjectAOT() {
         StructuredGraph result = compile("getStaticFinalObject", true);
         assertDeepEquals(1, getConstantNodes(result).count());
-        Stamp constantStamp = getConstantNodes(result).first().stamp();
+        Stamp constantStamp = getConstantNodes(result).first().stamp(NodeView.DEFAULT);
         Assert.assertTrue(constantStamp.toString(), constantStamp instanceof KlassPointerStamp);
-        assertDeepEquals(2, result.getNodes().filter(ReadNode.class).count());
+        int expected = runtime().getVMConfig().classMirrorIsHandle ? 3 : 2;
+        assertDeepEquals(expected, result.getNodes().filter(ReadNode.class).count());
     }
 
     @Test
@@ -99,8 +99,8 @@ public class AheadOfTimeCompilationTest extends GraalCompilerTest {
         assertDeepEquals(1, filter.count());
         HotSpotResolvedObjectType type = (HotSpotResolvedObjectType) getMetaAccess().lookupJavaType(AheadOfTimeCompilationTest.class);
         assertDeepEquals(type.klass(), filter.first().asConstant());
-
-        assertDeepEquals(1, result.getNodes().filter(ReadNode.class).count());
+        int expected = runtime().getVMConfig().classMirrorIsHandle ? 2 : 1;
+        assertDeepEquals(expected, result.getNodes().filter(ReadNode.class).count());
     }
 
     @Test
@@ -124,10 +124,10 @@ public class AheadOfTimeCompilationTest extends GraalCompilerTest {
         StructuredGraph result = compile("getPrimitiveClassObject", true);
         NodeIterable<ConstantNode> filter = getConstantNodes(result);
         assertDeepEquals(1, filter.count());
-        Stamp constantStamp = filter.first().stamp();
+        Stamp constantStamp = filter.first().stamp(NodeView.DEFAULT);
         Assert.assertTrue(constantStamp instanceof KlassPointerStamp);
-
-        assertDeepEquals(2, result.getNodes().filter(ReadNode.class).count());
+        int expected = runtime().getVMConfig().classMirrorIsHandle ? 3 : 2;
+        assertDeepEquals(expected, result.getNodes().filter(ReadNode.class).count());
     }
 
     @Test
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/CheckGraalIntrinsics.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/CheckGraalIntrinsics.java
index 8363e827907..8802c18dc3f 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/CheckGraalIntrinsics.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/CheckGraalIntrinsics.java
@@ -143,50 +143,49 @@ public class CheckGraalIntrinsics extends GraalTest {
     }
 
     static {
+        // These are dead
         add(IGNORE,
-                        // dead
                         "java/lang/Math.atan2(DD)D",
-                        // Used during stack walking
-                        "java/lang/Throwable.fillInStackTrace()Ljava/lang/Throwable;",
-                        // Marker intrinsic id
-                        "java/lang/invoke/MethodHandle.<compiledLambdaForm>*",
-                        // Marker intrinsic id
-                        "java/lang/invoke/MethodHandle.invoke*",
-                        // Implemented through lowering
-                        "java/lang/ref/Reference.get()Ljava/lang/Object;",
-                        // Used during stack walk
-                        "java/lang/reflect/Method.invoke(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;",
-                        // Only used by C1
-                        "java/nio/Buffer.checkIndex(I)I",
-                        // dead
+                        "jdk/internal/misc/Unsafe.park(ZJ)V",
+                        "jdk/internal/misc/Unsafe.unpark(Ljava/lang/Object;)V",
                         "sun/misc/Unsafe.park(ZJ)V",
-                        // dead
                         "sun/misc/Unsafe.prefetchRead(Ljava/lang/Object;J)V",
-                        // dead
                         "sun/misc/Unsafe.prefetchReadStatic(Ljava/lang/Object;J)V",
-                        // dead
                         "sun/misc/Unsafe.prefetchWrite(Ljava/lang/Object;J)V",
-                        // dead
                         "sun/misc/Unsafe.prefetchWriteStatic(Ljava/lang/Object;J)V",
-                        // dead
                         "sun/misc/Unsafe.unpark(Ljava/lang/Object;)V");
 
-        add(TO_BE_INVESTIGATED,
-                        // JDK 8
-                        "java/lang/Double.doubleToLongBits(D)J",
-                        "java/lang/Float.floatToIntBits(F)I",
-                        "java/lang/Integer.toString(I)Ljava/lang/String;",
-                        "java/lang/Math.decrementExact(I)I",
-                        "java/lang/Math.decrementExact(J)J",
-                        "java/lang/Math.incrementExact(I)I",
-                        "java/lang/Math.incrementExact(J)J",
+        // These only exist to assist escape analysis in C2
+        add(IGNORE,
+                        "java/lang/Throwable.fillInStackTrace()Ljava/lang/Throwable;");
+
+        // These are only used for the security handling during stack walking
+        add(IGNORE,
+                        "java/lang/reflect/Method.invoke(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;");
+
+        // These are marker intrinsic ids only
+        add(IGNORE,
+                        "java/lang/invoke/MethodHandle.<compiledLambdaForm>*",
+                        "java/lang/invoke/MethodHandle.invoke*");
+
+        // These are implemented through lowering
+        add(IGNORE,
+                        "java/lang/ref/Reference.get()Ljava/lang/Object;");
+
+        // These are only used by C1
+        add(IGNORE,
+                        "java/nio/Buffer.checkIndex(I)I");
+
+        // These do general compiler optimizations and convert min/max to cmov instructions. We are
+        // ignoring them as cmovs are not necessarily beneficial.
+        add(IGNORE,
                         "java/lang/Math.max(II)I",
-                        "java/lang/Math.min(II)I",
-                        "java/lang/Math.negateExact(I)I",
-                        "java/lang/Math.negateExact(J)J",
+                        "java/lang/Math.min(II)I");
+
+        // These are known to be implemented down stream
+        add(IGNORE,
+                        "java/lang/Integer.toString(I)Ljava/lang/String;",
                         "java/lang/String.<init>(Ljava/lang/String;)V",
-                        "java/lang/String.compareTo(Ljava/lang/String;)I",
-                        "java/lang/String.indexOf(Ljava/lang/String;)I",
                         "java/lang/StringBuffer.<init>()V",
                         "java/lang/StringBuffer.<init>(I)V",
                         "java/lang/StringBuffer.<init>(Ljava/lang/String;)V",
@@ -201,172 +200,11 @@ public class CheckGraalIntrinsics extends GraalTest {
                         "java/lang/StringBuilder.append(I)Ljava/lang/StringBuilder;",
                         "java/lang/StringBuilder.append(Ljava/lang/String;)Ljava/lang/StringBuilder;",
                         "java/lang/StringBuilder.toString()Ljava/lang/String;",
-                        "java/lang/reflect/Array.newArray(Ljava/lang/Class;I)Ljava/lang/Object;",
                         "java/util/Arrays.copyOf([Ljava/lang/Object;ILjava/lang/Class;)[Ljava/lang/Object;",
-                        "java/util/Arrays.copyOfRange([Ljava/lang/Object;IILjava/lang/Class;)[Ljava/lang/Object;",
-                        "oracle/jrockit/jfr/Timing.counterTime()J",
-                        "oracle/jrockit/jfr/VMJFR.classID0(Ljava/lang/Class;)J",
-                        "oracle/jrockit/jfr/VMJFR.threadID()I",
-                        "sun/nio/cs/ISO_8859_1$Encoder.encodeISOArray([CI[BII)I",
-                        "sun/security/provider/DigestBase.implCompressMultiBlock([BII)I",
-                        "sun/security/provider/SHA.implCompress([BI)V",
-                        "sun/security/provider/SHA2.implCompress([BI)V",
-                        "sun/security/provider/SHA5.implCompress([BI)V");
+                        "java/util/Arrays.copyOfRange([Ljava/lang/Object;IILjava/lang/Class;)[Ljava/lang/Object;");
 
-        add(TO_BE_INVESTIGATED,
-                        // JDK 9
-                        "com/sun/crypto/provider/CounterMode.implCrypt([BII[BI)I",
-                        "com/sun/crypto/provider/GHASH.processBlocks([BII[J[J)V",
-                        "java/lang/Math.fma(DDD)D",
-                        "java/lang/Math.fma(FFF)F",
-                        "java/lang/Object.notify()V",
-                        "java/lang/Object.notifyAll()V",
-                        "java/lang/StringCoding.hasNegatives([BII)Z",
-                        "java/lang/StringCoding.implEncodeISOArray([BI[BII)I",
-                        "java/lang/StringLatin1.compareTo([B[B)I",
-                        "java/lang/StringLatin1.compareToUTF16([B[B)I",
-                        "java/lang/StringLatin1.equals([B[B)Z",
-                        "java/lang/StringLatin1.indexOf([BI[BII)I",
-                        "java/lang/StringLatin1.indexOf([B[B)I",
-                        "java/lang/StringLatin1.inflate([BI[BII)V",
-                        "java/lang/StringLatin1.inflate([BI[CII)V",
-                        "java/lang/StringUTF16.compareTo([B[B)I",
-                        "java/lang/StringUTF16.compareToLatin1([B[B)I",
-                        "java/lang/StringUTF16.compress([BI[BII)I",
-                        "java/lang/StringUTF16.compress([CI[BII)I",
-                        "java/lang/StringUTF16.equals([B[B)Z",
-                        "java/lang/StringUTF16.getChar([BI)C",
-                        "java/lang/StringUTF16.getChars([BII[CI)V",
-                        "java/lang/StringUTF16.indexOf([BI[BII)I",
-                        "java/lang/StringUTF16.indexOf([B[B)I",
-                        "java/lang/StringUTF16.indexOfChar([BIII)I",
-                        "java/lang/StringUTF16.indexOfLatin1([BI[BII)I",
-                        "java/lang/StringUTF16.indexOfLatin1([B[B)I",
-                        "java/lang/StringUTF16.putChar([BII)V",
-                        "java/lang/StringUTF16.toBytes([CII)[B",
-                        "java/lang/Thread.onSpinWait()V",
-                        "java/lang/invoke/MethodHandleImpl.isCompileConstant(Ljava/lang/Object;)Z",
-                        "java/math/BigInteger.implMontgomeryMultiply([I[I[IIJ[I)[I",
-                        "java/math/BigInteger.implMontgomerySquare([I[IIJ[I)[I",
-                        "java/math/BigInteger.implMulAdd([I[IIII)I",
-                        "java/math/BigInteger.implSquareToLen([II[II)[I",
-                        "java/util/ArraysSupport.vectorizedMismatch(Ljava/lang/Object;JLjava/lang/Object;JII)I",
-                        "java/util/stream/Streams$RangeIntSpliterator.forEachRemaining(Ljava/util/function/IntConsumer;)V",
-                        "java/util/zip/Adler32.updateByteBuffer(IJII)I",
-                        "java/util/zip/Adler32.updateBytes(I[BII)I",
-                        "jdk/internal/misc/Unsafe.allocateUninitializedArray0(Ljava/lang/Class;I)Ljava/lang/Object;",
-                        "jdk/internal/misc/Unsafe.compareAndExchangeByte(Ljava/lang/Object;JBB)B",
-                        "jdk/internal/misc/Unsafe.compareAndExchangeByteAcquire(Ljava/lang/Object;JBB)B",
-                        "jdk/internal/misc/Unsafe.compareAndExchangeByteRelease(Ljava/lang/Object;JBB)B",
-                        "jdk/internal/misc/Unsafe.compareAndExchangeInt(Ljava/lang/Object;JII)I",
-                        "jdk/internal/misc/Unsafe.compareAndExchangeIntAcquire(Ljava/lang/Object;JII)I",
-                        "jdk/internal/misc/Unsafe.compareAndExchangeIntRelease(Ljava/lang/Object;JII)I",
-                        "jdk/internal/misc/Unsafe.compareAndExchangeLong(Ljava/lang/Object;JJJ)J",
-                        "jdk/internal/misc/Unsafe.compareAndExchangeLongAcquire(Ljava/lang/Object;JJJ)J",
-                        "jdk/internal/misc/Unsafe.compareAndExchangeLongRelease(Ljava/lang/Object;JJJ)J",
-                        "jdk/internal/misc/Unsafe.compareAndExchangeObject(Ljava/lang/Object;JLjava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;",
-                        "jdk/internal/misc/Unsafe.compareAndExchangeObjectAcquire(Ljava/lang/Object;JLjava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;",
-                        "jdk/internal/misc/Unsafe.compareAndExchangeObjectRelease(Ljava/lang/Object;JLjava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;",
-                        "jdk/internal/misc/Unsafe.compareAndExchangeShort(Ljava/lang/Object;JSS)S",
-                        "jdk/internal/misc/Unsafe.compareAndExchangeShortAcquire(Ljava/lang/Object;JSS)S",
-                        "jdk/internal/misc/Unsafe.compareAndExchangeShortRelease(Ljava/lang/Object;JSS)S",
-                        "jdk/internal/misc/Unsafe.compareAndSetByte(Ljava/lang/Object;JBB)Z",
-                        "jdk/internal/misc/Unsafe.compareAndSetShort(Ljava/lang/Object;JSS)Z",
-                        "jdk/internal/misc/Unsafe.getAndAddByte(Ljava/lang/Object;JB)B",
-                        "jdk/internal/misc/Unsafe.getAndAddShort(Ljava/lang/Object;JS)S",
-                        "jdk/internal/misc/Unsafe.getAndSetByte(Ljava/lang/Object;JB)B",
-                        "jdk/internal/misc/Unsafe.getAndSetShort(Ljava/lang/Object;JS)S",
-                        "jdk/internal/misc/Unsafe.getBooleanAcquire(Ljava/lang/Object;J)Z",
-                        "jdk/internal/misc/Unsafe.getBooleanOpaque(Ljava/lang/Object;J)Z",
-                        "jdk/internal/misc/Unsafe.getByteAcquire(Ljava/lang/Object;J)B",
-                        "jdk/internal/misc/Unsafe.getByteOpaque(Ljava/lang/Object;J)B",
-                        "jdk/internal/misc/Unsafe.getCharAcquire(Ljava/lang/Object;J)C",
-                        "jdk/internal/misc/Unsafe.getCharOpaque(Ljava/lang/Object;J)C",
-                        "jdk/internal/misc/Unsafe.getDoubleAcquire(Ljava/lang/Object;J)D",
-                        "jdk/internal/misc/Unsafe.getDoubleOpaque(Ljava/lang/Object;J)D",
-                        "jdk/internal/misc/Unsafe.getFloatAcquire(Ljava/lang/Object;J)F",
-                        "jdk/internal/misc/Unsafe.getFloatOpaque(Ljava/lang/Object;J)F",
-                        "jdk/internal/misc/Unsafe.getIntAcquire(Ljava/lang/Object;J)I",
-                        "jdk/internal/misc/Unsafe.getIntOpaque(Ljava/lang/Object;J)I",
-                        "jdk/internal/misc/Unsafe.getLongAcquire(Ljava/lang/Object;J)J",
-                        "jdk/internal/misc/Unsafe.getLongOpaque(Ljava/lang/Object;J)J",
-                        "jdk/internal/misc/Unsafe.getObjectAcquire(Ljava/lang/Object;J)Ljava/lang/Object;",
-                        "jdk/internal/misc/Unsafe.getObjectOpaque(Ljava/lang/Object;J)Ljava/lang/Object;",
-                        "jdk/internal/misc/Unsafe.getShortAcquire(Ljava/lang/Object;J)S",
-                        "jdk/internal/misc/Unsafe.getShortOpaque(Ljava/lang/Object;J)S",
-                        "jdk/internal/misc/Unsafe.park(ZJ)V",
-                        "jdk/internal/misc/Unsafe.putBooleanOpaque(Ljava/lang/Object;JZ)V",
-                        "jdk/internal/misc/Unsafe.putByteOpaque(Ljava/lang/Object;JB)V",
-                        "jdk/internal/misc/Unsafe.putCharOpaque(Ljava/lang/Object;JC)V",
-                        "jdk/internal/misc/Unsafe.putDoubleOpaque(Ljava/lang/Object;JD)V",
-                        "jdk/internal/misc/Unsafe.putFloatOpaque(Ljava/lang/Object;JF)V",
-                        "jdk/internal/misc/Unsafe.putIntOpaque(Ljava/lang/Object;JI)V",
-                        "jdk/internal/misc/Unsafe.putLongOpaque(Ljava/lang/Object;JJ)V",
-                        "jdk/internal/misc/Unsafe.putObjectOpaque(Ljava/lang/Object;JLjava/lang/Object;)V",
-                        "jdk/internal/misc/Unsafe.putShortOpaque(Ljava/lang/Object;JS)V",
-                        "jdk/internal/misc/Unsafe.unpark(Ljava/lang/Object;)V",
-                        "jdk/internal/misc/Unsafe.weakCompareAndSetByte(Ljava/lang/Object;JBB)Z",
-                        "jdk/internal/misc/Unsafe.weakCompareAndSetByteAcquire(Ljava/lang/Object;JBB)Z",
-                        "jdk/internal/misc/Unsafe.weakCompareAndSetBytePlain(Ljava/lang/Object;JBB)Z",
-                        "jdk/internal/misc/Unsafe.weakCompareAndSetByteRelease(Ljava/lang/Object;JBB)Z",
-                        "jdk/internal/misc/Unsafe.weakCompareAndSetInt(Ljava/lang/Object;JII)Z",
-                        "jdk/internal/misc/Unsafe.weakCompareAndSetIntAcquire(Ljava/lang/Object;JII)Z",
-                        "jdk/internal/misc/Unsafe.weakCompareAndSetIntPlain(Ljava/lang/Object;JII)Z",
-                        "jdk/internal/misc/Unsafe.weakCompareAndSetIntRelease(Ljava/lang/Object;JII)Z",
-                        "jdk/internal/misc/Unsafe.weakCompareAndSetLong(Ljava/lang/Object;JJJ)Z",
-                        "jdk/internal/misc/Unsafe.weakCompareAndSetLongAcquire(Ljava/lang/Object;JJJ)Z",
-                        "jdk/internal/misc/Unsafe.weakCompareAndSetLongPlain(Ljava/lang/Object;JJJ)Z",
-                        "jdk/internal/misc/Unsafe.weakCompareAndSetLongRelease(Ljava/lang/Object;JJJ)Z",
-                        "jdk/internal/misc/Unsafe.weakCompareAndSetObject(Ljava/lang/Object;JLjava/lang/Object;Ljava/lang/Object;)Z",
-                        "jdk/internal/misc/Unsafe.weakCompareAndSetObjectAcquire(Ljava/lang/Object;JLjava/lang/Object;Ljava/lang/Object;)Z",
-                        "jdk/internal/misc/Unsafe.weakCompareAndSetObjectPlain(Ljava/lang/Object;JLjava/lang/Object;Ljava/lang/Object;)Z",
-                        "jdk/internal/misc/Unsafe.weakCompareAndSetObjectRelease(Ljava/lang/Object;JLjava/lang/Object;Ljava/lang/Object;)Z",
-                        "jdk/internal/misc/Unsafe.weakCompareAndSetShort(Ljava/lang/Object;JSS)Z",
-                        "jdk/internal/misc/Unsafe.weakCompareAndSetShortAcquire(Ljava/lang/Object;JSS)Z",
-                        "jdk/internal/misc/Unsafe.weakCompareAndSetShortPlain(Ljava/lang/Object;JSS)Z",
-                        "jdk/internal/misc/Unsafe.weakCompareAndSetShortRelease(Ljava/lang/Object;JSS)Z",
-                        "jdk/internal/util/Preconditions.checkIndex(IILjava/util/function/BiFunction;)I",
-                        "jdk/jfr/internal/JVM.counterTime()J",
-                        "jdk/jfr/internal/JVM.getBufferWriter()Ljava/lang/Object;",
-                        "jdk/jfr/internal/JVM.getClassId(Ljava/lang/Class;)J",
-                        "sun/nio/cs/ISO_8859_1$Encoder.implEncodeISOArray([CI[BII)I",
-                        "sun/security/provider/DigestBase.implCompressMultiBlock0([BII)I",
-                        "sun/security/provider/SHA.implCompress0([BI)V",
-                        "sun/security/provider/SHA2.implCompress0([BI)V",
-                        "sun/security/provider/SHA5.implCompress0([BI)V");
-
-        if (!getHostArchitectureName().equals("amd64")) {
-            add(TO_BE_INVESTIGATED,
-                            // Can we implement these on non-AMD64 platforms? C2 seems to.
-                            "sun/misc/Unsafe.getAndAddInt(Ljava/lang/Object;JI)I",
-                            "sun/misc/Unsafe.getAndAddLong(Ljava/lang/Object;JJ)J",
-                            "sun/misc/Unsafe.getAndSetInt(Ljava/lang/Object;JI)I",
-                            "sun/misc/Unsafe.getAndSetLong(Ljava/lang/Object;JJ)J",
-                            "sun/misc/Unsafe.getAndSetObject(Ljava/lang/Object;JLjava/lang/Object;)Ljava/lang/Object;");
-            // JDK 9
-            add(TO_BE_INVESTIGATED,
-                            "jdk/internal/misc/Unsafe.getAndAddInt(Ljava/lang/Object;JI)I",
-                            "jdk/internal/misc/Unsafe.getAndAddLong(Ljava/lang/Object;JJ)J",
-                            "jdk/internal/misc/Unsafe.getAndSetInt(Ljava/lang/Object;JI)I",
-                            "jdk/internal/misc/Unsafe.getAndSetLong(Ljava/lang/Object;JJ)J",
-                            "jdk/internal/misc/Unsafe.getAndSetObject(Ljava/lang/Object;JLjava/lang/Object;)Ljava/lang/Object;",
-                            "jdk/internal/misc/Unsafe.getCharUnaligned(Ljava/lang/Object;J)C",
-                            "jdk/internal/misc/Unsafe.getIntUnaligned(Ljava/lang/Object;J)I",
-                            "jdk/internal/misc/Unsafe.getLongUnaligned(Ljava/lang/Object;J)J",
-                            "jdk/internal/misc/Unsafe.getShortUnaligned(Ljava/lang/Object;J)S",
-                            "jdk/internal/misc/Unsafe.putCharUnaligned(Ljava/lang/Object;JC)V",
-                            "jdk/internal/misc/Unsafe.putIntUnaligned(Ljava/lang/Object;JI)V",
-                            "jdk/internal/misc/Unsafe.putLongUnaligned(Ljava/lang/Object;JJ)V",
-                            "jdk/internal/misc/Unsafe.putShortUnaligned(Ljava/lang/Object;JS)V");
-        }
-
-        HotSpotGraalRuntimeProvider rt = (HotSpotGraalRuntimeProvider) Graal.getRequiredCapability(RuntimeProvider.class);
-        GraalHotSpotVMConfig config = rt.getVMConfig();
-
-        /*
-         * These are known to be implemented but the platform dependent conditions for when they are
-         * enabled are complex so just ignore them all the time.
-         */
+        // These are known to be implemented but the platform dependent conditions
+        // for when they are enabled are complex so just ignore them all the time.
         add(IGNORE,
                         "java/lang/Integer.bitCount(I)I",
                         "java/lang/Integer.numberOfLeadingZeros(I)I",
@@ -375,52 +213,323 @@ public class CheckGraalIntrinsics extends GraalTest {
                         "java/lang/Long.numberOfLeadingZeros(J)I",
                         "java/lang/Long.numberOfTrailingZeros(J)I");
 
-        if (!config.useCRC32Intrinsics) {
-            // Registration of the CRC32 plugins is guarded by UseCRC32Intrinsics
-            add(IGNORE, "java/util/zip/CRC32.update(II)I");
-            if (JDK9Method.JAVA_SPECIFICATION_VERSION < 9) {
-                add(IGNORE,
-                                "java/util/zip/CRC32.updateByteBuffer(IJII)I",
-                                "java/util/zip/CRC32.updateBytes(I[BII)I");
-            } else {
-                add(IGNORE,
-                                "java/util/zip/CRC32.updateByteBuffer0(IJII)I",
-                                "java/util/zip/CRC32.updateBytes0(I[BII)I",
-                                "java/util/zip/CRC32C.updateBytes(I[BII)I",
-                                "java/util/zip/CRC32C.updateDirectByteBuffer(IJII)I");
-            }
-        } else {
-            if (JDK9Method.JAVA_SPECIFICATION_VERSION >= 9) {
+        // Relevant for Java flight recorder
+        add(TO_BE_INVESTIGATED,
+                        "oracle/jrockit/jfr/Timing.counterTime()J",
+                        "oracle/jrockit/jfr/VMJFR.classID0(Ljava/lang/Class;)J",
+                        "oracle/jrockit/jfr/VMJFR.threadID()I");
+
+        add(TO_BE_INVESTIGATED,
+                        // Should be fairly easy to implement - C2 intrinsifies these to use "v !=
+                        // v" to check for NaN instead of looking at the bit pattern.
+                        "java/lang/Double.doubleToLongBits(D)J",
+                        "java/lang/Float.floatToIntBits(F)I",
+
+                        // Should be trivial to implement because we already have existing nodes
+                        "java/lang/Math.decrementExact(I)I",
+                        "java/lang/Math.decrementExact(J)J",
+                        "java/lang/Math.incrementExact(I)I",
+                        "java/lang/Math.incrementExact(J)J",
+
+                        // Similar to addExact
+                        "java/lang/Math.negateExact(I)I",
+                        // Similar to addExact
+                        "java/lang/Math.negateExact(J)J",
+                        // HotSpot MacroAssembler-based intrinsic
+                        "java/lang/String.compareTo(Ljava/lang/String;)I",
+                        // HotSpot MacroAssembler-based intrinsic
+                        "java/lang/String.indexOf(Ljava/lang/String;)I",
+                        // Can share most implementation parts with with
+                        // Unsafe.allocateUninitializedArray0
+                        "java/lang/reflect/Array.newArray(Ljava/lang/Class;I)Ljava/lang/Object;",
+                        // HotSpot MacroAssembler-based intrinsic
+                        "sun/nio/cs/ISO_8859_1$Encoder.encodeISOArray([CI[BII)I",
+                        // Stub based intrinsics but implementation seems complex in C2
+                        "sun/security/provider/DigestBase.implCompressMultiBlock([BII)I");
+
+        if (isJDK9OrHigher()) {
+            // Relevant for Java flight recorder
+            add(TO_BE_INVESTIGATED,
+                            "jdk/jfr/internal/JVM.counterTime()J",
+                            "jdk/jfr/internal/JVM.getBufferWriter()Ljava/lang/Object;",
+                            "jdk/jfr/internal/JVM.getClassId(Ljava/lang/Class;)J");
+
+            add(TO_BE_INVESTIGATED,
+                            // Some logic and a stub call
+                            "com/sun/crypto/provider/CounterMode.implCrypt([BII[BI)I",
+                            // Stub and very little logic
+                            "com/sun/crypto/provider/GHASH.processBlocks([BII[J[J)V",
+                            // HotSpot MacroAssembler-based intrinsic
+                            "java/lang/Math.fma(DDD)D",
+                            // HotSpot MacroAssembler-based intrinsic
+                            "java/lang/Math.fma(FFF)F",
+                            // Just a runtime call (the called C code has a better fast path)
+                            "java/lang/Object.notify()V",
+                            // Just a runtime call (the called C code has a better fast path)
+                            "java/lang/Object.notifyAll()V",
+                            // Emit pause instruction if os::is_MP()
+                            "java/lang/Thread.onSpinWait()V",
+                            // Just check if the argument is a compile time constant
+                            "java/lang/invoke/MethodHandleImpl.isCompileConstant(Ljava/lang/Object;)Z",
+                            // Some logic and a runtime call
+                            "java/util/ArraysSupport.vectorizedMismatch(Ljava/lang/Object;JLjava/lang/Object;JII)I",
+                            // Only used as a marker for vectorization?
+                            "java/util/stream/Streams$RangeIntSpliterator.forEachRemaining(Ljava/util/function/IntConsumer;)V",
+                            // Only implemented on non-AMD64 platforms (some logic and runtime call)
+                            "java/util/zip/Adler32.updateByteBuffer(IJII)I",
+                            // Only implemented on non-AMD64 platforms (some logic and runtime call)
+                            "java/util/zip/Adler32.updateBytes(I[BII)I",
+                            // similar to CRC32.updateBytes
+                            "java/util/zip/CRC32C.updateBytes(I[BII)I",
+                            // similar to CRC32.updateDirectByteBuffer
+                            "java/util/zip/CRC32C.updateDirectByteBuffer(IJII)I",
+                            // Emits a slow and a fast path and some dispatching logic
+                            "jdk/internal/misc/Unsafe.allocateUninitializedArray0(Ljava/lang/Class;I)Ljava/lang/Object;",
+
+                            // Should be easy to implement as it seems to match the logic that is
+                            // already implemented in ValueCompareAndSwapNode. On the high-level, we
+                            // would need something similar to UnsafeCompareAndSwapNode but with a
+                            // different result type.
+                            "jdk/internal/misc/Unsafe.compareAndExchangeByte(Ljava/lang/Object;JBB)B",
+                            "jdk/internal/misc/Unsafe.compareAndExchangeInt(Ljava/lang/Object;JII)I",
+                            "jdk/internal/misc/Unsafe.compareAndExchangeLong(Ljava/lang/Object;JJJ)J",
+                            "jdk/internal/misc/Unsafe.compareAndExchangeObject(Ljava/lang/Object;JLjava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;",
+                            "jdk/internal/misc/Unsafe.compareAndExchangeShort(Ljava/lang/Object;JSS)S",
+
+                            // Should be easy to implement as we already have an implementation for
+                            // int, long, and Object.
+                            "jdk/internal/misc/Unsafe.compareAndSetByte(Ljava/lang/Object;JBB)Z",
+                            "jdk/internal/misc/Unsafe.compareAndSetShort(Ljava/lang/Object;JSS)Z",
+
+                            // Should be easy to implement as we already have an implementation for
+                            // int and long.
+                            "jdk/internal/misc/Unsafe.getAndAddByte(Ljava/lang/Object;JB)B",
+                            "jdk/internal/misc/Unsafe.getAndAddShort(Ljava/lang/Object;JS)S",
+
+                            // Should be easy to implement as we already have an implementation for
+                            // int, long, and Object.
+                            "jdk/internal/misc/Unsafe.getAndSetByte(Ljava/lang/Object;JB)B",
+                            "jdk/internal/misc/Unsafe.getAndSetShort(Ljava/lang/Object;JS)S",
+
+                            // Control flow, deopts, and a cast
+                            "jdk/internal/util/Preconditions.checkIndex(IILjava/util/function/BiFunction;)I",
+                            // HotSpot MacroAssembler-based intrinsic
+                            "sun/nio/cs/ISO_8859_1$Encoder.implEncodeISOArray([CI[BII)I",
+                            // Runtime call and some complex compiler logic
+                            "sun/security/provider/DigestBase.implCompressMultiBlock0([BII)I");
+            /*
+             * Per default, all these operations are mapped to some generic method for which we
+             * already have compiler intrinsics. Performance-wise it would be better to support them
+             * explicitly as the more generic method might be more restrictive and therefore slower
+             * than necessary.
+             */
+            add(TO_BE_INVESTIGATED,
+                            // Mapped to compareAndExchange*
+                            "jdk/internal/misc/Unsafe.compareAndExchangeByteAcquire(Ljava/lang/Object;JBB)B",
+                            "jdk/internal/misc/Unsafe.compareAndExchangeByteRelease(Ljava/lang/Object;JBB)B",
+                            "jdk/internal/misc/Unsafe.compareAndExchangeIntAcquire(Ljava/lang/Object;JII)I",
+                            "jdk/internal/misc/Unsafe.compareAndExchangeIntRelease(Ljava/lang/Object;JII)I",
+                            "jdk/internal/misc/Unsafe.compareAndExchangeLongAcquire(Ljava/lang/Object;JJJ)J",
+                            "jdk/internal/misc/Unsafe.compareAndExchangeLongRelease(Ljava/lang/Object;JJJ)J",
+                            "jdk/internal/misc/Unsafe.compareAndExchangeObjectAcquire(Ljava/lang/Object;JLjava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;",
+                            "jdk/internal/misc/Unsafe.compareAndExchangeObjectRelease(Ljava/lang/Object;JLjava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;",
+                            "jdk/internal/misc/Unsafe.compareAndExchangeShortAcquire(Ljava/lang/Object;JSS)S",
+                            "jdk/internal/misc/Unsafe.compareAndExchangeShortRelease(Ljava/lang/Object;JSS)S",
+
+                            // Mapped to get*Volatile
+                            "jdk/internal/misc/Unsafe.getBooleanAcquire(Ljava/lang/Object;J)Z",
+                            "jdk/internal/misc/Unsafe.getBooleanOpaque(Ljava/lang/Object;J)Z",
+                            "jdk/internal/misc/Unsafe.getByteAcquire(Ljava/lang/Object;J)B",
+                            "jdk/internal/misc/Unsafe.getByteOpaque(Ljava/lang/Object;J)B",
+                            "jdk/internal/misc/Unsafe.getCharAcquire(Ljava/lang/Object;J)C",
+                            "jdk/internal/misc/Unsafe.getCharOpaque(Ljava/lang/Object;J)C",
+                            "jdk/internal/misc/Unsafe.getDoubleAcquire(Ljava/lang/Object;J)D",
+                            "jdk/internal/misc/Unsafe.getDoubleOpaque(Ljava/lang/Object;J)D",
+                            "jdk/internal/misc/Unsafe.getFloatAcquire(Ljava/lang/Object;J)F",
+                            "jdk/internal/misc/Unsafe.getFloatOpaque(Ljava/lang/Object;J)F",
+                            "jdk/internal/misc/Unsafe.getIntAcquire(Ljava/lang/Object;J)I",
+                            "jdk/internal/misc/Unsafe.getIntOpaque(Ljava/lang/Object;J)I",
+                            "jdk/internal/misc/Unsafe.getLongAcquire(Ljava/lang/Object;J)J",
+                            "jdk/internal/misc/Unsafe.getLongOpaque(Ljava/lang/Object;J)J",
+                            "jdk/internal/misc/Unsafe.getObjectAcquire(Ljava/lang/Object;J)Ljava/lang/Object;",
+                            "jdk/internal/misc/Unsafe.getObjectOpaque(Ljava/lang/Object;J)Ljava/lang/Object;",
+                            "jdk/internal/misc/Unsafe.getShortAcquire(Ljava/lang/Object;J)S",
+                            "jdk/internal/misc/Unsafe.getShortOpaque(Ljava/lang/Object;J)S",
+
+                            // Mapped to put*Volatile
+                            "jdk/internal/misc/Unsafe.putBooleanOpaque(Ljava/lang/Object;JZ)V",
+                            "jdk/internal/misc/Unsafe.putByteOpaque(Ljava/lang/Object;JB)V",
+                            "jdk/internal/misc/Unsafe.putCharOpaque(Ljava/lang/Object;JC)V",
+                            "jdk/internal/misc/Unsafe.putDoubleOpaque(Ljava/lang/Object;JD)V",
+                            "jdk/internal/misc/Unsafe.putFloatOpaque(Ljava/lang/Object;JF)V",
+                            "jdk/internal/misc/Unsafe.putIntOpaque(Ljava/lang/Object;JI)V",
+                            "jdk/internal/misc/Unsafe.putLongOpaque(Ljava/lang/Object;JJ)V",
+                            "jdk/internal/misc/Unsafe.putObjectOpaque(Ljava/lang/Object;JLjava/lang/Object;)V",
+                            "jdk/internal/misc/Unsafe.putShortOpaque(Ljava/lang/Object;JS)V",
+
+                            // Mapped to compareAndSet*
+                            "jdk/internal/misc/Unsafe.weakCompareAndSetByte(Ljava/lang/Object;JBB)Z",
+                            "jdk/internal/misc/Unsafe.weakCompareAndSetByteAcquire(Ljava/lang/Object;JBB)Z",
+                            "jdk/internal/misc/Unsafe.weakCompareAndSetBytePlain(Ljava/lang/Object;JBB)Z",
+                            "jdk/internal/misc/Unsafe.weakCompareAndSetByteRelease(Ljava/lang/Object;JBB)Z",
+                            "jdk/internal/misc/Unsafe.weakCompareAndSetInt(Ljava/lang/Object;JII)Z",
+                            "jdk/internal/misc/Unsafe.weakCompareAndSetIntAcquire(Ljava/lang/Object;JII)Z",
+                            "jdk/internal/misc/Unsafe.weakCompareAndSetIntPlain(Ljava/lang/Object;JII)Z",
+                            "jdk/internal/misc/Unsafe.weakCompareAndSetIntRelease(Ljava/lang/Object;JII)Z",
+                            "jdk/internal/misc/Unsafe.weakCompareAndSetLong(Ljava/lang/Object;JJJ)Z",
+                            "jdk/internal/misc/Unsafe.weakCompareAndSetLongAcquire(Ljava/lang/Object;JJJ)Z",
+                            "jdk/internal/misc/Unsafe.weakCompareAndSetLongPlain(Ljava/lang/Object;JJJ)Z",
+                            "jdk/internal/misc/Unsafe.weakCompareAndSetLongRelease(Ljava/lang/Object;JJJ)Z",
+                            "jdk/internal/misc/Unsafe.weakCompareAndSetObject(Ljava/lang/Object;JLjava/lang/Object;Ljava/lang/Object;)Z",
+                            "jdk/internal/misc/Unsafe.weakCompareAndSetObjectAcquire(Ljava/lang/Object;JLjava/lang/Object;Ljava/lang/Object;)Z",
+                            "jdk/internal/misc/Unsafe.weakCompareAndSetObjectPlain(Ljava/lang/Object;JLjava/lang/Object;Ljava/lang/Object;)Z",
+                            "jdk/internal/misc/Unsafe.weakCompareAndSetObjectRelease(Ljava/lang/Object;JLjava/lang/Object;Ljava/lang/Object;)Z",
+                            "jdk/internal/misc/Unsafe.weakCompareAndSetShort(Ljava/lang/Object;JSS)Z",
+                            "jdk/internal/misc/Unsafe.weakCompareAndSetShortAcquire(Ljava/lang/Object;JSS)Z",
+                            "jdk/internal/misc/Unsafe.weakCompareAndSetShortPlain(Ljava/lang/Object;JSS)Z",
+                            "jdk/internal/misc/Unsafe.weakCompareAndSetShortRelease(Ljava/lang/Object;JSS)Z");
+
+            // Compact string support - HotSpot MacroAssembler-based intrinsic or complex C2 logic.
+            add(TO_BE_INVESTIGATED,
+                            "java/lang/StringCoding.hasNegatives([BII)Z",
+                            "java/lang/StringCoding.implEncodeISOArray([BI[BII)I",
+                            "java/lang/StringLatin1.compareTo([B[B)I",
+                            "java/lang/StringLatin1.compareToUTF16([B[B)I",
+                            "java/lang/StringLatin1.equals([B[B)Z",
+                            "java/lang/StringLatin1.indexOf([BI[BII)I",
+                            "java/lang/StringLatin1.indexOf([B[B)I",
+                            "java/lang/StringLatin1.inflate([BI[BII)V",
+                            "java/lang/StringLatin1.inflate([BI[CII)V",
+                            "java/lang/StringUTF16.compareTo([B[B)I",
+                            "java/lang/StringUTF16.compareToLatin1([B[B)I",
+                            "java/lang/StringUTF16.compress([BI[BII)I",
+                            "java/lang/StringUTF16.compress([CI[BII)I",
+                            "java/lang/StringUTF16.equals([B[B)Z",
+                            "java/lang/StringUTF16.getChar([BI)C",
+                            "java/lang/StringUTF16.getChars([BII[CI)V",
+                            "java/lang/StringUTF16.indexOf([BI[BII)I",
+                            "java/lang/StringUTF16.indexOf([B[B)I",
+                            "java/lang/StringUTF16.indexOfChar([BIII)I",
+                            "java/lang/StringUTF16.indexOfLatin1([BI[BII)I",
+                            "java/lang/StringUTF16.indexOfLatin1([B[B)I",
+                            "java/lang/StringUTF16.putChar([BII)V",
+                            "java/lang/StringUTF16.toBytes([CII)[B");
+        }
+
+        if (!getHostArchitectureName().equals("amd64")) {
+            // Can we implement these on non-AMD64 platforms? C2 seems to.
+            add(TO_BE_INVESTIGATED,
+                            "sun/misc/Unsafe.getAndAddInt(Ljava/lang/Object;JI)I",
+                            "sun/misc/Unsafe.getAndAddLong(Ljava/lang/Object;JJ)J",
+                            "sun/misc/Unsafe.getAndSetInt(Ljava/lang/Object;JI)I",
+                            "sun/misc/Unsafe.getAndSetLong(Ljava/lang/Object;JJ)J",
+                            "sun/misc/Unsafe.getAndSetObject(Ljava/lang/Object;JLjava/lang/Object;)Ljava/lang/Object;");
+
+            if (isJDK9OrHigher()) {
                 add(TO_BE_INVESTIGATED,
-                                "java/util/zip/CRC32C.updateBytes(I[BII)I",
-                                "java/util/zip/CRC32C.updateDirectByteBuffer(IJII)I");
+                                "jdk/internal/misc/Unsafe.getAndAddInt(Ljava/lang/Object;JI)I",
+                                "jdk/internal/misc/Unsafe.getAndAddLong(Ljava/lang/Object;JJ)J",
+                                "jdk/internal/misc/Unsafe.getAndSetInt(Ljava/lang/Object;JI)I",
+                                "jdk/internal/misc/Unsafe.getAndSetLong(Ljava/lang/Object;JJ)J",
+                                "jdk/internal/misc/Unsafe.getAndSetObject(Ljava/lang/Object;JLjava/lang/Object;)Ljava/lang/Object;",
+                                "jdk/internal/misc/Unsafe.getCharUnaligned(Ljava/lang/Object;J)C",
+                                "jdk/internal/misc/Unsafe.getIntUnaligned(Ljava/lang/Object;J)I",
+                                "jdk/internal/misc/Unsafe.getLongUnaligned(Ljava/lang/Object;J)J",
+                                "jdk/internal/misc/Unsafe.getShortUnaligned(Ljava/lang/Object;J)S",
+                                "jdk/internal/misc/Unsafe.putCharUnaligned(Ljava/lang/Object;JC)V",
+                                "jdk/internal/misc/Unsafe.putIntUnaligned(Ljava/lang/Object;JI)V",
+                                "jdk/internal/misc/Unsafe.putLongUnaligned(Ljava/lang/Object;JJ)V",
+                                "jdk/internal/misc/Unsafe.putShortUnaligned(Ljava/lang/Object;JS)V");
             }
         }
 
-        if (!config.useAESIntrinsics) {
-            // Registration of the AES plugins is guarded by UseAESIntrinsics
-            if (JDK9Method.JAVA_SPECIFICATION_VERSION < 9) {
+        HotSpotGraalRuntimeProvider rt = (HotSpotGraalRuntimeProvider) Graal.getRequiredCapability(RuntimeProvider.class);
+        GraalHotSpotVMConfig config = rt.getVMConfig();
+
+        /*
+         * The intrinsics down here are known to be implemented but they are not always enabled on
+         * the HotSpot side (e.g., because they require certain CPU features). So, we are ignoring
+         * them if the HotSpot config tells us that they can't be used.
+         */
+
+        // CRC32 intrinsics
+        if (!config.useCRC32Intrinsics) {
+            add(IGNORE, "java/util/zip/CRC32.update(II)I");
+            if (isJDK9OrHigher()) {
                 add(IGNORE,
-                                "com/sun/crypto/provider/AESCrypt.decryptBlock([BI[BI)V",
-                                "com/sun/crypto/provider/AESCrypt.encryptBlock([BI[BI)V",
-                                "com/sun/crypto/provider/CipherBlockChaining.decrypt([BII[BI)I",
-                                "com/sun/crypto/provider/CipherBlockChaining.encrypt([BII[BI)I");
+                                "java/util/zip/CRC32.updateByteBuffer0(IJII)I",
+                                "java/util/zip/CRC32.updateBytes0(I[BII)I");
             } else {
+                add(IGNORE,
+                                "java/util/zip/CRC32.updateByteBuffer(IJII)I",
+                                "java/util/zip/CRC32.updateBytes(I[BII)I");
+            }
+        }
+
+        // AES intrinsics
+        if (!config.useAESIntrinsics) {
+            if (isJDK9OrHigher()) {
                 add(IGNORE,
                                 "com/sun/crypto/provider/AESCrypt.implDecryptBlock([BI[BI)V",
                                 "com/sun/crypto/provider/AESCrypt.implEncryptBlock([BI[BI)V",
                                 "com/sun/crypto/provider/CipherBlockChaining.implDecrypt([BII[BI)I",
                                 "com/sun/crypto/provider/CipherBlockChaining.implEncrypt([BII[BI)I");
-            }
-        }
-        if (!config.useMultiplyToLenIntrinsic()) {
-            // Registration of the AES plugins is guarded by UseAESIntrinsics
-            if (JDK9Method.JAVA_SPECIFICATION_VERSION < 9) {
-                add(IGNORE, "java/math/BigInteger.multiplyToLen([II[II[I)[I");
             } else {
-                add(IGNORE, "java/math/BigInteger.implMultiplyToLen([II[II[I)[I");
+                add(IGNORE,
+                                "com/sun/crypto/provider/AESCrypt.decryptBlock([BI[BI)V",
+                                "com/sun/crypto/provider/AESCrypt.encryptBlock([BI[BI)V",
+                                "com/sun/crypto/provider/CipherBlockChaining.decrypt([BII[BI)I",
+                                "com/sun/crypto/provider/CipherBlockChaining.encrypt([BII[BI)I");
             }
         }
+
+        // BigInteger intrinsics
+        if (!config.useMultiplyToLenIntrinsic()) {
+            if (isJDK9OrHigher()) {
+                add(IGNORE, "java/math/BigInteger.implMultiplyToLen([II[II[I)[I");
+            } else {
+                add(IGNORE, "java/math/BigInteger.multiplyToLen([II[II[I)[I");
+            }
+        }
+        if (!config.useMulAddIntrinsic()) {
+            add(IGNORE, "java/math/BigInteger.implMulAdd([I[IIII)I");
+        }
+        if (!config.useMontgomeryMultiplyIntrinsic()) {
+            add(IGNORE, "java/math/BigInteger.implMontgomeryMultiply([I[I[IIJ[I)[I");
+        }
+        if (!config.useMontgomerySquareIntrinsic()) {
+            add(IGNORE, "java/math/BigInteger.implMontgomerySquare([I[IIJ[I)[I");
+        }
+        if (!config.useSquareToLenIntrinsic()) {
+            add(IGNORE, "java/math/BigInteger.implSquareToLen([II[II)[I");
+        }
+
+        // SHA intrinsics
+        if (!config.useSHA1Intrinsics()) {
+            if (isJDK9OrHigher()) {
+                add(IGNORE, "sun/security/provider/SHA.implCompress0([BI)V");
+            } else {
+                add(IGNORE, "sun/security/provider/SHA.implCompress([BI)V");
+            }
+        }
+        if (!config.useSHA256Intrinsics()) {
+            if (isJDK9OrHigher()) {
+                add(IGNORE, "sun/security/provider/SHA2.implCompress0([BI)V");
+            } else {
+                add(IGNORE, "sun/security/provider/SHA2.implCompress([BI)V");
+            }
+        }
+        if (!config.useSHA512Intrinsics()) {
+            if (isJDK9OrHigher()) {
+                add(IGNORE, "sun/security/provider/SHA5.implCompress0([BI)V");
+            } else {
+                add(IGNORE, "sun/security/provider/SHA5.implCompress([BI)V");
+            }
+        }
+    }
+
+    private static boolean isJDK9OrHigher() {
+        return JDK9Method.JAVA_SPECIFICATION_VERSION >= 9;
     }
 
     private static String getHostArchitectureName() {
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/CompilationWrapperTest.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/CompilationWrapperTest.java
index 4f53585d4d4..dea1d25a430 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/CompilationWrapperTest.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/CompilationWrapperTest.java
@@ -106,7 +106,12 @@ public class CompilationWrapperTest extends GraalCompilerTest {
         final int maxProblems = 4;
         Probe[] probes = {
                         new Probe("To capture more information for diagnosing or reporting a compilation", maxProblems),
-                        new Probe("Retrying compilation of", maxProblems),
+                        new Probe("Retrying compilation of", maxProblems) {
+                            @Override
+                            String test() {
+                                return actualOccurrences > 0 && actualOccurrences <= maxProblems ? null : String.format("expected occurrences to be in [1 .. %d]", maxProblems);
+                            }
+                        },
                         new Probe("adjusting CompilationFailureAction from Diagnose to Print", 1),
                         new Probe("adjusting CompilationFailureAction from Print to Silent", 1),
         };
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/HotSpotStackIntrospectionTest.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/HotSpotStackIntrospectionTest.java
new file mode 100644
index 00000000000..98692350b86
--- /dev/null
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/HotSpotStackIntrospectionTest.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright (c) 2017, 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 org.graalvm.compiler.hotspot.test;
+
+import java.util.function.Function;
+
+import org.graalvm.compiler.test.GraalTest;
+import org.junit.Assume;
+import org.junit.Test;
+
+import jdk.vm.ci.code.InstalledCode;
+import jdk.vm.ci.code.InvalidInstalledCodeException;
+import jdk.vm.ci.code.stack.InspectedFrame;
+import jdk.vm.ci.code.stack.StackIntrospection;
+import jdk.vm.ci.hotspot.HotSpotJVMCIRuntime;
+import jdk.vm.ci.meta.ResolvedJavaMethod;
+
+/**
+ * Create a single object which is referenced from a local, the expression stack and the lock state
+ * and then ensure that identity is maintained when the frame is forced to be materialized by
+ * {@link InspectedFrame#materializeVirtualObjects(boolean)}.
+ */
+public class HotSpotStackIntrospectionTest extends HotSpotGraalCompilerTest {
+
+    static StackIntrospection stackIntrospection = HotSpotJVMCIRuntime.runtime().getHostJVMCIBackend().getStackIntrospection();
+    static volatile int v;
+
+    public static void testSynchronizedSnippet(Function<Void, Void> f) {
+        Object a = new Object();
+        synchronized (a) {
+            testOnStack(a, forceFrameState(a, f), a);
+            // This object should be locked so try to notify on it
+            a.notify();
+        }
+    }
+
+    public static void testSnippet(Function<Void, Void> f) {
+        Object a = new Object();
+        testOnStack(a, forceFrameState(a, f), a);
+    }
+
+    private static void testOnStack(Object a, Object a2, Object a3) {
+        if (a != a2 || a != a3) {
+            throw new InternalError();
+        }
+    }
+
+    private static Object forceFrameState(Object a, Function<Void, Void> f) {
+        // Use a volatile store to ensure a FrameState is captured after this point.
+        v++;
+        f.apply(null);
+        return a;
+    }
+
+    @Test(timeout = 20000)
+    public void run() throws InvalidInstalledCodeException {
+        // The JDK9 bits are currently broken
+        Assume.assumeTrue(GraalTest.Java8OrEarlier);
+        test("testSnippet");
+    }
+
+    @Test(timeout = 20000)
+    public void runSynchronized() throws InvalidInstalledCodeException {
+        // The JDK9 bits are currently broken
+        Assume.assumeTrue(GraalTest.Java8OrEarlier);
+        test("testSynchronizedSnippet");
+    }
+
+    private void test(String name) throws InvalidInstalledCodeException {
+        ResolvedJavaMethod method = getMetaAccess().lookupJavaMethod(getMethod(name));
+        Function<Void, Void> f = o -> {
+            stackIntrospection.iterateFrames(null, null, 0, frame -> {
+                if (frame.getMethod().equals(method)) {
+                    frame.materializeVirtualObjects(true);
+                }
+                return null;
+            });
+            return null;
+        };
+        InstalledCode code = getCode(method);
+        code.executeVarargs(f);
+    }
+
+}
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/HotSpotUnsafeSubstitutionTest.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/HotSpotUnsafeSubstitutionTest.java
index 7864f301c10..bbbadb9df58 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/HotSpotUnsafeSubstitutionTest.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/HotSpotUnsafeSubstitutionTest.java
@@ -22,6 +22,7 @@
  */
 package org.graalvm.compiler.hotspot.test;
 
+import org.graalvm.compiler.hotspot.meta.HotSpotUnsafeSubstitutions;
 import org.graalvm.compiler.replacements.test.MethodSubstitutionTest;
 import org.junit.Test;
 
@@ -56,7 +57,7 @@ public class HotSpotUnsafeSubstitutionTest extends MethodSubstitutionTest {
 
     @Test
     public void testUnsafeSubstitutions() throws Exception {
-        testGraph("unsafeCopyMemory");
+        testGraph("unsafeCopyMemory", HotSpotUnsafeSubstitutions.copyMemoryName);
     }
 
     public void unsafeCopyMemory(Object srcBase, long srcOffset, Object dstBase, long dstOffset, long bytes) {
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/GraalHotSpotVMConfig.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/GraalHotSpotVMConfig.java
index 15e709c3056..97c0c281bc4 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/GraalHotSpotVMConfig.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/GraalHotSpotVMConfig.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2017, 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
@@ -165,6 +165,7 @@ public class GraalHotSpotVMConfig extends HotSpotVMConfigAccess {
     public final boolean usePopCountInstruction = getFlag("UsePopCountInstruction", Boolean.class);
     public final boolean useAESIntrinsics = getFlag("UseAESIntrinsics", Boolean.class);
     public final boolean useCRC32Intrinsics = getFlag("UseCRC32Intrinsics", Boolean.class);
+    public final boolean threadLocalHandshakes = getFlag("ThreadLocalHandshakes", Boolean.class, false);
 
     private final boolean useMultiplyToLenIntrinsic = getFlag("UseMultiplyToLenIntrinsic", Boolean.class);
     private final boolean useSHA1Intrinsics = getFlag("UseSHA1Intrinsics", Boolean.class);
@@ -594,6 +595,7 @@ public class GraalHotSpotVMConfig extends HotSpotVMConfigAccess {
     public final int basicLockSize = getFieldValue("CompilerToVM::Data::sizeof_BasicLock", Integer.class, "int");
     public final int basicLockDisplacedHeaderOffset = getFieldOffset("BasicLock::_displaced_header", Integer.class, "markOop");
 
+    public final int threadPollingPageOffset = getFieldOffset("Thread::_polling_page", Integer.class, "address", -1);
     public final int threadAllocatedBytesOffset = getFieldOffset("Thread::_allocated_bytes", Integer.class, "jlong");
 
     public final int tlabRefillWasteIncrement = getFlag("TLABWasteIncrement", Integer.class);
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/meta/DefaultHotSpotLoweringProvider.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/meta/DefaultHotSpotLoweringProvider.java
index d3c57cfe2c0..395521ad0fd 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/meta/DefaultHotSpotLoweringProvider.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/meta/DefaultHotSpotLoweringProvider.java
@@ -103,6 +103,7 @@ import org.graalvm.compiler.nodes.FixedNode;
 import org.graalvm.compiler.nodes.Invoke;
 import org.graalvm.compiler.nodes.LogicNode;
 import org.graalvm.compiler.nodes.LoweredCallTargetNode;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.ParameterNode;
 import org.graalvm.compiler.nodes.SafepointNode;
 import org.graalvm.compiler.nodes.StartNode;
@@ -254,7 +255,7 @@ public class DefaultHotSpotLoweringProvider extends DefaultJavaLoweringProvider
                 instanceofSnippets.lower(instanceOfDynamicNode, tool);
             } else {
                 ValueNode mirror = instanceOfDynamicNode.getMirrorOrHub();
-                if (mirror.stamp().getStackKind() == JavaKind.Object) {
+                if (mirror.stamp(NodeView.DEFAULT).getStackKind() == JavaKind.Object) {
                     ClassGetHubNode classGetHub = graph.unique(new ClassGetHubNode(mirror));
                     instanceOfDynamicNode.setMirror(classGetHub);
                 }
@@ -409,7 +410,7 @@ public class DefaultHotSpotLoweringProvider extends DefaultJavaLoweringProvider
         StructuredGraph graph = n.graph();
         assert !n.getHub().isConstant();
         AddressNode address = createOffsetAddress(graph, n.getHub(), runtime.getVMConfig().klassLayoutHelperOffset);
-        n.replaceAtUsagesAndDelete(graph.unique(new FloatingReadNode(address, KLASS_LAYOUT_HELPER_LOCATION, null, n.stamp(), null, BarrierType.NONE)));
+        n.replaceAtUsagesAndDelete(graph.unique(new FloatingReadNode(address, KLASS_LAYOUT_HELPER_LOCATION, null, n.stamp(NodeView.DEFAULT), null, BarrierType.NONE)));
     }
 
     private void lowerHubGetClassNode(HubGetClassNode n, LoweringTool tool) {
@@ -422,11 +423,12 @@ public class DefaultHotSpotLoweringProvider extends DefaultJavaLoweringProvider
         StructuredGraph graph = n.graph();
         assert !hub.isConstant() || GraalOptions.ImmutableCode.getValue(graph.getOptions());
         AddressNode mirrorAddress = createOffsetAddress(graph, hub, vmConfig.classMirrorOffset);
-        FloatingReadNode read = graph.unique(new FloatingReadNode(mirrorAddress, CLASS_MIRROR_LOCATION, null, vmConfig.classMirrorIsHandle ? StampFactory.forKind(target.wordJavaKind) : n.stamp(),
-                        null, BarrierType.NONE));
+        FloatingReadNode read = graph.unique(
+                        new FloatingReadNode(mirrorAddress, CLASS_MIRROR_LOCATION, null, vmConfig.classMirrorIsHandle ? StampFactory.forKind(target.wordJavaKind) : n.stamp(NodeView.DEFAULT),
+                                        null, BarrierType.NONE));
         if (vmConfig.classMirrorIsHandle) {
             AddressNode address = createOffsetAddress(graph, read, 0);
-            read = graph.unique(new FloatingReadNode(address, CLASS_MIRROR_HANDLE_LOCATION, null, n.stamp(), null, BarrierType.NONE));
+            read = graph.unique(new FloatingReadNode(address, CLASS_MIRROR_HANDLE_LOCATION, null, n.stamp(NodeView.DEFAULT), null, BarrierType.NONE));
         }
         n.replaceAtUsagesAndDelete(read);
     }
@@ -439,7 +441,7 @@ public class DefaultHotSpotLoweringProvider extends DefaultJavaLoweringProvider
         StructuredGraph graph = n.graph();
         assert !n.getValue().isConstant();
         AddressNode address = createOffsetAddress(graph, n.getValue(), runtime.getVMConfig().klassOffset);
-        FloatingReadNode read = graph.unique(new FloatingReadNode(address, CLASS_KLASS_LOCATION, null, n.stamp(), null, BarrierType.NONE));
+        FloatingReadNode read = graph.unique(new FloatingReadNode(address, CLASS_KLASS_LOCATION, null, n.stamp(NodeView.DEFAULT), null, BarrierType.NONE));
         n.replaceAtUsagesAndDelete(read);
     }
 
@@ -448,7 +450,7 @@ public class DefaultHotSpotLoweringProvider extends DefaultJavaLoweringProvider
             MethodCallTargetNode callTarget = (MethodCallTargetNode) invoke.callTarget();
             NodeInputList<ValueNode> parameters = callTarget.arguments();
             ValueNode receiver = parameters.size() <= 0 ? null : parameters.get(0);
-            if (!callTarget.isStatic() && receiver.stamp() instanceof ObjectStamp && !StampTool.isPointerNonNull(receiver)) {
+            if (!callTarget.isStatic() && receiver.stamp(NodeView.DEFAULT) instanceof ObjectStamp && !StampTool.isPointerNonNull(receiver)) {
                 ValueNode nonNullReceiver = createNullCheckedValue(receiver, invoke.asNode(), tool);
                 parameters.set(0, nonNullReceiver);
                 receiver = nonNullReceiver;
@@ -586,7 +588,7 @@ public class DefaultHotSpotLoweringProvider extends DefaultJavaLoweringProvider
                 int size = osrLocal.getStackKind().getSlotCount();
                 int offset = localsOffset - (osrLocal.index() + size - 1) * wordSize;
                 AddressNode address = createOffsetAddress(graph, buffer, offset);
-                ReadNode load = graph.add(new ReadNode(address, any(), osrLocal.stamp(), BarrierType.NONE));
+                ReadNode load = graph.add(new ReadNode(address, any(), osrLocal.stamp(NodeView.DEFAULT), BarrierType.NONE));
                 osrLocal.replaceAndDelete(load);
                 graph.addBeforeFixed(migrationEnd, load);
             }
@@ -609,7 +611,7 @@ public class DefaultHotSpotLoweringProvider extends DefaultJavaLoweringProvider
 
                 // load the displaced mark from the osr buffer
                 AddressNode addressDisplacedHeader = createOffsetAddress(graph, buffer, offsetDisplacedHeader);
-                ReadNode loadDisplacedHeader = graph.add(new ReadNode(addressDisplacedHeader, any(), lock.stamp(), BarrierType.NONE));
+                ReadNode loadDisplacedHeader = graph.add(new ReadNode(addressDisplacedHeader, any(), lock.stamp(NodeView.DEFAULT), BarrierType.NONE));
                 graph.addBeforeFixed(migrationEnd, loadDisplacedHeader);
 
                 // we need to initialize the stack slot for the lock
@@ -623,7 +625,7 @@ public class DefaultHotSpotLoweringProvider extends DefaultJavaLoweringProvider
 
                 // load the lock object from the osr buffer
                 AddressNode addressLockObject = createOffsetAddress(graph, buffer, offsetLockObject);
-                ReadNode loadObject = graph.add(new ReadNode(addressLockObject, any(), lock.stamp(), BarrierType.NONE));
+                ReadNode loadObject = graph.add(new ReadNode(addressLockObject, any(), lock.stamp(NodeView.DEFAULT), BarrierType.NONE));
                 lock.replaceAndDelete(loadObject);
                 graph.addBeforeFixed(migrationEnd, loadObject);
             }
@@ -688,7 +690,7 @@ public class DefaultHotSpotLoweringProvider extends DefaultJavaLoweringProvider
         }
 
         StructuredGraph graph = node.graph();
-        ForeignCallNode foreignCallNode = graph.add(new ForeignCallNode(foreignCalls, descriptor, node.stamp(), node.getArguments()));
+        ForeignCallNode foreignCallNode = graph.add(new ForeignCallNode(foreignCalls, descriptor, node.stamp(NodeView.DEFAULT), node.getArguments()));
         graph.replaceFixedWithFixed(node, foreignCallNode);
     }
 
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/meta/HotSpotGraphBuilderPlugins.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/meta/HotSpotGraphBuilderPlugins.java
index 5e1504ab14c..84dcaad7b8f 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/meta/HotSpotGraphBuilderPlugins.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/meta/HotSpotGraphBuilderPlugins.java
@@ -66,6 +66,7 @@ import org.graalvm.compiler.nodes.DynamicPiNode;
 import org.graalvm.compiler.nodes.FixedGuardNode;
 import org.graalvm.compiler.nodes.LogicNode;
 import org.graalvm.compiler.nodes.NamedLocationIdentity;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.PiNode;
 import org.graalvm.compiler.nodes.ValueNode;
 import org.graalvm.compiler.nodes.calc.AddNode;
@@ -318,7 +319,7 @@ public class HotSpotGraphBuilderPlugins {
     private static boolean readMetaspaceConstantPoolElement(GraphBuilderContext b, ValueNode constantPoolOop, ValueNode index, JavaKind elementKind, WordTypes wordTypes, GraalHotSpotVMConfig config) {
         ValueNode constants = getMetaspaceConstantPool(b, constantPoolOop, wordTypes, config);
         int shift = CodeUtil.log2(wordTypes.getWordKind().getByteCount());
-        ValueNode scaledIndex = b.add(new LeftShiftNode(IntegerConvertNode.convert(index, StampFactory.forKind(JavaKind.Long)), b.add(ConstantNode.forInt(shift))));
+        ValueNode scaledIndex = b.add(new LeftShiftNode(IntegerConvertNode.convert(index, StampFactory.forKind(JavaKind.Long), NodeView.DEFAULT), b.add(ConstantNode.forInt(shift))));
         ValueNode offset = b.add(new AddNode(scaledIndex, b.add(ConstantNode.forLong(config.constantPoolSize))));
         AddressNode elementAddress = b.add(new OffsetAddressNode(constants, offset));
         boolean notCompressible = false;
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/meta/HotSpotInvokeDynamicPlugin.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/meta/HotSpotInvokeDynamicPlugin.java
index ec2c62b3eb4..e9f01f60583 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/meta/HotSpotInvokeDynamicPlugin.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/meta/HotSpotInvokeDynamicPlugin.java
@@ -28,6 +28,7 @@ import org.graalvm.compiler.debug.GraalError;
 import org.graalvm.compiler.hotspot.nodes.aot.ResolveDynamicConstantNode;
 import org.graalvm.compiler.nodes.ConstantNode;
 import org.graalvm.compiler.nodes.FrameState;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.ValueNode;
 import org.graalvm.compiler.nodes.graphbuilderconf.InvokeDynamicPlugin;
 import org.graalvm.compiler.nodes.graphbuilderconf.GraphBuilderContext;
@@ -136,7 +137,7 @@ public class HotSpotInvokeDynamicPlugin implements InvokeDynamicPlugin {
 
         ConstantNode appendixNode = ConstantNode.forConstant(appendix, builder.getMetaAccess(), builder.getGraph());
 
-        Stamp appendixStamp = appendixNode.stamp();
+        Stamp appendixStamp = appendixNode.stamp(NodeView.DEFAULT);
         Stamp resolveStamp = treatAppendixAsConstant ? appendixStamp : appendixStamp.unrestricted();
         ResolveDynamicConstantNode resolveNode = new ResolveDynamicConstantNode(resolveStamp, appendixNode);
         ResolveDynamicConstantNode added = builder.append(resolveNode);
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/meta/HotSpotSnippetReflectionProvider.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/meta/HotSpotSnippetReflectionProvider.java
index f1c7359eba3..376325e759b 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/meta/HotSpotSnippetReflectionProvider.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/meta/HotSpotSnippetReflectionProvider.java
@@ -87,7 +87,7 @@ public class HotSpotSnippetReflectionProvider implements SnippetReflectionProvid
         // Need to test all fields since there no guarantee under the JMM
         // about the order in which these fields are written.
         GraalHotSpotVMConfig config = runtime.getVMConfig();
-        if (configType == null || wordTypesType == null || configType == null) {
+        if (configType == null || wordTypesType == null || runtimeType == null) {
             wordTypesType = wordTypes.getClass();
             runtimeType = runtime.getClass();
             configType = config.getClass();
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/meta/HotSpotWordOperationPlugin.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/meta/HotSpotWordOperationPlugin.java
index 354b8e1f8af..246491cef7d 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/meta/HotSpotWordOperationPlugin.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/meta/HotSpotWordOperationPlugin.java
@@ -40,6 +40,7 @@ import org.graalvm.compiler.hotspot.word.HotSpotOperation;
 import org.graalvm.compiler.hotspot.word.HotSpotOperation.HotspotOpcode;
 import org.graalvm.compiler.hotspot.word.PointerCastNode;
 import org.graalvm.compiler.nodes.LogicNode;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.ValueNode;
 import org.graalvm.compiler.nodes.calc.ConditionalNode;
 import org.graalvm.compiler.nodes.calc.IsNullNode;
@@ -102,23 +103,23 @@ class HotSpotWordOperationPlugin extends WordOperationPlugin {
                 HotspotOpcode opcode = operation.opcode();
                 ValueNode left = args[0];
                 ValueNode right = args[1];
-                assert left.stamp() instanceof MetaspacePointerStamp : left + " " + left.stamp();
-                assert right.stamp() instanceof MetaspacePointerStamp : right + " " + right.stamp();
+                assert left.stamp(NodeView.DEFAULT) instanceof MetaspacePointerStamp : left + " " + left.stamp(NodeView.DEFAULT);
+                assert right.stamp(NodeView.DEFAULT) instanceof MetaspacePointerStamp : right + " " + right.stamp(NodeView.DEFAULT);
                 assert opcode == POINTER_EQ || opcode == POINTER_NE;
 
                 PointerEqualsNode comparison = b.add(new PointerEqualsNode(left, right));
                 ValueNode eqValue = b.add(forBoolean(opcode == POINTER_EQ));
                 ValueNode neValue = b.add(forBoolean(opcode == POINTER_NE));
-                b.addPush(returnKind, ConditionalNode.create(comparison, eqValue, neValue));
+                b.addPush(returnKind, ConditionalNode.create(comparison, eqValue, neValue, NodeView.DEFAULT));
                 break;
 
             case IS_NULL:
                 assert args.length == 1;
                 ValueNode pointer = args[0];
-                assert pointer.stamp() instanceof MetaspacePointerStamp;
+                assert pointer.stamp(NodeView.DEFAULT) instanceof MetaspacePointerStamp;
 
                 LogicNode isNull = b.addWithInputs(IsNullNode.create(pointer));
-                b.addPush(returnKind, ConditionalNode.create(isNull, b.add(forBoolean(true)), b.add(forBoolean(false))));
+                b.addPush(returnKind, ConditionalNode.create(isNull, b.add(forBoolean(true)), b.add(forBoolean(false)), NodeView.DEFAULT));
                 break;
 
             case FROM_POINTER:
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/nodes/HotSpotCompressionNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/nodes/HotSpotCompressionNode.java
index ecd8ba0b811..b164b1226b5 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/nodes/HotSpotCompressionNode.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/nodes/HotSpotCompressionNode.java
@@ -32,6 +32,7 @@ import org.graalvm.compiler.graph.NodeClass;
 import org.graalvm.compiler.hotspot.nodes.type.HotSpotNarrowOopStamp;
 import org.graalvm.compiler.nodeinfo.NodeInfo;
 import org.graalvm.compiler.nodes.CompressionNode;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.ValueNode;
 
 import jdk.vm.ci.hotspot.HotSpotCompressedNullConstant;
@@ -45,7 +46,7 @@ public final class HotSpotCompressionNode extends CompressionNode {
     public static final NodeClass<HotSpotCompressionNode> TYPE = NodeClass.create(HotSpotCompressionNode.class);
 
     public HotSpotCompressionNode(CompressionOp op, ValueNode input, CompressEncoding encoding) {
-        super(TYPE, op, input, HotSpotNarrowOopStamp.mkStamp(op, input.stamp(), encoding), encoding);
+        super(TYPE, op, input, HotSpotNarrowOopStamp.mkStamp(op, input.stamp(NodeView.DEFAULT), encoding), encoding);
     }
 
     public static HotSpotCompressionNode compress(ValueNode input, CompressEncoding encoding) {
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/nodes/aot/InitializeKlassNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/nodes/aot/InitializeKlassNode.java
index aff5ce9b872..63674b901da 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/nodes/aot/InitializeKlassNode.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/nodes/aot/InitializeKlassNode.java
@@ -29,6 +29,7 @@ import static org.graalvm.compiler.nodeinfo.NodeSize.SIZE_16;
 import org.graalvm.compiler.graph.NodeClass;
 import org.graalvm.compiler.nodeinfo.NodeInfo;
 import org.graalvm.compiler.nodes.DeoptimizingFixedWithNextNode;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.ValueNode;
 import org.graalvm.compiler.nodes.memory.MemoryCheckpoint;
 import org.graalvm.compiler.nodes.spi.Lowerable;
@@ -42,7 +43,7 @@ public class InitializeKlassNode extends DeoptimizingFixedWithNextNode implement
     @Input ValueNode value;
 
     public InitializeKlassNode(ValueNode value) {
-        super(TYPE, value.stamp());
+        super(TYPE, value.stamp(NodeView.DEFAULT));
         this.value = value;
     }
 
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/nodes/aot/InitializeKlassStubCall.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/nodes/aot/InitializeKlassStubCall.java
index 2630a60dcd3..b7af9ab772a 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/nodes/aot/InitializeKlassStubCall.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/nodes/aot/InitializeKlassStubCall.java
@@ -36,6 +36,7 @@ import org.graalvm.compiler.nodeinfo.InputType;
 import org.graalvm.compiler.nodeinfo.NodeInfo;
 import org.graalvm.compiler.nodes.DeoptimizingNode;
 import org.graalvm.compiler.nodes.FrameState;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.ValueNode;
 import org.graalvm.compiler.nodes.memory.AbstractMemoryCheckpoint;
 import org.graalvm.compiler.nodes.memory.MemoryCheckpoint;
@@ -61,7 +62,7 @@ public class InitializeKlassStubCall extends AbstractMemoryCheckpoint implements
     protected Constant constant;
 
     protected InitializeKlassStubCall(ValueNode value, ValueNode string) {
-        super(TYPE, value.stamp());
+        super(TYPE, value.stamp(NodeView.DEFAULT));
         this.value = value;
         this.string = string;
     }
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/nodes/aot/LoadConstantIndirectlyFixedNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/nodes/aot/LoadConstantIndirectlyFixedNode.java
index 2603e78cee4..85fac77d08b 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/nodes/aot/LoadConstantIndirectlyFixedNode.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/nodes/aot/LoadConstantIndirectlyFixedNode.java
@@ -36,6 +36,7 @@ import org.graalvm.compiler.hotspot.word.KlassPointer;
 import org.graalvm.compiler.hotspot.word.MethodPointer;
 import org.graalvm.compiler.nodeinfo.NodeInfo;
 import org.graalvm.compiler.nodes.FixedWithNextNode;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.ValueNode;
 import org.graalvm.compiler.nodes.spi.LIRLowerable;
 import org.graalvm.compiler.nodes.spi.NodeLIRBuilderTool;
@@ -56,14 +57,14 @@ public class LoadConstantIndirectlyFixedNode extends FixedWithNextNode implement
     protected HotSpotConstantLoadAction action;
 
     public LoadConstantIndirectlyFixedNode(ValueNode value) {
-        super(TYPE, value.stamp());
+        super(TYPE, value.stamp(NodeView.DEFAULT));
         this.value = value;
         this.constant = null;
         this.action = HotSpotConstantLoadAction.RESOLVE;
     }
 
     public LoadConstantIndirectlyFixedNode(ValueNode value, HotSpotConstantLoadAction action) {
-        super(TYPE, value.stamp());
+        super(TYPE, value.stamp(NodeView.DEFAULT));
         this.value = value;
         this.constant = null;
         this.action = action;
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/nodes/aot/LoadConstantIndirectlyNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/nodes/aot/LoadConstantIndirectlyNode.java
index 8758c02cba3..7cba4506833 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/nodes/aot/LoadConstantIndirectlyNode.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/nodes/aot/LoadConstantIndirectlyNode.java
@@ -34,6 +34,7 @@ import org.graalvm.compiler.hotspot.HotSpotLIRGenerator;
 import org.graalvm.compiler.hotspot.meta.HotSpotConstantLoadAction;
 import org.graalvm.compiler.hotspot.word.KlassPointer;
 import org.graalvm.compiler.nodeinfo.NodeInfo;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.ValueNode;
 import org.graalvm.compiler.nodes.calc.FloatingNode;
 import org.graalvm.compiler.nodes.spi.LIRLowerable;
@@ -55,14 +56,14 @@ public class LoadConstantIndirectlyNode extends FloatingNode implements Canonica
     protected HotSpotConstantLoadAction action;
 
     public LoadConstantIndirectlyNode(ValueNode value) {
-        super(TYPE, value.stamp());
+        super(TYPE, value.stamp(NodeView.DEFAULT));
         this.value = value;
         this.constant = null;
         this.action = HotSpotConstantLoadAction.RESOLVE;
     }
 
     public LoadConstantIndirectlyNode(ValueNode value, HotSpotConstantLoadAction action) {
-        super(TYPE, value.stamp());
+        super(TYPE, value.stamp(NodeView.DEFAULT));
         this.value = value;
         this.constant = null;
         this.action = action;
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/nodes/aot/ResolveConstantNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/nodes/aot/ResolveConstantNode.java
index 73202626047..c6b131658aa 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/nodes/aot/ResolveConstantNode.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/nodes/aot/ResolveConstantNode.java
@@ -29,6 +29,7 @@ import org.graalvm.compiler.graph.NodeClass;
 import org.graalvm.compiler.hotspot.meta.HotSpotConstantLoadAction;
 import org.graalvm.compiler.nodeinfo.NodeInfo;
 import org.graalvm.compiler.nodes.DeoptimizingFixedWithNextNode;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.ValueNode;
 import org.graalvm.compiler.nodes.spi.Lowerable;
 import org.graalvm.compiler.nodes.spi.LoweringTool;
@@ -41,13 +42,13 @@ public class ResolveConstantNode extends DeoptimizingFixedWithNextNode implement
     protected HotSpotConstantLoadAction action;
 
     public ResolveConstantNode(ValueNode value, HotSpotConstantLoadAction action) {
-        super(TYPE, value.stamp());
+        super(TYPE, value.stamp(NodeView.DEFAULT));
         this.value = value;
         this.action = action;
     }
 
     public ResolveConstantNode(ValueNode value) {
-        super(TYPE, value.stamp());
+        super(TYPE, value.stamp(NodeView.DEFAULT));
         this.value = value;
         this.action = HotSpotConstantLoadAction.RESOLVE;
     }
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/nodes/aot/ResolveConstantStubCall.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/nodes/aot/ResolveConstantStubCall.java
index 6a6393b1990..912d295f2d4 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/nodes/aot/ResolveConstantStubCall.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/nodes/aot/ResolveConstantStubCall.java
@@ -36,6 +36,7 @@ import org.graalvm.compiler.hotspot.nodes.DeoptimizingStubCall;
 import org.graalvm.compiler.hotspot.word.KlassPointer;
 import org.graalvm.compiler.lir.LIRFrameState;
 import org.graalvm.compiler.nodeinfo.NodeInfo;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.ValueNode;
 import org.graalvm.compiler.nodes.spi.LIRLowerable;
 import org.graalvm.compiler.nodes.spi.NodeLIRBuilderTool;
@@ -59,14 +60,14 @@ public class ResolveConstantStubCall extends DeoptimizingStubCall implements Can
     protected HotSpotConstantLoadAction action;
 
     public ResolveConstantStubCall(ValueNode value, ValueNode string) {
-        super(TYPE, value.stamp());
+        super(TYPE, value.stamp(NodeView.DEFAULT));
         this.value = value;
         this.string = string;
         this.action = HotSpotConstantLoadAction.RESOLVE;
     }
 
     public ResolveConstantStubCall(ValueNode value, ValueNode string, HotSpotConstantLoadAction action) {
-        super(TYPE, value.stamp());
+        super(TYPE, value.stamp(NodeView.DEFAULT));
         this.value = value;
         this.string = string;
         this.action = action;
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/nodes/aot/ResolveDynamicStubCall.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/nodes/aot/ResolveDynamicStubCall.java
index dd3a558d79e..f811e3d923f 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/nodes/aot/ResolveDynamicStubCall.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/nodes/aot/ResolveDynamicStubCall.java
@@ -36,6 +36,7 @@ import org.graalvm.compiler.nodeinfo.InputType;
 import org.graalvm.compiler.nodeinfo.NodeInfo;
 import org.graalvm.compiler.nodes.DeoptimizingNode;
 import org.graalvm.compiler.nodes.FrameState;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.ValueNode;
 import org.graalvm.compiler.nodes.spi.LIRLowerable;
 import org.graalvm.compiler.nodes.spi.NodeLIRBuilderTool;
@@ -59,7 +60,7 @@ public class ResolveDynamicStubCall extends AbstractMemoryCheckpoint implements
     protected Constant constant;
 
     public ResolveDynamicStubCall(ValueNode value) {
-        super(TYPE, value.stamp());
+        super(TYPE, value.stamp(NodeView.DEFAULT));
         this.value = value;
     }
 
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/phases/OnStackReplacementPhase.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/phases/OnStackReplacementPhase.java
index 116ef56436b..e790daea4fd 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/phases/OnStackReplacementPhase.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/phases/OnStackReplacementPhase.java
@@ -50,6 +50,7 @@ import org.graalvm.compiler.nodes.FixedNode;
 import org.graalvm.compiler.nodes.FrameState;
 import org.graalvm.compiler.nodes.LogicNode;
 import org.graalvm.compiler.nodes.LoopBeginNode;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.ParameterNode;
 import org.graalvm.compiler.nodes.PiNode;
 import org.graalvm.compiler.nodes.StartNode;
@@ -185,8 +186,8 @@ public class OnStackReplacementPhase extends Phase {
                  * (if a branch was not parsed for example). In cases when this is possible, we
                  * insert a guard and narrow the OSRLocal stamp at its usages.
                  */
-                Stamp narrowedStamp = proxy.value().stamp();
-                Stamp unrestrictedStamp = proxy.stamp().unrestricted();
+                Stamp narrowedStamp = proxy.value().stamp(NodeView.DEFAULT);
+                Stamp unrestrictedStamp = proxy.stamp(NodeView.DEFAULT).unrestricted();
                 ValueNode osrLocal;
                 if (i >= localsSize) {
                     osrLocal = graph.addOrUnique(new OSRLockNode(i - localsSize, unrestrictedStamp));
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/phases/profiling/FinalizeProfileNodesPhase.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/phases/profiling/FinalizeProfileNodesPhase.java
index 07576103017..b3781b00f0e 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/phases/profiling/FinalizeProfileNodesPhase.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/phases/profiling/FinalizeProfileNodesPhase.java
@@ -35,6 +35,7 @@ import org.graalvm.compiler.hotspot.nodes.profiling.RandomSeedNode;
 import org.graalvm.compiler.nodes.ConstantNode;
 import org.graalvm.compiler.nodes.InvokeNode;
 import org.graalvm.compiler.nodes.LoopBeginNode;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.PhiNode;
 import org.graalvm.compiler.nodes.StructuredGraph;
 import org.graalvm.compiler.nodes.ValueNode;
@@ -130,7 +131,7 @@ public class FinalizeProfileNodesPhase extends BasePhase<PhaseContext> {
                 LoopBeginNode loopBegin = (LoopBeginNode) loop.getHeader().getBeginNode();
                 random = loopRandomValueCache.get(loopBegin);
                 if (random == null) {
-                    PhiNode phi = graph.addWithoutUnique(new ValuePhiNode(seed.stamp(), loopBegin));
+                    PhiNode phi = graph.addWithoutUnique(new ValuePhiNode(seed.stamp(NodeView.DEFAULT), loopBegin));
                     phi.addInput(seed);
                     // X_{n+1} = a*X_n + c, using glibc-like constants
                     ValueNode a = ConstantNode.forInt(1103515245, graph);
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/ClassGetHubNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/ClassGetHubNode.java
index a385134ccb4..62bf05e689e 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/ClassGetHubNode.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/ClassGetHubNode.java
@@ -38,6 +38,7 @@ import org.graalvm.compiler.hotspot.nodes.type.KlassPointerStamp;
 import org.graalvm.compiler.hotspot.word.KlassPointer;
 import org.graalvm.compiler.nodeinfo.NodeInfo;
 import org.graalvm.compiler.nodes.ConstantNode;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.PiNode;
 import org.graalvm.compiler.nodes.ValueNode;
 import org.graalvm.compiler.nodes.calc.ConvertNode;
@@ -118,7 +119,7 @@ public final class ClassGetHubNode extends FloatingNode implements Lowerable, Ca
 
     @Override
     public Node canonical(CanonicalizerTool tool) {
-        return canonical(this, tool.getMetaAccess(), tool.getConstantReflection(), tool.allUsagesAvailable(), stamp(), clazz);
+        return canonical(this, tool.getMetaAccess(), tool.getConstantReflection(), tool.allUsagesAvailable(), stamp(NodeView.DEFAULT), clazz);
     }
 
     @Override
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/HotSpotReplacementsUtil.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/HotSpotReplacementsUtil.java
index 29a508882d8..0966cf6f1de 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/HotSpotReplacementsUtil.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/HotSpotReplacementsUtil.java
@@ -45,6 +45,7 @@ import org.graalvm.compiler.nodes.CanonicalizableLocation;
 import org.graalvm.compiler.nodes.CompressionNode;
 import org.graalvm.compiler.nodes.ConstantNode;
 import org.graalvm.compiler.nodes.NamedLocationIdentity;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.ValueNode;
 import org.graalvm.compiler.nodes.extended.ForeignCallNode;
 import org.graalvm.compiler.nodes.extended.LoadHubNode;
@@ -124,7 +125,7 @@ public class HotSpotReplacementsUtil {
                     AddressNode address = access.getAddress();
                     if (address instanceof OffsetAddressNode) {
                         OffsetAddressNode offset = (OffsetAddressNode) address;
-                        assert offset.getBase().stamp().isCompatible(read.stamp());
+                        assert offset.getBase().stamp(NodeView.DEFAULT).isCompatible(read.stamp(NodeView.DEFAULT));
                         return offset.getBase();
                     }
                 }
@@ -370,8 +371,8 @@ public class HotSpotReplacementsUtil {
         public ValueNode canonicalizeRead(ValueNode read, AddressNode location, ValueNode object, CanonicalizerTool tool) {
             ValueNode javaObject = findReadHub(object);
             if (javaObject != null) {
-                if (javaObject.stamp() instanceof ObjectStamp) {
-                    ObjectStamp stamp = (ObjectStamp) javaObject.stamp();
+                if (javaObject.stamp(NodeView.DEFAULT) instanceof ObjectStamp) {
+                    ObjectStamp stamp = (ObjectStamp) javaObject.stamp(NodeView.DEFAULT);
                     HotSpotResolvedObjectType type = (HotSpotResolvedObjectType) stamp.javaType(tool.getMetaAccess());
                     if (type.isArray() && !type.getComponentType().isPrimitive()) {
                         int layout = type.layoutHelper();
@@ -437,7 +438,7 @@ public class HotSpotReplacementsUtil {
         public ValueNode canonicalizeRead(ValueNode read, AddressNode location, ValueNode object, CanonicalizerTool tool) {
             TypeReference constantType = StampTool.typeReferenceOrNull(object);
             if (constantType != null && constantType.isExact()) {
-                return ConstantNode.forConstant(read.stamp(), tool.getConstantReflection().asObjectHub(constantType.getType()), tool.getMetaAccess());
+                return ConstantNode.forConstant(read.stamp(NodeView.DEFAULT), tool.getConstantReflection().asObjectHub(constantType.getType()), tool.getMetaAccess());
             }
             return read;
         }
@@ -448,7 +449,8 @@ public class HotSpotReplacementsUtil {
         public ValueNode canonicalizeRead(ValueNode read, AddressNode location, ValueNode object, CanonicalizerTool tool) {
             TypeReference constantType = StampTool.typeReferenceOrNull(object);
             if (constantType != null && constantType.isExact()) {
-                return ConstantNode.forConstant(read.stamp(), ((HotSpotMetaspaceConstant) tool.getConstantReflection().asObjectHub(constantType.getType())).compress(), tool.getMetaAccess());
+                return ConstantNode.forConstant(read.stamp(NodeView.DEFAULT), ((HotSpotMetaspaceConstant) tool.getConstantReflection().asObjectHub(constantType.getType())).compress(),
+                                tool.getMetaAccess());
             }
             return read;
         }
@@ -962,7 +964,7 @@ public class HotSpotReplacementsUtil {
                         AssumptionResult<ResolvedJavaType> leafType = element.findLeafConcreteSubtype();
                         if (leafType != null && leafType.canRecordTo(assumptions)) {
                             leafType.recordTo(assumptions);
-                            return ConstantNode.forConstant(read.stamp(), tool.getConstantReflection().asObjectHub(leafType.getResult()), tool.getMetaAccess());
+                            return ConstantNode.forConstant(read.stamp(NodeView.DEFAULT), tool.getConstantReflection().asObjectHub(leafType.getResult()), tool.getMetaAccess());
                         }
                     }
                 }
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/IdentityHashCodeNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/IdentityHashCodeNode.java
index a28f4559f67..66971954ac3 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/IdentityHashCodeNode.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/IdentityHashCodeNode.java
@@ -35,6 +35,7 @@ import org.graalvm.compiler.graph.spi.CanonicalizerTool;
 import org.graalvm.compiler.nodeinfo.NodeInfo;
 import org.graalvm.compiler.nodes.ConstantNode;
 import org.graalvm.compiler.nodes.FixedWithNextNode;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.ValueNode;
 import org.graalvm.compiler.nodes.memory.MemoryCheckpoint;
 import org.graalvm.compiler.nodes.spi.Lowerable;
@@ -65,7 +66,7 @@ public class IdentityHashCodeNode extends FixedWithNextNode implements Canonical
     @Override
     public Node canonical(CanonicalizerTool tool) {
         if (object.isConstant()) {
-            assert object.stamp() instanceof AbstractObjectStamp;
+            assert object.stamp(NodeView.DEFAULT) instanceof AbstractObjectStamp;
             JavaConstant c = (JavaConstant) object.asConstant();
             if (ImmutableCode.getValue(tool.getOptions())) {
                 return this;
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/InstanceOfSnippets.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/InstanceOfSnippets.java
index 29453e464ce..1ca01ebb9b0 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/InstanceOfSnippets.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/InstanceOfSnippets.java
@@ -53,6 +53,7 @@ import org.graalvm.compiler.hotspot.replacements.TypeCheckSnippetUtils.Hints;
 import org.graalvm.compiler.hotspot.word.KlassPointer;
 import org.graalvm.compiler.nodes.ConstantNode;
 import org.graalvm.compiler.nodes.DeoptimizeNode;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.PiNode;
 import org.graalvm.compiler.nodes.SnippetAnchorNode;
 import org.graalvm.compiler.nodes.StructuredGraph;
@@ -332,7 +333,7 @@ public class InstanceOfSnippets implements Snippets {
             } else if (replacer.instanceOf instanceof ClassIsAssignableFromNode) {
                 ClassIsAssignableFromNode isAssignable = (ClassIsAssignableFromNode) replacer.instanceOf;
                 Arguments args = new Arguments(isAssignableFrom, isAssignable.graph().getGuardsStage(), tool.getLoweringStage());
-                assert ((ObjectStamp) isAssignable.getThisClass().stamp()).nonNull();
+                assert ((ObjectStamp) isAssignable.getThisClass().stamp(NodeView.DEFAULT)).nonNull();
                 args.add("thisClassNonNull", isAssignable.getThisClass());
                 args.add("otherClass", isAssignable.getOtherClass());
                 args.add("trueValue", replacer.trueValue);
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/KlassLayoutHelperNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/KlassLayoutHelperNode.java
index 7cbcd3887f9..bdde73fd117 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/KlassLayoutHelperNode.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/KlassLayoutHelperNode.java
@@ -38,6 +38,7 @@ import org.graalvm.compiler.graph.spi.CanonicalizerTool;
 import org.graalvm.compiler.hotspot.GraalHotSpotVMConfig;
 import org.graalvm.compiler.nodeinfo.NodeInfo;
 import org.graalvm.compiler.nodes.ConstantNode;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.ValueNode;
 import org.graalvm.compiler.nodes.calc.FloatingNode;
 import org.graalvm.compiler.nodes.extended.LoadHubNode;
@@ -83,7 +84,7 @@ public final class KlassLayoutHelperNode extends FloatingNode implements Canonic
     public boolean inferStamp() {
         if (klass instanceof LoadHubNode) {
             LoadHubNode hub = (LoadHubNode) klass;
-            Stamp hubStamp = hub.getValue().stamp();
+            Stamp hubStamp = hub.getValue().stamp(NodeView.DEFAULT);
             if (hubStamp instanceof ObjectStamp) {
                 ObjectStamp objectStamp = (ObjectStamp) hubStamp;
                 ResolvedJavaType type = objectStamp.type();
@@ -108,7 +109,7 @@ public final class KlassLayoutHelperNode extends FloatingNode implements Canonic
         if (tool.allUsagesAvailable() && hasNoUsages()) {
             return null;
         } else {
-            return canonical(this, config, klass, stamp(), tool.getConstantReflection(), tool.getMetaAccess());
+            return canonical(this, config, klass, stamp(NodeView.DEFAULT), tool.getConstantReflection(), tool.getMetaAccess());
         }
     }
 
@@ -123,7 +124,7 @@ public final class KlassLayoutHelperNode extends FloatingNode implements Canonic
         }
         if (klass instanceof LoadHubNode) {
             LoadHubNode hub = (LoadHubNode) klass;
-            Stamp hubStamp = hub.getValue().stamp();
+            Stamp hubStamp = hub.getValue().stamp(NodeView.DEFAULT);
             if (hubStamp instanceof ObjectStamp) {
                 ObjectStamp ostamp = (ObjectStamp) hubStamp;
                 HotSpotResolvedObjectType type = (HotSpotResolvedObjectType) ostamp.type();
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/MonitorSnippets.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/MonitorSnippets.java
index 83a6060d95d..40fe62ea766 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/MonitorSnippets.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/MonitorSnippets.java
@@ -99,6 +99,7 @@ import org.graalvm.compiler.nodes.DeoptimizeNode;
 import org.graalvm.compiler.nodes.FrameState;
 import org.graalvm.compiler.nodes.InvokeNode;
 import org.graalvm.compiler.nodes.NamedLocationIdentity;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.ReturnNode;
 import org.graalvm.compiler.nodes.StructuredGraph;
 import org.graalvm.compiler.nodes.ValueNode;
@@ -736,7 +737,7 @@ public class MonitorSnippets implements Snippets {
             StructuredGraph graph = monitorenterNode.graph();
             checkBalancedMonitors(graph, tool);
 
-            assert ((ObjectStamp) monitorenterNode.object().stamp()).nonNull();
+            assert ((ObjectStamp) monitorenterNode.object().stamp(NodeView.DEFAULT)).nonNull();
 
             Arguments args;
             if (useFastLocking) {
@@ -781,7 +782,7 @@ public class MonitorSnippets implements Snippets {
         }
 
         public static boolean isTracingEnabledForType(ValueNode object) {
-            ResolvedJavaType type = StampTool.typeOrNull(object.stamp());
+            ResolvedJavaType type = StampTool.typeOrNull(object.stamp(NodeView.DEFAULT));
             String filter = TraceMonitorsTypeFilter.getValue(object.getOptions());
             if (filter == null) {
                 return false;
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/ObjectCloneNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/ObjectCloneNode.java
index b12cf66155f..02bf3438173 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/ObjectCloneNode.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/ObjectCloneNode.java
@@ -31,6 +31,7 @@ import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.graph.NodeClass;
 import org.graalvm.compiler.nodeinfo.NodeInfo;
 import org.graalvm.compiler.nodes.CallTargetNode.InvokeKind;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.ParameterNode;
 import org.graalvm.compiler.nodes.ReturnNode;
 import org.graalvm.compiler.nodes.StructuredGraph;
@@ -62,14 +63,14 @@ public final class ObjectCloneNode extends BasicObjectCloneNode implements Virtu
 
     @Override
     protected Stamp computeStamp(ValueNode object) {
-        if (getConcreteType(object.stamp()) != null) {
-            return AbstractPointerStamp.pointerNonNull(object.stamp());
+        if (getConcreteType(object.stamp(NodeView.DEFAULT)) != null) {
+            return AbstractPointerStamp.pointerNonNull(object.stamp(NodeView.DEFAULT));
         }
         /*
          * If this call can't be intrinsified don't report a non-null stamp, otherwise the stamp
          * would change when this is lowered back to an invoke and we might lose a null check.
          */
-        return AbstractPointerStamp.pointerMaybeNull(object.stamp());
+        return AbstractPointerStamp.pointerMaybeNull(object.stamp(NodeView.DEFAULT));
     }
 
     @Override
@@ -91,16 +92,16 @@ public final class ObjectCloneNode extends BasicObjectCloneNode implements Virtu
                     }
 
                     assert snippetGraph != null : "ObjectCloneSnippets should be installed";
-                    assert getConcreteType(stamp()) != null;
+                    assert getConcreteType(stamp(NodeView.DEFAULT)) != null;
                     return lowerReplacement((StructuredGraph) snippetGraph.copy(getDebug()), tool);
                 }
                 assert false : "unhandled array type " + type.getComponentType().getJavaKind();
             } else {
                 Assumptions assumptions = graph().getAssumptions();
-                type = getConcreteType(getObject().stamp());
+                type = getConcreteType(getObject().stamp(NodeView.DEFAULT));
                 if (type != null) {
                     StructuredGraph newGraph = new StructuredGraph.Builder(graph().getOptions(), graph().getDebug(), AllowAssumptions.ifNonNull(assumptions)).build();
-                    ParameterNode param = newGraph.addWithoutUnique(new ParameterNode(0, StampPair.createSingle(getObject().stamp())));
+                    ParameterNode param = newGraph.addWithoutUnique(new ParameterNode(0, StampPair.createSingle(getObject().stamp(NodeView.DEFAULT))));
                     NewInstanceNode newInstance = newGraph.add(new NewInstanceNode(type, true));
                     newGraph.addAfterFixed(newGraph.start(), newInstance);
                     ReturnNode returnNode = newGraph.add(new ReturnNode(newInstance));
@@ -111,12 +112,12 @@ public final class ObjectCloneNode extends BasicObjectCloneNode implements Virtu
                         newGraph.addBeforeFixed(returnNode, load);
                         newGraph.addBeforeFixed(returnNode, newGraph.add(new StoreFieldNode(newInstance, field, load)));
                     }
-                    assert getConcreteType(stamp()) != null;
+                    assert getConcreteType(stamp(NodeView.DEFAULT)) != null;
                     return lowerReplacement(newGraph, tool);
                 }
             }
         }
-        assert getConcreteType(stamp()) == null;
+        assert getConcreteType(stamp(NodeView.DEFAULT)) == null;
         return null;
     }
 }
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/WriteBarrierSnippets.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/WriteBarrierSnippets.java
index b4cdc08ace8..9a3c4011948 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/WriteBarrierSnippets.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/WriteBarrierSnippets.java
@@ -66,6 +66,7 @@ import org.graalvm.compiler.hotspot.nodes.SerialArrayRangeWriteBarrier;
 import org.graalvm.compiler.hotspot.nodes.SerialWriteBarrier;
 import org.graalvm.compiler.hotspot.nodes.VMErrorNode;
 import org.graalvm.compiler.nodes.NamedLocationIdentity;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.StructuredGraph;
 import org.graalvm.compiler.nodes.ValueNode;
 import org.graalvm.compiler.nodes.extended.FixedValueAnchorNode;
@@ -447,7 +448,7 @@ public class WriteBarrierSnippets implements Snippets {
             }
 
             ValueNode expected = writeBarrierPre.getExpectedObject();
-            if (expected != null && expected.stamp() instanceof NarrowOopStamp) {
+            if (expected != null && expected.stamp(NodeView.DEFAULT) instanceof NarrowOopStamp) {
                 assert oopEncoding != null;
                 expected = HotSpotCompressionNode.uncompress(expected, oopEncoding);
             }
@@ -472,7 +473,7 @@ public class WriteBarrierSnippets implements Snippets {
             }
 
             ValueNode expected = readBarrier.getExpectedObject();
-            if (expected != null && expected.stamp() instanceof NarrowOopStamp) {
+            if (expected != null && expected.stamp(NodeView.DEFAULT) instanceof NarrowOopStamp) {
                 assert oopEncoding != null;
                 expected = HotSpotCompressionNode.uncompress(expected, oopEncoding);
             }
@@ -503,7 +504,7 @@ public class WriteBarrierSnippets implements Snippets {
             }
 
             ValueNode value = writeBarrierPost.getValue();
-            if (value.stamp() instanceof NarrowOopStamp) {
+            if (value.stamp(NodeView.DEFAULT) instanceof NarrowOopStamp) {
                 assert oopEncoding != null;
                 value = HotSpotCompressionNode.uncompress(value, oopEncoding);
             }
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/arraycopy/ArrayCopyCallNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/arraycopy/ArrayCopyCallNode.java
index 8f4e370020f..863db348004 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/arraycopy/ArrayCopyCallNode.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/arraycopy/ArrayCopyCallNode.java
@@ -44,6 +44,7 @@ import org.graalvm.compiler.nodeinfo.NodeInfo;
 import org.graalvm.compiler.nodes.ConstantNode;
 import org.graalvm.compiler.nodes.FixedWithNextNode;
 import org.graalvm.compiler.nodes.NamedLocationIdentity;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.StructuredGraph;
 import org.graalvm.compiler.nodes.ValueNode;
 import org.graalvm.compiler.nodes.calc.AddNode;
@@ -143,7 +144,7 @@ public final class ArrayCopyCallNode extends AbstractMemoryCheckpoint implements
         FixedWithNextNode basePtr = graph().add(new GetObjectAddressNode(base));
         graph().addBeforeFixed(this, basePtr);
         Stamp wordStamp = StampFactory.forKind(runtime.getTarget().wordJavaKind);
-        ValueNode wordPos = IntegerConvertNode.convert(pos, wordStamp, graph());
+        ValueNode wordPos = IntegerConvertNode.convert(pos, wordStamp, graph(), NodeView.DEFAULT);
         int shift = CodeUtil.log2(getArrayIndexScale(elementKind));
         ValueNode scaledIndex = graph().unique(new LeftShiftNode(wordPos, ConstantNode.forInt(shift, graph())));
         ValueNode offset = graph().unique(new AddNode(scaledIndex, ConstantNode.forIntegerStamp(wordStamp, getArrayBaseOffset(elementKind), graph())));
@@ -160,8 +161,8 @@ public final class ArrayCopyCallNode extends AbstractMemoryCheckpoint implements
             ValueNode srcAddr = computeBase(getSource(), getSourcePosition());
             ValueNode destAddr = computeBase(getDestination(), getDestinationPosition());
             ValueNode len = getLength();
-            if (len.stamp().getStackKind() != JavaKind.Long) {
-                len = IntegerConvertNode.convert(len, StampFactory.forKind(JavaKind.Long), graph());
+            if (len.stamp(NodeView.DEFAULT).getStackKind() != JavaKind.Long) {
+                len = IntegerConvertNode.convert(len, StampFactory.forKind(JavaKind.Long), graph(), NodeView.DEFAULT);
             }
             ForeignCallNode call = graph.add(new ForeignCallNode(runtime.getHostBackend().getForeignCalls(), desc, srcAddr, destAddr, len));
             call.setStateAfter(stateAfter());
@@ -232,8 +233,8 @@ public final class ArrayCopyCallNode extends AbstractMemoryCheckpoint implements
             // Can treat as disjoint
             disjoint = true;
         }
-        PrimitiveConstant constantSrc = (PrimitiveConstant) srcPos.stamp().asConstant();
-        PrimitiveConstant constantDst = (PrimitiveConstant) destPos.stamp().asConstant();
+        PrimitiveConstant constantSrc = (PrimitiveConstant) srcPos.stamp(NodeView.DEFAULT).asConstant();
+        PrimitiveConstant constantDst = (PrimitiveConstant) destPos.stamp(NodeView.DEFAULT).asConstant();
         if (constantSrc != null && constantDst != null) {
             if (!aligned) {
                 aligned = isHeapWordAligned(constantSrc, componentKind) && isHeapWordAligned(constantDst, componentKind);
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/arraycopy/ArrayCopySnippets.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/arraycopy/ArrayCopySnippets.java
index a8c59ade39b..216fb279e0f 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/arraycopy/ArrayCopySnippets.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/arraycopy/ArrayCopySnippets.java
@@ -54,6 +54,7 @@ import org.graalvm.compiler.nodes.DeoptimizeNode;
 import org.graalvm.compiler.nodes.Invoke;
 import org.graalvm.compiler.nodes.InvokeNode;
 import org.graalvm.compiler.nodes.NamedLocationIdentity;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.PiNode;
 import org.graalvm.compiler.nodes.StructuredGraph;
 import org.graalvm.compiler.nodes.ValueNode;
@@ -90,11 +91,12 @@ public class ArrayCopySnippets implements Snippets {
 
     private enum ArrayCopyTypeCheck {
         UNDEFINED_ARRAY_TYPE_CHECK,
-        // we know that both objects are arrays and have the same type
+        // either we know that both objects are arrays and have the same type,
+        // or we apply generic array copy snippet, which enforces type check
         NO_ARRAY_TYPE_CHECK,
         // can be used when we know that one of the objects is a primitive array
         HUB_BASED_ARRAY_TYPE_CHECK,
-        // must be used when we don't have sufficient information to use one of the others
+        // can be used when we know that one of the objects is an object array
         LAYOUT_HELPER_BASED_ARRAY_TYPE_CHECK
     }
 
@@ -232,18 +234,18 @@ public class ArrayCopySnippets implements Snippets {
 
     @Snippet(allowPartialIntrinsicArgumentMismatch = true)
     public static void genericArraycopyWithSlowPathWork(Object src, int srcPos, Object dest, int destPos, int length, @ConstantParameter Counters counters) {
-        if (probability(FREQUENT_PROBABILITY, length > 0)) {
-            counters.genericArraycopyDifferentTypeCounter.inc();
-            counters.genericArraycopyDifferentTypeCopiedCounter.add(length);
-            int copiedElements = GenericArrayCopyCallNode.genericArraycopy(src, srcPos, dest, destPos, length);
-            if (probability(SLOW_PATH_PROBABILITY, copiedElements != 0)) {
-                /*
-                 * the stub doesn't throw the ArrayStoreException, but returns the number of copied
-                 * elements (xor'd with -1).
-                 */
-                copiedElements ^= -1;
-                System.arraycopy(src, srcPos + copiedElements, dest, destPos + copiedElements, length - copiedElements);
-            }
+        // The length > 0 check should not be placed here because generic array copy stub should
+        // enforce type check. This is fine performance-wise because this snippet is rarely used.
+        counters.genericArraycopyDifferentTypeCounter.inc();
+        counters.genericArraycopyDifferentTypeCopiedCounter.add(length);
+        int copiedElements = GenericArrayCopyCallNode.genericArraycopy(src, srcPos, dest, destPos, length);
+        if (probability(SLOW_PATH_PROBABILITY, copiedElements != 0)) {
+            /*
+             * the stub doesn't throw the ArrayStoreException, but returns the number of copied
+             * elements (xor'd with -1).
+             */
+            copiedElements ^= -1;
+            System.arraycopy(src, srcPos + copiedElements, dest, destPos + copiedElements, length - copiedElements);
         }
     }
 
@@ -275,21 +277,14 @@ public class ArrayCopySnippets implements Snippets {
         } else if (arrayTypeCheck == ArrayCopyTypeCheck.LAYOUT_HELPER_BASED_ARRAY_TYPE_CHECK) {
             KlassPointer srcHub = loadHub(nonNullSrc);
             KlassPointer destHub = loadHub(nonNullDest);
-            checkArrayType(srcHub);
-            checkArrayType(destHub);
+            if (probability(SLOW_PATH_PROBABILITY, readLayoutHelper(srcHub) != readLayoutHelper(destHub))) {
+                DeoptimizeNode.deopt(DeoptimizationAction.None, DeoptimizationReason.RuntimeConstraint);
+            }
         } else {
             ReplacementsUtil.staticAssert(false, "unknown array type check");
         }
     }
 
-    private static int checkArrayType(KlassPointer nonNullHub) {
-        int layoutHelper = readLayoutHelper(nonNullHub);
-        if (probability(SLOW_PATH_PROBABILITY, layoutHelper >= 0)) {
-            DeoptimizeNode.deopt(DeoptimizationAction.None, DeoptimizationReason.RuntimeConstraint);
-        }
-        return layoutHelper;
-    }
-
     static class Counters {
         final SnippetCounter checkSuccessCounter;
         final SnippetCounter checkAIOOBECounter;
@@ -381,8 +376,8 @@ public class ArrayCopySnippets implements Snippets {
             SnippetInfo snippetInfo;
             ArrayCopyTypeCheck arrayTypeCheck;
 
-            ResolvedJavaType srcType = StampTool.typeOrNull(arraycopy.getSource().stamp());
-            ResolvedJavaType destType = StampTool.typeOrNull(arraycopy.getDestination().stamp());
+            ResolvedJavaType srcType = StampTool.typeOrNull(arraycopy.getSource().stamp(NodeView.DEFAULT));
+            ResolvedJavaType destType = StampTool.typeOrNull(arraycopy.getDestination().stamp(NodeView.DEFAULT));
             if (!canBeArray(srcType) || !canBeArray(destType)) {
                 // at least one of the objects is definitely not an array - use the native call
                 // right away as the copying will fail anyways
@@ -399,7 +394,8 @@ public class ArrayCopySnippets implements Snippets {
                 } else if (srcComponentType == null && destComponentType == null) {
                     // we don't know anything about the types - use the generic copying
                     snippetInfo = arraycopyGenericSnippet;
-                    arrayTypeCheck = ArrayCopyTypeCheck.LAYOUT_HELPER_BASED_ARRAY_TYPE_CHECK;
+                    // no need for additional type check to avoid duplicated work
+                    arrayTypeCheck = ArrayCopyTypeCheck.NO_ARRAY_TYPE_CHECK;
                 } else if (srcComponentType != null && destComponentType != null) {
                     if (!srcComponentType.isPrimitive() && !destComponentType.isPrimitive()) {
                         // it depends on the array content if the copy succeeds - we need
@@ -416,14 +412,14 @@ public class ArrayCopySnippets implements Snippets {
                 } else {
                     ResolvedJavaType nonNullComponentType = srcComponentType != null ? srcComponentType : destComponentType;
                     if (nonNullComponentType.isPrimitive()) {
-                        // one involved object is a primitive array - we can safely assume that we
-                        // are copying primitive arrays
+                        // one involved object is a primitive array - it is sufficient to directly
+                        // compare the hub.
                         snippetInfo = arraycopyExactSnippet;
                         arrayTypeCheck = ArrayCopyTypeCheck.HUB_BASED_ARRAY_TYPE_CHECK;
                         elementKind = nonNullComponentType.getJavaKind();
                     } else {
-                        // one involved object is an object array - we can safely assume that we are
-                        // copying object arrays that might require a store check
+                        // one involved object is an object array - the other array's element type
+                        // may be primitive or object, hence we compare the layout helper.
                         snippetInfo = arraycopyCheckcastSnippet;
                         arrayTypeCheck = ArrayCopyTypeCheck.LAYOUT_HELPER_BASED_ARRAY_TYPE_CHECK;
                     }
@@ -432,7 +428,12 @@ public class ArrayCopySnippets implements Snippets {
 
             // a few special cases that are easier to handle when all other variables already have a
             // value
-            if (arraycopy.getLength().isConstant() && arraycopy.getLength().asJavaConstant().asLong() == 0) {
+            if (snippetInfo != arraycopyNativeSnippet && snippetInfo != arraycopyGenericSnippet && arraycopy.getLength().isConstant() && arraycopy.getLength().asJavaConstant().asLong() == 0) {
+                // Copying 0 element between object arrays with conflicting types will not throw an
+                // exception - once we pass the preliminary element type checks that we are not
+                // mixing arrays of different basic types, ArrayStoreException is only thrown when
+                // an *astore would have thrown it. Therefore, copying null between object arrays
+                // with conflicting types will also succeed (we do not optimize for such case here).
                 snippetInfo = arraycopyZeroLengthSnippet;
             } else if (snippetInfo == arraycopyExactSnippet && shouldUnroll(arraycopy.getLength())) {
                 snippetInfo = arraycopyUnrolledSnippet;
@@ -495,8 +496,8 @@ public class ArrayCopySnippets implements Snippets {
         }
 
         public static JavaKind selectComponentKind(BasicArrayCopyNode arraycopy) {
-            ResolvedJavaType srcType = StampTool.typeOrNull(arraycopy.getSource().stamp());
-            ResolvedJavaType destType = StampTool.typeOrNull(arraycopy.getDestination().stamp());
+            ResolvedJavaType srcType = StampTool.typeOrNull(arraycopy.getSource().stamp(NodeView.DEFAULT));
+            ResolvedJavaType destType = StampTool.typeOrNull(arraycopy.getDestination().stamp(NodeView.DEFAULT));
 
             if (srcType == null || !srcType.isArray() || destType == null || !destType.isArray()) {
                 return null;
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/arraycopy/CheckcastArrayCopyCallNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/arraycopy/CheckcastArrayCopyCallNode.java
index e90d486a98d..e6fb867a83e 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/arraycopy/CheckcastArrayCopyCallNode.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/arraycopy/CheckcastArrayCopyCallNode.java
@@ -39,6 +39,7 @@ import org.graalvm.compiler.nodeinfo.InputType;
 import org.graalvm.compiler.nodeinfo.NodeInfo;
 import org.graalvm.compiler.nodes.ConstantNode;
 import org.graalvm.compiler.nodes.FixedWithNextNode;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.StructuredGraph;
 import org.graalvm.compiler.nodes.ValueNode;
 import org.graalvm.compiler.nodes.calc.AddNode;
@@ -115,9 +116,10 @@ public final class CheckcastArrayCopyCallNode extends AbstractMemoryCheckpoint i
         graph().addBeforeFixed(this, basePtr);
 
         int shift = CodeUtil.log2(getArrayIndexScale(JavaKind.Object));
-        ValueNode extendedPos = IntegerConvertNode.convert(pos, StampFactory.forKind(runtime.getTarget().wordJavaKind), graph());
+        ValueNode extendedPos = IntegerConvertNode.convert(pos, StampFactory.forKind(runtime.getTarget().wordJavaKind), graph(), NodeView.DEFAULT);
         ValueNode scaledIndex = graph().unique(new LeftShiftNode(extendedPos, ConstantNode.forInt(shift, graph())));
-        ValueNode offset = graph().unique(new AddNode(scaledIndex, ConstantNode.forIntegerBits(PrimitiveStamp.getBits(scaledIndex.stamp()), getArrayBaseOffset(JavaKind.Object), graph())));
+        ValueNode offset = graph().unique(
+                        new AddNode(scaledIndex, ConstantNode.forIntegerBits(PrimitiveStamp.getBits(scaledIndex.stamp(NodeView.DEFAULT)), getArrayBaseOffset(JavaKind.Object), graph())));
         return graph().unique(new OffsetAddressNode(basePtr, offset));
     }
 
@@ -129,8 +131,8 @@ public final class CheckcastArrayCopyCallNode extends AbstractMemoryCheckpoint i
             ValueNode srcAddr = computeBase(getSource(), getSourcePosition());
             ValueNode destAddr = computeBase(getDestination(), getDestinationPosition());
             ValueNode len = getLength();
-            if (len.stamp().getStackKind() != runtime.getTarget().wordJavaKind) {
-                len = IntegerConvertNode.convert(len, StampFactory.forKind(runtime.getTarget().wordJavaKind), graph());
+            if (len.stamp(NodeView.DEFAULT).getStackKind() != runtime.getTarget().wordJavaKind) {
+                len = IntegerConvertNode.convert(len, StampFactory.forKind(runtime.getTarget().wordJavaKind), graph(), NodeView.DEFAULT);
             }
             ForeignCallNode call = graph.add(new ForeignCallNode(runtime.getHostBackend().getForeignCalls(), desc, srcAddr, destAddr, len, superCheckOffset, destElemKlass));
             call.setStateAfter(stateAfter());
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/arraycopy/GenericArrayCopyCallNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/arraycopy/GenericArrayCopyCallNode.java
index 1017b07c2b2..70373bfe4e1 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/arraycopy/GenericArrayCopyCallNode.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/arraycopy/GenericArrayCopyCallNode.java
@@ -33,6 +33,7 @@ import org.graalvm.compiler.hotspot.HotSpotGraalRuntimeProvider;
 import org.graalvm.compiler.hotspot.nodes.GetObjectAddressNode;
 import org.graalvm.compiler.nodeinfo.InputType;
 import org.graalvm.compiler.nodeinfo.NodeInfo;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.StructuredGraph;
 import org.graalvm.compiler.nodes.ValueNode;
 import org.graalvm.compiler.nodes.calc.IntegerConvertNode;
@@ -106,8 +107,8 @@ public final class GenericArrayCopyCallNode extends AbstractMemoryCheckpoint imp
     }
 
     private ValueNode wordValue(ValueNode value) {
-        if (value.stamp().getStackKind() != runtime.getTarget().wordJavaKind) {
-            return IntegerConvertNode.convert(value, StampFactory.forKind(runtime.getTarget().wordJavaKind), graph());
+        if (value.stamp(NodeView.DEFAULT).getStackKind() != runtime.getTarget().wordJavaKind) {
+            return IntegerConvertNode.convert(value, StampFactory.forKind(runtime.getTarget().wordJavaKind), graph(), NodeView.DEFAULT);
         }
         return value;
     }
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/stubs/SnippetStub.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/stubs/SnippetStub.java
index 658c3ce30fb..01dc9cdab21 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/stubs/SnippetStub.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/stubs/SnippetStub.java
@@ -38,6 +38,7 @@ import org.graalvm.compiler.debug.GraalError;
 import org.graalvm.compiler.hotspot.HotSpotForeignCallLinkage;
 import org.graalvm.compiler.hotspot.meta.HotSpotProviders;
 import org.graalvm.compiler.java.GraphBuilderPhase;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.ParameterNode;
 import org.graalvm.compiler.nodes.StructuredGraph;
 import org.graalvm.compiler.nodes.StructuredGraph.GuardsStage;
@@ -120,7 +121,7 @@ public abstract class SnippetStub extends Stub implements Snippets {
             for (ParameterNode param : graph.getNodes(ParameterNode.TYPE)) {
                 int index = param.index();
                 if (method.getParameterAnnotation(NonNullParameter.class, index) != null) {
-                    param.setStamp(param.stamp().join(StampFactory.objectNonNull()));
+                    param.setStamp(param.stamp(NodeView.DEFAULT).join(StampFactory.objectNonNull()));
                 }
             }
 
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/word/PointerCastNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/word/PointerCastNode.java
index 866ba59bd0b..18a27d002e5 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/word/PointerCastNode.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/word/PointerCastNode.java
@@ -30,6 +30,7 @@ import org.graalvm.compiler.graph.Node;
 import org.graalvm.compiler.graph.NodeClass;
 import org.graalvm.compiler.hotspot.word.HotSpotOperation.HotspotOpcode;
 import org.graalvm.compiler.nodeinfo.NodeInfo;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.ValueNode;
 import org.graalvm.compiler.nodes.calc.FloatingNode;
 import org.graalvm.compiler.nodes.spi.LIRLowerable;
@@ -59,7 +60,7 @@ public final class PointerCastNode extends FloatingNode implements LIRLowerable,
     @Override
     public void generate(NodeLIRBuilderTool generator) {
         Value value = generator.operand(input);
-        assert value.getValueKind().equals(generator.getLIRGeneratorTool().getLIRKind(stamp())) : "PointerCastNode shouldn't change the LIRKind";
+        assert value.getValueKind().equals(generator.getLIRGeneratorTool().getLIRKind(stamp(NodeView.DEFAULT))) : "PointerCastNode shouldn't change the LIRKind";
 
         generator.setResult(this, value);
     }
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.java/src/org/graalvm/compiler/java/BytecodeParser.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.java/src/org/graalvm/compiler/java/BytecodeParser.java
index 8a8299098fe..e39464a0f6a 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.java/src/org/graalvm/compiler/java/BytecodeParser.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.java/src/org/graalvm/compiler/java/BytecodeParser.java
@@ -330,6 +330,7 @@ import org.graalvm.compiler.nodes.LoopBeginNode;
 import org.graalvm.compiler.nodes.LoopEndNode;
 import org.graalvm.compiler.nodes.LoopExitNode;
 import org.graalvm.compiler.nodes.MergeNode;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.ParameterNode;
 import org.graalvm.compiler.nodes.PiNode;
 import org.graalvm.compiler.nodes.ReturnNode;
@@ -1092,71 +1093,71 @@ public class BytecodeParser implements GraphBuilderContext {
     }
 
     protected ValueNode genIntegerAdd(ValueNode x, ValueNode y) {
-        return AddNode.create(x, y);
+        return AddNode.create(x, y, NodeView.DEFAULT);
     }
 
     protected ValueNode genIntegerSub(ValueNode x, ValueNode y) {
-        return SubNode.create(x, y);
+        return SubNode.create(x, y, NodeView.DEFAULT);
     }
 
     protected ValueNode genIntegerMul(ValueNode x, ValueNode y) {
-        return MulNode.create(x, y);
+        return MulNode.create(x, y, NodeView.DEFAULT);
     }
 
     protected ValueNode genFloatAdd(ValueNode x, ValueNode y) {
-        return AddNode.create(x, y);
+        return AddNode.create(x, y, NodeView.DEFAULT);
     }
 
     protected ValueNode genFloatSub(ValueNode x, ValueNode y) {
-        return SubNode.create(x, y);
+        return SubNode.create(x, y, NodeView.DEFAULT);
     }
 
     protected ValueNode genFloatMul(ValueNode x, ValueNode y) {
-        return MulNode.create(x, y);
+        return MulNode.create(x, y, NodeView.DEFAULT);
     }
 
     protected ValueNode genFloatDiv(ValueNode x, ValueNode y) {
-        return FloatDivNode.create(x, y);
+        return FloatDivNode.create(x, y, NodeView.DEFAULT);
     }
 
     protected ValueNode genFloatRem(ValueNode x, ValueNode y) {
-        return new RemNode(x, y);
+        return RemNode.create(x, y, NodeView.DEFAULT);
     }
 
     protected ValueNode genIntegerDiv(ValueNode x, ValueNode y) {
-        return new SignedDivNode(x, y);
+        return SignedDivNode.create(x, y, NodeView.DEFAULT);
     }
 
     protected ValueNode genIntegerRem(ValueNode x, ValueNode y) {
-        return new SignedRemNode(x, y);
+        return SignedRemNode.create(x, y, NodeView.DEFAULT);
     }
 
     protected ValueNode genNegateOp(ValueNode x) {
-        return NegateNode.create(x);
+        return NegateNode.create(x, NodeView.DEFAULT);
     }
 
     protected ValueNode genLeftShift(ValueNode x, ValueNode y) {
-        return LeftShiftNode.create(x, y);
+        return LeftShiftNode.create(x, y, NodeView.DEFAULT);
     }
 
     protected ValueNode genRightShift(ValueNode x, ValueNode y) {
-        return RightShiftNode.create(x, y);
+        return RightShiftNode.create(x, y, NodeView.DEFAULT);
     }
 
     protected ValueNode genUnsignedRightShift(ValueNode x, ValueNode y) {
-        return new UnsignedRightShiftNode(x, y);
+        return UnsignedRightShiftNode.create(x, y, NodeView.DEFAULT);
     }
 
     protected ValueNode genAnd(ValueNode x, ValueNode y) {
-        return AndNode.create(x, y);
+        return AndNode.create(x, y, NodeView.DEFAULT);
     }
 
     protected ValueNode genOr(ValueNode x, ValueNode y) {
-        return OrNode.create(x, y);
+        return OrNode.create(x, y, NodeView.DEFAULT);
     }
 
     protected ValueNode genXor(ValueNode x, ValueNode y) {
-        return XorNode.create(x, y);
+        return XorNode.create(x, y, NodeView.DEFAULT);
     }
 
     protected ValueNode genNormalizeCompare(ValueNode x, ValueNode y, boolean isUnorderedLess) {
@@ -1164,19 +1165,19 @@ public class BytecodeParser implements GraphBuilderContext {
     }
 
     protected ValueNode genFloatConvert(FloatConvert op, ValueNode input) {
-        return FloatConvertNode.create(op, input);
+        return FloatConvertNode.create(op, input, NodeView.DEFAULT);
     }
 
     protected ValueNode genNarrow(ValueNode input, int bitCount) {
-        return NarrowNode.create(input, bitCount);
+        return NarrowNode.create(input, bitCount, NodeView.DEFAULT);
     }
 
     protected ValueNode genSignExtend(ValueNode input, int bitCount) {
-        return SignExtendNode.create(input, bitCount);
+        return SignExtendNode.create(input, bitCount, NodeView.DEFAULT);
     }
 
     protected ValueNode genZeroExtend(ValueNode input, int bitCount) {
-        return ZeroExtendNode.create(input, bitCount);
+        return ZeroExtendNode.create(input, bitCount, NodeView.DEFAULT);
     }
 
     protected void genGoto() {
@@ -1191,15 +1192,15 @@ public class BytecodeParser implements GraphBuilderContext {
     }
 
     protected LogicNode genObjectEquals(ValueNode x, ValueNode y) {
-        return ObjectEqualsNode.create(constantReflection, metaAccess, options, x, y);
+        return ObjectEqualsNode.create(constantReflection, metaAccess, options, x, y, NodeView.DEFAULT);
     }
 
     protected LogicNode genIntegerEquals(ValueNode x, ValueNode y) {
-        return IntegerEqualsNode.create(constantReflection, metaAccess, options, null, x, y);
+        return IntegerEqualsNode.create(constantReflection, metaAccess, options, null, x, y, NodeView.DEFAULT);
     }
 
     protected LogicNode genIntegerLessThan(ValueNode x, ValueNode y) {
-        return IntegerLessThanNode.create(constantReflection, metaAccess, options, null, x, y);
+        return IntegerLessThanNode.create(constantReflection, metaAccess, options, null, x, y, NodeView.DEFAULT);
     }
 
     protected ValueNode genUnique(ValueNode x) {
@@ -1219,7 +1220,7 @@ public class BytecodeParser implements GraphBuilderContext {
 
         ValueNode exception = frameState.pop(JavaKind.Object);
         FixedGuardNode nullCheck = append(new FixedGuardNode(graph.addOrUniqueWithInputs(IsNullNode.create(exception)), NullCheckException, InvalidateReprofile, true));
-        ValueNode nonNullException = graph.maybeAddOrUnique(PiNode.create(exception, exception.stamp().join(objectNonNull()), nullCheck));
+        ValueNode nonNullException = graph.maybeAddOrUnique(PiNode.create(exception, exception.stamp(NodeView.DEFAULT).join(objectNonNull()), nullCheck));
         lastInstr.setNext(handleException(nonNullException, bci(), false));
     }
 
@@ -1244,7 +1245,7 @@ public class BytecodeParser implements GraphBuilderContext {
     }
 
     protected ValueNode genConditional(ValueNode x) {
-        return ConditionalNode.create((LogicNode) x);
+        return ConditionalNode.create((LogicNode) x, NodeView.DEFAULT);
     }
 
     protected NewInstanceNode createNewInstance(ResolvedJavaType type, boolean fillContents) {
@@ -1275,7 +1276,7 @@ public class BytecodeParser implements GraphBuilderContext {
     }
 
     protected ValueNode emitExplicitNullCheck(ValueNode receiver) {
-        if (StampTool.isPointerNonNull(receiver.stamp())) {
+        if (StampTool.isPointerNonNull(receiver.stamp(NodeView.DEFAULT))) {
             return receiver;
         }
         BytecodeExceptionNode exception = graph.add(new BytecodeExceptionNode(metaAccess, NullPointerException.class));
@@ -1293,7 +1294,7 @@ public class BytecodeParser implements GraphBuilderContext {
     protected void emitExplicitBoundsCheck(ValueNode index, ValueNode length) {
         AbstractBeginNode trueSucc = graph.add(new BeginNode());
         BytecodeExceptionNode exception = graph.add(new BytecodeExceptionNode(metaAccess, ArrayIndexOutOfBoundsException.class, index));
-        append(new IfNode(genUnique(IntegerBelowNode.create(constantReflection, metaAccess, options, null, index, length)), trueSucc, exception, FAST_PATH_PROBABILITY));
+        append(new IfNode(genUnique(IntegerBelowNode.create(constantReflection, metaAccess, options, null, index, length, NodeView.DEFAULT)), trueSucc, exception, FAST_PATH_PROBABILITY));
         lastInstr = trueSucc;
 
         exception.setStateAfter(createFrameState(bci(), exception));
@@ -1803,7 +1804,7 @@ public class BytecodeParser implements GraphBuilderContext {
             this.args = args;
             this.resultType = resultType;
             this.beforeStackSize = frameState.stackSize();
-            this.needsNullCheck = !targetMethod.isStatic() && args[0].getStackKind() == JavaKind.Object && !StampTool.isPointerNonNull(args[0].stamp());
+            this.needsNullCheck = !targetMethod.isStatic() && args[0].getStackKind() == JavaKind.Object && !StampTool.isPointerNonNull(args[0].stamp(NodeView.DEFAULT));
             this.nodeCount = graph.getNodeCount();
             this.mark = graph.getMark();
         }
@@ -1817,7 +1818,8 @@ public class BytecodeParser implements GraphBuilderContext {
                 int expectedStackSize = beforeStackSize + resultType.getSlotCount();
                 assert expectedStackSize == frameState.stackSize() : error("plugin manipulated the stack incorrectly: expected=%d, actual=%d", expectedStackSize, frameState.stackSize());
                 NodeIterable<Node> newNodes = graph.getNewNodes(mark);
-                assert !needsNullCheck || isPointerNonNull(args[0].stamp()) : error("plugin needs to null check the receiver of %s: receiver=%s", targetMethod.format("%H.%n(%p)"), args[0]);
+                assert !needsNullCheck || isPointerNonNull(args[0].stamp(NodeView.DEFAULT)) : error("plugin needs to null check the receiver of %s: receiver=%s", targetMethod.format("%H.%n(%p)"),
+                                args[0]);
                 for (Node n : newNodes) {
                     if (n instanceof StateSplit) {
                         StateSplit stateSplit = (StateSplit) n;
@@ -1891,7 +1893,7 @@ public class BytecodeParser implements GraphBuilderContext {
             LoadHubNode hub = graph.unique(new LoadHubNode(stampProvider, nonNullReceiver));
             LoadMethodNode actual = append(new LoadMethodNode(methodStamp, targetMethod, receiverType, method.getDeclaringClass(), hub));
             ConstantNode expected = graph.unique(ConstantNode.forConstant(methodStamp, targetMethod.getEncoding(), getMetaAccess()));
-            LogicNode compare = graph.addOrUniqueWithInputs(CompareNode.createCompareNode(constantReflection, metaAccess, options, null, Condition.EQ, actual, expected));
+            LogicNode compare = graph.addOrUniqueWithInputs(CompareNode.createCompareNode(constantReflection, metaAccess, options, null, Condition.EQ, actual, expected, NodeView.DEFAULT));
 
             JavaTypeProfile profile = null;
             if (profilingInfo != null && this.optimisticOpts.useTypeCheckHints(getOptions())) {
@@ -2394,7 +2396,7 @@ public class BytecodeParser implements GraphBuilderContext {
         if (kind != returnKind) {
             // sub-word integer
             assert returnKind.isNumericInteger() && returnKind.getStackKind() == JavaKind.Int;
-            IntegerStamp stamp = (IntegerStamp) value.stamp();
+            IntegerStamp stamp = (IntegerStamp) value.stamp(NodeView.DEFAULT);
 
             // the bytecode verifier doesn't check that the value is in the correct range
             if (stamp.lowerBound() < returnKind.getMinValue() || returnKind.getMaxValue() < stamp.upperBound()) {
@@ -2480,7 +2482,7 @@ public class BytecodeParser implements GraphBuilderContext {
         JsrScope scope = currentBlock.getJsrScope();
         int retAddress = scope.nextReturnAddress();
         ConstantNode returnBciNode = getJsrConstant(retAddress);
-        LogicNode guard = IntegerEqualsNode.create(constantReflection, metaAccess, options, null, local, returnBciNode);
+        LogicNode guard = IntegerEqualsNode.create(constantReflection, metaAccess, options, null, local, returnBciNode, NodeView.DEFAULT);
         guard = graph.addOrUniqueWithInputs(guard);
         append(new FixedGuardNode(guard, JavaSubroutineMismatch, InvalidateReprofile));
         if (!successor.getJsrScope().equals(scope.pop())) {
@@ -3184,7 +3186,7 @@ public class BytecodeParser implements GraphBuilderContext {
     private void genConditionalForIf(BciBlock trueBlock, LogicNode condition, int oldBci, int trueBlockInt, int falseBlockInt, boolean genReturn) {
         ConstantNode trueValue = graph.unique(ConstantNode.forInt(trueBlockInt));
         ConstantNode falseValue = graph.unique(ConstantNode.forInt(falseBlockInt));
-        ValueNode conditionalNode = ConditionalNode.create(condition, trueValue, falseValue);
+        ValueNode conditionalNode = ConditionalNode.create(condition, trueValue, falseValue, NodeView.DEFAULT);
         if (conditionalNode.graph() == null) {
             conditionalNode = graph.addOrUniqueWithInputs(conditionalNode);
         }
@@ -3716,7 +3718,7 @@ public class BytecodeParser implements GraphBuilderContext {
             }
         }
 
-        boolean nonNull = ((ObjectStamp) object.stamp()).nonNull();
+        boolean nonNull = ((ObjectStamp) object.stamp(NodeView.DEFAULT)).nonNull();
         if (castNode == null) {
             LogicNode condition = genUnique(createInstanceOfAllowNull(checkedType, object, null));
             if (condition.isTautology()) {
@@ -4174,7 +4176,8 @@ public class BytecodeParser implements GraphBuilderContext {
         ValueNode value = frameState.pop(JavaKind.Int);
 
         int nofCases = bs.numberOfCases();
-        double[] keyProbabilities = switchProbability(nofCases + 1, bci);
+        int nofCasesPlusDefault = nofCases + 1;
+        double[] keyProbabilities = switchProbability(nofCasesPlusDefault, bci);
 
         EconomicMap<Integer, SuccessorInfo> bciToBlockSuccessorIndex = EconomicMap.create(Equivalence.DEFAULT);
         for (int i = 0; i < currentBlock.getSuccessorCount(); i++) {
@@ -4184,11 +4187,11 @@ public class BytecodeParser implements GraphBuilderContext {
 
         ArrayList<BciBlock> actualSuccessors = new ArrayList<>();
         int[] keys = new int[nofCases];
-        int[] keySuccessors = new int[nofCases + 1];
+        int[] keySuccessors = new int[nofCasesPlusDefault];
         int deoptSuccessorIndex = -1;
         int nextSuccessorIndex = 0;
         boolean constantValue = value.isConstant();
-        for (int i = 0; i < nofCases + 1; i++) {
+        for (int i = 0; i < nofCasesPlusDefault; i++) {
             if (i < nofCases) {
                 keys[i] = bs.keyAt(i);
             }
@@ -4200,7 +4203,7 @@ public class BytecodeParser implements GraphBuilderContext {
                 }
                 keySuccessors[i] = deoptSuccessorIndex;
             } else {
-                int targetBci = i >= nofCases ? bs.defaultTarget() : bs.targetAt(i);
+                int targetBci = i < nofCases ? bs.targetAt(i) : bs.defaultTarget();
                 SuccessorInfo info = bciToBlockSuccessorIndex.get(targetBci);
                 if (info.actualIndex < 0) {
                     info.actualIndex = nextSuccessorIndex++;
@@ -4209,6 +4212,48 @@ public class BytecodeParser implements GraphBuilderContext {
                 keySuccessors[i] = info.actualIndex;
             }
         }
+        /*
+         * When the profile indicates a case is never taken, the above code will cause the case to
+         * deopt should it be subsequently encountered. However, the case may share code with
+         * another case that is taken according to the profile.
+         *
+         * For example:
+         * // @formatter:off
+         * switch (opcode) {
+         *     case GOTO:
+         *     case GOTO_W: {
+         *         // emit goto code
+         *         break;
+         *     }
+         * }
+         * // @formatter:on
+         *
+         * The profile may indicate the GOTO_W case is never taken, and thus a deoptimization stub
+         * will be emitted. There might be optimization opportunity if additional branching based
+         * on opcode is within the case block. Specially, if there is only single case that
+         * reaches a target, we have better chance cutting out unused branches. Otherwise,
+         * it might be beneficial routing to the same code instead of deopting.
+         *
+         * The following code rewires deoptimization stub to existing resolved branch target if
+         * the target is connected by more than 1 cases.
+         */
+        if (deoptSuccessorIndex >= 0) {
+            int[] connectedCases = new int[nextSuccessorIndex];
+            for (int i = 0; i < nofCasesPlusDefault; i++) {
+                connectedCases[keySuccessors[i]]++;
+            }
+
+            for (int i = 0; i < nofCasesPlusDefault; i++) {
+                if (keySuccessors[i] == deoptSuccessorIndex) {
+                    int targetBci = i < nofCases ? bs.targetAt(i) : bs.defaultTarget();
+                    SuccessorInfo info = bciToBlockSuccessorIndex.get(targetBci);
+                    int rewiredIndex = info.actualIndex;
+                    if (rewiredIndex >= 0 && connectedCases[rewiredIndex] > 1) {
+                        keySuccessors[i] = info.actualIndex;
+                    }
+                }
+            }
+        }
 
         genIntegerSwitch(value, actualSuccessors, keys, keyProbabilities, keySuccessors);
 
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.java/src/org/graalvm/compiler/java/FrameStateBuilder.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.java/src/org/graalvm/compiler/java/FrameStateBuilder.java
index b924c25f85c..04b2c315ed4 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.java/src/org/graalvm/compiler/java/FrameStateBuilder.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.java/src/org/graalvm/compiler/java/FrameStateBuilder.java
@@ -55,6 +55,7 @@ import org.graalvm.compiler.nodes.ConstantNode;
 import org.graalvm.compiler.nodes.FrameState;
 import org.graalvm.compiler.nodes.LoopBeginNode;
 import org.graalvm.compiler.nodes.LoopExitNode;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.ParameterNode;
 import org.graalvm.compiler.nodes.PhiNode;
 import org.graalvm.compiler.nodes.ProxyNode;
@@ -460,7 +461,7 @@ public final class FrameStateBuilder implements SideEffectsState {
     }
 
     private ValuePhiNode createValuePhi(ValueNode currentValue, ValueNode otherValue, AbstractMergeNode block) {
-        ValuePhiNode phi = graph.addWithoutUnique(new ValuePhiNode(currentValue.stamp().unrestricted(), block));
+        ValuePhiNode phi = graph.addWithoutUnique(new ValuePhiNode(currentValue.stamp(NodeView.DEFAULT).unrestricted(), block));
         for (int i = 0; i < block.phiPredecessorCount(); i++) {
             phi.addInput(currentValue);
         }
@@ -558,7 +559,7 @@ public final class FrameStateBuilder implements SideEffectsState {
         }
         assert !block.isPhiAtMerge(value) : "phi function for this block already created";
 
-        ValuePhiNode phi = graph.addWithoutUnique(new ValuePhiNode(stampFromValue ? value.stamp() : value.stamp().unrestricted(), block));
+        ValuePhiNode phi = graph.addWithoutUnique(new ValuePhiNode(stampFromValue ? value.stamp(NodeView.DEFAULT) : value.stamp(NodeView.DEFAULT).unrestricted(), block));
         phi.addInput(value);
         return phi;
     }
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.jtt/src/org/graalvm/compiler/jtt/jdk/Unsafe_compareAndSwapNullCheck.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.jtt/src/org/graalvm/compiler/jtt/jdk/Unsafe_compareAndSwapNullCheck.java
index a69bf2d8059..25d05b83774 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.jtt/src/org/graalvm/compiler/jtt/jdk/Unsafe_compareAndSwapNullCheck.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.jtt/src/org/graalvm/compiler/jtt/jdk/Unsafe_compareAndSwapNullCheck.java
@@ -23,7 +23,6 @@
 package org.graalvm.compiler.jtt.jdk;
 
 import org.graalvm.compiler.jtt.JTTTest;
-import org.junit.Assume;
 import org.junit.Test;
 
 public class Unsafe_compareAndSwapNullCheck extends JTTTest {
@@ -48,8 +47,6 @@ public class Unsafe_compareAndSwapNullCheck extends JTTTest {
 
     @Test
     public void run0() throws Throwable {
-        // GR-2921: Unsafe_compareAndSwapNullCheck test crashes on jdk9
-        Assume.assumeTrue(Java8OrEarlier);
         runTest(getInitialOptions(), EMPTY, false, true, "test", null, 1L, 2L);
     }
 }
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir.amd64/src/org/graalvm/compiler/lir/amd64/AMD64Move.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir.amd64/src/org/graalvm/compiler/lir/amd64/AMD64Move.java
index 38fdd263a6c..7b9add5daaf 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir.amd64/src/org/graalvm/compiler/lir/amd64/AMD64Move.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir.amd64/src/org/graalvm/compiler/lir/amd64/AMD64Move.java
@@ -299,16 +299,23 @@ public class AMD64Move {
 
         @Def({REG}) protected AllocatableValue result;
         @Use({COMPOSITE, UNINITIALIZED}) protected AMD64AddressValue address;
+        private final OperandSize size;
 
-        public LeaOp(AllocatableValue result, AMD64AddressValue address) {
+        public LeaOp(AllocatableValue result, AMD64AddressValue address, OperandSize size) {
             super(TYPE);
             this.result = result;
             this.address = address;
+            this.size = size;
         }
 
         @Override
         public void emitCode(CompilationResultBuilder crb, AMD64MacroAssembler masm) {
-            masm.leaq(asRegister(result, AMD64Kind.QWORD), address.toAddress());
+            if (size == OperandSize.QWORD) {
+                masm.leaq(asRegister(result, AMD64Kind.QWORD), address.toAddress());
+            } else {
+                assert size == OperandSize.DWORD;
+                masm.lead(asRegister(result, AMD64Kind.DWORD), address.toAddress());
+            }
         }
     }
 
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/LIRIntrospection.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/LIRIntrospection.java
index b3f3bdd9440..5e4a5c513cd 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/LIRIntrospection.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/LIRIntrospection.java
@@ -340,7 +340,7 @@ abstract class LIRIntrospection<T> extends FieldIntrospection<T> {
     private static boolean isPrintableAsciiString(byte[] array) {
         for (byte b : array) {
             char c = (char) b;
-            if (c != 0 && c < 0x20 && c > 0x7F) {
+            if (c != 0 && (c < 0x20 || c > 0x7F)) {
                 return false;
             }
         }
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/alloc/lsra/LinearScanLifetimeAnalysisPhase.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/alloc/lsra/LinearScanLifetimeAnalysisPhase.java
index 82665f59712..cb02e6653cb 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/alloc/lsra/LinearScanLifetimeAnalysisPhase.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/alloc/lsra/LinearScanLifetimeAnalysisPhase.java
@@ -159,97 +159,101 @@ public class LinearScanLifetimeAnalysisPhase extends LinearScanAllocationPhase {
 
         intervalInLoop = new BitMap2D(allocator.operandSize(), allocator.numLoops());
 
-        // iterate all blocks
-        for (final AbstractBlockBase<?> block : allocator.sortedBlocks()) {
-            try (Indent indent = debug.logAndIndent("compute local live sets for block %s", block)) {
+        try {
+            // iterate all blocks
+            for (final AbstractBlockBase<?> block : allocator.sortedBlocks()) {
+                try (Indent indent = debug.logAndIndent("compute local live sets for block %s", block)) {
 
-                final BitSet liveGen = new BitSet(liveSize);
-                final BitSet liveKill = new BitSet(liveSize);
+                    final BitSet liveGen = new BitSet(liveSize);
+                    final BitSet liveKill = new BitSet(liveSize);
 
-                ArrayList<LIRInstruction> instructions = allocator.getLIR().getLIRforBlock(block);
-                int numInst = instructions.size();
+                    ArrayList<LIRInstruction> instructions = allocator.getLIR().getLIRforBlock(block);
+                    int numInst = instructions.size();
 
-                ValueConsumer useConsumer = (operand, mode, flags) -> {
-                    if (isVariable(operand)) {
-                        int operandNum = allocator.operandNumber(operand);
-                        if (!liveKill.get(operandNum)) {
-                            liveGen.set(operandNum);
-                            if (debug.isLogEnabled()) {
-                                debug.log("liveGen for operand %d(%s)", operandNum, operand);
+                    ValueConsumer useConsumer = (operand, mode, flags) -> {
+                        if (isVariable(operand)) {
+                            int operandNum = allocator.operandNumber(operand);
+                            if (!liveKill.get(operandNum)) {
+                                liveGen.set(operandNum);
+                                if (debug.isLogEnabled()) {
+                                    debug.log("liveGen for operand %d(%s)", operandNum, operand);
+                                }
+                            }
+                            if (block.getLoop() != null) {
+                                intervalInLoop.setBit(operandNum, block.getLoop().getIndex());
                             }
                         }
-                        if (block.getLoop() != null) {
-                            intervalInLoop.setBit(operandNum, block.getLoop().getIndex());
-                        }
-                    }
 
-                    if (allocator.detailedAsserts) {
-                        verifyInput(block, liveKill, operand);
-                    }
-                };
-                ValueConsumer stateConsumer = (operand, mode, flags) -> {
-                    if (LinearScan.isVariableOrRegister(operand)) {
-                        int operandNum = allocator.operandNumber(operand);
-                        if (!liveKill.get(operandNum)) {
-                            liveGen.set(operandNum);
-                            if (debug.isLogEnabled()) {
-                                debug.log("liveGen in state for operand %d(%s)", operandNum, operand);
+                        if (allocator.detailedAsserts) {
+                            verifyInput(block, liveKill, operand);
+                        }
+                    };
+                    ValueConsumer stateConsumer = (operand, mode, flags) -> {
+                        if (LinearScan.isVariableOrRegister(operand)) {
+                            int operandNum = allocator.operandNumber(operand);
+                            if (!liveKill.get(operandNum)) {
+                                liveGen.set(operandNum);
+                                if (debug.isLogEnabled()) {
+                                    debug.log("liveGen in state for operand %d(%s)", operandNum, operand);
+                                }
                             }
                         }
-                    }
-                };
-                ValueConsumer defConsumer = (operand, mode, flags) -> {
-                    if (isVariable(operand)) {
-                        int varNum = allocator.operandNumber(operand);
-                        liveKill.set(varNum);
-                        if (debug.isLogEnabled()) {
-                            debug.log("liveKill for operand %d(%s)", varNum, operand);
+                    };
+                    ValueConsumer defConsumer = (operand, mode, flags) -> {
+                        if (isVariable(operand)) {
+                            int varNum = allocator.operandNumber(operand);
+                            liveKill.set(varNum);
+                            if (debug.isLogEnabled()) {
+                                debug.log("liveKill for operand %d(%s)", varNum, operand);
+                            }
+                            if (block.getLoop() != null) {
+                                intervalInLoop.setBit(varNum, block.getLoop().getIndex());
+                            }
                         }
-                        if (block.getLoop() != null) {
-                            intervalInLoop.setBit(varNum, block.getLoop().getIndex());
+
+                        if (allocator.detailedAsserts) {
+                            /*
+                             * Fixed intervals are never live at block boundaries, so they need not
+                             * be processed in live sets. Process them only in debug mode so that
+                             * this can be checked
+                             */
+                            verifyTemp(liveKill, operand);
                         }
+                    };
+
+                    // iterate all instructions of the block
+                    for (int j = 0; j < numInst; j++) {
+                        final LIRInstruction op = instructions.get(j);
+
+                        try (Indent indent2 = debug.logAndIndent("handle op %d: %s", op.id(), op)) {
+                            op.visitEachInput(useConsumer);
+                            op.visitEachAlive(useConsumer);
+                            /*
+                             * Add uses of live locals from interpreter's point of view for proper
+                             * debug information generation.
+                             */
+                            op.visitEachState(stateConsumer);
+                            op.visitEachTemp(defConsumer);
+                            op.visitEachOutput(defConsumer);
+                        }
+                    } // end of instruction iteration
+
+                    BlockData blockSets = allocator.getBlockData(block);
+                    blockSets.liveGen = liveGen;
+                    blockSets.liveKill = liveKill;
+                    blockSets.liveIn = new BitSet(liveSize);
+                    blockSets.liveOut = new BitSet(liveSize);
+
+                    if (debug.isLogEnabled()) {
+                        debug.log("liveGen  B%d %s", block.getId(), blockSets.liveGen);
+                        debug.log("liveKill B%d %s", block.getId(), blockSets.liveKill);
                     }
 
-                    if (allocator.detailedAsserts) {
-                        /*
-                         * Fixed intervals are never live at block boundaries, so they need not be
-                         * processed in live sets. Process them only in debug mode so that this can
-                         * be checked
-                         */
-                        verifyTemp(liveKill, operand);
-                    }
-                };
-
-                // iterate all instructions of the block
-                for (int j = 0; j < numInst; j++) {
-                    final LIRInstruction op = instructions.get(j);
-
-                    try (Indent indent2 = debug.logAndIndent("handle op %d: %s", op.id(), op)) {
-                        op.visitEachInput(useConsumer);
-                        op.visitEachAlive(useConsumer);
-                        /*
-                         * Add uses of live locals from interpreter's point of view for proper debug
-                         * information generation.
-                         */
-                        op.visitEachState(stateConsumer);
-                        op.visitEachTemp(defConsumer);
-                        op.visitEachOutput(defConsumer);
-                    }
-                } // end of instruction iteration
-
-                BlockData blockSets = allocator.getBlockData(block);
-                blockSets.liveGen = liveGen;
-                blockSets.liveKill = liveKill;
-                blockSets.liveIn = new BitSet(liveSize);
-                blockSets.liveOut = new BitSet(liveSize);
-
-                if (debug.isLogEnabled()) {
-                    debug.log("liveGen  B%d %s", block.getId(), blockSets.liveGen);
-                    debug.log("liveKill B%d %s", block.getId(), blockSets.liveKill);
                 }
-
-            }
-        } // end of block iteration
+            } // end of block iteration
+        } catch (OutOfMemoryError oom) {
+            throw new PermanentBailoutException(oom, "Out-of-memory during live set allocation of size %d", liveSize);
+        }
     }
 
     private void verifyTemp(BitSet liveKill, Value operand) {
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/constopt/ConstantTree.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/constopt/ConstantTree.java
index 0aac4b7ecf8..d6b6de653a9 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/constopt/ConstantTree.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.lir/src/org/graalvm/compiler/lir/constopt/ConstantTree.java
@@ -76,7 +76,7 @@ public class ConstantTree extends PrintableDominatorOptimizationProblem<Constant
 
         public List<UseEntry> getUsages() {
             if (usages == null) {
-                Collections.emptyList();
+                return Collections.emptyList();
             }
             return usages;
         }
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop/src/org/graalvm/compiler/loop/BasicInductionVariable.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop/src/org/graalvm/compiler/loop/BasicInductionVariable.java
index 6d27b6414dc..5c2683200e9 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop/src/org/graalvm/compiler/loop/BasicInductionVariable.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop/src/org/graalvm/compiler/loop/BasicInductionVariable.java
@@ -30,6 +30,7 @@ import org.graalvm.compiler.core.common.type.IntegerStamp;
 import org.graalvm.compiler.core.common.type.Stamp;
 import org.graalvm.compiler.debug.GraalError;
 import org.graalvm.compiler.nodes.ConstantNode;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.StructuredGraph;
 import org.graalvm.compiler.nodes.ValueNode;
 import org.graalvm.compiler.nodes.ValuePhiNode;
@@ -70,7 +71,7 @@ public class BasicInductionVariable extends InductionVariable {
 
     @Override
     public Direction direction() {
-        Stamp stamp = rawStride.stamp();
+        Stamp stamp = rawStride.stamp(NodeView.DEFAULT);
         if (stamp instanceof IntegerStamp) {
             IntegerStamp integerStamp = (IntegerStamp) stamp;
             Direction dir = null;
@@ -140,27 +141,27 @@ public class BasicInductionVariable extends InductionVariable {
 
     @Override
     public ValueNode extremumNode(boolean assumePositiveTripCount, Stamp stamp) {
-        Stamp fromStamp = phi.stamp();
+        Stamp fromStamp = phi.stamp(NodeView.DEFAULT);
         StructuredGraph graph = graph();
         ValueNode stride = strideNode();
         ValueNode initNode = this.initNode();
         if (!fromStamp.isCompatible(stamp)) {
-            stride = IntegerConvertNode.convert(stride, stamp, graph());
-            initNode = IntegerConvertNode.convert(initNode, stamp, graph());
+            stride = IntegerConvertNode.convert(stride, stamp, graph(), NodeView.DEFAULT);
+            initNode = IntegerConvertNode.convert(initNode, stamp, graph(), NodeView.DEFAULT);
         }
         ValueNode maxTripCount = loop.counted().maxTripCountNode(assumePositiveTripCount);
-        if (!maxTripCount.stamp().isCompatible(stamp)) {
-            maxTripCount = IntegerConvertNode.convert(maxTripCount, stamp, graph());
+        if (!maxTripCount.stamp(NodeView.DEFAULT).isCompatible(stamp)) {
+            maxTripCount = IntegerConvertNode.convert(maxTripCount, stamp, graph(), NodeView.DEFAULT);
         }
         return add(graph, mul(graph, stride, sub(graph, maxTripCount, ConstantNode.forIntegerStamp(stamp, 1, graph))), initNode);
     }
 
     @Override
     public ValueNode exitValueNode() {
-        Stamp stamp = phi.stamp();
+        Stamp stamp = phi.stamp(NodeView.DEFAULT);
         ValueNode maxTripCount = loop.counted().maxTripCountNode(false);
-        if (!maxTripCount.stamp().isCompatible(stamp)) {
-            maxTripCount = IntegerConvertNode.convert(maxTripCount, stamp, graph());
+        if (!maxTripCount.stamp(NodeView.DEFAULT).isCompatible(stamp)) {
+            maxTripCount = IntegerConvertNode.convert(maxTripCount, stamp, graph(), NodeView.DEFAULT);
         }
         return add(graph(), mul(graph(), strideNode(), maxTripCount), initNode());
     }
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop/src/org/graalvm/compiler/loop/CountedLoopInfo.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop/src/org/graalvm/compiler/loop/CountedLoopInfo.java
index ae6bb412278..86a942eeda4 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop/src/org/graalvm/compiler/loop/CountedLoopInfo.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop/src/org/graalvm/compiler/loop/CountedLoopInfo.java
@@ -33,6 +33,7 @@ import org.graalvm.compiler.nodes.AbstractBeginNode;
 import org.graalvm.compiler.nodes.ConstantNode;
 import org.graalvm.compiler.nodes.GuardNode;
 import org.graalvm.compiler.nodes.IfNode;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.StructuredGraph;
 import org.graalvm.compiler.nodes.ValueNode;
 import org.graalvm.compiler.nodes.calc.CompareNode;
@@ -69,7 +70,7 @@ public class CountedLoopInfo {
 
     public ValueNode maxTripCountNode(boolean assumePositive) {
         StructuredGraph graph = iv.valueNode().graph();
-        Stamp stamp = iv.valueNode().stamp();
+        Stamp stamp = iv.valueNode().stamp(NodeView.DEFAULT);
         ValueNode range = sub(graph, end, iv.initNode());
 
         ValueNode oneDirection;
@@ -84,7 +85,7 @@ public class CountedLoopInfo {
         }
         // round-away-from-zero divison: (range + stride -/+ 1) / stride
         ValueNode denominator = range;
-        if (!oneDirection.stamp().equals(iv.strideNode().stamp())) {
+        if (!oneDirection.stamp(NodeView.DEFAULT).equals(iv.strideNode().stamp(NodeView.DEFAULT))) {
             ValueNode subedRanged = sub(graph, range, oneDirection);
             denominator = add(graph, subedRanged, iv.strideNode());
         }
@@ -204,7 +205,7 @@ public class CountedLoopInfo {
         if (overflowGuard != null) {
             return overflowGuard;
         }
-        IntegerStamp stamp = (IntegerStamp) iv.valueNode().stamp();
+        IntegerStamp stamp = (IntegerStamp) iv.valueNode().stamp(NodeView.DEFAULT);
         StructuredGraph graph = iv.valueNode().graph();
         CompareNode cond; // we use a negated guard with a < condition to achieve a >=
         ConstantNode one = ConstantNode.forIntegerStamp(stamp, 1, graph);
@@ -230,6 +231,6 @@ public class CountedLoopInfo {
     }
 
     public IntegerStamp getStamp() {
-        return (IntegerStamp) iv.valueNode().stamp();
+        return (IntegerStamp) iv.valueNode().stamp(NodeView.DEFAULT);
     }
 }
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop/src/org/graalvm/compiler/loop/DerivedConvertedInductionVariable.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop/src/org/graalvm/compiler/loop/DerivedConvertedInductionVariable.java
index dc0ef63febd..79a078496a7 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop/src/org/graalvm/compiler/loop/DerivedConvertedInductionVariable.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop/src/org/graalvm/compiler/loop/DerivedConvertedInductionVariable.java
@@ -23,6 +23,7 @@
 package org.graalvm.compiler.loop;
 
 import org.graalvm.compiler.core.common.type.Stamp;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.ValueNode;
 import org.graalvm.compiler.nodes.calc.IntegerConvertNode;
 
@@ -49,12 +50,12 @@ public class DerivedConvertedInductionVariable extends DerivedInductionVariable
 
     @Override
     public ValueNode initNode() {
-        return IntegerConvertNode.convert(base.initNode(), stamp, graph());
+        return IntegerConvertNode.convert(base.initNode(), stamp, graph(), NodeView.DEFAULT);
     }
 
     @Override
     public ValueNode strideNode() {
-        return IntegerConvertNode.convert(base.strideNode(), stamp, graph());
+        return IntegerConvertNode.convert(base.strideNode(), stamp, graph(), NodeView.DEFAULT);
     }
 
     @Override
@@ -84,7 +85,7 @@ public class DerivedConvertedInductionVariable extends DerivedInductionVariable
 
     @Override
     public ValueNode exitValueNode() {
-        return IntegerConvertNode.convert(base.exitValueNode(), stamp, graph());
+        return IntegerConvertNode.convert(base.exitValueNode(), stamp, graph(), NodeView.DEFAULT);
     }
 
     @Override
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop/src/org/graalvm/compiler/loop/DerivedOffsetInductionVariable.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop/src/org/graalvm/compiler/loop/DerivedOffsetInductionVariable.java
index 33d0ed1636f..64aecebe331 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop/src/org/graalvm/compiler/loop/DerivedOffsetInductionVariable.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop/src/org/graalvm/compiler/loop/DerivedOffsetInductionVariable.java
@@ -27,6 +27,7 @@ import static org.graalvm.compiler.loop.MathUtil.sub;
 
 import org.graalvm.compiler.core.common.type.Stamp;
 import org.graalvm.compiler.debug.GraalError;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.ValueNode;
 import org.graalvm.compiler.nodes.calc.AddNode;
 import org.graalvm.compiler.nodes.calc.BinaryArithmeticNode;
@@ -90,14 +91,14 @@ public class DerivedOffsetInductionVariable extends DerivedInductionVariable {
     @Override
     public ValueNode strideNode() {
         if (value instanceof SubNode && base.valueNode() == value.getY()) {
-            return graph().addOrUniqueWithInputs(NegateNode.create(base.strideNode()));
+            return graph().addOrUniqueWithInputs(NegateNode.create(base.strideNode(), NodeView.DEFAULT));
         }
         return base.strideNode();
     }
 
     @Override
     public ValueNode extremumNode(boolean assumePositiveTripCount, Stamp stamp) {
-        return op(base.extremumNode(assumePositiveTripCount, stamp), IntegerConvertNode.convert(offset, stamp, graph()));
+        return op(base.extremumNode(assumePositiveTripCount, stamp), IntegerConvertNode.convert(offset, stamp, graph(), NodeView.DEFAULT));
     }
 
     @Override
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop/src/org/graalvm/compiler/loop/DerivedScaledInductionVariable.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop/src/org/graalvm/compiler/loop/DerivedScaledInductionVariable.java
index 7d897981616..b85f0ea5df5 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop/src/org/graalvm/compiler/loop/DerivedScaledInductionVariable.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop/src/org/graalvm/compiler/loop/DerivedScaledInductionVariable.java
@@ -27,6 +27,7 @@ import static org.graalvm.compiler.loop.MathUtil.mul;
 import org.graalvm.compiler.core.common.type.IntegerStamp;
 import org.graalvm.compiler.core.common.type.Stamp;
 import org.graalvm.compiler.nodes.ConstantNode;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.ValueNode;
 import org.graalvm.compiler.nodes.calc.IntegerConvertNode;
 import org.graalvm.compiler.nodes.calc.NegateNode;
@@ -45,7 +46,7 @@ public class DerivedScaledInductionVariable extends DerivedInductionVariable {
 
     public DerivedScaledInductionVariable(LoopEx loop, InductionVariable base, NegateNode value) {
         super(loop, base);
-        this.scale = ConstantNode.forIntegerStamp(value.stamp(), -1, value.graph());
+        this.scale = ConstantNode.forIntegerStamp(value.stamp(NodeView.DEFAULT), -1, value.graph());
         this.value = value;
     }
 
@@ -60,7 +61,7 @@ public class DerivedScaledInductionVariable extends DerivedInductionVariable {
 
     @Override
     public Direction direction() {
-        Stamp stamp = scale.stamp();
+        Stamp stamp = scale.stamp(NodeView.DEFAULT);
         if (stamp instanceof IntegerStamp) {
             IntegerStamp integerStamp = (IntegerStamp) stamp;
             if (integerStamp.isStrictlyPositive()) {
@@ -104,7 +105,7 @@ public class DerivedScaledInductionVariable extends DerivedInductionVariable {
 
     @Override
     public ValueNode extremumNode(boolean assumePositiveTripCount, Stamp stamp) {
-        return mul(graph(), base.extremumNode(assumePositiveTripCount, stamp), IntegerConvertNode.convert(scale, stamp, graph()));
+        return mul(graph(), base.extremumNode(assumePositiveTripCount, stamp), IntegerConvertNode.convert(scale, stamp, graph(), NodeView.DEFAULT));
     }
 
     @Override
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop/src/org/graalvm/compiler/loop/InductionVariable.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop/src/org/graalvm/compiler/loop/InductionVariable.java
index 2384ad832ca..d4edf5b69d3 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop/src/org/graalvm/compiler/loop/InductionVariable.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop/src/org/graalvm/compiler/loop/InductionVariable.java
@@ -24,6 +24,7 @@ package org.graalvm.compiler.loop;
 
 import org.graalvm.compiler.core.common.type.Stamp;
 import org.graalvm.compiler.debug.GraalError;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.StructuredGraph;
 import org.graalvm.compiler.nodes.ValueNode;
 
@@ -93,7 +94,7 @@ public abstract class InductionVariable {
      * {@link CountedLoopInfo#isExactTripCount()} returns false for the containing loop.
      */
     public ValueNode extremumNode() {
-        return extremumNode(false, valueNode().stamp());
+        return extremumNode(false, valueNode().stamp(NodeView.DEFAULT));
     }
 
     public abstract ValueNode extremumNode(boolean assumePositiveTripCount, Stamp stamp);
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop/src/org/graalvm/compiler/loop/LoopEx.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop/src/org/graalvm/compiler/loop/LoopEx.java
index 79158e4d672..b0a2931db66 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop/src/org/graalvm/compiler/loop/LoopEx.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop/src/org/graalvm/compiler/loop/LoopEx.java
@@ -44,6 +44,7 @@ import org.graalvm.compiler.nodes.FullInfopointNode;
 import org.graalvm.compiler.nodes.IfNode;
 import org.graalvm.compiler.nodes.LogicNode;
 import org.graalvm.compiler.nodes.LoopBeginNode;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.PhiNode;
 import org.graalvm.compiler.nodes.PiNode;
 import org.graalvm.compiler.nodes.StructuredGraph;
@@ -188,7 +189,7 @@ public class LoopEx {
             if (!binary.isAssociative()) {
                 continue;
             }
-            ValueNode result = BinaryArithmeticNode.reassociate(binary, invariant, binary.getX(), binary.getY());
+            ValueNode result = BinaryArithmeticNode.reassociate(binary, invariant, binary.getX(), binary.getY(), NodeView.DEFAULT);
             if (result != binary) {
                 if (!result.isAlive()) {
                     assert !result.isDeleted();
@@ -259,8 +260,8 @@ public class LoopEx {
                     if (!iv.isConstantStride() || Math.abs(iv.constantStride()) != 1) {
                         return false;
                     }
-                    IntegerStamp initStamp = (IntegerStamp) iv.initNode().stamp();
-                    IntegerStamp limitStamp = (IntegerStamp) limit.stamp();
+                    IntegerStamp initStamp = (IntegerStamp) iv.initNode().stamp(NodeView.DEFAULT);
+                    IntegerStamp limitStamp = (IntegerStamp) limit.stamp(NodeView.DEFAULT);
                     if (iv.direction() == Direction.Up) {
                         if (initStamp.upperBound() > limitStamp.lowerBound()) {
                             return false;
@@ -392,12 +393,12 @@ public class LoopEx {
                 } else {
                     boolean isValidConvert = op instanceof PiNode || op instanceof SignExtendNode;
                     if (!isValidConvert && op instanceof ZeroExtendNode) {
-                        IntegerStamp inputStamp = (IntegerStamp) ((ZeroExtendNode) op).getValue().stamp();
+                        IntegerStamp inputStamp = (IntegerStamp) ((ZeroExtendNode) op).getValue().stamp(NodeView.DEFAULT);
                         isValidConvert = inputStamp.isPositive();
                     }
 
                     if (isValidConvert) {
-                        iv = new DerivedConvertedInductionVariable(loop, baseIv, op.stamp(), op);
+                        iv = new DerivedConvertedInductionVariable(loop, baseIv, op.stamp(NodeView.DEFAULT), op);
                     }
                 }
 
@@ -411,7 +412,7 @@ public class LoopEx {
     }
 
     private static ValueNode addSub(LoopEx loop, ValueNode op, ValueNode base) {
-        if (op.stamp() instanceof IntegerStamp && (op instanceof AddNode || op instanceof SubNode)) {
+        if (op.stamp(NodeView.DEFAULT) instanceof IntegerStamp && (op instanceof AddNode || op instanceof SubNode)) {
             BinaryArithmeticNode<?> aritOp = (BinaryArithmeticNode<?>) op;
             if (aritOp.getX() == base && loop.isOutsideLoop(aritOp.getY())) {
                 return aritOp.getY();
@@ -434,7 +435,7 @@ public class LoopEx {
         if (op instanceof LeftShiftNode) {
             LeftShiftNode shift = (LeftShiftNode) op;
             if (shift.getX() == base && shift.getY().isConstant()) {
-                return ConstantNode.forIntegerStamp(base.stamp(), 1 << shift.getY().asJavaConstant().asInt(), base.graph());
+                return ConstantNode.forIntegerStamp(base.stamp(NodeView.DEFAULT), 1 << shift.getY().asJavaConstant().asInt(), base.graph());
             }
         }
         return null;
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop/src/org/graalvm/compiler/loop/LoopFragment.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop/src/org/graalvm/compiler/loop/LoopFragment.java
index 1a778623b34..9323ec4bb9f 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop/src/org/graalvm/compiler/loop/LoopFragment.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop/src/org/graalvm/compiler/loop/LoopFragment.java
@@ -39,6 +39,7 @@ import org.graalvm.compiler.nodes.GuardProxyNode;
 import org.graalvm.compiler.nodes.Invoke;
 import org.graalvm.compiler.nodes.LoopExitNode;
 import org.graalvm.compiler.nodes.MergeNode;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.PhiNode;
 import org.graalvm.compiler.nodes.ProxyNode;
 import org.graalvm.compiler.nodes.StructuredGraph;
@@ -460,7 +461,7 @@ public abstract class LoopFragment {
                 if (newVpn != null) {
                     PhiNode phi;
                     if (vpn instanceof ValueProxyNode) {
-                        phi = graph.addWithoutUnique(new ValuePhiNode(vpn.stamp(), merge));
+                        phi = graph.addWithoutUnique(new ValuePhiNode(vpn.stamp(NodeView.DEFAULT), merge));
                     } else if (vpn instanceof GuardProxyNode) {
                         phi = graph.addWithoutUnique(new GuardPhiNode(merge));
                     } else {
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop/src/org/graalvm/compiler/loop/LoopFragmentInside.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop/src/org/graalvm/compiler/loop/LoopFragmentInside.java
index a13ea7bbf08..8ae334b11d5 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop/src/org/graalvm/compiler/loop/LoopFragmentInside.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop/src/org/graalvm/compiler/loop/LoopFragmentInside.java
@@ -48,6 +48,7 @@ import org.graalvm.compiler.nodes.LoopBeginNode;
 import org.graalvm.compiler.nodes.LoopEndNode;
 import org.graalvm.compiler.nodes.LoopExitNode;
 import org.graalvm.compiler.nodes.MergeNode;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.PhiNode;
 import org.graalvm.compiler.nodes.ProxyNode;
 import org.graalvm.compiler.nodes.SafepointNode;
@@ -213,11 +214,11 @@ public class LoopFragmentInside extends LoopFragment {
             }
             long originalStride = unrollFactor == 1 ? iv.constantStride() : iv.constantStride() / unrollFactor;
             if (iv.direction() == InductionVariable.Direction.Up) {
-                ConstantNode aboveVal = graph.unique(ConstantNode.forIntegerStamp(iv.initNode().stamp(), unrollFactor * originalStride));
+                ConstantNode aboveVal = graph.unique(ConstantNode.forIntegerStamp(iv.initNode().stamp(NodeView.DEFAULT), unrollFactor * originalStride));
                 ValueNode newLimit = graph.addWithoutUnique(new SubNode(compareBound, aboveVal));
                 compareNode.replaceFirstInput(compareBound, newLimit);
             } else if (iv.direction() == InductionVariable.Direction.Down) {
-                ConstantNode aboveVal = graph.unique(ConstantNode.forIntegerStamp(iv.initNode().stamp(), unrollFactor * -originalStride));
+                ConstantNode aboveVal = graph.unique(ConstantNode.forIntegerStamp(iv.initNode().stamp(NodeView.DEFAULT), unrollFactor * -originalStride));
                 ValueNode newLimit = graph.addWithoutUnique(new AddNode(compareBound, aboveVal));
                 compareNode.replaceFirstInput(compareBound, newLimit);
             }
@@ -391,7 +392,7 @@ public class LoopFragmentInside extends LoopFragment {
     private static PhiNode patchPhi(StructuredGraph graph, PhiNode phi, AbstractMergeNode merge) {
         PhiNode ret;
         if (phi instanceof ValuePhiNode) {
-            ret = new ValuePhiNode(phi.stamp(), merge);
+            ret = new ValuePhiNode(phi.stamp(NodeView.DEFAULT), merge);
         } else if (phi instanceof GuardPhiNode) {
             ret = new GuardPhiNode(merge);
         } else if (phi instanceof MemoryPhiNode) {
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop/src/org/graalvm/compiler/loop/MathUtil.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop/src/org/graalvm/compiler/loop/MathUtil.java
index 9c4333edd40..6a20d17faae 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop/src/org/graalvm/compiler/loop/MathUtil.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.loop/src/org/graalvm/compiler/loop/MathUtil.java
@@ -24,9 +24,11 @@ package org.graalvm.compiler.loop;
 
 import org.graalvm.compiler.core.common.type.IntegerStamp;
 import org.graalvm.compiler.nodes.FixedNode;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.StructuredGraph;
 import org.graalvm.compiler.nodes.ValueNode;
 import org.graalvm.compiler.nodes.calc.BinaryArithmeticNode;
+import org.graalvm.compiler.nodes.calc.FixedBinaryNode;
 import org.graalvm.compiler.nodes.calc.SignedDivNode;
 
 /**
@@ -34,11 +36,11 @@ import org.graalvm.compiler.nodes.calc.SignedDivNode;
  */
 public class MathUtil {
     private static boolean isConstantOne(ValueNode v1) {
-        return v1.isConstant() && v1.stamp() instanceof IntegerStamp && v1.asJavaConstant().asLong() == 1;
+        return v1.isConstant() && v1.stamp(NodeView.DEFAULT) instanceof IntegerStamp && v1.asJavaConstant().asLong() == 1;
     }
 
     private static boolean isConstantZero(ValueNode v1) {
-        return v1.isConstant() && v1.stamp() instanceof IntegerStamp && v1.asJavaConstant().asLong() == 0;
+        return v1.isConstant() && v1.stamp(NodeView.DEFAULT) instanceof IntegerStamp && v1.asJavaConstant().asLong() == 0;
     }
 
     public static ValueNode add(StructuredGraph graph, ValueNode v1, ValueNode v2) {
@@ -48,7 +50,7 @@ public class MathUtil {
         if (isConstantZero(v2)) {
             return v1;
         }
-        return BinaryArithmeticNode.add(graph, v1, v2);
+        return BinaryArithmeticNode.add(graph, v1, v2, NodeView.DEFAULT);
     }
 
     public static ValueNode mul(StructuredGraph graph, ValueNode v1, ValueNode v2) {
@@ -58,22 +60,24 @@ public class MathUtil {
         if (isConstantOne(v2)) {
             return v1;
         }
-        return BinaryArithmeticNode.mul(graph, v1, v2);
+        return BinaryArithmeticNode.mul(graph, v1, v2, NodeView.DEFAULT);
     }
 
     public static ValueNode sub(StructuredGraph graph, ValueNode v1, ValueNode v2) {
         if (isConstantZero(v2)) {
             return v1;
         }
-        return BinaryArithmeticNode.sub(graph, v1, v2);
+        return BinaryArithmeticNode.sub(graph, v1, v2, NodeView.DEFAULT);
     }
 
     public static ValueNode divBefore(StructuredGraph graph, FixedNode before, ValueNode dividend, ValueNode divisor) {
         if (isConstantOne(divisor)) {
             return dividend;
         }
-        SignedDivNode div = graph.add(new SignedDivNode(dividend, divisor));
-        graph.addBeforeFixed(before, div);
+        ValueNode div = graph.addOrUniqueWithInputs(SignedDivNode.create(dividend, divisor, NodeView.DEFAULT));
+        if (div instanceof FixedBinaryNode) {
+            graph.addBeforeFixed(before, (FixedBinaryNode) div);
+        }
         return div;
     }
 }
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodeinfo.processor/src/org/graalvm/compiler/nodeinfo/processor/GraphNodeProcessor.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodeinfo.processor/src/org/graalvm/compiler/nodeinfo/processor/GraphNodeProcessor.java
index 1d21ccbcab0..f1ee5d20db8 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodeinfo.processor/src/org/graalvm/compiler/nodeinfo/processor/GraphNodeProcessor.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodeinfo.processor/src/org/graalvm/compiler/nodeinfo/processor/GraphNodeProcessor.java
@@ -103,7 +103,6 @@ public class GraphNodeProcessor extends AbstractProcessor {
     private void reportException(Kind kind, Element element, Throwable t) {
         StringWriter buf = new StringWriter();
         t.printStackTrace(new PrintWriter(buf));
-        buf.toString();
         message(kind, element, "Exception thrown during processing: %s", buf.toString());
     }
 
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes.test/src/org/graalvm/compiler/nodes/test/IntegerStampTest.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes.test/src/org/graalvm/compiler/nodes/test/IntegerStampTest.java
index 04832ecd1a3..756661318ff 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes.test/src/org/graalvm/compiler/nodes/test/IntegerStampTest.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes.test/src/org/graalvm/compiler/nodes/test/IntegerStampTest.java
@@ -37,6 +37,7 @@ import org.graalvm.compiler.core.common.type.StampFactory;
 import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.graph.test.GraphTest;
 import org.graalvm.compiler.nodes.ConstantNode;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.StructuredGraph;
 import org.graalvm.compiler.nodes.StructuredGraph.AllowAssumptions;
 import org.graalvm.compiler.options.OptionValues;
@@ -67,52 +68,52 @@ public class IntegerStampTest extends GraphTest {
 
     @Test
     public void testBooleanConstant() {
-        assertEquals(IntegerStamp.create(32, 1, 1, 0x1, 0x1), ConstantNode.forBoolean(true, graph).stamp());
-        assertEquals(IntegerStamp.create(32, 0, 0, 0x0, 0x0), ConstantNode.forBoolean(false, graph).stamp());
+        assertEquals(IntegerStamp.create(32, 1, 1, 0x1, 0x1), ConstantNode.forBoolean(true, graph).stamp(NodeView.DEFAULT));
+        assertEquals(IntegerStamp.create(32, 0, 0, 0x0, 0x0), ConstantNode.forBoolean(false, graph).stamp(NodeView.DEFAULT));
     }
 
     @Test
     public void testByteConstant() {
-        assertEquals(IntegerStamp.create(32, 0, 0, 0x0, 0x0), ConstantNode.forByte((byte) 0, graph).stamp());
-        assertEquals(IntegerStamp.create(32, 16, 16, 0x10, 0x10), ConstantNode.forByte((byte) 16, graph).stamp());
-        assertEquals(IntegerStamp.create(32, -16, -16, 0xfffffff0L, 0xfffffff0L), ConstantNode.forByte((byte) -16, graph).stamp());
-        assertEquals(IntegerStamp.create(32, 127, 127, 0x7f, 0x7f), ConstantNode.forByte((byte) 127, graph).stamp());
-        assertEquals(IntegerStamp.create(32, -128, -128, 0xffffff80L, 0xffffff80L), ConstantNode.forByte((byte) -128, graph).stamp());
+        assertEquals(IntegerStamp.create(32, 0, 0, 0x0, 0x0), ConstantNode.forByte((byte) 0, graph).stamp(NodeView.DEFAULT));
+        assertEquals(IntegerStamp.create(32, 16, 16, 0x10, 0x10), ConstantNode.forByte((byte) 16, graph).stamp(NodeView.DEFAULT));
+        assertEquals(IntegerStamp.create(32, -16, -16, 0xfffffff0L, 0xfffffff0L), ConstantNode.forByte((byte) -16, graph).stamp(NodeView.DEFAULT));
+        assertEquals(IntegerStamp.create(32, 127, 127, 0x7f, 0x7f), ConstantNode.forByte((byte) 127, graph).stamp(NodeView.DEFAULT));
+        assertEquals(IntegerStamp.create(32, -128, -128, 0xffffff80L, 0xffffff80L), ConstantNode.forByte((byte) -128, graph).stamp(NodeView.DEFAULT));
     }
 
     @Test
     public void testShortConstant() {
-        assertEquals(IntegerStamp.create(32, 0, 0, 0x0, 0x0), ConstantNode.forShort((short) 0, graph).stamp());
-        assertEquals(IntegerStamp.create(32, 128, 128, 0x80, 0x80), ConstantNode.forShort((short) 128, graph).stamp());
-        assertEquals(IntegerStamp.create(32, -128, -128, 0xffffff80L, 0xffffff80L), ConstantNode.forShort((short) -128, graph).stamp());
-        assertEquals(IntegerStamp.create(32, 32767, 32767, 0x7fff, 0x7fff), ConstantNode.forShort((short) 32767, graph).stamp());
-        assertEquals(IntegerStamp.create(32, -32768, -32768, 0xffff8000L, 0xffff8000L), ConstantNode.forShort((short) -32768, graph).stamp());
+        assertEquals(IntegerStamp.create(32, 0, 0, 0x0, 0x0), ConstantNode.forShort((short) 0, graph).stamp(NodeView.DEFAULT));
+        assertEquals(IntegerStamp.create(32, 128, 128, 0x80, 0x80), ConstantNode.forShort((short) 128, graph).stamp(NodeView.DEFAULT));
+        assertEquals(IntegerStamp.create(32, -128, -128, 0xffffff80L, 0xffffff80L), ConstantNode.forShort((short) -128, graph).stamp(NodeView.DEFAULT));
+        assertEquals(IntegerStamp.create(32, 32767, 32767, 0x7fff, 0x7fff), ConstantNode.forShort((short) 32767, graph).stamp(NodeView.DEFAULT));
+        assertEquals(IntegerStamp.create(32, -32768, -32768, 0xffff8000L, 0xffff8000L), ConstantNode.forShort((short) -32768, graph).stamp(NodeView.DEFAULT));
     }
 
     @Test
     public void testCharConstant() {
-        assertEquals(IntegerStamp.create(32, 0, 0, 0x0, 0x0), ConstantNode.forChar((char) 0, graph).stamp());
-        assertEquals(IntegerStamp.create(32, 'A', 'A', 'A', 'A'), ConstantNode.forChar('A', graph).stamp());
-        assertEquals(IntegerStamp.create(32, 128, 128, 0x80, 0x80), ConstantNode.forChar((char) 128, graph).stamp());
-        assertEquals(IntegerStamp.create(32, 65535, 65535, 0xffff, 0xffff), ConstantNode.forChar((char) 65535, graph).stamp());
+        assertEquals(IntegerStamp.create(32, 0, 0, 0x0, 0x0), ConstantNode.forChar((char) 0, graph).stamp(NodeView.DEFAULT));
+        assertEquals(IntegerStamp.create(32, 'A', 'A', 'A', 'A'), ConstantNode.forChar('A', graph).stamp(NodeView.DEFAULT));
+        assertEquals(IntegerStamp.create(32, 128, 128, 0x80, 0x80), ConstantNode.forChar((char) 128, graph).stamp(NodeView.DEFAULT));
+        assertEquals(IntegerStamp.create(32, 65535, 65535, 0xffff, 0xffff), ConstantNode.forChar((char) 65535, graph).stamp(NodeView.DEFAULT));
     }
 
     @Test
     public void testIntConstant() {
-        assertEquals(IntegerStamp.create(32, 0, 0, 0x0, 0x0), ConstantNode.forInt(0, graph).stamp());
-        assertEquals(IntegerStamp.create(32, 128, 128, 0x80, 0x80), ConstantNode.forInt(128, graph).stamp());
-        assertEquals(IntegerStamp.create(32, -128, -128, 0xffffff80L, 0xffffff80L), ConstantNode.forInt(-128, graph).stamp());
-        assertEquals(IntegerStamp.create(32, Integer.MAX_VALUE, Integer.MAX_VALUE, 0x7fffffff, 0x7fffffff), ConstantNode.forInt(Integer.MAX_VALUE, graph).stamp());
-        assertEquals(IntegerStamp.create(32, Integer.MIN_VALUE, Integer.MIN_VALUE, 0x80000000L, 0x80000000L), ConstantNode.forInt(Integer.MIN_VALUE, graph).stamp());
+        assertEquals(IntegerStamp.create(32, 0, 0, 0x0, 0x0), ConstantNode.forInt(0, graph).stamp(NodeView.DEFAULT));
+        assertEquals(IntegerStamp.create(32, 128, 128, 0x80, 0x80), ConstantNode.forInt(128, graph).stamp(NodeView.DEFAULT));
+        assertEquals(IntegerStamp.create(32, -128, -128, 0xffffff80L, 0xffffff80L), ConstantNode.forInt(-128, graph).stamp(NodeView.DEFAULT));
+        assertEquals(IntegerStamp.create(32, Integer.MAX_VALUE, Integer.MAX_VALUE, 0x7fffffff, 0x7fffffff), ConstantNode.forInt(Integer.MAX_VALUE, graph).stamp(NodeView.DEFAULT));
+        assertEquals(IntegerStamp.create(32, Integer.MIN_VALUE, Integer.MIN_VALUE, 0x80000000L, 0x80000000L), ConstantNode.forInt(Integer.MIN_VALUE, graph).stamp(NodeView.DEFAULT));
     }
 
     @Test
     public void testLongConstant() {
-        assertEquals(IntegerStamp.create(64, 0, 0, 0x0, 0x0), ConstantNode.forLong(0, graph).stamp());
-        assertEquals(IntegerStamp.create(64, 128, 128, 0x80, 0x80), ConstantNode.forLong(128, graph).stamp());
-        assertEquals(IntegerStamp.create(64, -128, -128, 0xffffffffffffff80L, 0xffffffffffffff80L), ConstantNode.forLong(-128, graph).stamp());
-        assertEquals(IntegerStamp.create(64, Long.MAX_VALUE, Long.MAX_VALUE, 0x7fffffffffffffffL, 0x7fffffffffffffffL), ConstantNode.forLong(Long.MAX_VALUE, graph).stamp());
-        assertEquals(IntegerStamp.create(64, Long.MIN_VALUE, Long.MIN_VALUE, 0x8000000000000000L, 0x8000000000000000L), ConstantNode.forLong(Long.MIN_VALUE, graph).stamp());
+        assertEquals(IntegerStamp.create(64, 0, 0, 0x0, 0x0), ConstantNode.forLong(0, graph).stamp(NodeView.DEFAULT));
+        assertEquals(IntegerStamp.create(64, 128, 128, 0x80, 0x80), ConstantNode.forLong(128, graph).stamp(NodeView.DEFAULT));
+        assertEquals(IntegerStamp.create(64, -128, -128, 0xffffffffffffff80L, 0xffffffffffffff80L), ConstantNode.forLong(-128, graph).stamp(NodeView.DEFAULT));
+        assertEquals(IntegerStamp.create(64, Long.MAX_VALUE, Long.MAX_VALUE, 0x7fffffffffffffffL, 0x7fffffffffffffffL), ConstantNode.forLong(Long.MAX_VALUE, graph).stamp(NodeView.DEFAULT));
+        assertEquals(IntegerStamp.create(64, Long.MIN_VALUE, Long.MIN_VALUE, 0x8000000000000000L, 0x8000000000000000L), ConstantNode.forLong(Long.MIN_VALUE, graph).stamp(NodeView.DEFAULT));
     }
 
     @Test
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes.test/src/org/graalvm/compiler/nodes/test/NegateNodeCanonicalizationTest.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes.test/src/org/graalvm/compiler/nodes/test/NegateNodeCanonicalizationTest.java
index d5061078b07..3f7a0b75253 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes.test/src/org/graalvm/compiler/nodes/test/NegateNodeCanonicalizationTest.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes.test/src/org/graalvm/compiler/nodes/test/NegateNodeCanonicalizationTest.java
@@ -29,6 +29,7 @@ import org.graalvm.compiler.core.common.type.ArithmeticOpTable;
 import org.graalvm.compiler.debug.DebugHandlersFactory;
 import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.nodes.ConstantNode;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.StructuredGraph;
 import org.graalvm.compiler.nodes.StructuredGraph.AllowAssumptions;
 import org.graalvm.compiler.options.OptionValues;
@@ -57,7 +58,7 @@ public class NegateNodeCanonicalizationTest {
         for (byte i : a) {
             ConstantNode node = ConstantNode.forByte(i, graph);
             JavaConstant expected = JavaConstant.forInt(-i);
-            assertEquals(expected, ArithmeticOpTable.forStamp(node.stamp()).getNeg().foldConstant(node.asConstant()));
+            assertEquals(expected, ArithmeticOpTable.forStamp(node.stamp(NodeView.DEFAULT)).getNeg().foldConstant(node.asConstant()));
         }
     }
 
@@ -67,7 +68,7 @@ public class NegateNodeCanonicalizationTest {
         for (char i : a) {
             ConstantNode node = ConstantNode.forChar(i, graph);
             JavaConstant expected = JavaConstant.forInt(-i);
-            assertEquals(expected, ArithmeticOpTable.forStamp(node.stamp()).getNeg().foldConstant(node.asConstant()));
+            assertEquals(expected, ArithmeticOpTable.forStamp(node.stamp(NodeView.DEFAULT)).getNeg().foldConstant(node.asConstant()));
         }
     }
 
@@ -77,7 +78,7 @@ public class NegateNodeCanonicalizationTest {
         for (short i : a) {
             ConstantNode node = ConstantNode.forShort(i, graph);
             JavaConstant expected = JavaConstant.forInt(-i);
-            assertEquals(expected, ArithmeticOpTable.forStamp(node.stamp()).getNeg().foldConstant(node.asConstant()));
+            assertEquals(expected, ArithmeticOpTable.forStamp(node.stamp(NodeView.DEFAULT)).getNeg().foldConstant(node.asConstant()));
         }
     }
 
@@ -87,7 +88,7 @@ public class NegateNodeCanonicalizationTest {
         for (int i : a) {
             ConstantNode node = ConstantNode.forInt(i, graph);
             JavaConstant expected = JavaConstant.forInt(-i);
-            assertEquals(expected, ArithmeticOpTable.forStamp(node.stamp()).getNeg().foldConstant(node.asConstant()));
+            assertEquals(expected, ArithmeticOpTable.forStamp(node.stamp(NodeView.DEFAULT)).getNeg().foldConstant(node.asConstant()));
         }
     }
 
@@ -97,7 +98,7 @@ public class NegateNodeCanonicalizationTest {
         for (long i : a) {
             ConstantNode node = ConstantNode.forLong(i, graph);
             JavaConstant expected = JavaConstant.forLong(-i);
-            assertEquals(expected, ArithmeticOpTable.forStamp(node.stamp()).getNeg().foldConstant(node.asConstant()));
+            assertEquals(expected, ArithmeticOpTable.forStamp(node.stamp(NodeView.DEFAULT)).getNeg().foldConstant(node.asConstant()));
         }
     }
 
@@ -107,7 +108,7 @@ public class NegateNodeCanonicalizationTest {
         for (float i : a) {
             ConstantNode node = ConstantNode.forFloat(i, graph);
             JavaConstant expected = JavaConstant.forFloat(-i);
-            assertEquals(expected, ArithmeticOpTable.forStamp(node.stamp()).getNeg().foldConstant(node.asConstant()));
+            assertEquals(expected, ArithmeticOpTable.forStamp(node.stamp(NodeView.DEFAULT)).getNeg().foldConstant(node.asConstant()));
         }
     }
 
@@ -117,7 +118,7 @@ public class NegateNodeCanonicalizationTest {
         for (double i : a) {
             ConstantNode node = ConstantNode.forDouble(i, graph);
             JavaConstant expected = JavaConstant.forDouble(-i);
-            assertEquals(expected, ArithmeticOpTable.forStamp(node.stamp()).getNeg().foldConstant(node.asConstant()));
+            assertEquals(expected, ArithmeticOpTable.forStamp(node.stamp(NodeView.DEFAULT)).getNeg().foldConstant(node.asConstant()));
         }
     }
 
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes.test/src/org/graalvm/compiler/nodes/test/ReinterpretStampDoubleToLongTest.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes.test/src/org/graalvm/compiler/nodes/test/ReinterpretStampDoubleToLongTest.java
index f2575f20505..e68101e8336 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes.test/src/org/graalvm/compiler/nodes/test/ReinterpretStampDoubleToLongTest.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes.test/src/org/graalvm/compiler/nodes/test/ReinterpretStampDoubleToLongTest.java
@@ -26,6 +26,8 @@ import java.util.ArrayList;
 import java.util.Collection;
 import java.util.List;
 
+import org.graalvm.compiler.nodes.NodeView;
+import org.graalvm.compiler.nodes.ValueNode;
 import org.junit.Assert;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -77,9 +79,9 @@ public class ReinterpretStampDoubleToLongTest extends ReinterpretStampTest {
     @Test
     public void run() {
         ParameterNode param = new ParameterNode(0, StampPair.createSingle(inputStamp));
-        ReinterpretNode reinterpret = new ReinterpretNode(JavaKind.Long, param);
+        ValueNode reinterpret = ReinterpretNode.create(JavaKind.Long, param, NodeView.DEFAULT);
 
-        IntegerStamp resultStamp = (IntegerStamp) reinterpret.stamp();
+        IntegerStamp resultStamp = (IntegerStamp) reinterpret.stamp(NodeView.DEFAULT);
         Assert.assertEquals(Long.SIZE, resultStamp.getBits());
 
         for (long result : interestingLongs) {
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes.test/src/org/graalvm/compiler/nodes/test/ReinterpretStampFloatToIntTest.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes.test/src/org/graalvm/compiler/nodes/test/ReinterpretStampFloatToIntTest.java
index b24285d7a00..fb7f6115761 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes.test/src/org/graalvm/compiler/nodes/test/ReinterpretStampFloatToIntTest.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes.test/src/org/graalvm/compiler/nodes/test/ReinterpretStampFloatToIntTest.java
@@ -26,6 +26,8 @@ import java.util.ArrayList;
 import java.util.Collection;
 import java.util.List;
 
+import org.graalvm.compiler.nodes.NodeView;
+import org.graalvm.compiler.nodes.ValueNode;
 import org.junit.Assert;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -77,10 +79,10 @@ public class ReinterpretStampFloatToIntTest extends ReinterpretStampTest {
     @Test
     public void run() {
         ParameterNode param = new ParameterNode(0, StampPair.createSingle(inputStamp));
-        ReinterpretNode reinterpret = new ReinterpretNode(JavaKind.Int, param);
+        ValueNode reinterpret = ReinterpretNode.create(JavaKind.Int, param, NodeView.DEFAULT);
         reinterpret.inferStamp();
 
-        IntegerStamp resultStamp = (IntegerStamp) reinterpret.stamp();
+        IntegerStamp resultStamp = (IntegerStamp) reinterpret.stamp(NodeView.DEFAULT);
         Assert.assertEquals(Integer.SIZE, resultStamp.getBits());
 
         for (int result : interestingInts) {
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes.test/src/org/graalvm/compiler/nodes/test/ReinterpretStampIntToFloatTest.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes.test/src/org/graalvm/compiler/nodes/test/ReinterpretStampIntToFloatTest.java
index b4fd7b48011..510680f0934 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes.test/src/org/graalvm/compiler/nodes/test/ReinterpretStampIntToFloatTest.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes.test/src/org/graalvm/compiler/nodes/test/ReinterpretStampIntToFloatTest.java
@@ -26,6 +26,8 @@ import java.util.ArrayList;
 import java.util.Collection;
 import java.util.List;
 
+import org.graalvm.compiler.nodes.NodeView;
+import org.graalvm.compiler.nodes.ValueNode;
 import org.junit.Assert;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -68,10 +70,10 @@ public class ReinterpretStampIntToFloatTest extends ReinterpretStampTest {
     @Test
     public void run() {
         ParameterNode param = new ParameterNode(0, StampPair.createSingle(inputStamp));
-        ReinterpretNode reinterpret = new ReinterpretNode(JavaKind.Float, param);
+        ValueNode reinterpret = ReinterpretNode.create(JavaKind.Float, param, NodeView.DEFAULT);
         reinterpret.inferStamp();
 
-        FloatStamp resultStamp = (FloatStamp) reinterpret.stamp();
+        FloatStamp resultStamp = (FloatStamp) reinterpret.stamp(NodeView.DEFAULT);
         Assert.assertEquals(Float.SIZE, resultStamp.getBits());
 
         for (int input : interestingInts) {
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes.test/src/org/graalvm/compiler/nodes/test/ReinterpretStampLongToDoubleTest.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes.test/src/org/graalvm/compiler/nodes/test/ReinterpretStampLongToDoubleTest.java
index 08642f3bd14..b18389e9118 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes.test/src/org/graalvm/compiler/nodes/test/ReinterpretStampLongToDoubleTest.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes.test/src/org/graalvm/compiler/nodes/test/ReinterpretStampLongToDoubleTest.java
@@ -26,6 +26,8 @@ import java.util.ArrayList;
 import java.util.Collection;
 import java.util.List;
 
+import org.graalvm.compiler.nodes.NodeView;
+import org.graalvm.compiler.nodes.ValueNode;
 import org.junit.Assert;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -68,10 +70,10 @@ public class ReinterpretStampLongToDoubleTest extends ReinterpretStampTest {
     @Test
     public void run() {
         ParameterNode param = new ParameterNode(0, StampPair.createSingle(inputStamp));
-        ReinterpretNode reinterpret = new ReinterpretNode(JavaKind.Double, param);
+        ValueNode reinterpret = ReinterpretNode.create(JavaKind.Double, param, NodeView.DEFAULT);
         reinterpret.inferStamp();
 
-        FloatStamp resultStamp = (FloatStamp) reinterpret.stamp();
+        FloatStamp resultStamp = (FloatStamp) reinterpret.stamp(NodeView.DEFAULT);
         Assert.assertEquals(Double.SIZE, resultStamp.getBits());
 
         for (long input : interestingLongs) {
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/CompressionNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/CompressionNode.java
index 6f9a420af59..3b7b0f0f9a1 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/CompressionNode.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/CompressionNode.java
@@ -68,7 +68,7 @@ public abstract class CompressionNode extends UnaryNode implements ConvertNode,
 
     @Override
     public Stamp foldStamp(Stamp newStamp) {
-        assert newStamp.isCompatible(getValue().stamp());
+        assert newStamp.isCompatible(getValue().stamp(NodeView.DEFAULT));
         return mkStamp(newStamp);
     }
 
@@ -124,7 +124,8 @@ public abstract class CompressionNode extends UnaryNode implements ConvertNode,
             }
 
             ConstantNode constant = (ConstantNode) forValue;
-            return ConstantNode.forConstant(stamp(), convert(constant.getValue(), tool.getConstantReflection()), constant.getStableDimension(), constant.isDefaultStable(), tool.getMetaAccess());
+            return ConstantNode.forConstant(stamp(NodeView.DEFAULT), convert(constant.getValue(), tool.getConstantReflection()), constant.getStableDimension(), constant.isDefaultStable(),
+                            tool.getMetaAccess());
         } else if (forValue instanceof CompressionNode) {
             CompressionNode other = (CompressionNode) forValue;
             if (op != other.op && encoding.equals(other.encoding)) {
@@ -137,8 +138,8 @@ public abstract class CompressionNode extends UnaryNode implements ConvertNode,
     @Override
     public void generate(NodeLIRBuilderTool gen) {
         boolean nonNull;
-        if (value.stamp() instanceof AbstractObjectStamp) {
-            nonNull = StampTool.isPointerNonNull(value.stamp());
+        if (value.stamp(NodeView.DEFAULT) instanceof AbstractObjectStamp) {
+            nonNull = StampTool.isPointerNonNull(value.stamp(NodeView.DEFAULT));
         } else {
             // metaspace pointers are never null
             nonNull = true;
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/ConstantNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/ConstantNode.java
index 4dd797fe32c..87ee52b0800 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/ConstantNode.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/ConstantNode.java
@@ -134,7 +134,7 @@ public final class ConstantNode extends FloatingNode implements LIRLowerable {
 
     @Override
     public void generate(NodeLIRBuilderTool gen) {
-        LIRKind kind = gen.getLIRGeneratorTool().getLIRKind(stamp());
+        LIRKind kind = gen.getLIRGeneratorTool().getLIRKind(stamp(NodeView.DEFAULT));
         if (onlyUsedInVirtualState()) {
             gen.setResult(this, new ConstantValue(kind, value));
         } else {
@@ -525,7 +525,7 @@ public final class ConstantNode extends FloatingNode implements LIRLowerable {
     @Override
     public String toString(Verbosity verbosity) {
         if (verbosity == Verbosity.Name) {
-            return super.toString(Verbosity.Name) + "(" + value.toValueString() + ", " + stamp().unrestricted().toString() + ")";
+            return super.toString(Verbosity.Name) + "(" + value.toValueString() + ", " + stamp(NodeView.DEFAULT).unrestricted().toString() + ")";
         } else {
             return super.toString(verbosity);
         }
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/EntryProxyNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/EntryProxyNode.java
index 95d3d17beae..838a11c3dda 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/EntryProxyNode.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/EntryProxyNode.java
@@ -44,7 +44,7 @@ public final class EntryProxyNode extends FloatingNode implements ValueProxy {
     @Input ValueNode value;
 
     public EntryProxyNode(ValueNode value, EntryMarkerNode proxyPoint) {
-        super(TYPE, value.stamp().unrestricted());
+        super(TYPE, value.stamp(NodeView.DEFAULT).unrestricted());
         this.value = value;
         this.proxyPoint = proxyPoint;
     }
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/GraphDecoder.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/GraphDecoder.java
index 1f3f2629e48..9b08cbaffaf 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/GraphDecoder.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/GraphDecoder.java
@@ -310,7 +310,7 @@ public class GraphDecoder {
         @Input(InputType.Unchecked) Node proxyPoint;
 
         public ProxyPlaceholder(ValueNode value, MergeNode proxyPoint) {
-            super(TYPE, value.stamp());
+            super(TYPE, value.stamp(NodeView.DEFAULT));
             this.value = value;
             this.proxyPoint = proxyPoint;
         }
@@ -868,7 +868,7 @@ public class GraphDecoder {
                 /* Now we have two different values, so we need to create a phi node. */
                 PhiNode phi;
                 if (proxy instanceof ValueProxyNode) {
-                    phi = graph.addWithoutUnique(new ValuePhiNode(proxy.stamp(), merge));
+                    phi = graph.addWithoutUnique(new ValuePhiNode(proxy.stamp(NodeView.DEFAULT), merge));
                 } else if (proxy instanceof GuardProxyNode) {
                     phi = graph.addWithoutUnique(new GuardPhiNode(merge));
                 } else {
@@ -1630,7 +1630,7 @@ class LoopDetector implements Runnable {
         List<PhiNode> loopBeginPhis = new ArrayList<>(mergePhis.size());
         for (int i = 0; i < mergePhis.size(); i++) {
             PhiNode mergePhi = mergePhis.get(i);
-            PhiNode loopBeginPhi = graph.addWithoutUnique(new ValuePhiNode(mergePhi.stamp(), loopBegin));
+            PhiNode loopBeginPhi = graph.addWithoutUnique(new ValuePhiNode(mergePhi.stamp(NodeView.DEFAULT), loopBegin));
             mergePhi.replaceAtUsages(loopBeginPhi);
             /*
              * The first input of the new phi function is the original phi function, for the one
@@ -1793,7 +1793,7 @@ class LoopDetector implements Runnable {
             assert irreducibleLoopHandler.header.phis().isEmpty();
 
             /* The new phi function for the loop variable. */
-            loopVariablePhi = graph.addWithoutUnique(new ValuePhiNode(explosionHeadValue.stamp().unrestricted(), irreducibleLoopHandler.header));
+            loopVariablePhi = graph.addWithoutUnique(new ValuePhiNode(explosionHeadValue.stamp(NodeView.DEFAULT).unrestricted(), irreducibleLoopHandler.header));
             for (int i = 0; i < irreducibleLoopHandler.header.phiPredecessorCount(); i++) {
                 loopVariablePhi.addInput(explosionHeadValue);
             }
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/GuardedValueNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/GuardedValueNode.java
index 42a2e0a277e..2f0ba9b6def 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/GuardedValueNode.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/GuardedValueNode.java
@@ -53,7 +53,7 @@ public final class GuardedValueNode extends FloatingGuardedNode implements LIRLo
     @Input ValueNode object;
 
     public GuardedValueNode(ValueNode object, GuardingNode guard) {
-        super(TYPE, object.stamp(), guard);
+        super(TYPE, object.stamp(NodeView.DEFAULT), guard);
         this.object = object;
     }
 
@@ -70,7 +70,7 @@ public final class GuardedValueNode extends FloatingGuardedNode implements LIRLo
 
     @Override
     public boolean inferStamp() {
-        return updateStamp(object().stamp());
+        return updateStamp(object().stamp(NodeView.DEFAULT));
     }
 
     @Override
@@ -84,10 +84,10 @@ public final class GuardedValueNode extends FloatingGuardedNode implements LIRLo
     @Override
     public Node canonical(CanonicalizerTool tool) {
         if (getGuard() == null) {
-            if (stamp().equals(object().stamp())) {
+            if (stamp(NodeView.DEFAULT).equals(object().stamp(NodeView.DEFAULT))) {
                 return object();
             } else {
-                return PiNode.create(object(), stamp());
+                return PiNode.create(object(), stamp(NodeView.DEFAULT));
             }
         }
         return this;
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/IfNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/IfNode.java
index 616b1de0ac1..ff7428f7799 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/IfNode.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/IfNode.java
@@ -65,7 +65,6 @@ import org.graalvm.util.EconomicMap;
 import org.graalvm.util.Equivalence;
 
 import jdk.vm.ci.meta.Constant;
-import jdk.vm.ci.meta.ConstantReflectionProvider;
 import jdk.vm.ci.meta.JavaConstant;
 import jdk.vm.ci.meta.JavaKind;
 import jdk.vm.ci.meta.PrimitiveConstant;
@@ -238,7 +237,7 @@ public final class IfNode extends ControlSplitNode implements Simplifiable, LIRL
             if (this.trueSuccessorProbability < probabilityB) {
                 // Reordering of those two if statements is beneficial from the point of view of
                 // their probabilities.
-                if (prepareForSwap(tool.getConstantReflection(), condition(), nextIf.condition())) {
+                if (prepareForSwap(tool, condition(), nextIf.condition())) {
                     // Reordering is allowed from (if1 => begin => if2) to (if2 => begin => if1).
                     assert intermediateBegin.next() == nextIf;
                     AbstractBeginNode bothFalseBegin = nextIf.falseSuccessor();
@@ -267,19 +266,19 @@ public final class IfNode extends ControlSplitNode implements Simplifiable, LIRL
         }
     }
 
-    private boolean isUnboxedFrom(MetaAccessProvider meta, ValueNode x, ValueNode src) {
+    private boolean isUnboxedFrom(MetaAccessProvider meta, NodeView view, ValueNode x, ValueNode src) {
         if (x == src) {
             return true;
         } else if (x instanceof UnboxNode) {
-            return isUnboxedFrom(meta, ((UnboxNode) x).getValue(), src);
+            return isUnboxedFrom(meta, view, ((UnboxNode) x).getValue(), src);
         } else if (x instanceof PiNode) {
             PiNode pi = (PiNode) x;
-            return isUnboxedFrom(meta, pi.getOriginalNode(), src);
+            return isUnboxedFrom(meta, view, pi.getOriginalNode(), src);
         } else if (x instanceof LoadFieldNode) {
             LoadFieldNode load = (LoadFieldNode) x;
             ResolvedJavaType integerType = meta.lookupJavaType(Integer.class);
-            if (load.getValue().stamp().javaType(meta).equals(integerType)) {
-                return isUnboxedFrom(meta, load.getValue(), src);
+            if (load.getValue().stamp(view).javaType(meta).equals(integerType)) {
+                return isUnboxedFrom(meta, view, load.getValue(), src);
             } else {
                 return false;
             }
@@ -321,7 +320,8 @@ public final class IfNode extends ControlSplitNode implements Simplifiable, LIRL
         ResolvedJavaType integerType = meta.lookupJavaType(Integer.class);
 
         // At least one argument for reference equal must be a boxed primitive.
-        if (!x.stamp().javaType(meta).equals(integerType) && !y.stamp().javaType(meta).equals(integerType)) {
+        NodeView view = NodeView.from(tool);
+        if (!x.stamp(view).javaType(meta).equals(integerType) && !y.stamp(view).javaType(meta).equals(integerType)) {
             return false;
         }
 
@@ -366,7 +366,8 @@ public final class IfNode extends ControlSplitNode implements Simplifiable, LIRL
                 continue;
             }
             IntegerEqualsNode equals = (IntegerEqualsNode) fixed.condition();
-            if ((isUnboxedFrom(meta, equals.getX(), x) && isUnboxedFrom(meta, equals.getY(), y)) || (isUnboxedFrom(meta, equals.getX(), y) && isUnboxedFrom(meta, equals.getY(), x))) {
+            if ((isUnboxedFrom(meta, view, equals.getX(), x) && isUnboxedFrom(meta, view, equals.getY(), y)) ||
+                            (isUnboxedFrom(meta, view, equals.getX(), y) && isUnboxedFrom(meta, view, equals.getY(), x))) {
                 unboxCheck = fixed;
             }
         }
@@ -406,7 +407,8 @@ public final class IfNode extends ControlSplitNode implements Simplifiable, LIRL
             ValueNode falseValue = phi.valueAt(falseEnd);
             ValueNode trueValue = phi.valueAt(trueEnd);
 
-            ValueNode result = ConditionalNode.canonicalizeConditional(condition, trueValue, falseValue, phi.stamp());
+            NodeView view = NodeView.from(tool);
+            ValueNode result = ConditionalNode.canonicalizeConditional(condition, trueValue, falseValue, phi.stamp(view), view);
             if (result != null) {
                 /*
                  * canonicalizeConditional returns possibly new nodes so add them to the graph.
@@ -477,8 +479,9 @@ public final class IfNode extends ControlSplitNode implements Simplifiable, LIRL
     private boolean checkForUnsignedCompare(SimplifierTool tool) {
         assert trueSuccessor().hasNoUsages() && falseSuccessor().hasNoUsages();
         if (condition() instanceof IntegerLessThanNode) {
+            NodeView view = NodeView.from(tool);
             IntegerLessThanNode lessThan = (IntegerLessThanNode) condition();
-            Constant y = lessThan.getY().stamp().asConstant();
+            Constant y = lessThan.getY().stamp(view).asConstant();
             if (y instanceof PrimitiveConstant && ((PrimitiveConstant) y).asLong() == 0 && falseSuccessor().next() instanceof IfNode) {
                 IfNode ifNode2 = (IfNode) falseSuccessor().next();
                 if (ifNode2.condition() instanceof IntegerLessThanNode) {
@@ -490,7 +493,8 @@ public final class IfNode extends ControlSplitNode implements Simplifiable, LIRL
                      * Convert x >= 0 && x < positive which is represented as !(x < 0) && x <
                      * <positive> into an unsigned compare.
                      */
-                    if (lessThan2.getX() == lessThan.getX() && lessThan2.getY().stamp() instanceof IntegerStamp && ((IntegerStamp) lessThan2.getY().stamp()).isPositive() &&
+                    if (lessThan2.getX() == lessThan.getX() && lessThan2.getY().stamp(view) instanceof IntegerStamp &&
+                                    ((IntegerStamp) lessThan2.getY().stamp(view)).isPositive() &&
                                     sameDestination(trueSuccessor(), ifNode2.falseSuccessor)) {
                         below = graph().unique(new IntegerBelowNode(lessThan2.getX(), lessThan2.getY()));
                         // swap direction
@@ -506,7 +510,7 @@ public final class IfNode extends ControlSplitNode implements Simplifiable, LIRL
                          */
                         JavaConstant positive = lessThan2.getX().asJavaConstant();
                         if (positive != null && positive.asLong() > 0 && positive.asLong() < positive.getJavaKind().getMaxValue()) {
-                            ConstantNode newLimit = ConstantNode.forIntegerStamp(lessThan2.getX().stamp(), positive.asLong() + 1, graph());
+                            ConstantNode newLimit = ConstantNode.forIntegerStamp(lessThan2.getX().stamp(view), positive.asLong() + 1, graph());
                             below = graph().unique(new IntegerBelowNode(lessThan.getX(), newLimit));
                         }
                     }
@@ -574,7 +578,7 @@ public final class IfNode extends ControlSplitNode implements Simplifiable, LIRL
         return false;
     }
 
-    private static boolean prepareForSwap(ConstantReflectionProvider constantReflection, LogicNode a, LogicNode b) {
+    private static boolean prepareForSwap(SimplifierTool tool, LogicNode a, LogicNode b) {
         DebugContext debug = a.getDebug();
         if (a instanceof InstanceOfNode) {
             InstanceOfNode instanceOfA = (InstanceOfNode) a;
@@ -625,13 +629,13 @@ public final class IfNode extends ControlSplitNode implements Simplifiable, LIRL
                     }
                 } else if (conditionA == Condition.EQ && conditionB == Condition.EQ) {
                     boolean canSwap = false;
-                    if ((compareA.getX() == compareB.getX() && valuesDistinct(constantReflection, compareA.getY(), compareB.getY()))) {
+                    if ((compareA.getX() == compareB.getX() && valuesDistinct(tool, compareA.getY(), compareB.getY()))) {
                         canSwap = true;
-                    } else if ((compareA.getX() == compareB.getY() && valuesDistinct(constantReflection, compareA.getY(), compareB.getX()))) {
+                    } else if ((compareA.getX() == compareB.getY() && valuesDistinct(tool, compareA.getY(), compareB.getX()))) {
                         canSwap = true;
-                    } else if ((compareA.getY() == compareB.getX() && valuesDistinct(constantReflection, compareA.getX(), compareB.getY()))) {
+                    } else if ((compareA.getY() == compareB.getX() && valuesDistinct(tool, compareA.getX(), compareB.getY()))) {
                         canSwap = true;
-                    } else if ((compareA.getY() == compareB.getY() && valuesDistinct(constantReflection, compareA.getX(), compareB.getX()))) {
+                    } else if ((compareA.getY() == compareB.getY() && valuesDistinct(tool, compareA.getX(), compareB.getX()))) {
                         canSwap = true;
                     }
 
@@ -646,16 +650,17 @@ public final class IfNode extends ControlSplitNode implements Simplifiable, LIRL
         return false;
     }
 
-    private static boolean valuesDistinct(ConstantReflectionProvider constantReflection, ValueNode a, ValueNode b) {
+    private static boolean valuesDistinct(SimplifierTool tool, ValueNode a, ValueNode b) {
         if (a.isConstant() && b.isConstant()) {
-            Boolean equal = constantReflection.constantEquals(a.asConstant(), b.asConstant());
+            Boolean equal = tool.getConstantReflection().constantEquals(a.asConstant(), b.asConstant());
             if (equal != null) {
                 return !equal.booleanValue();
             }
         }
 
-        Stamp stampA = a.stamp();
-        Stamp stampB = b.stamp();
+        NodeView view = NodeView.from(tool);
+        Stamp stampA = a.stamp(view);
+        Stamp stampB = b.stamp(view);
         return stampA.alwaysDistinct(stampB);
     }
 
@@ -691,7 +696,7 @@ public final class IfNode extends ControlSplitNode implements Simplifiable, LIRL
                 } else if (distinct == 1) {
                     ValueNode trueValue = singlePhi.valueAt(trueEnd);
                     ValueNode falseValue = singlePhi.valueAt(falseEnd);
-                    ValueNode conditional = canonicalizeConditionalCascade(trueValue, falseValue);
+                    ValueNode conditional = canonicalizeConditionalCascade(tool, trueValue, falseValue);
                     if (conditional != null) {
                         singlePhi.setValueAt(trueEnd, conditional);
                         removeThroughFalseBranch(tool, merge);
@@ -710,7 +715,7 @@ public final class IfNode extends ControlSplitNode implements Simplifiable, LIRL
                 if (trueValue == falseValue) {
                     value = trueValue;
                 } else {
-                    value = canonicalizeConditionalCascade(trueValue, falseValue);
+                    value = canonicalizeConditionalCascade(tool, trueValue, falseValue);
                     if (value == null) {
                         return false;
                     }
@@ -745,7 +750,7 @@ public final class IfNode extends ControlSplitNode implements Simplifiable, LIRL
         }
     }
 
-    private ValueNode canonicalizeConditionalCascade(ValueNode trueValue, ValueNode falseValue) {
+    private ValueNode canonicalizeConditionalCascade(SimplifierTool tool, ValueNode trueValue, ValueNode falseValue) {
         if (trueValue.getStackKind() != falseValue.getStackKind()) {
             return null;
         }
@@ -796,8 +801,9 @@ public final class IfNode extends ControlSplitNode implements Simplifiable, LIRL
                 }
                 if (lessThan != null) {
                     assert equals != null;
+                    NodeView view = NodeView.from(tool);
                     if ((lessThan.getX() == equals.getX() && lessThan.getY() == equals.getY()) || (lessThan.getX() == equals.getY() && lessThan.getY() == equals.getX())) {
-                        return graph().unique(new NormalizeCompareNode(lessThan.getX(), lessThan.getY(), conditional.trueValue().stamp().getStackKind(), false));
+                        return graph().unique(new NormalizeCompareNode(lessThan.getX(), lessThan.getY(), conditional.trueValue().stamp(view).getStackKind(), false));
                     }
                 }
             }
@@ -1287,10 +1293,11 @@ public final class IfNode extends ControlSplitNode implements Simplifiable, LIRL
                 GraphUtil.killCFG(end);
             } else {
                 // Need a new phi in case the frame state is used by more than the merge being
-                // removed
+                // removed.
+                NodeView view = NodeView.from(tool);
                 AbstractMergeNode newMerge = graph().add(new MergeNode());
                 PhiNode oldPhi = (PhiNode) oldMerge.usages().first();
-                PhiNode newPhi = graph().addWithoutUnique(new ValuePhiNode(oldPhi.stamp(), newMerge));
+                PhiNode newPhi = graph().addWithoutUnique(new ValuePhiNode(oldPhi.stamp(view), newMerge));
 
                 for (EndNode end : ends) {
                     newPhi.addInput(phiValues.get(end));
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/LoopBeginNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/LoopBeginNode.java
index cf475f2f7bb..0c7da53f3e4 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/LoopBeginNode.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/LoopBeginNode.java
@@ -342,7 +342,7 @@ public final class LoopBeginNode extends AbstractMergeNode implements IterableNo
         for (int i = 0; i < phi.valueCount(); i++) {
             ValueNode input = phi.valueAt(i);
             long increment = NO_INCREMENT;
-            if (input != null && input instanceof AddNode && input.stamp() instanceof IntegerStamp) {
+            if (input != null && input instanceof AddNode && input.stamp(NodeView.DEFAULT) instanceof IntegerStamp) {
                 AddNode add = (AddNode) input;
                 if (add.getX() == phi && add.getY().isConstant()) {
                     increment = add.getY().asJavaConstant().asLong();
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/NodeView.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/NodeView.java
new file mode 100644
index 00000000000..636f2af87c4
--- /dev/null
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/NodeView.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2017, 2017, 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 org.graalvm.compiler.nodes;
+
+import org.graalvm.compiler.core.common.type.Stamp;
+import org.graalvm.compiler.graph.spi.CanonicalizerTool;
+
+/**
+ * Interface that overrides properties of a node, such as the node's stamp.
+ *
+ * This interface allows richer canonicalizations when the current compilation context can provide a
+ * narrower stamp than the one stored in the node itself. One such example is performing
+ * canonicalization late in the compilation, when the nodes are already scheduled, and benefit from
+ * additional stamp information from conditional checks in branches.
+ *
+ * For example, in the following code, <code>offset + i</code> can be canonicalized once it is
+ * scheduled into the branch:
+ *
+ * <pre>
+ * public void update(int offset, int i) {
+ *     if (i == 0) {
+ *         array[offset + i];
+ *     }
+ * }
+ * </pre>
+ */
+public interface NodeView {
+
+    NodeView DEFAULT = new Default();
+
+    class Default implements NodeView {
+        @Override
+        public Stamp stamp(ValueNode node) {
+            return node.stamp;
+        }
+    }
+
+    /**
+     * Return a view-specific stamp of the node.
+     *
+     * This stamp must be more specific than the default stamp.
+     */
+    Stamp stamp(ValueNode node);
+
+    static NodeView from(CanonicalizerTool tool) {
+        if (tool instanceof NodeView) {
+            return (NodeView) tool;
+        }
+        return DEFAULT;
+    }
+}
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/PhiNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/PhiNode.java
index 2d517d17ba5..410b82d6b00 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/PhiNode.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/PhiNode.java
@@ -152,7 +152,7 @@ public abstract class PhiNode extends FloatingNode implements Canonicalizable {
 
     public void addInput(ValueNode x) {
         assert !(x instanceof ValuePhiNode) || ((ValuePhiNode) x).merge() instanceof LoopBeginNode || ((ValuePhiNode) x).merge() != this.merge();
-        assert !(this instanceof ValuePhiNode) || x.stamp().isCompatible(stamp());
+        assert !(this instanceof ValuePhiNode) || x.stamp(NodeView.DEFAULT).isCompatible(stamp(NodeView.DEFAULT));
         values().add(x);
     }
 
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/PiNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/PiNode.java
index 2b0207c53b5..6fd619b78a5 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/PiNode.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/PiNode.java
@@ -76,7 +76,7 @@ public class PiNode extends FloatingGuardedNode implements LIRLowerable, Virtual
         super(c, stamp, guard);
         this.object = object;
         this.piStamp = stamp;
-        assert piStamp.isCompatible(object.stamp()) : "Object stamp not compatible to piStamp";
+        assert piStamp.isCompatible(object.stamp(NodeView.DEFAULT)) : "Object stamp not compatible to piStamp";
         inferStamp();
     }
 
@@ -89,11 +89,12 @@ public class PiNode extends FloatingGuardedNode implements LIRLowerable, Virtual
     }
 
     public PiNode(ValueNode object, ValueNode guard) {
-        this(object, AbstractPointerStamp.pointerNonNull(object.stamp()), guard);
+        this(object, AbstractPointerStamp.pointerNonNull(object.stamp(NodeView.DEFAULT)), guard);
     }
 
     public PiNode(ValueNode object, ResolvedJavaType toType, boolean exactType, boolean nonNull) {
-        this(object, StampFactory.object(exactType ? TypeReference.createExactTrusted(toType) : TypeReference.createWithoutAssumptions(toType), nonNull || StampTool.isPointerNonNull(object.stamp())));
+        this(object, StampFactory.object(exactType ? TypeReference.createExactTrusted(toType) : TypeReference.createWithoutAssumptions(toType),
+                        nonNull || StampTool.isPointerNonNull(object.stamp(NodeView.DEFAULT))));
     }
 
     public static ValueNode create(ValueNode object, Stamp stamp) {
@@ -113,7 +114,7 @@ public class PiNode extends FloatingGuardedNode implements LIRLowerable, Virtual
     }
 
     public static ValueNode create(ValueNode object, ValueNode guard) {
-        Stamp stamp = AbstractPointerStamp.pointerNonNull(object.stamp());
+        Stamp stamp = AbstractPointerStamp.pointerNonNull(object.stamp(NodeView.DEFAULT));
         ValueNode value = canonical(object, stamp, (GuardingNode) guard);
         if (value != null) {
             return value;
@@ -123,7 +124,7 @@ public class PiNode extends FloatingGuardedNode implements LIRLowerable, Virtual
 
     @SuppressWarnings("unused")
     public static boolean intrinsify(GraphBuilderContext b, ResolvedJavaMethod method, ValueNode object, ValueNode guard) {
-        Stamp stamp = AbstractPointerStamp.pointerNonNull(object.stamp());
+        Stamp stamp = AbstractPointerStamp.pointerNonNull(object.stamp(NodeView.DEFAULT));
         ValueNode value = canonical(object, stamp, (GuardingNode) guard);
         if (value == null) {
             value = new PiNode(object, stamp, guard);
@@ -134,7 +135,8 @@ public class PiNode extends FloatingGuardedNode implements LIRLowerable, Virtual
 
     @SuppressWarnings("unused")
     public static boolean intrinsify(GraphBuilderContext b, ResolvedJavaMethod method, ValueNode object, ResolvedJavaType toType, boolean exactType, boolean nonNull) {
-        Stamp stamp = StampFactory.object(exactType ? TypeReference.createExactTrusted(toType) : TypeReference.createWithoutAssumptions(toType), nonNull || StampTool.isPointerNonNull(object.stamp()));
+        Stamp stamp = StampFactory.object(exactType ? TypeReference.createExactTrusted(toType) : TypeReference.createWithoutAssumptions(toType),
+                        nonNull || StampTool.isPointerNonNull(object.stamp(NodeView.DEFAULT)));
         ValueNode value = canonical(object, stamp, null);
         if (value == null) {
             value = new PiNode(object, stamp);
@@ -165,7 +167,7 @@ public class PiNode extends FloatingGuardedNode implements LIRLowerable, Virtual
     }
 
     private Stamp computeStamp() {
-        return piStamp.improveWith(object().stamp());
+        return piStamp.improveWith(object().stamp(NodeView.DEFAULT));
     }
 
     @Override
@@ -181,10 +183,10 @@ public class PiNode extends FloatingGuardedNode implements LIRLowerable, Virtual
 
     public static ValueNode canonical(ValueNode object, Stamp stamp, GuardingNode guard) {
         // Use most up to date stamp.
-        Stamp computedStamp = stamp.improveWith(object.stamp());
+        Stamp computedStamp = stamp.improveWith(object.stamp(NodeView.DEFAULT));
 
         // The pi node does not give any additional information => skip it.
-        if (computedStamp.equals(object.stamp())) {
+        if (computedStamp.equals(object.stamp(NodeView.DEFAULT))) {
             return object;
         }
 
@@ -192,14 +194,14 @@ public class PiNode extends FloatingGuardedNode implements LIRLowerable, Virtual
             // Try to merge the pi node with a load node.
             if (object instanceof ReadNode) {
                 ReadNode readNode = (ReadNode) object;
-                readNode.setStamp(readNode.stamp().improveWith(stamp));
+                readNode.setStamp(readNode.stamp(NodeView.DEFAULT).improveWith(stamp));
                 return readNode;
             }
         } else {
             for (Node n : guard.asNode().usages()) {
                 if (n instanceof PiNode) {
                     PiNode otherPi = (PiNode) n;
-                    if (object == otherPi.object() && computedStamp.equals(otherPi.stamp())) {
+                    if (object == otherPi.object() && computedStamp.equals(otherPi.stamp(NodeView.DEFAULT))) {
                         /*
                          * Two PiNodes with the same guard and same result, so return the one with
                          * the more precise piStamp.
@@ -217,7 +219,7 @@ public class PiNode extends FloatingGuardedNode implements LIRLowerable, Virtual
 
     @Override
     public Node canonical(CanonicalizerTool tool) {
-        Node value = canonical(object(), stamp(), getGuard());
+        Node value = canonical(object(), stamp(NodeView.DEFAULT), getGuard());
         if (value != null) {
             return value;
         }
@@ -232,7 +234,7 @@ public class PiNode extends FloatingGuardedNode implements LIRLowerable, Virtual
     public void setOriginalNode(ValueNode newNode) {
         this.updateUsages(object, newNode);
         this.object = newNode;
-        assert piStamp.isCompatible(object.stamp()) : "New object stamp not compatible to piStamp";
+        assert piStamp.isCompatible(object.stamp(NodeView.DEFAULT)) : "New object stamp not compatible to piStamp";
     }
 
     /**
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/StructuredGraph.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/StructuredGraph.java
index 2255a228221..8db681571ed 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/StructuredGraph.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/StructuredGraph.java
@@ -360,9 +360,9 @@ public final class StructuredGraph extends Graph implements JavaMethodContext {
             ValueNode result = returnNode.result();
             if (result != null) {
                 if (returnStamp == null) {
-                    returnStamp = result.stamp();
+                    returnStamp = result.stamp(NodeView.DEFAULT);
                 } else {
-                    returnStamp = returnStamp.meet(result.stamp());
+                    returnStamp = returnStamp.meet(result.stamp(NodeView.DEFAULT));
                 }
             }
         }
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/ValueNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/ValueNode.java
index 8dae4d637b3..e2e0b575384 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/ValueNode.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/ValueNode.java
@@ -56,8 +56,8 @@ public abstract class ValueNode extends org.graalvm.compiler.graph.Node implemen
         this.stamp = stamp;
     }
 
-    public final Stamp stamp() {
-        return stamp;
+    public final Stamp stamp(NodeView view) {
+        return view.stamp(this);
     }
 
     public final void setStamp(Stamp stamp) {
@@ -99,7 +99,7 @@ public abstract class ValueNode extends org.graalvm.compiler.graph.Node implemen
     }
 
     public final JavaKind getStackKind() {
-        return stamp().getStackKind();
+        return stamp(NodeView.DEFAULT).getStackKind();
     }
 
     /**
@@ -197,9 +197,9 @@ public abstract class ValueNode extends org.graalvm.compiler.graph.Node implemen
 
     private boolean checkReplaceAtUsagesInvariants(Node other) {
         assert other == null || other instanceof ValueNode;
-        if (this.hasUsages() && !this.stamp().isEmpty() && !(other instanceof PhiNode) && other != null) {
-            assert ((ValueNode) other).stamp().getClass() == stamp().getClass() : "stamp have to be of same class";
-            boolean morePrecise = ((ValueNode) other).stamp().join(stamp()).equals(((ValueNode) other).stamp());
+        if (this.hasUsages() && !this.stamp(NodeView.DEFAULT).isEmpty() && !(other instanceof PhiNode) && other != null) {
+            assert ((ValueNode) other).stamp(NodeView.DEFAULT).getClass() == stamp(NodeView.DEFAULT).getClass() : "stamp have to be of same class";
+            boolean morePrecise = ((ValueNode) other).stamp(NodeView.DEFAULT).join(stamp(NodeView.DEFAULT)).equals(((ValueNode) other).stamp(NodeView.DEFAULT));
             assert morePrecise : "stamp can only get more precise " + toString(Verbosity.All) + " " +
                             other.toString(Verbosity.All);
         }
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/ValuePhiNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/ValuePhiNode.java
index 8a60c072cdd..978df0e4c9c 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/ValuePhiNode.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/ValuePhiNode.java
@@ -105,11 +105,11 @@ public class ValuePhiNode extends PhiNode implements ArrayLengthProvider {
         for (ValueNode input : values()) {
             assert input != null;
             if (s == null) {
-                s = input.stamp();
+                s = input.stamp(NodeView.DEFAULT);
             } else {
-                if (!s.isCompatible(input.stamp())) {
+                if (!s.isCompatible(input.stamp(NodeView.DEFAULT))) {
                     fail("Phi Input Stamps are not compatible. Phi:%s inputs:%s", this,
-                                    CollectionsUtil.mapAndJoin(values(), x -> x.toString() + ":" + x.stamp(), ", "));
+                                    CollectionsUtil.mapAndJoin(values(), x -> x.toString() + ":" + x.stamp(NodeView.DEFAULT), ", "));
                 }
             }
         }
@@ -118,7 +118,7 @@ public class ValuePhiNode extends PhiNode implements ArrayLengthProvider {
 
     @Override
     protected String valueDescription() {
-        return stamp().unrestricted().toString();
+        return stamp(NodeView.DEFAULT).unrestricted().toString();
     }
 
     @Override
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/ValueProxyNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/ValueProxyNode.java
index 413e9060a0b..0e30b466866 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/ValueProxyNode.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/ValueProxyNode.java
@@ -41,7 +41,7 @@ public final class ValueProxyNode extends ProxyNode implements Canonicalizable,
     private final boolean loopPhiProxy;
 
     public ValueProxyNode(ValueNode value, LoopExitNode loopExit) {
-        super(TYPE, value.stamp(), loopExit);
+        super(TYPE, value.stamp(NodeView.DEFAULT), loopExit);
         this.value = value;
         loopPhiProxy = loopExit.loopBegin().isPhiAtMerge(value);
     }
@@ -53,7 +53,7 @@ public final class ValueProxyNode extends ProxyNode implements Canonicalizable,
 
     @Override
     public boolean inferStamp() {
-        return updateStamp(value.stamp());
+        return updateStamp(value.stamp(NodeView.DEFAULT));
     }
 
     @Override
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/AbsNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/AbsNode.java
index 7c46a5ceaa6..18a3a6dd876 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/AbsNode.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/AbsNode.java
@@ -31,6 +31,7 @@ import org.graalvm.compiler.graph.NodeClass;
 import org.graalvm.compiler.graph.spi.CanonicalizerTool;
 import org.graalvm.compiler.lir.gen.ArithmeticLIRGeneratorTool;
 import org.graalvm.compiler.nodeinfo.NodeInfo;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.ValueNode;
 import org.graalvm.compiler.nodes.spi.ArithmeticLIRLowerable;
 import org.graalvm.compiler.nodes.spi.NodeLIRBuilderTool;
@@ -46,6 +47,23 @@ public final class AbsNode extends UnaryArithmeticNode<Abs> implements Arithmeti
         super(TYPE, ArithmeticOpTable::getAbs, x);
     }
 
+    public static ValueNode create(ValueNode value, NodeView view) {
+        ValueNode synonym = findSynonym(value, view);
+        if (synonym != null) {
+            return synonym;
+        }
+        return new NegateNode(value);
+    }
+
+    protected static ValueNode findSynonym(ValueNode forValue, NodeView view) {
+        ArithmeticOpTable.UnaryOp<Abs> absOp = ArithmeticOpTable.forStamp(forValue.stamp(view)).getAbs();
+        ValueNode synonym = UnaryArithmeticNode.findSynonym(forValue, absOp);
+        if (synonym != null) {
+            return synonym;
+        }
+        return null;
+    }
+
     @Override
     public ValueNode canonical(CanonicalizerTool tool, ValueNode forValue) {
         ValueNode ret = super.canonical(tool, forValue);
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/AddNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/AddNode.java
index 778bed510cc..dbf2fb13a9d 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/AddNode.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/AddNode.java
@@ -32,6 +32,7 @@ import org.graalvm.compiler.graph.spi.CanonicalizerTool;
 import org.graalvm.compiler.lir.gen.ArithmeticLIRGeneratorTool;
 import org.graalvm.compiler.nodeinfo.NodeInfo;
 import org.graalvm.compiler.nodes.ConstantNode;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.ValueNode;
 import org.graalvm.compiler.nodes.spi.NodeLIRBuilderTool;
 
@@ -51,21 +52,21 @@ public class AddNode extends BinaryArithmeticNode<Add> implements NarrowableArit
         super(c, ArithmeticOpTable::getAdd, x, y);
     }
 
-    public static ValueNode create(ValueNode x, ValueNode y) {
-        BinaryOp<Add> op = ArithmeticOpTable.forStamp(x.stamp()).getAdd();
-        Stamp stamp = op.foldStamp(x.stamp(), y.stamp());
-        ConstantNode tryConstantFold = tryConstantFold(op, x, y, stamp);
+    public static ValueNode create(ValueNode x, ValueNode y, NodeView view) {
+        BinaryOp<Add> op = ArithmeticOpTable.forStamp(x.stamp(view)).getAdd();
+        Stamp stamp = op.foldStamp(x.stamp(view), y.stamp(view));
+        ConstantNode tryConstantFold = tryConstantFold(op, x, y, stamp, view);
         if (tryConstantFold != null) {
             return tryConstantFold;
         }
         if (x.isConstant() && !y.isConstant()) {
-            return canonical(null, op, y, x);
+            return canonical(null, op, y, x, view);
         } else {
-            return canonical(null, op, x, y);
+            return canonical(null, op, x, y, view);
         }
     }
 
-    private static ValueNode canonical(AddNode addNode, BinaryOp<Add> op, ValueNode forX, ValueNode forY) {
+    private static ValueNode canonical(AddNode addNode, BinaryOp<Add> op, ValueNode forX, ValueNode forY, NodeView view) {
         AddNode self = addNode;
         boolean associative = op.isAssociative();
         if (associative) {
@@ -91,16 +92,16 @@ public class AddNode extends BinaryArithmeticNode<Add> implements NarrowableArit
             }
             if (associative && self != null) {
                 // canonicalize expressions like "(a + 1) + 2"
-                ValueNode reassociated = reassociate(self, ValueNode.isConstantPredicate(), forX, forY);
+                ValueNode reassociated = reassociate(self, ValueNode.isConstantPredicate(), forX, forY, view);
                 if (reassociated != self) {
                     return reassociated;
                 }
             }
         }
         if (forX instanceof NegateNode) {
-            return BinaryArithmeticNode.sub(forY, ((NegateNode) forX).getValue());
+            return BinaryArithmeticNode.sub(forY, ((NegateNode) forX).getValue(), view);
         } else if (forY instanceof NegateNode) {
-            return BinaryArithmeticNode.sub(forX, ((NegateNode) forY).getValue());
+            return BinaryArithmeticNode.sub(forX, ((NegateNode) forY).getValue(), view);
         }
         if (self == null) {
             self = (AddNode) new AddNode(forX, forY).maybeCommuteInputs();
@@ -125,7 +126,8 @@ public class AddNode extends BinaryArithmeticNode<Add> implements NarrowableArit
             return new AddNode(forY, forX);
         }
         BinaryOp<Add> op = getOp(forX, forY);
-        return canonical(this, op, forX, forY);
+        NodeView view = NodeView.from(tool);
+        return canonical(this, op, forX, forY, view);
     }
 
     @Override
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/AndNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/AndNode.java
index f97dbb7210d..e6c8231f10b 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/AndNode.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/AndNode.java
@@ -34,6 +34,7 @@ import org.graalvm.compiler.graph.spi.CanonicalizerTool;
 import org.graalvm.compiler.lir.gen.ArithmeticLIRGeneratorTool;
 import org.graalvm.compiler.nodeinfo.NodeInfo;
 import org.graalvm.compiler.nodes.ConstantNode;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.ValueNode;
 import org.graalvm.compiler.nodes.spi.NodeLIRBuilderTool;
 import org.graalvm.compiler.nodes.util.GraphUtil;
@@ -51,14 +52,14 @@ public final class AndNode extends BinaryArithmeticNode<And> implements Narrowab
         super(TYPE, ArithmeticOpTable::getAnd, x, y);
     }
 
-    public static ValueNode create(ValueNode x, ValueNode y) {
-        BinaryOp<And> op = ArithmeticOpTable.forStamp(x.stamp()).getAnd();
-        Stamp stamp = op.foldStamp(x.stamp(), y.stamp());
-        ConstantNode tryConstantFold = tryConstantFold(op, x, y, stamp);
+    public static ValueNode create(ValueNode x, ValueNode y, NodeView view) {
+        BinaryOp<And> op = ArithmeticOpTable.forStamp(x.stamp(view)).getAnd();
+        Stamp stamp = op.foldStamp(x.stamp(view), y.stamp(view));
+        ConstantNode tryConstantFold = tryConstantFold(op, x, y, stamp, view);
         if (tryConstantFold != null) {
             return tryConstantFold;
         }
-        return canonical(null, op, stamp, x, y);
+        return canonical(null, op, stamp, x, y, view);
     }
 
     @Override
@@ -68,10 +69,11 @@ public final class AndNode extends BinaryArithmeticNode<And> implements Narrowab
             return ret;
         }
 
-        return canonical(this, getOp(forX, forY), stamp(), forX, forY);
+        NodeView view = NodeView.from(tool);
+        return canonical(this, getOp(forX, forY), stamp(view), forX, forY, view);
     }
 
-    private static ValueNode canonical(AndNode self, BinaryOp<And> op, Stamp stamp, ValueNode forX, ValueNode forY) {
+    private static ValueNode canonical(AndNode self, BinaryOp<And> op, Stamp stamp, ValueNode forX, ValueNode forY, NodeView view) {
         if (GraphUtil.unproxify(forX) == GraphUtil.unproxify(forY)) {
             return forX;
         }
@@ -96,17 +98,17 @@ public final class AndNode extends BinaryArithmeticNode<And> implements Narrowab
                         return new ZeroExtendNode(ext.getValue(), ext.getResultBits());
                     }
                 }
-                IntegerStamp xStamp = (IntegerStamp) forX.stamp();
+                IntegerStamp xStamp = (IntegerStamp) forX.stamp(view);
                 if (((xStamp.upMask() | xStamp.downMask()) & ~rawY) == 0) {
                     // No bits are set which are outside the mask, so the mask will have no effect.
                     return forX;
                 }
             }
 
-            return reassociate(self != null ? self : (AndNode) new AndNode(forX, forY).maybeCommuteInputs(), ValueNode.isConstantPredicate(), forX, forY);
+            return reassociate(self != null ? self : (AndNode) new AndNode(forX, forY).maybeCommuteInputs(), ValueNode.isConstantPredicate(), forX, forY, view);
         }
         if (forX instanceof NotNode && forY instanceof NotNode) {
-            return new NotNode(OrNode.create(((NotNode) forX).getValue(), ((NotNode) forY).getValue()));
+            return new NotNode(OrNode.create(((NotNode) forX).getValue(), ((NotNode) forY).getValue(), view));
         }
         return self != null ? self : new AndNode(forX, forY).maybeCommuteInputs();
     }
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/BinaryArithmeticNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/BinaryArithmeticNode.java
index 7593daecd74..84853e52dd7 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/BinaryArithmeticNode.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/BinaryArithmeticNode.java
@@ -41,6 +41,7 @@ import org.graalvm.compiler.graph.spi.CanonicalizerTool;
 import org.graalvm.compiler.nodeinfo.NodeInfo;
 import org.graalvm.compiler.nodes.ArithmeticOperation;
 import org.graalvm.compiler.nodes.ConstantNode;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.StructuredGraph;
 import org.graalvm.compiler.nodes.ValueNode;
 import org.graalvm.compiler.nodes.ValuePhiNode;
@@ -60,13 +61,13 @@ public abstract class BinaryArithmeticNode<OP> extends BinaryNode implements Ari
     protected final SerializableBinaryFunction<OP> getOp;
 
     protected BinaryArithmeticNode(NodeClass<? extends BinaryArithmeticNode<OP>> c, SerializableBinaryFunction<OP> getOp, ValueNode x, ValueNode y) {
-        super(c, getOp.apply(ArithmeticOpTable.forStamp(x.stamp())).foldStamp(x.stamp(), y.stamp()), x, y);
+        super(c, getOp.apply(ArithmeticOpTable.forStamp(x.stamp(NodeView.DEFAULT))).foldStamp(x.stamp(NodeView.DEFAULT), y.stamp(NodeView.DEFAULT)), x, y);
         this.getOp = getOp;
     }
 
     protected final BinaryOp<OP> getOp(ValueNode forX, ValueNode forY) {
-        ArithmeticOpTable table = ArithmeticOpTable.forStamp(forX.stamp());
-        assert table.equals(ArithmeticOpTable.forStamp(forY.stamp()));
+        ArithmeticOpTable table = ArithmeticOpTable.forStamp(forX.stamp(NodeView.DEFAULT));
+        assert table.equals(ArithmeticOpTable.forStamp(forY.stamp(NodeView.DEFAULT)));
         return getOp.apply(table);
     }
 
@@ -81,14 +82,16 @@ public abstract class BinaryArithmeticNode<OP> extends BinaryNode implements Ari
 
     @Override
     public ValueNode canonical(CanonicalizerTool tool, ValueNode forX, ValueNode forY) {
-        ValueNode result = tryConstantFold(getOp(forX, forY), forX, forY, stamp());
+        NodeView view = NodeView.from(tool);
+        ValueNode result = tryConstantFold(getOp(forX, forY), forX, forY, stamp(view), view);
         if (result != null) {
             return result;
         }
         return this;
     }
 
-    public static <OP> ConstantNode tryConstantFold(BinaryOp<OP> op, ValueNode forX, ValueNode forY, Stamp stamp) {
+    @SuppressWarnings("unused")
+    public static <OP> ConstantNode tryConstantFold(BinaryOp<OP> op, ValueNode forX, ValueNode forY, Stamp stamp, NodeView view) {
         if (forX.isConstant() && forY.isConstant()) {
             Constant ret = op.foldConstant(forX.asConstant(), forY.asConstant());
             if (ret != null) {
@@ -100,32 +103,32 @@ public abstract class BinaryArithmeticNode<OP> extends BinaryNode implements Ari
 
     @Override
     public Stamp foldStamp(Stamp stampX, Stamp stampY) {
-        assert stampX.isCompatible(x.stamp()) && stampY.isCompatible(y.stamp());
+        assert stampX.isCompatible(x.stamp(NodeView.DEFAULT)) && stampY.isCompatible(y.stamp(NodeView.DEFAULT));
         return getArithmeticOp().foldStamp(stampX, stampY);
     }
 
-    public static ValueNode add(StructuredGraph graph, ValueNode v1, ValueNode v2) {
-        return graph.addOrUniqueWithInputs(AddNode.create(v1, v2));
+    public static ValueNode add(StructuredGraph graph, ValueNode v1, ValueNode v2, NodeView view) {
+        return graph.addOrUniqueWithInputs(AddNode.create(v1, v2, view));
     }
 
-    public static ValueNode add(ValueNode v1, ValueNode v2) {
-        return AddNode.create(v1, v2);
+    public static ValueNode add(ValueNode v1, ValueNode v2, NodeView view) {
+        return AddNode.create(v1, v2, view);
     }
 
-    public static ValueNode mul(StructuredGraph graph, ValueNode v1, ValueNode v2) {
-        return graph.addOrUniqueWithInputs(MulNode.create(v1, v2));
+    public static ValueNode mul(StructuredGraph graph, ValueNode v1, ValueNode v2, NodeView view) {
+        return graph.addOrUniqueWithInputs(MulNode.create(v1, v2, view));
     }
 
-    public static ValueNode mul(ValueNode v1, ValueNode v2) {
-        return MulNode.create(v1, v2);
+    public static ValueNode mul(ValueNode v1, ValueNode v2, NodeView view) {
+        return MulNode.create(v1, v2, view);
     }
 
-    public static ValueNode sub(StructuredGraph graph, ValueNode v1, ValueNode v2) {
-        return graph.addOrUniqueWithInputs(SubNode.create(v1, v2));
+    public static ValueNode sub(StructuredGraph graph, ValueNode v1, ValueNode v2, NodeView view) {
+        return graph.addOrUniqueWithInputs(SubNode.create(v1, v2, view));
     }
 
-    public static ValueNode sub(ValueNode v1, ValueNode v2) {
-        return SubNode.create(v1, v2);
+    public static ValueNode sub(ValueNode v1, ValueNode v2, NodeView view) {
+        return SubNode.create(v1, v2, view);
     }
 
     private enum ReassociateMatch {
@@ -193,7 +196,7 @@ public abstract class BinaryArithmeticNode<OP> extends BinaryNode implements Ari
      * @param forY
      * @param forX
      */
-    public static ValueNode reassociate(BinaryArithmeticNode<?> node, NodePredicate criterion, ValueNode forX, ValueNode forY) {
+    public static ValueNode reassociate(BinaryArithmeticNode<?> node, NodePredicate criterion, ValueNode forX, ValueNode forY, NodeView view) {
         assert node.getOp(forX, forY).isAssociative();
         ReassociateMatch match1 = findReassociate(node, criterion);
         if (match1 == null) {
@@ -239,21 +242,21 @@ public abstract class BinaryArithmeticNode<OP> extends BinaryNode implements Ari
         if (node instanceof AddNode || node instanceof SubNode) {
             ValueNode associated;
             if (invertM1) {
-                associated = BinaryArithmeticNode.sub(m2, m1);
+                associated = BinaryArithmeticNode.sub(m2, m1, view);
             } else if (invertM2) {
-                associated = BinaryArithmeticNode.sub(m1, m2);
+                associated = BinaryArithmeticNode.sub(m1, m2, view);
             } else {
-                associated = BinaryArithmeticNode.add(m1, m2);
+                associated = BinaryArithmeticNode.add(m1, m2, view);
             }
             if (invertA) {
-                return BinaryArithmeticNode.sub(associated, a);
+                return BinaryArithmeticNode.sub(associated, a, view);
             }
             if (aSub) {
-                return BinaryArithmeticNode.sub(a, associated);
+                return BinaryArithmeticNode.sub(a, associated, view);
             }
-            return BinaryArithmeticNode.add(a, associated);
+            return BinaryArithmeticNode.add(a, associated, view);
         } else if (node instanceof MulNode) {
-            return BinaryArithmeticNode.mul(a, AddNode.mul(m1, m2));
+            return BinaryArithmeticNode.mul(a, AddNode.mul(m1, m2, view), view);
         } else if (node instanceof AndNode) {
             return new AndNode(a, new AndNode(m1, m2));
         } else if (node instanceof OrNode) {
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/BinaryNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/BinaryNode.java
index 3e45aa760f1..52cf1a00fef 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/BinaryNode.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/BinaryNode.java
@@ -26,6 +26,7 @@ import org.graalvm.compiler.core.common.type.Stamp;
 import org.graalvm.compiler.graph.NodeClass;
 import org.graalvm.compiler.graph.spi.Canonicalizable;
 import org.graalvm.compiler.nodeinfo.NodeInfo;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.ValueNode;
 
 /**
@@ -73,7 +74,7 @@ public abstract class BinaryNode extends FloatingNode implements Canonicalizable
 
     @Override
     public boolean inferStamp() {
-        return updateStamp(foldStamp(getX().stamp(), getY().stamp()));
+        return updateStamp(foldStamp(getX().stamp(NodeView.DEFAULT), getY().stamp(NodeView.DEFAULT)));
     }
 
     /**
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/CompareNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/CompareNode.java
index 55437cd2de4..14e102dc243 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/CompareNode.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/CompareNode.java
@@ -39,6 +39,7 @@ import org.graalvm.compiler.nodes.ConstantNode;
 import org.graalvm.compiler.nodes.LogicConstantNode;
 import org.graalvm.compiler.nodes.LogicNegationNode;
 import org.graalvm.compiler.nodes.LogicNode;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.StructuredGraph;
 import org.graalvm.compiler.nodes.ValueNode;
 
@@ -91,7 +92,8 @@ public abstract class CompareNode extends BinaryOpLogicNode implements Canonical
         return null;
     }
 
-    public static LogicNode tryConstantFoldPrimitive(Condition condition, ValueNode forX, ValueNode forY, boolean unorderedIsTrue) {
+    @SuppressWarnings("unused")
+    public static LogicNode tryConstantFoldPrimitive(Condition condition, ValueNode forX, ValueNode forY, boolean unorderedIsTrue, NodeView view) {
         if (forX.asConstant() instanceof PrimitiveConstant && forY.asConstant() instanceof PrimitiveConstant) {
             return LogicConstantNode.forBoolean(condition.foldCondition((PrimitiveConstant) forX.asConstant(), (PrimitiveConstant) forY.asConstant(), unorderedIsTrue));
         }
@@ -110,27 +112,27 @@ public abstract class CompareNode extends BinaryOpLogicNode implements Canonical
 
     public abstract static class CompareOp {
         public LogicNode canonical(ConstantReflectionProvider constantReflection, MetaAccessProvider metaAccess, OptionValues options, Integer smallestCompareWidth, Condition condition,
-                        boolean unorderedIsTrue, ValueNode forX, ValueNode forY) {
+                        boolean unorderedIsTrue, ValueNode forX, ValueNode forY, NodeView view) {
             LogicNode constantCondition = tryConstantFold(condition, forX, forY, constantReflection, unorderedIsTrue);
             if (constantCondition != null) {
                 return constantCondition;
             }
             LogicNode result;
             if (forX.isConstant()) {
-                if ((result = canonicalizeSymmetricConstant(constantReflection, metaAccess, options, smallestCompareWidth, condition, forX.asConstant(), forY, true, unorderedIsTrue)) != null) {
+                if ((result = canonicalizeSymmetricConstant(constantReflection, metaAccess, options, smallestCompareWidth, condition, forX.asConstant(), forY, true, unorderedIsTrue, view)) != null) {
                     return result;
                 }
             } else if (forY.isConstant()) {
-                if ((result = canonicalizeSymmetricConstant(constantReflection, metaAccess, options, smallestCompareWidth, condition, forY.asConstant(), forX, false, unorderedIsTrue)) != null) {
+                if ((result = canonicalizeSymmetricConstant(constantReflection, metaAccess, options, smallestCompareWidth, condition, forY.asConstant(), forX, false, unorderedIsTrue, view)) != null) {
                     return result;
                 }
             } else if (forX instanceof ConvertNode && forY instanceof ConvertNode) {
                 ConvertNode convertX = (ConvertNode) forX;
                 ConvertNode convertY = (ConvertNode) forY;
-                if (convertX.preservesOrder(condition) && convertY.preservesOrder(condition) && convertX.getValue().stamp().isCompatible(convertY.getValue().stamp())) {
+                if (convertX.preservesOrder(condition) && convertY.preservesOrder(condition) && convertX.getValue().stamp(view).isCompatible(convertY.getValue().stamp(view))) {
                     boolean supported = true;
-                    if (convertX.getValue().stamp() instanceof IntegerStamp) {
-                        IntegerStamp intStamp = (IntegerStamp) convertX.getValue().stamp();
+                    if (convertX.getValue().stamp(view) instanceof IntegerStamp) {
+                        IntegerStamp intStamp = (IntegerStamp) convertX.getValue().stamp(view);
                         supported = smallestCompareWidth != null && intStamp.getBits() >= smallestCompareWidth;
                     }
 
@@ -141,7 +143,7 @@ public abstract class CompareNode extends BinaryOpLogicNode implements Canonical
                             // of the value.
                             return null;
                         }
-                        return duplicateModified(convertX.getValue(), convertY.getValue(), unorderedIsTrue);
+                        return duplicateModified(convertX.getValue(), convertY.getValue(), unorderedIsTrue, view);
                     }
                 }
             }
@@ -149,11 +151,11 @@ public abstract class CompareNode extends BinaryOpLogicNode implements Canonical
         }
 
         protected LogicNode canonicalizeSymmetricConstant(ConstantReflectionProvider constantReflection, MetaAccessProvider metaAccess, OptionValues options, Integer smallestCompareWidth,
-                        Condition condition, Constant constant, ValueNode nonConstant, boolean mirrored, boolean unorderedIsTrue) {
+                        Condition condition, Constant constant, ValueNode nonConstant, boolean mirrored, boolean unorderedIsTrue, NodeView view) {
             if (nonConstant instanceof ConditionalNode) {
                 return optimizeConditional(constant, (ConditionalNode) nonConstant, constantReflection, mirrored ? condition.mirror() : condition, unorderedIsTrue);
             } else if (nonConstant instanceof NormalizeCompareNode) {
-                return optimizeNormalizeCompare(constantReflection, metaAccess, options, smallestCompareWidth, constant, (NormalizeCompareNode) nonConstant, mirrored);
+                return optimizeNormalizeCompare(constantReflection, metaAccess, options, smallestCompareWidth, constant, (NormalizeCompareNode) nonConstant, mirrored, view);
             } else if (nonConstant instanceof ConvertNode) {
                 ConvertNode convert = (ConvertNode) nonConstant;
                 boolean multiUsage = (convert.asNode().hasMoreThanOneUsage() && convert.getValue().hasExactlyOneUsage());
@@ -164,18 +166,18 @@ public abstract class CompareNode extends BinaryOpLogicNode implements Canonical
                 }
 
                 boolean supported = true;
-                if (convert.getValue().stamp() instanceof IntegerStamp) {
-                    IntegerStamp intStamp = (IntegerStamp) convert.getValue().stamp();
+                if (convert.getValue().stamp(view) instanceof IntegerStamp) {
+                    IntegerStamp intStamp = (IntegerStamp) convert.getValue().stamp(view);
                     supported = smallestCompareWidth != null && intStamp.getBits() > smallestCompareWidth;
                 }
 
                 if (supported) {
-                    ConstantNode newConstant = canonicalConvertConstant(constantReflection, metaAccess, options, condition, convert, constant);
+                    ConstantNode newConstant = canonicalConvertConstant(constantReflection, metaAccess, options, condition, convert, constant, view);
                     if (newConstant != null) {
                         if (mirrored) {
-                            return duplicateModified(newConstant, convert.getValue(), unorderedIsTrue);
+                            return duplicateModified(newConstant, convert.getValue(), unorderedIsTrue, view);
                         } else {
-                            return duplicateModified(convert.getValue(), newConstant, unorderedIsTrue);
+                            return duplicateModified(convert.getValue(), newConstant, unorderedIsTrue, view);
                         }
                     }
                 }
@@ -185,7 +187,7 @@ public abstract class CompareNode extends BinaryOpLogicNode implements Canonical
         }
 
         private static ConstantNode canonicalConvertConstant(ConstantReflectionProvider constantReflection, MetaAccessProvider metaAccess, OptionValues options, Condition condition,
-                        ConvertNode convert, Constant constant) {
+                        ConvertNode convert, Constant constant, NodeView view) {
             if (convert.preservesOrder(condition, constant, constantReflection)) {
                 Constant reverseConverted = convert.reverse(constant, constantReflection);
                 if (reverseConverted != null && convert.convert(reverseConverted, constantReflection).equals(constant)) {
@@ -193,7 +195,7 @@ public abstract class CompareNode extends BinaryOpLogicNode implements Canonical
                         // We always want uncompressed constants
                         return null;
                     }
-                    return ConstantNode.forConstant(convert.getValue().stamp(), reverseConverted, metaAccess);
+                    return ConstantNode.forConstant(convert.getValue().stamp(view), reverseConverted, metaAccess);
                 }
             }
             return null;
@@ -201,7 +203,7 @@ public abstract class CompareNode extends BinaryOpLogicNode implements Canonical
 
         @SuppressWarnings("unused")
         protected LogicNode optimizeNormalizeCompare(ConstantReflectionProvider constantReflection, MetaAccessProvider metaAccess, OptionValues options, Integer smallestCompareWidth,
-                        Constant constant, NormalizeCompareNode normalizeNode, boolean mirrored) {
+                        Constant constant, NormalizeCompareNode normalizeNode, boolean mirrored, NodeView view) {
             throw new GraalError("NormalizeCompareNode connected to %s (%s %s %s)", this, constant, normalizeNode, mirrored);
         }
 
@@ -230,71 +232,71 @@ public abstract class CompareNode extends BinaryOpLogicNode implements Canonical
             return null;
         }
 
-        protected abstract LogicNode duplicateModified(ValueNode newW, ValueNode newY, boolean unorderedIsTrue);
+        protected abstract LogicNode duplicateModified(ValueNode newW, ValueNode newY, boolean unorderedIsTrue, NodeView view);
     }
 
-    public static LogicNode createCompareNode(StructuredGraph graph, Condition condition, ValueNode x, ValueNode y, ConstantReflectionProvider constantReflection) {
-        LogicNode result = createCompareNode(condition, x, y, constantReflection);
+    public static LogicNode createCompareNode(StructuredGraph graph, Condition condition, ValueNode x, ValueNode y, ConstantReflectionProvider constantReflection, NodeView view) {
+        LogicNode result = createCompareNode(condition, x, y, constantReflection, view);
         return (result.graph() == null ? graph.addOrUniqueWithInputs(result) : result);
     }
 
-    public static LogicNode createCompareNode(Condition condition, ValueNode x, ValueNode y, ConstantReflectionProvider constantReflection) {
+    public static LogicNode createCompareNode(Condition condition, ValueNode x, ValueNode y, ConstantReflectionProvider constantReflection, NodeView view) {
         assert x.getStackKind() == y.getStackKind();
         assert condition.isCanonical();
         assert !x.getStackKind().isNumericFloat();
 
         LogicNode comparison;
         if (condition == Condition.EQ) {
-            if (x.stamp() instanceof AbstractObjectStamp) {
-                comparison = ObjectEqualsNode.create(x, y, constantReflection);
-            } else if (x.stamp() instanceof AbstractPointerStamp) {
-                comparison = PointerEqualsNode.create(x, y);
+            if (x.stamp(view) instanceof AbstractObjectStamp) {
+                comparison = ObjectEqualsNode.create(x, y, constantReflection, view);
+            } else if (x.stamp(view) instanceof AbstractPointerStamp) {
+                comparison = PointerEqualsNode.create(x, y, view);
             } else {
                 assert x.getStackKind().isNumericInteger();
-                comparison = IntegerEqualsNode.create(x, y);
+                comparison = IntegerEqualsNode.create(x, y, view);
             }
         } else if (condition == Condition.LT) {
             assert x.getStackKind().isNumericInteger();
-            comparison = IntegerLessThanNode.create(x, y);
+            comparison = IntegerLessThanNode.create(x, y, view);
         } else {
             assert condition == Condition.BT;
             assert x.getStackKind().isNumericInteger();
-            comparison = IntegerBelowNode.create(x, y);
+            comparison = IntegerBelowNode.create(x, y, view);
         }
 
         return comparison;
     }
 
     public static LogicNode createCompareNode(StructuredGraph graph, ConstantReflectionProvider constantReflection, MetaAccessProvider metaAccess, OptionValues options, Integer smallestCompareWidth,
-                    Condition condition, ValueNode x, ValueNode y) {
-        LogicNode result = createCompareNode(constantReflection, metaAccess, options, smallestCompareWidth, condition, x, y);
+                    Condition condition, ValueNode x, ValueNode y, NodeView view) {
+        LogicNode result = createCompareNode(constantReflection, metaAccess, options, smallestCompareWidth, condition, x, y, view);
         return (result.graph() == null ? graph.addOrUniqueWithInputs(result) : result);
     }
 
     public static LogicNode createCompareNode(ConstantReflectionProvider constantReflection, MetaAccessProvider metaAccess, OptionValues options, Integer smallestCompareWidth,
-                    Condition condition, ValueNode x, ValueNode y) {
+                    Condition condition, ValueNode x, ValueNode y, NodeView view) {
         assert x.getStackKind() == y.getStackKind();
         assert condition.isCanonical();
         assert !x.getStackKind().isNumericFloat();
 
         LogicNode comparison;
         if (condition == Condition.EQ) {
-            if (x.stamp() instanceof AbstractObjectStamp) {
+            if (x.stamp(view) instanceof AbstractObjectStamp) {
                 assert smallestCompareWidth == null;
-                comparison = ObjectEqualsNode.create(constantReflection, metaAccess, options, x, y);
-            } else if (x.stamp() instanceof AbstractPointerStamp) {
-                comparison = PointerEqualsNode.create(x, y);
+                comparison = ObjectEqualsNode.create(constantReflection, metaAccess, options, x, y, view);
+            } else if (x.stamp(view) instanceof AbstractPointerStamp) {
+                comparison = PointerEqualsNode.create(x, y, view);
             } else {
                 assert x.getStackKind().isNumericInteger();
-                comparison = IntegerEqualsNode.create(constantReflection, metaAccess, options, smallestCompareWidth, x, y);
+                comparison = IntegerEqualsNode.create(constantReflection, metaAccess, options, smallestCompareWidth, x, y, view);
             }
         } else if (condition == Condition.LT) {
             assert x.getStackKind().isNumericInteger();
-            comparison = IntegerLessThanNode.create(constantReflection, metaAccess, options, smallestCompareWidth, x, y);
+            comparison = IntegerLessThanNode.create(constantReflection, metaAccess, options, smallestCompareWidth, x, y, view);
         } else {
             assert condition == Condition.BT;
             assert x.getStackKind().isNumericInteger();
-            comparison = IntegerBelowNode.create(constantReflection, metaAccess, options, smallestCompareWidth, x, y);
+            comparison = IntegerBelowNode.create(constantReflection, metaAccess, options, smallestCompareWidth, x, y, view);
         }
 
         return comparison;
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/ConditionalNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/ConditionalNode.java
index c06eea37c9b..bff140ebc63 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/ConditionalNode.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/ConditionalNode.java
@@ -39,6 +39,7 @@ import org.graalvm.compiler.nodes.ConstantNode;
 import org.graalvm.compiler.nodes.LogicConstantNode;
 import org.graalvm.compiler.nodes.LogicNegationNode;
 import org.graalvm.compiler.nodes.LogicNode;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.StructuredGraph;
 import org.graalvm.compiler.nodes.ValueNode;
 import org.graalvm.compiler.nodes.spi.LIRLowerable;
@@ -55,8 +56,8 @@ public final class ConditionalNode extends FloatingNode implements Canonicalizab
 
     public static final NodeClass<ConditionalNode> TYPE = NodeClass.create(ConditionalNode.class);
     @Input(InputType.Condition) LogicNode condition;
-    @Input ValueNode trueValue;
-    @Input ValueNode falseValue;
+    @Input(InputType.Value) ValueNode trueValue;
+    @Input(InputType.Value) ValueNode falseValue;
 
     public LogicNode condition() {
         return condition;
@@ -67,23 +68,23 @@ public final class ConditionalNode extends FloatingNode implements Canonicalizab
     }
 
     public ConditionalNode(LogicNode condition, ValueNode trueValue, ValueNode falseValue) {
-        super(TYPE, trueValue.stamp().meet(falseValue.stamp()));
-        assert trueValue.stamp().isCompatible(falseValue.stamp());
+        super(TYPE, trueValue.stamp(NodeView.DEFAULT).meet(falseValue.stamp(NodeView.DEFAULT)));
+        assert trueValue.stamp(NodeView.DEFAULT).isCompatible(falseValue.stamp(NodeView.DEFAULT));
         this.condition = condition;
         this.trueValue = trueValue;
         this.falseValue = falseValue;
     }
 
-    public static ValueNode create(LogicNode condition) {
-        return create(condition, ConstantNode.forInt(1, condition.graph()), ConstantNode.forInt(0, condition.graph()));
+    public static ValueNode create(LogicNode condition, NodeView view) {
+        return create(condition, ConstantNode.forInt(1, condition.graph()), ConstantNode.forInt(0, condition.graph()), view);
     }
 
-    public static ValueNode create(LogicNode condition, ValueNode trueValue, ValueNode falseValue) {
-        ValueNode synonym = findSynonym(condition, trueValue, falseValue);
+    public static ValueNode create(LogicNode condition, ValueNode trueValue, ValueNode falseValue, NodeView view) {
+        ValueNode synonym = findSynonym(condition, trueValue, falseValue, view);
         if (synonym != null) {
             return synonym;
         }
-        ValueNode result = canonicalizeConditional(condition, trueValue, falseValue, trueValue.stamp().meet(falseValue.stamp()));
+        ValueNode result = canonicalizeConditional(condition, trueValue, falseValue, trueValue.stamp(view).meet(falseValue.stamp(view)), view);
         if (result != null) {
             return result;
         }
@@ -92,7 +93,7 @@ public final class ConditionalNode extends FloatingNode implements Canonicalizab
 
     @Override
     public boolean inferStamp() {
-        Stamp valueStamp = trueValue.stamp().meet(falseValue.stamp());
+        Stamp valueStamp = trueValue.stamp(NodeView.DEFAULT).meet(falseValue.stamp(NodeView.DEFAULT));
         if (condition instanceof IntegerLessThanNode) {
             IntegerLessThanNode lessThan = (IntegerLessThanNode) condition;
             if (lessThan.getX() == trueValue && lessThan.getY() == falseValue) {
@@ -130,12 +131,13 @@ public final class ConditionalNode extends FloatingNode implements Canonicalizab
 
     @Override
     public ValueNode canonical(CanonicalizerTool tool) {
-        ValueNode synonym = findSynonym(condition, trueValue(), falseValue());
+        NodeView view = NodeView.from(tool);
+        ValueNode synonym = findSynonym(condition, trueValue(), falseValue(), view);
         if (synonym != null) {
             return synonym;
         }
 
-        ValueNode result = canonicalizeConditional(condition, trueValue(), falseValue(), stamp);
+        ValueNode result = canonicalizeConditional(condition, trueValue(), falseValue(), stamp, view);
         if (result != null) {
             return result;
         }
@@ -143,7 +145,7 @@ public final class ConditionalNode extends FloatingNode implements Canonicalizab
         return this;
     }
 
-    public static ValueNode canonicalizeConditional(LogicNode condition, ValueNode trueValue, ValueNode falseValue, Stamp stamp) {
+    public static ValueNode canonicalizeConditional(LogicNode condition, ValueNode trueValue, ValueNode falseValue, Stamp stamp, NodeView view) {
         if (trueValue == falseValue) {
             return trueValue;
         }
@@ -156,12 +158,12 @@ public final class ConditionalNode extends FloatingNode implements Canonicalizab
             }
         }
 
-        if (trueValue.stamp() instanceof IntegerStamp) {
+        if (trueValue.stamp(view) instanceof IntegerStamp) {
             // check if the conditional is redundant
             if (condition instanceof IntegerLessThanNode) {
                 IntegerLessThanNode lessThan = (IntegerLessThanNode) condition;
-                IntegerStamp falseValueStamp = (IntegerStamp) falseValue.stamp();
-                IntegerStamp trueValueStamp = (IntegerStamp) trueValue.stamp();
+                IntegerStamp falseValueStamp = (IntegerStamp) falseValue.stamp(view);
+                IntegerStamp trueValueStamp = (IntegerStamp) trueValue.stamp(view);
                 if (lessThan.getX() == trueValue && lessThan.getY() == falseValue) {
                     // return "x" for "x < y ? x : y" in case that we know "x <= y"
                     if (trueValueStamp.upperBound() <= falseValueStamp.lowerBound()) {
@@ -182,25 +184,25 @@ public final class ConditionalNode extends FloatingNode implements Canonicalizab
                 long constFalseValue = falseValue.asJavaConstant().asLong();
                 if (condition instanceof IntegerEqualsNode) {
                     IntegerEqualsNode equals = (IntegerEqualsNode) condition;
-                    if (equals.getY().isConstant() && equals.getX().stamp() instanceof IntegerStamp) {
-                        IntegerStamp equalsXStamp = (IntegerStamp) equals.getX().stamp();
+                    if (equals.getY().isConstant() && equals.getX().stamp(view) instanceof IntegerStamp) {
+                        IntegerStamp equalsXStamp = (IntegerStamp) equals.getX().stamp(view);
                         if (equalsXStamp.upMask() == 1) {
                             long equalsY = equals.getY().asJavaConstant().asLong();
                             if (equalsY == 0) {
                                 if (constTrueValue == 0 && constFalseValue == 1) {
                                     // return x when: x == 0 ? 0 : 1;
-                                    return IntegerConvertNode.convertUnsigned(equals.getX(), stamp);
+                                    return IntegerConvertNode.convertUnsigned(equals.getX(), stamp, view);
                                 } else if (constTrueValue == 1 && constFalseValue == 0) {
                                     // negate a boolean value via xor
-                                    return IntegerConvertNode.convertUnsigned(XorNode.create(equals.getX(), ConstantNode.forIntegerStamp(equals.getX().stamp(), 1)), stamp);
+                                    return IntegerConvertNode.convertUnsigned(XorNode.create(equals.getX(), ConstantNode.forIntegerStamp(equals.getX().stamp(view), 1), view), stamp, view);
                                 }
                             } else if (equalsY == 1) {
                                 if (constTrueValue == 1 && constFalseValue == 0) {
                                     // return x when: x == 1 ? 1 : 0;
-                                    return IntegerConvertNode.convertUnsigned(equals.getX(), stamp);
+                                    return IntegerConvertNode.convertUnsigned(equals.getX(), stamp, view);
                                 } else if (constTrueValue == 0 && constFalseValue == 1) {
                                     // negate a boolean value via xor
-                                    return IntegerConvertNode.convertUnsigned(XorNode.create(equals.getX(), ConstantNode.forIntegerStamp(equals.getX().stamp(), 1)), stamp);
+                                    return IntegerConvertNode.convertUnsigned(XorNode.create(equals.getX(), ConstantNode.forIntegerStamp(equals.getX().stamp(view), 1), view), stamp, view);
                                 }
                             }
                         }
@@ -211,10 +213,10 @@ public final class ConditionalNode extends FloatingNode implements Canonicalizab
                     // (value & 1) == 1 ? 1 : 0
                     IntegerTestNode integerTestNode = (IntegerTestNode) condition;
                     if (integerTestNode.getY().isConstant()) {
-                        assert integerTestNode.getX().stamp() instanceof IntegerStamp;
+                        assert integerTestNode.getX().stamp(view) instanceof IntegerStamp;
                         long testY = integerTestNode.getY().asJavaConstant().asLong();
                         if (testY == 1 && constTrueValue == 0 && constFalseValue == 1) {
-                            return IntegerConvertNode.convertUnsigned(AndNode.create(integerTestNode.getX(), integerTestNode.getY()), stamp);
+                            return IntegerConvertNode.convertUnsigned(AndNode.create(integerTestNode.getX(), integerTestNode.getY(), view), stamp, view);
                         }
                     }
                 }
@@ -231,7 +233,7 @@ public final class ConditionalNode extends FloatingNode implements Canonicalizab
                         if (trueValue instanceof AddNode) {
                             AddNode add = (AddNode) trueValue;
                             if (add.getX() == falseValue) {
-                                int bits = ((IntegerStamp) trueValue.stamp()).getBits();
+                                int bits = ((IntegerStamp) trueValue.stamp(NodeView.DEFAULT)).getBits();
                                 ValueNode shift = new RightShiftNode(lt.getX(), ConstantNode.forIntegerBits(32, bits - 1));
                                 ValueNode and = new AndNode(shift, add.getY());
                                 return new AddNode(add.getX(), and);
@@ -245,10 +247,10 @@ public final class ConditionalNode extends FloatingNode implements Canonicalizab
         return null;
     }
 
-    private static ValueNode findSynonym(ValueNode condition, ValueNode trueValue, ValueNode falseValue) {
+    private static ValueNode findSynonym(ValueNode condition, ValueNode trueValue, ValueNode falseValue, NodeView view) {
         if (condition instanceof LogicNegationNode) {
             LogicNegationNode negated = (LogicNegationNode) condition;
-            return ConditionalNode.create(negated.getValue(), falseValue, trueValue);
+            return ConditionalNode.create(negated.getValue(), falseValue, trueValue, view);
         }
         if (condition instanceof LogicConstantNode) {
             LogicConstantNode c = (LogicConstantNode) condition;
@@ -267,6 +269,6 @@ public final class ConditionalNode extends FloatingNode implements Canonicalizab
     }
 
     public ConditionalNode(StructuredGraph graph, Condition condition, ValueNode x, ValueNode y) {
-        this(createCompareNode(graph, condition, x, y, null));
+        this(createCompareNode(graph, condition, x, y, null, NodeView.DEFAULT));
     }
 }
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/FloatConvertNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/FloatConvertNode.java
index 25a82292cb4..450fa2a782e 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/FloatConvertNode.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/FloatConvertNode.java
@@ -33,6 +33,7 @@ import org.graalvm.compiler.graph.NodeClass;
 import org.graalvm.compiler.graph.spi.CanonicalizerTool;
 import org.graalvm.compiler.lir.gen.ArithmeticLIRGeneratorTool;
 import org.graalvm.compiler.nodeinfo.NodeInfo;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.ValueNode;
 import org.graalvm.compiler.nodes.spi.ArithmeticLIRLowerable;
 import org.graalvm.compiler.nodes.spi.Lowerable;
@@ -65,8 +66,8 @@ public final class FloatConvertNode extends UnaryArithmeticNode<FloatConvertOp>
         this.op = op;
     }
 
-    public static ValueNode create(FloatConvert op, ValueNode input) {
-        ValueNode synonym = findSynonym(input, ArithmeticOpTable.forStamp(input.stamp()).getFloatConvert(op));
+    public static ValueNode create(FloatConvert op, ValueNode input, NodeView view) {
+        ValueNode synonym = findSynonym(input, ArithmeticOpTable.forStamp(input.stamp(view)).getFloatConvert(op));
         if (synonym != null) {
             return synonym;
         }
@@ -84,7 +85,7 @@ public final class FloatConvertNode extends UnaryArithmeticNode<FloatConvertOp>
 
     @Override
     public Constant reverse(Constant c, ConstantReflectionProvider constantReflection) {
-        FloatConvertOp reverse = ArithmeticOpTable.forStamp(stamp()).getFloatConvert(op.reverse());
+        FloatConvertOp reverse = ArithmeticOpTable.forStamp(stamp(NodeView.DEFAULT)).getFloatConvert(op.reverse());
         return reverse.foldConstant(c);
     }
 
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/FloatDivNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/FloatDivNode.java
index 195241a9c2a..0f2c081fea5 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/FloatDivNode.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/FloatDivNode.java
@@ -34,6 +34,7 @@ import org.graalvm.compiler.graph.spi.CanonicalizerTool;
 import org.graalvm.compiler.lir.gen.ArithmeticLIRGeneratorTool;
 import org.graalvm.compiler.nodeinfo.NodeInfo;
 import org.graalvm.compiler.nodes.ConstantNode;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.ValueNode;
 import org.graalvm.compiler.nodes.spi.NodeLIRBuilderTool;
 
@@ -53,10 +54,10 @@ public class FloatDivNode extends BinaryArithmeticNode<Div> {
         assert stamp instanceof FloatStamp;
     }
 
-    public static ValueNode create(ValueNode x, ValueNode y) {
-        BinaryOp<Div> op = ArithmeticOpTable.forStamp(x.stamp()).getDiv();
-        Stamp stamp = op.foldStamp(x.stamp(), y.stamp());
-        ConstantNode tryConstantFold = tryConstantFold(op, x, y, stamp);
+    public static ValueNode create(ValueNode x, ValueNode y, NodeView view) {
+        BinaryOp<Div> op = ArithmeticOpTable.forStamp(x.stamp(view)).getDiv();
+        Stamp stamp = op.foldStamp(x.stamp(view), y.stamp(view));
+        ConstantNode tryConstantFold = tryConstantFold(op, x, y, stamp, view);
         if (tryConstantFold != null) {
             return tryConstantFold;
         }
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/FloatEqualsNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/FloatEqualsNode.java
index 871973aa2b2..09b7072d31b 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/FloatEqualsNode.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/FloatEqualsNode.java
@@ -37,6 +37,7 @@ import org.graalvm.compiler.graph.spi.CanonicalizerTool;
 import org.graalvm.compiler.nodeinfo.NodeInfo;
 import org.graalvm.compiler.nodes.LogicConstantNode;
 import org.graalvm.compiler.nodes.LogicNode;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.ValueNode;
 import org.graalvm.compiler.nodes.util.GraphUtil;
 import org.graalvm.compiler.options.OptionValues;
@@ -50,12 +51,12 @@ public final class FloatEqualsNode extends CompareNode implements BinaryCommutat
 
     public FloatEqualsNode(ValueNode x, ValueNode y) {
         super(TYPE, Condition.EQ, false, x, y);
-        assert x.stamp() instanceof FloatStamp && y.stamp() instanceof FloatStamp : x.stamp() + " " + y.stamp();
-        assert x.stamp().isCompatible(y.stamp());
+        assert x.stamp(NodeView.DEFAULT) instanceof FloatStamp && y.stamp(NodeView.DEFAULT) instanceof FloatStamp : x.stamp(NodeView.DEFAULT) + " " + y.stamp(NodeView.DEFAULT);
+        assert x.stamp(NodeView.DEFAULT).isCompatible(y.stamp(NodeView.DEFAULT));
     }
 
-    public static LogicNode create(ValueNode x, ValueNode y) {
-        LogicNode result = CompareNode.tryConstantFoldPrimitive(Condition.EQ, x, y, false);
+    public static LogicNode create(ValueNode x, ValueNode y, NodeView view) {
+        LogicNode result = CompareNode.tryConstantFoldPrimitive(Condition.EQ, x, y, false, view);
         if (result != null) {
             return result;
         } else {
@@ -64,18 +65,18 @@ public final class FloatEqualsNode extends CompareNode implements BinaryCommutat
     }
 
     public static LogicNode create(ConstantReflectionProvider constantReflection, MetaAccessProvider metaAccess, OptionValues options, Integer smallestCompareWidth,
-                    ValueNode x, ValueNode y) {
-        LogicNode value = OP.canonical(constantReflection, metaAccess, options, smallestCompareWidth, Condition.EQ, false, x, y);
+                    ValueNode x, ValueNode y, NodeView view) {
+        LogicNode value = OP.canonical(constantReflection, metaAccess, options, smallestCompareWidth, Condition.EQ, false, x, y, view);
         if (value != null) {
             return value;
         }
-        return create(x, y);
+        return create(x, y, view);
     }
 
     @Override
     public boolean isIdentityComparison() {
-        FloatStamp xStamp = (FloatStamp) x.stamp();
-        FloatStamp yStamp = (FloatStamp) y.stamp();
+        FloatStamp xStamp = (FloatStamp) x.stamp(NodeView.DEFAULT);
+        FloatStamp yStamp = (FloatStamp) y.stamp(NodeView.DEFAULT);
         /*
          * If both stamps have at most one 0.0 and it's the same 0.0 then this is an identity
          * comparison. FloatStamp isn't careful about tracking the presence of -0.0 so assume that
@@ -87,7 +88,8 @@ public final class FloatEqualsNode extends CompareNode implements BinaryCommutat
 
     @Override
     public Node canonical(CanonicalizerTool tool, ValueNode forX, ValueNode forY) {
-        ValueNode value = OP.canonical(tool.getConstantReflection(), tool.getMetaAccess(), tool.getOptions(), tool.smallestCompareWidth(), Condition.EQ, unorderedIsTrue, forX, forY);
+        NodeView view = NodeView.from(tool);
+        ValueNode value = OP.canonical(tool.getConstantReflection(), tool.getMetaAccess(), tool.getOptions(), tool.smallestCompareWidth(), Condition.EQ, unorderedIsTrue, forX, forY, view);
         if (value != null) {
             return value;
         }
@@ -98,13 +100,13 @@ public final class FloatEqualsNode extends CompareNode implements BinaryCommutat
 
         @Override
         public LogicNode canonical(ConstantReflectionProvider constantReflection, MetaAccessProvider metaAccess, OptionValues options, Integer smallestCompareWidth, Condition condition,
-                        boolean unorderedIsTrue, ValueNode forX, ValueNode forY) {
-            LogicNode result = super.canonical(constantReflection, metaAccess, options, smallestCompareWidth, condition, unorderedIsTrue, forX, forY);
+                        boolean unorderedIsTrue, ValueNode forX, ValueNode forY, NodeView view) {
+            LogicNode result = super.canonical(constantReflection, metaAccess, options, smallestCompareWidth, condition, unorderedIsTrue, forX, forY, view);
             if (result != null) {
                 return result;
             }
-            Stamp xStampGeneric = forX.stamp();
-            Stamp yStampGeneric = forY.stamp();
+            Stamp xStampGeneric = forX.stamp(view);
+            Stamp yStampGeneric = forY.stamp(view);
             if (xStampGeneric instanceof FloatStamp && yStampGeneric instanceof FloatStamp) {
                 FloatStamp xStamp = (FloatStamp) xStampGeneric;
                 FloatStamp yStamp = (FloatStamp) yStampGeneric;
@@ -118,10 +120,10 @@ public final class FloatEqualsNode extends CompareNode implements BinaryCommutat
         }
 
         @Override
-        protected CompareNode duplicateModified(ValueNode newX, ValueNode newY, boolean unorderedIsTrue) {
-            if (newX.stamp() instanceof FloatStamp && newY.stamp() instanceof FloatStamp) {
+        protected CompareNode duplicateModified(ValueNode newX, ValueNode newY, boolean unorderedIsTrue, NodeView view) {
+            if (newX.stamp(view) instanceof FloatStamp && newY.stamp(view) instanceof FloatStamp) {
                 return new FloatEqualsNode(newX, newY);
-            } else if (newX.stamp() instanceof IntegerStamp && newY.stamp() instanceof IntegerStamp) {
+            } else if (newX.stamp(view) instanceof IntegerStamp && newY.stamp(view) instanceof IntegerStamp) {
                 return new IntegerEqualsNode(newX, newY);
             }
             throw GraalError.shouldNotReachHere();
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/FloatLessThanNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/FloatLessThanNode.java
index 2f6666bab4f..8b1885ff9bd 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/FloatLessThanNode.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/FloatLessThanNode.java
@@ -36,6 +36,7 @@ import org.graalvm.compiler.graph.spi.CanonicalizerTool;
 import org.graalvm.compiler.nodeinfo.NodeInfo;
 import org.graalvm.compiler.nodes.LogicConstantNode;
 import org.graalvm.compiler.nodes.LogicNode;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.ValueNode;
 import org.graalvm.compiler.nodes.util.GraphUtil;
 import org.graalvm.compiler.options.OptionValues;
@@ -49,12 +50,12 @@ public final class FloatLessThanNode extends CompareNode {
 
     public FloatLessThanNode(ValueNode x, ValueNode y, boolean unorderedIsTrue) {
         super(TYPE, Condition.LT, unorderedIsTrue, x, y);
-        assert x.stamp() instanceof FloatStamp && y.stamp() instanceof FloatStamp;
-        assert x.stamp().isCompatible(y.stamp());
+        assert x.stamp(NodeView.DEFAULT) instanceof FloatStamp && y.stamp(NodeView.DEFAULT) instanceof FloatStamp;
+        assert x.stamp(NodeView.DEFAULT).isCompatible(y.stamp(NodeView.DEFAULT));
     }
 
-    public static LogicNode create(ValueNode x, ValueNode y, boolean unorderedIsTrue) {
-        LogicNode result = CompareNode.tryConstantFoldPrimitive(Condition.LT, x, y, unorderedIsTrue);
+    public static LogicNode create(ValueNode x, ValueNode y, boolean unorderedIsTrue, NodeView view) {
+        LogicNode result = CompareNode.tryConstantFoldPrimitive(Condition.LT, x, y, unorderedIsTrue, view);
         if (result != null) {
             return result;
         }
@@ -62,17 +63,18 @@ public final class FloatLessThanNode extends CompareNode {
     }
 
     public static LogicNode create(ConstantReflectionProvider constantReflection, MetaAccessProvider metaAccess, OptionValues options, Integer smallestCompareWidth,
-                    ValueNode x, ValueNode y, boolean unorderedIsTrue) {
-        LogicNode result = OP.canonical(constantReflection, metaAccess, options, smallestCompareWidth, Condition.LT, unorderedIsTrue, x, y);
+                    ValueNode x, ValueNode y, boolean unorderedIsTrue, NodeView view) {
+        LogicNode result = OP.canonical(constantReflection, metaAccess, options, smallestCompareWidth, Condition.LT, unorderedIsTrue, x, y, view);
         if (result != null) {
             return result;
         }
-        return create(x, y, unorderedIsTrue);
+        return create(x, y, unorderedIsTrue, view);
     }
 
     @Override
     public Node canonical(CanonicalizerTool tool, ValueNode forX, ValueNode forY) {
-        ValueNode value = OP.canonical(tool.getConstantReflection(), tool.getMetaAccess(), tool.getOptions(), tool.smallestCompareWidth(), Condition.LT, unorderedIsTrue, forX, forY);
+        NodeView view = NodeView.from(tool);
+        ValueNode value = OP.canonical(tool.getConstantReflection(), tool.getMetaAccess(), tool.getOptions(), tool.smallestCompareWidth(), Condition.LT, unorderedIsTrue, forX, forY, view);
         if (value != null) {
             return value;
         }
@@ -83,8 +85,8 @@ public final class FloatLessThanNode extends CompareNode {
 
         @Override
         public LogicNode canonical(ConstantReflectionProvider constantReflection, MetaAccessProvider metaAccess, OptionValues options, Integer smallestCompareWidth, Condition condition,
-                        boolean unorderedIsTrue, ValueNode forX, ValueNode forY) {
-            LogicNode result = super.canonical(constantReflection, metaAccess, options, smallestCompareWidth, condition, unorderedIsTrue, forX, forY);
+                        boolean unorderedIsTrue, ValueNode forX, ValueNode forY, NodeView view) {
+            LogicNode result = super.canonical(constantReflection, metaAccess, options, smallestCompareWidth, condition, unorderedIsTrue, forX, forY, view);
             if (result != null) {
                 return result;
             }
@@ -95,10 +97,10 @@ public final class FloatLessThanNode extends CompareNode {
         }
 
         @Override
-        protected CompareNode duplicateModified(ValueNode newX, ValueNode newY, boolean unorderedIsTrue) {
-            if (newX.stamp() instanceof FloatStamp && newY.stamp() instanceof FloatStamp) {
+        protected CompareNode duplicateModified(ValueNode newX, ValueNode newY, boolean unorderedIsTrue, NodeView view) {
+            if (newX.stamp(NodeView.DEFAULT) instanceof FloatStamp && newY.stamp(NodeView.DEFAULT) instanceof FloatStamp) {
                 return new FloatLessThanNode(newX, newY, unorderedIsTrue);
-            } else if (newX.stamp() instanceof IntegerStamp && newY.stamp() instanceof IntegerStamp) {
+            } else if (newX.stamp(NodeView.DEFAULT) instanceof IntegerStamp && newY.stamp(NodeView.DEFAULT) instanceof IntegerStamp) {
                 return new IntegerLessThanNode(newX, newY);
             }
             throw GraalError.shouldNotReachHere();
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/IntegerBelowNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/IntegerBelowNode.java
index 4d870566e73..03e963d18d4 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/IntegerBelowNode.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/IntegerBelowNode.java
@@ -33,6 +33,7 @@ import org.graalvm.compiler.graph.NodeClass;
 import org.graalvm.compiler.graph.spi.CanonicalizerTool;
 import org.graalvm.compiler.nodeinfo.NodeInfo;
 import org.graalvm.compiler.nodes.LogicNode;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.ValueNode;
 
 import jdk.vm.ci.code.CodeUtil;
@@ -45,25 +46,27 @@ public final class IntegerBelowNode extends IntegerLowerThanNode {
 
     public IntegerBelowNode(ValueNode x, ValueNode y) {
         super(TYPE, x, y, OP);
-        assert x.stamp() instanceof IntegerStamp;
-        assert y.stamp() instanceof IntegerStamp;
+        assert x.stamp(NodeView.DEFAULT) instanceof IntegerStamp;
+        assert y.stamp(NodeView.DEFAULT) instanceof IntegerStamp;
     }
 
-    public static LogicNode create(ValueNode x, ValueNode y) {
-        return OP.create(x, y);
+    public static LogicNode create(ValueNode x, ValueNode y, NodeView view) {
+        return OP.create(x, y, view);
     }
 
-    public static LogicNode create(ConstantReflectionProvider constantReflection, MetaAccessProvider metaAccess, OptionValues options, Integer smallestCompareWidth, ValueNode x, ValueNode y) {
-        LogicNode value = OP.canonical(constantReflection, metaAccess, options, smallestCompareWidth, OP.getCondition(), false, x, y);
+    public static LogicNode create(ConstantReflectionProvider constantReflection, MetaAccessProvider metaAccess, OptionValues options, Integer smallestCompareWidth, ValueNode x, ValueNode y,
+                    NodeView view) {
+        LogicNode value = OP.canonical(constantReflection, metaAccess, options, smallestCompareWidth, OP.getCondition(), false, x, y, view);
         if (value != null) {
             return value;
         }
-        return create(x, y);
+        return create(x, y, view);
     }
 
     @Override
     public Node canonical(CanonicalizerTool tool, ValueNode forX, ValueNode forY) {
-        ValueNode value = OP.canonical(tool.getConstantReflection(), tool.getMetaAccess(), tool.getOptions(), tool.smallestCompareWidth(), OP.getCondition(), false, forX, forY);
+        NodeView view = NodeView.from(tool);
+        ValueNode value = OP.canonical(tool.getConstantReflection(), tool.getMetaAccess(), tool.getOptions(), tool.smallestCompareWidth(), OP.getCondition(), false, forX, forY, view);
         if (value != null) {
             return value;
         }
@@ -72,8 +75,8 @@ public final class IntegerBelowNode extends IntegerLowerThanNode {
 
     public static class BelowOp extends LowerOp {
         @Override
-        protected CompareNode duplicateModified(ValueNode newX, ValueNode newY, boolean unorderedIsTrue) {
-            assert newX.stamp() instanceof IntegerStamp && newY.stamp() instanceof IntegerStamp;
+        protected CompareNode duplicateModified(ValueNode newX, ValueNode newY, boolean unorderedIsTrue, NodeView view) {
+            assert newX.stamp(NodeView.DEFAULT) instanceof IntegerStamp && newY.stamp(NodeView.DEFAULT) instanceof IntegerStamp;
             return new IntegerBelowNode(newX, newY);
         }
 
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/IntegerConvertNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/IntegerConvertNode.java
index 90bfdfde901..0e601f2910e 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/IntegerConvertNode.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/IntegerConvertNode.java
@@ -35,6 +35,7 @@ import org.graalvm.compiler.graph.spi.CanonicalizerTool;
 import org.graalvm.compiler.nodeinfo.NodeInfo;
 import org.graalvm.compiler.nodes.ArithmeticOperation;
 import org.graalvm.compiler.nodes.ConstantNode;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.StructuredGraph;
 import org.graalvm.compiler.nodes.ValueNode;
 import org.graalvm.compiler.nodes.spi.ArithmeticLIRLowerable;
@@ -61,12 +62,12 @@ public abstract class IntegerConvertNode<OP, REV> extends UnaryNode implements A
 
     protected IntegerConvertNode(NodeClass<? extends IntegerConvertNode<OP, REV>> c, SerializableIntegerConvertFunction<OP> getOp, SerializableIntegerConvertFunction<REV> getReverseOp, int inputBits,
                     int resultBits, ValueNode input) {
-        super(c, getOp.apply(ArithmeticOpTable.forStamp(input.stamp())).foldStamp(inputBits, resultBits, input.stamp()), input);
+        super(c, getOp.apply(ArithmeticOpTable.forStamp(input.stamp(NodeView.DEFAULT))).foldStamp(inputBits, resultBits, input.stamp(NodeView.DEFAULT)), input);
         this.getOp = getOp;
         this.getReverseOp = getReverseOp;
         this.inputBits = inputBits;
         this.resultBits = resultBits;
-        assert ((PrimitiveStamp) input.stamp()).getBits() == inputBits;
+        assert ((PrimitiveStamp) input.stamp(NodeView.DEFAULT)).getBits() == inputBits;
     }
 
     public int getInputBits() {
@@ -78,7 +79,7 @@ public abstract class IntegerConvertNode<OP, REV> extends UnaryNode implements A
     }
 
     protected final IntegerConvertOp<OP> getOp(ValueNode forValue) {
-        return getOp.apply(ArithmeticOpTable.forStamp(forValue.stamp()));
+        return getOp.apply(ArithmeticOpTable.forStamp(forValue.stamp(NodeView.DEFAULT)));
     }
 
     @Override
@@ -93,19 +94,19 @@ public abstract class IntegerConvertNode<OP, REV> extends UnaryNode implements A
 
     @Override
     public Constant reverse(Constant c, ConstantReflectionProvider constantReflection) {
-        IntegerConvertOp<REV> reverse = getReverseOp.apply(ArithmeticOpTable.forStamp(stamp()));
+        IntegerConvertOp<REV> reverse = getReverseOp.apply(ArithmeticOpTable.forStamp(stamp(NodeView.DEFAULT)));
         return reverse.foldConstant(getResultBits(), getInputBits(), c);
     }
 
     @Override
     public Stamp foldStamp(Stamp newStamp) {
-        assert newStamp.isCompatible(getValue().stamp());
+        assert newStamp.isCompatible(getValue().stamp(NodeView.DEFAULT));
         return getArithmeticOp().foldStamp(inputBits, resultBits, newStamp);
     }
 
     @Override
     public ValueNode canonical(CanonicalizerTool tool, ValueNode forValue) {
-        ValueNode synonym = findSynonym(getOp(forValue), forValue, inputBits, resultBits, stamp());
+        ValueNode synonym = findSynonym(getOp(forValue), forValue, inputBits, resultBits, stamp(NodeView.DEFAULT));
         if (synonym != null) {
             return synonym;
         }
@@ -121,12 +122,12 @@ public abstract class IntegerConvertNode<OP, REV> extends UnaryNode implements A
         return null;
     }
 
-    public static ValueNode convert(ValueNode input, Stamp stamp) {
-        return convert(input, stamp, false);
+    public static ValueNode convert(ValueNode input, Stamp stamp, NodeView view) {
+        return convert(input, stamp, false, view);
     }
 
-    public static ValueNode convert(ValueNode input, Stamp stamp, StructuredGraph graph) {
-        ValueNode convert = convert(input, stamp, false);
+    public static ValueNode convert(ValueNode input, Stamp stamp, StructuredGraph graph, NodeView view) {
+        ValueNode convert = convert(input, stamp, false, view);
         if (!convert.isAlive()) {
             assert !convert.isDeleted();
             convert = graph.addOrUniqueWithInputs(convert);
@@ -134,12 +135,12 @@ public abstract class IntegerConvertNode<OP, REV> extends UnaryNode implements A
         return convert;
     }
 
-    public static ValueNode convertUnsigned(ValueNode input, Stamp stamp) {
-        return convert(input, stamp, true);
+    public static ValueNode convertUnsigned(ValueNode input, Stamp stamp, NodeView view) {
+        return convert(input, stamp, true, view);
     }
 
-    public static ValueNode convert(ValueNode input, Stamp stamp, boolean zeroExtend) {
-        IntegerStamp fromStamp = (IntegerStamp) input.stamp();
+    public static ValueNode convert(ValueNode input, Stamp stamp, boolean zeroExtend, NodeView view) {
+        IntegerStamp fromStamp = (IntegerStamp) input.stamp(view);
         IntegerStamp toStamp = (IntegerStamp) stamp;
 
         ValueNode result;
@@ -149,13 +150,13 @@ public abstract class IntegerConvertNode<OP, REV> extends UnaryNode implements A
             result = new NarrowNode(input, fromStamp.getBits(), toStamp.getBits());
         } else if (zeroExtend) {
             // toStamp.getBits() > fromStamp.getBits()
-            result = ZeroExtendNode.create(input, toStamp.getBits());
+            result = ZeroExtendNode.create(input, toStamp.getBits(), view);
         } else {
             // toStamp.getBits() > fromStamp.getBits()
-            result = SignExtendNode.create(input, toStamp.getBits());
+            result = SignExtendNode.create(input, toStamp.getBits(), view);
         }
 
-        IntegerStamp resultStamp = (IntegerStamp) result.stamp();
+        IntegerStamp resultStamp = (IntegerStamp) result.stamp(view);
         assert toStamp.getBits() == resultStamp.getBits();
         return result;
     }
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/IntegerDivRemNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/IntegerDivRemNode.java
index 97766cabec8..7a25afd4025 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/IntegerDivRemNode.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/IntegerDivRemNode.java
@@ -29,6 +29,7 @@ import org.graalvm.compiler.core.common.type.IntegerStamp;
 import org.graalvm.compiler.core.common.type.Stamp;
 import org.graalvm.compiler.graph.NodeClass;
 import org.graalvm.compiler.nodeinfo.NodeInfo;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.ValueNode;
 import org.graalvm.compiler.nodes.spi.Lowerable;
 import org.graalvm.compiler.nodes.spi.LoweringTool;
@@ -59,7 +60,7 @@ public abstract class IntegerDivRemNode extends FixedBinaryNode implements Lower
 
         // Assigning canDeopt during constructor, because it must never change during lifetime of
         // the node.
-        this.canDeopt = ((IntegerStamp) getY().stamp()).contains(0);
+        this.canDeopt = ((IntegerStamp) getY().stamp(NodeView.DEFAULT)).contains(0);
     }
 
     public final Op getOp() {
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/IntegerEqualsNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/IntegerEqualsNode.java
index dbb929d9ff9..8408d73e315 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/IntegerEqualsNode.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/IntegerEqualsNode.java
@@ -39,6 +39,7 @@ import org.graalvm.compiler.nodes.ConstantNode;
 import org.graalvm.compiler.nodes.LogicConstantNode;
 import org.graalvm.compiler.nodes.LogicNegationNode;
 import org.graalvm.compiler.nodes.LogicNode;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.ValueNode;
 import org.graalvm.compiler.nodes.util.GraphUtil;
 
@@ -59,8 +60,8 @@ public final class IntegerEqualsNode extends CompareNode implements BinaryCommut
         assert !y.getStackKind().isNumericFloat() && y.getStackKind() != JavaKind.Object;
     }
 
-    public static LogicNode create(ValueNode x, ValueNode y) {
-        LogicNode result = CompareNode.tryConstantFoldPrimitive(Condition.EQ, x, y, false);
+    public static LogicNode create(ValueNode x, ValueNode y, NodeView view) {
+        LogicNode result = CompareNode.tryConstantFoldPrimitive(Condition.EQ, x, y, false, view);
         if (result != null) {
             return result;
         }
@@ -84,17 +85,19 @@ public final class IntegerEqualsNode extends CompareNode implements BinaryCommut
         return new IntegerEqualsNode(x, y).maybeCommuteInputs();
     }
 
-    public static LogicNode create(ConstantReflectionProvider constantReflection, MetaAccessProvider metaAccess, OptionValues options, Integer smallestCompareWidth, ValueNode x, ValueNode y) {
-        LogicNode value = OP.canonical(constantReflection, metaAccess, options, smallestCompareWidth, Condition.EQ, false, x, y);
+    public static LogicNode create(ConstantReflectionProvider constantReflection, MetaAccessProvider metaAccess, OptionValues options, Integer smallestCompareWidth, ValueNode x, ValueNode y,
+                    NodeView view) {
+        LogicNode value = OP.canonical(constantReflection, metaAccess, options, smallestCompareWidth, Condition.EQ, false, x, y, view);
         if (value != null) {
             return value;
         }
-        return create(x, y);
+        return create(x, y, view);
     }
 
     @Override
     public Node canonical(CanonicalizerTool tool, ValueNode forX, ValueNode forY) {
-        ValueNode value = OP.canonical(tool.getConstantReflection(), tool.getMetaAccess(), tool.getOptions(), tool.smallestCompareWidth(), Condition.EQ, false, forX, forY);
+        NodeView view = NodeView.from(tool);
+        ValueNode value = OP.canonical(tool.getConstantReflection(), tool.getMetaAccess(), tool.getOptions(), tool.smallestCompareWidth(), Condition.EQ, false, forX, forY, view);
         if (value != null) {
             return value;
         }
@@ -104,7 +107,7 @@ public final class IntegerEqualsNode extends CompareNode implements BinaryCommut
     public static class IntegerEqualsOp extends CompareOp {
         @Override
         protected LogicNode optimizeNormalizeCompare(ConstantReflectionProvider constantReflection, MetaAccessProvider metaAccess, OptionValues options, Integer smallestCompareWidth,
-                        Constant constant, NormalizeCompareNode normalizeNode, boolean mirrored) {
+                        Constant constant, NormalizeCompareNode normalizeNode, boolean mirrored, NodeView view) {
             PrimitiveConstant primitive = (PrimitiveConstant) constant;
             ValueNode a = normalizeNode.getX();
             ValueNode b = normalizeNode.getY();
@@ -112,21 +115,21 @@ public final class IntegerEqualsNode extends CompareNode implements BinaryCommut
 
             if (cst == 0) {
                 if (normalizeNode.getX().getStackKind() == JavaKind.Double || normalizeNode.getX().getStackKind() == JavaKind.Float) {
-                    return FloatEqualsNode.create(constantReflection, metaAccess, options, smallestCompareWidth, a, b);
+                    return FloatEqualsNode.create(constantReflection, metaAccess, options, smallestCompareWidth, a, b, view);
                 } else {
-                    return IntegerEqualsNode.create(constantReflection, metaAccess, options, smallestCompareWidth, a, b);
+                    return IntegerEqualsNode.create(constantReflection, metaAccess, options, smallestCompareWidth, a, b, view);
                 }
             } else if (cst == 1) {
                 if (normalizeNode.getX().getStackKind() == JavaKind.Double || normalizeNode.getX().getStackKind() == JavaKind.Float) {
-                    return FloatLessThanNode.create(b, a, !normalizeNode.isUnorderedLess);
+                    return FloatLessThanNode.create(b, a, !normalizeNode.isUnorderedLess, view);
                 } else {
-                    return IntegerLessThanNode.create(constantReflection, metaAccess, options, smallestCompareWidth, b, a);
+                    return IntegerLessThanNode.create(constantReflection, metaAccess, options, smallestCompareWidth, b, a, view);
                 }
             } else if (cst == -1) {
                 if (normalizeNode.getX().getStackKind() == JavaKind.Double || normalizeNode.getX().getStackKind() == JavaKind.Float) {
-                    return FloatLessThanNode.create(a, b, normalizeNode.isUnorderedLess);
+                    return FloatLessThanNode.create(a, b, normalizeNode.isUnorderedLess, view);
                 } else {
-                    return IntegerLessThanNode.create(constantReflection, metaAccess, options, smallestCompareWidth, a, b);
+                    return IntegerLessThanNode.create(constantReflection, metaAccess, options, smallestCompareWidth, a, b, view);
                 }
             } else {
                 return LogicConstantNode.contradiction();
@@ -134,12 +137,12 @@ public final class IntegerEqualsNode extends CompareNode implements BinaryCommut
         }
 
         @Override
-        protected CompareNode duplicateModified(ValueNode newX, ValueNode newY, boolean unorderedIsTrue) {
-            if (newX.stamp() instanceof FloatStamp && newY.stamp() instanceof FloatStamp) {
+        protected CompareNode duplicateModified(ValueNode newX, ValueNode newY, boolean unorderedIsTrue, NodeView view) {
+            if (newX.stamp(view) instanceof FloatStamp && newY.stamp(view) instanceof FloatStamp) {
                 return new FloatEqualsNode(newX, newY);
-            } else if (newX.stamp() instanceof IntegerStamp && newY.stamp() instanceof IntegerStamp) {
+            } else if (newX.stamp(view) instanceof IntegerStamp && newY.stamp(view) instanceof IntegerStamp) {
                 return new IntegerEqualsNode(newX, newY);
-            } else if (newX.stamp() instanceof AbstractPointerStamp && newY.stamp() instanceof AbstractPointerStamp) {
+            } else if (newX.stamp(view) instanceof AbstractPointerStamp && newY.stamp(view) instanceof AbstractPointerStamp) {
                 return new IntegerEqualsNode(newX, newY);
             }
             throw GraalError.shouldNotReachHere();
@@ -147,10 +150,10 @@ public final class IntegerEqualsNode extends CompareNode implements BinaryCommut
 
         @Override
         public LogicNode canonical(ConstantReflectionProvider constantReflection, MetaAccessProvider metaAccess, OptionValues options, Integer smallestCompareWidth, Condition condition,
-                        boolean unorderedIsTrue, ValueNode forX, ValueNode forY) {
+                        boolean unorderedIsTrue, ValueNode forX, ValueNode forY, NodeView view) {
             if (GraphUtil.unproxify(forX) == GraphUtil.unproxify(forY)) {
                 return LogicConstantNode.tautology();
-            } else if (forX.stamp().alwaysDistinct(forY.stamp())) {
+            } else if (forX.stamp(view).alwaysDistinct(forY.stamp(view))) {
                 return LogicConstantNode.contradiction();
             }
 
@@ -174,19 +177,19 @@ public final class IntegerEqualsNode extends CompareNode implements BinaryCommut
                 }
                 if (v1 != null) {
                     assert v2 != null;
-                    return create(v1, v2);
+                    return create(v1, v2, view);
                 }
             }
 
-            return super.canonical(constantReflection, metaAccess, options, smallestCompareWidth, condition, unorderedIsTrue, forX, forY);
+            return super.canonical(constantReflection, metaAccess, options, smallestCompareWidth, condition, unorderedIsTrue, forX, forY, view);
         }
 
         @Override
         protected LogicNode canonicalizeSymmetricConstant(ConstantReflectionProvider constantReflection, MetaAccessProvider metaAccess, OptionValues options, Integer smallestCompareWidth,
-                        Condition condition, Constant constant, ValueNode nonConstant, boolean mirrored, boolean unorderedIsTrue) {
+                        Condition condition, Constant constant, ValueNode nonConstant, boolean mirrored, boolean unorderedIsTrue, NodeView view) {
             if (constant instanceof PrimitiveConstant) {
                 PrimitiveConstant primitiveConstant = (PrimitiveConstant) constant;
-                IntegerStamp nonConstantStamp = ((IntegerStamp) nonConstant.stamp());
+                IntegerStamp nonConstantStamp = ((IntegerStamp) nonConstant.stamp(view));
                 if ((primitiveConstant.asLong() == 1 && nonConstantStamp.upperBound() == 1 && nonConstantStamp.lowerBound() == 0) ||
                                 (primitiveConstant.asLong() == -1 && nonConstantStamp.upperBound() == 0 && nonConstantStamp.lowerBound() == -1)) {
                     // nonConstant can only be 0 or 1 (respective -1), test against 0 instead of 1
@@ -194,15 +197,16 @@ public final class IntegerEqualsNode extends CompareNode implements BinaryCommut
                     // execution
                     // on specific platforms.
                     return LogicNegationNode.create(
-                                    IntegerEqualsNode.create(constantReflection, metaAccess, options, smallestCompareWidth, nonConstant, ConstantNode.forIntegerKind(nonConstant.getStackKind(), 0)));
+                                    IntegerEqualsNode.create(constantReflection, metaAccess, options, smallestCompareWidth, nonConstant, ConstantNode.forIntegerKind(nonConstant.getStackKind(), 0),
+                                                    view));
                 } else if (primitiveConstant.asLong() == 0) {
                     if (nonConstant instanceof AndNode) {
                         AndNode andNode = (AndNode) nonConstant;
                         return new IntegerTestNode(andNode.getX(), andNode.getY());
                     } else if (nonConstant instanceof SubNode) {
                         SubNode subNode = (SubNode) nonConstant;
-                        return IntegerEqualsNode.create(constantReflection, metaAccess, options, smallestCompareWidth, subNode.getX(), subNode.getY());
-                    } else if (nonConstant instanceof ShiftNode && nonConstant.stamp() instanceof IntegerStamp) {
+                        return IntegerEqualsNode.create(constantReflection, metaAccess, options, smallestCompareWidth, subNode.getX(), subNode.getY(), view);
+                    } else if (nonConstant instanceof ShiftNode && nonConstant.stamp(view) instanceof IntegerStamp) {
                         if (nonConstant instanceof LeftShiftNode) {
                             LeftShiftNode shift = (LeftShiftNode) nonConstant;
                             if (shift.getY().isConstant()) {
@@ -217,7 +221,7 @@ public final class IntegerEqualsNode extends CompareNode implements BinaryCommut
                             }
                         } else if (nonConstant instanceof RightShiftNode) {
                             RightShiftNode shift = (RightShiftNode) nonConstant;
-                            if (shift.getY().isConstant() && ((IntegerStamp) shift.getX().stamp()).isPositive()) {
+                            if (shift.getY().isConstant() && ((IntegerStamp) shift.getX().stamp(view)).isPositive()) {
                                 int mask = shift.getShiftAmountMask();
                                 int amount = shift.getY().asJavaConstant().asInt() & mask;
                                 if (shift.getX().getStackKind() == JavaKind.Int) {
@@ -258,16 +262,16 @@ public final class IntegerEqualsNode extends CompareNode implements BinaryCommut
                     }
                 }
 
-                if (nonConstant instanceof XorNode && nonConstant.stamp() instanceof IntegerStamp) {
+                if (nonConstant instanceof XorNode && nonConstant.stamp(view) instanceof IntegerStamp) {
                     XorNode xorNode = (XorNode) nonConstant;
-                    if (xorNode.getY().isJavaConstant() && xorNode.getY().asJavaConstant().asLong() == 1 && ((IntegerStamp) xorNode.getX().stamp()).upMask() == 1) {
+                    if (xorNode.getY().isJavaConstant() && xorNode.getY().asJavaConstant().asLong() == 1 && ((IntegerStamp) xorNode.getX().stamp(view)).upMask() == 1) {
                         // x ^ 1 == 0 is the same as x == 1 if x in [0, 1]
                         // x ^ 1 == 1 is the same as x == 0 if x in [0, 1]
-                        return new IntegerEqualsNode(xorNode.getX(), ConstantNode.forIntegerStamp(xorNode.getX().stamp(), primitiveConstant.asLong() ^ 1));
+                        return new IntegerEqualsNode(xorNode.getX(), ConstantNode.forIntegerStamp(xorNode.getX().stamp(view), primitiveConstant.asLong() ^ 1));
                     }
                 }
             }
-            return super.canonicalizeSymmetricConstant(constantReflection, metaAccess, options, smallestCompareWidth, condition, constant, nonConstant, mirrored, unorderedIsTrue);
+            return super.canonicalizeSymmetricConstant(constantReflection, metaAccess, options, smallestCompareWidth, condition, constant, nonConstant, mirrored, unorderedIsTrue, view);
         }
     }
 
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/IntegerLessThanNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/IntegerLessThanNode.java
index bce2a2c39f3..bab63e1f7d9 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/IntegerLessThanNode.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/IntegerLessThanNode.java
@@ -40,6 +40,7 @@ import org.graalvm.compiler.nodes.ConstantNode;
 import org.graalvm.compiler.nodes.LogicConstantNode;
 import org.graalvm.compiler.nodes.LogicNegationNode;
 import org.graalvm.compiler.nodes.LogicNode;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.ValueNode;
 
 import jdk.vm.ci.code.CodeUtil;
@@ -60,22 +61,23 @@ public final class IntegerLessThanNode extends IntegerLowerThanNode {
         assert !y.getStackKind().isNumericFloat() && y.getStackKind() != JavaKind.Object;
     }
 
-    public static LogicNode create(ValueNode x, ValueNode y) {
-        return OP.create(x, y);
+    public static LogicNode create(ValueNode x, ValueNode y, NodeView view) {
+        return OP.create(x, y, view);
     }
 
     public static LogicNode create(ConstantReflectionProvider constantReflection, MetaAccessProvider metaAccess, OptionValues options, Integer smallestCompareWidth,
-                    ValueNode x, ValueNode y) {
-        LogicNode value = OP.canonical(constantReflection, metaAccess, options, smallestCompareWidth, OP.getCondition(), false, x, y);
+                    ValueNode x, ValueNode y, NodeView view) {
+        LogicNode value = OP.canonical(constantReflection, metaAccess, options, smallestCompareWidth, OP.getCondition(), false, x, y, view);
         if (value != null) {
             return value;
         }
-        return create(x, y);
+        return create(x, y, view);
     }
 
     @Override
     public Node canonical(CanonicalizerTool tool, ValueNode forX, ValueNode forY) {
-        ValueNode value = OP.canonical(tool.getConstantReflection(), tool.getMetaAccess(), tool.getOptions(), tool.smallestCompareWidth(), OP.getCondition(), false, forX, forY);
+        NodeView view = NodeView.from(tool);
+        ValueNode value = OP.canonical(tool.getConstantReflection(), tool.getMetaAccess(), tool.getOptions(), tool.smallestCompareWidth(), OP.getCondition(), false, forX, forY, view);
         if (value != null) {
             return value;
         }
@@ -98,11 +100,11 @@ public final class IntegerLessThanNode extends IntegerLowerThanNode {
 
     public static class LessThanOp extends LowerOp {
         @Override
-        protected CompareNode duplicateModified(ValueNode newX, ValueNode newY, boolean unorderedIsTrue) {
-            if (newX.stamp() instanceof FloatStamp && newY.stamp() instanceof FloatStamp) {
+        protected CompareNode duplicateModified(ValueNode newX, ValueNode newY, boolean unorderedIsTrue, NodeView view) {
+            if (newX.stamp(view) instanceof FloatStamp && newY.stamp(view) instanceof FloatStamp) {
                 return new FloatLessThanNode(newX, newY, unorderedIsTrue); // TODO: Is the last arg
                                                                            // supposed to be true?
-            } else if (newX.stamp() instanceof IntegerStamp && newY.stamp() instanceof IntegerStamp) {
+            } else if (newX.stamp(view) instanceof IntegerStamp && newY.stamp(view) instanceof IntegerStamp) {
                 return new IntegerLessThanNode(newX, newY);
             }
             throw GraalError.shouldNotReachHere();
@@ -110,7 +112,7 @@ public final class IntegerLessThanNode extends IntegerLowerThanNode {
 
         @Override
         protected LogicNode optimizeNormalizeCompare(ConstantReflectionProvider constantReflection, MetaAccessProvider metaAccess, OptionValues options, Integer smallestCompareWidth,
-                        Constant constant, NormalizeCompareNode normalizeNode, boolean mirrored) {
+                        Constant constant, NormalizeCompareNode normalizeNode, boolean mirrored, NodeView view) {
             PrimitiveConstant primitive = (PrimitiveConstant) constant;
             /* @formatter:off
              * a NC b < c  (not mirrored)
@@ -138,18 +140,18 @@ public final class IntegerLessThanNode extends IntegerLowerThanNode {
 
             if (cst == 0) {
                 if (normalizeNode.getX().getStackKind() == JavaKind.Double || normalizeNode.getX().getStackKind() == JavaKind.Float) {
-                    return FloatLessThanNode.create(constantReflection, metaAccess, options, smallestCompareWidth, a, b, mirrored ^ normalizeNode.isUnorderedLess);
+                    return FloatLessThanNode.create(constantReflection, metaAccess, options, smallestCompareWidth, a, b, mirrored ^ normalizeNode.isUnorderedLess, view);
                 } else {
-                    return IntegerLessThanNode.create(constantReflection, metaAccess, options, smallestCompareWidth, a, b);
+                    return IntegerLessThanNode.create(constantReflection, metaAccess, options, smallestCompareWidth, a, b, view);
                 }
             } else if (cst == 1) {
                 // a <= b <=> !(a > b)
                 LogicNode compare;
                 if (normalizeNode.getX().getStackKind() == JavaKind.Double || normalizeNode.getX().getStackKind() == JavaKind.Float) {
                     // since we negate, we have to reverse the unordered result
-                    compare = FloatLessThanNode.create(constantReflection, metaAccess, options, smallestCompareWidth, b, a, mirrored == normalizeNode.isUnorderedLess);
+                    compare = FloatLessThanNode.create(constantReflection, metaAccess, options, smallestCompareWidth, b, a, mirrored == normalizeNode.isUnorderedLess, view);
                 } else {
-                    compare = IntegerLessThanNode.create(constantReflection, metaAccess, options, smallestCompareWidth, b, a);
+                    compare = IntegerLessThanNode.create(constantReflection, metaAccess, options, smallestCompareWidth, b, a, view);
                 }
                 return LogicNegationNode.create(compare);
             } else if (cst <= -1) {
@@ -161,13 +163,13 @@ public final class IntegerLessThanNode extends IntegerLowerThanNode {
         }
 
         @Override
-        protected LogicNode findSynonym(ValueNode forX, ValueNode forY) {
-            LogicNode result = super.findSynonym(forX, forY);
+        protected LogicNode findSynonym(ValueNode forX, ValueNode forY, NodeView view) {
+            LogicNode result = super.findSynonym(forX, forY, view);
             if (result != null) {
                 return result;
             }
-            if (forX.stamp() instanceof IntegerStamp && forY.stamp() instanceof IntegerStamp) {
-                if (IntegerStamp.sameSign((IntegerStamp) forX.stamp(), (IntegerStamp) forY.stamp())) {
+            if (forX.stamp(view) instanceof IntegerStamp && forY.stamp(view) instanceof IntegerStamp) {
+                if (IntegerStamp.sameSign((IntegerStamp) forX.stamp(view), (IntegerStamp) forY.stamp(view))) {
                     return new IntegerBelowNode(forX, forY);
                 }
             }
@@ -188,8 +190,8 @@ public final class IntegerLessThanNode extends IntegerLowerThanNode {
                 }
                 if (xx != null) {
                     assert yy != null;
-                    IntegerStamp xStamp = (IntegerStamp) sub.getX().stamp();
-                    IntegerStamp yStamp = (IntegerStamp) sub.getY().stamp();
+                    IntegerStamp xStamp = (IntegerStamp) sub.getX().stamp(view);
+                    IntegerStamp yStamp = (IntegerStamp) sub.getY().stamp(view);
                     long minValue = CodeUtil.minValue(xStamp.getBits());
                     long maxValue = CodeUtil.maxValue(xStamp.getBits());
 
@@ -203,10 +205,10 @@ public final class IntegerLessThanNode extends IntegerLowerThanNode {
                 }
             }
 
-            if (forX.stamp() instanceof IntegerStamp) {
-                assert forY.stamp() instanceof IntegerStamp;
-                int bits = ((IntegerStamp) forX.stamp()).getBits();
-                assert ((IntegerStamp) forY.stamp()).getBits() == bits;
+            if (forX.stamp(view) instanceof IntegerStamp) {
+                assert forY.stamp(view) instanceof IntegerStamp;
+                int bits = ((IntegerStamp) forX.stamp(view)).getBits();
+                assert ((IntegerStamp) forY.stamp(view)).getBits() == bits;
                 long min = OP.minValue(bits);
                 long xResidue = 0;
                 ValueNode left = null;
@@ -240,12 +242,12 @@ public final class IntegerLessThanNode extends IntegerLowerThanNode {
                             if (left == null) {
                                 left = ConstantNode.forIntegerBits(bits, leftCst.asLong() - min);
                             } else if (xResidue != 0) {
-                                left = AddNode.create(left, ConstantNode.forIntegerBits(bits, xResidue));
+                                left = AddNode.create(left, ConstantNode.forIntegerBits(bits, xResidue), view);
                             }
                             if (right == null) {
                                 right = ConstantNode.forIntegerBits(bits, rightCst.asLong() - min);
                             } else if (yResidue != 0) {
-                                right = AddNode.create(right, ConstantNode.forIntegerBits(bits, yResidue));
+                                right = AddNode.create(right, ConstantNode.forIntegerBits(bits, yResidue), view);
                             }
                             return new IntegerBelowNode(left, right);
                         }
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/IntegerLowerThanNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/IntegerLowerThanNode.java
index 9ec1d8fe20d..0294ef39a7f 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/IntegerLowerThanNode.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/IntegerLowerThanNode.java
@@ -33,6 +33,7 @@ import org.graalvm.compiler.nodes.ConstantNode;
 import org.graalvm.compiler.nodes.LogicConstantNode;
 import org.graalvm.compiler.nodes.LogicNegationNode;
 import org.graalvm.compiler.nodes.LogicNode;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.ValueNode;
 import org.graalvm.compiler.nodes.util.GraphUtil;
 
@@ -76,12 +77,12 @@ public abstract class IntegerLowerThanNode extends CompareNode {
             IntegerStamp xStamp = (IntegerStamp) xStampGeneric;
             AddNode addNode = (AddNode) forY;
             IntegerStamp aStamp = null;
-            if (addNode.getX() == forX && addNode.getY().stamp() instanceof IntegerStamp) {
+            if (addNode.getX() == forX && addNode.getY().stamp(NodeView.DEFAULT) instanceof IntegerStamp) {
                 // x < x + a
-                aStamp = (IntegerStamp) addNode.getY().stamp();
-            } else if (addNode.getY() == forX && addNode.getX().stamp() instanceof IntegerStamp) {
+                aStamp = (IntegerStamp) addNode.getY().stamp(NodeView.DEFAULT);
+            } else if (addNode.getY() == forX && addNode.getX().stamp(NodeView.DEFAULT) instanceof IntegerStamp) {
                 // x < a + x
-                aStamp = (IntegerStamp) addNode.getX().stamp();
+                aStamp = (IntegerStamp) addNode.getX().stamp(NodeView.DEFAULT);
             }
             if (aStamp != null) {
                 IntegerStamp result = getOp().getSucceedingStampForXLowerXPlusA(mirror, strict, aStamp);
@@ -117,12 +118,12 @@ public abstract class IntegerLowerThanNode extends CompareNode {
     public abstract static class LowerOp extends CompareOp {
         @Override
         public LogicNode canonical(ConstantReflectionProvider constantReflection, MetaAccessProvider metaAccess, OptionValues options, Integer smallestCompareWidth, Condition condition,
-                        boolean unorderedIsTrue, ValueNode forX, ValueNode forY) {
-            LogicNode result = super.canonical(constantReflection, metaAccess, options, smallestCompareWidth, condition, unorderedIsTrue, forX, forY);
+                        boolean unorderedIsTrue, ValueNode forX, ValueNode forY, NodeView view) {
+            LogicNode result = super.canonical(constantReflection, metaAccess, options, smallestCompareWidth, condition, unorderedIsTrue, forX, forY, view);
             if (result != null) {
                 return result;
             }
-            LogicNode synonym = findSynonym(forX, forY);
+            LogicNode synonym = findSynonym(forX, forY, view);
             if (synonym != null) {
                 return synonym;
             }
@@ -159,12 +160,12 @@ public abstract class IntegerLowerThanNode extends CompareNode {
 
         protected abstract IntegerLowerThanNode createNode(ValueNode x, ValueNode y);
 
-        public LogicNode create(ValueNode x, ValueNode y) {
-            LogicNode result = CompareNode.tryConstantFoldPrimitive(getCondition(), x, y, false);
+        public LogicNode create(ValueNode x, ValueNode y, NodeView view) {
+            LogicNode result = CompareNode.tryConstantFoldPrimitive(getCondition(), x, y, false, view);
             if (result != null) {
                 return result;
             } else {
-                result = findSynonym(x, y);
+                result = findSynonym(x, y, view);
                 if (result != null) {
                     return result;
                 }
@@ -172,47 +173,47 @@ public abstract class IntegerLowerThanNode extends CompareNode {
             }
         }
 
-        protected LogicNode findSynonym(ValueNode forX, ValueNode forY) {
+        protected LogicNode findSynonym(ValueNode forX, ValueNode forY, NodeView view) {
             if (GraphUtil.unproxify(forX) == GraphUtil.unproxify(forY)) {
                 return LogicConstantNode.contradiction();
             }
-            TriState fold = tryFold(forX.stamp(), forY.stamp());
+            TriState fold = tryFold(forX.stamp(view), forY.stamp(view));
             if (fold.isTrue()) {
                 return LogicConstantNode.tautology();
             } else if (fold.isFalse()) {
                 return LogicConstantNode.contradiction();
             }
-            if (forY.stamp() instanceof IntegerStamp) {
-                IntegerStamp yStamp = (IntegerStamp) forY.stamp();
+            if (forY.stamp(view) instanceof IntegerStamp) {
+                IntegerStamp yStamp = (IntegerStamp) forY.stamp(view);
                 int bits = yStamp.getBits();
                 if (forX.isJavaConstant() && !forY.isConstant()) {
                     // bring the constant on the right
                     long xValue = forX.asJavaConstant().asLong();
                     if (xValue != maxValue(bits)) {
                         // c < x <=> !(c >= x) <=> !(x <= c) <=> !(x < c + 1)
-                        return LogicNegationNode.create(create(forY, ConstantNode.forIntegerStamp(yStamp, xValue + 1)));
+                        return LogicNegationNode.create(create(forY, ConstantNode.forIntegerStamp(yStamp, xValue + 1), view));
                     }
                 }
                 if (forY.isJavaConstant()) {
                     long yValue = forY.asJavaConstant().asLong();
                     if (yValue == maxValue(bits)) {
                         // x < MAX <=> x != MAX
-                        return LogicNegationNode.create(IntegerEqualsNode.create(forX, forY));
+                        return LogicNegationNode.create(IntegerEqualsNode.create(forX, forY, view));
                     }
                     if (yValue == minValue(bits) + 1) {
                         // x < MIN + 1 <=> x <= MIN <=> x == MIN
-                        return IntegerEqualsNode.create(forX, ConstantNode.forIntegerStamp(yStamp, minValue(bits)));
+                        return IntegerEqualsNode.create(forX, ConstantNode.forIntegerStamp(yStamp, minValue(bits)), view);
                     }
                 } else if (forY instanceof AddNode) {
                     AddNode addNode = (AddNode) forY;
-                    LogicNode canonical = canonicalizeXLowerXPlusA(forX, addNode, false, true);
+                    LogicNode canonical = canonicalizeXLowerXPlusA(forX, addNode, false, true, view);
                     if (canonical != null) {
                         return canonical;
                     }
                 }
                 if (forX instanceof AddNode) {
                     AddNode addNode = (AddNode) forX;
-                    LogicNode canonical = canonicalizeXLowerXPlusA(forY, addNode, true, false);
+                    LogicNode canonical = canonicalizeXLowerXPlusA(forY, addNode, true, false, view);
                     if (canonical != null) {
                         return canonical;
                     }
@@ -221,32 +222,32 @@ public abstract class IntegerLowerThanNode extends CompareNode {
             return null;
         }
 
-        private LogicNode canonicalizeXLowerXPlusA(ValueNode forX, AddNode addNode, boolean mirrored, boolean strict) {
+        private LogicNode canonicalizeXLowerXPlusA(ValueNode forX, AddNode addNode, boolean mirrored, boolean strict, NodeView view) {
             // x < x + a
             IntegerStamp succeedingXStamp;
             boolean exact;
-            if (addNode.getX() == forX && addNode.getY().stamp() instanceof IntegerStamp) {
-                IntegerStamp aStamp = (IntegerStamp) addNode.getY().stamp();
+            if (addNode.getX() == forX && addNode.getY().stamp(view) instanceof IntegerStamp) {
+                IntegerStamp aStamp = (IntegerStamp) addNode.getY().stamp(view);
                 succeedingXStamp = getSucceedingStampForXLowerXPlusA(mirrored, strict, aStamp);
                 exact = aStamp.lowerBound() == aStamp.upperBound();
-            } else if (addNode.getY() == forX && addNode.getX().stamp() instanceof IntegerStamp) {
-                IntegerStamp aStamp = (IntegerStamp) addNode.getX().stamp();
+            } else if (addNode.getY() == forX && addNode.getX().stamp(view) instanceof IntegerStamp) {
+                IntegerStamp aStamp = (IntegerStamp) addNode.getX().stamp(view);
                 succeedingXStamp = getSucceedingStampForXLowerXPlusA(mirrored, strict, aStamp);
                 exact = aStamp.lowerBound() == aStamp.upperBound();
             } else {
                 return null;
             }
-            if (succeedingXStamp.join(forX.stamp()).isEmpty()) {
+            if (succeedingXStamp.join(forX.stamp(view)).isEmpty()) {
                 return LogicConstantNode.contradiction();
             } else if (exact && !succeedingXStamp.isEmpty()) {
                 int bits = succeedingXStamp.getBits();
                 if (compare(lowerBound(succeedingXStamp), minValue(bits)) > 0) {
                     assert upperBound(succeedingXStamp) == maxValue(bits);
                     // x must be in [L..MAX] <=> x >= L <=> !(x < L)
-                    return LogicNegationNode.create(create(forX, ConstantNode.forIntegerStamp(succeedingXStamp, lowerBound(succeedingXStamp))));
+                    return LogicNegationNode.create(create(forX, ConstantNode.forIntegerStamp(succeedingXStamp, lowerBound(succeedingXStamp)), view));
                 } else if (compare(upperBound(succeedingXStamp), maxValue(bits)) < 0) {
                     // x must be in [MIN..H] <=> x <= H <=> !(H < x)
-                    return LogicNegationNode.create(create(ConstantNode.forIntegerStamp(succeedingXStamp, upperBound(succeedingXStamp)), forX));
+                    return LogicNegationNode.create(create(ConstantNode.forIntegerStamp(succeedingXStamp, upperBound(succeedingXStamp)), forX, view));
                 }
             }
             return null;
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/IntegerTestNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/IntegerTestNode.java
index cf7feb6971e..0e837972d75 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/IntegerTestNode.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/IntegerTestNode.java
@@ -33,6 +33,7 @@ import org.graalvm.compiler.graph.spi.CanonicalizerTool;
 import org.graalvm.compiler.nodeinfo.NodeInfo;
 import org.graalvm.compiler.nodes.BinaryOpLogicNode;
 import org.graalvm.compiler.nodes.LogicConstantNode;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.ValueNode;
 
 import jdk.vm.ci.meta.TriState;
@@ -52,12 +53,13 @@ public final class IntegerTestNode extends BinaryOpLogicNode implements BinaryCo
 
     @Override
     public ValueNode canonical(CanonicalizerTool tool, ValueNode forX, ValueNode forY) {
+        NodeView view = NodeView.from(tool);
         if (forX.isConstant() && forY.isConstant()) {
             return LogicConstantNode.forBoolean((forX.asJavaConstant().asLong() & forY.asJavaConstant().asLong()) == 0);
         }
-        if (forX.stamp() instanceof IntegerStamp && forY.stamp() instanceof IntegerStamp) {
-            IntegerStamp xStamp = (IntegerStamp) forX.stamp();
-            IntegerStamp yStamp = (IntegerStamp) forY.stamp();
+        if (forX.stamp(view) instanceof IntegerStamp && forY.stamp(view) instanceof IntegerStamp) {
+            IntegerStamp xStamp = (IntegerStamp) forX.stamp(view);
+            IntegerStamp yStamp = (IntegerStamp) forY.stamp(view);
             if ((xStamp.upMask() & yStamp.upMask()) == 0) {
                 return LogicConstantNode.tautology();
             } else if ((xStamp.downMask() & yStamp.downMask()) != 0) {
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/IsNullNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/IsNullNode.java
index 68b5700559b..85de8712869 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/IsNullNode.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/IsNullNode.java
@@ -31,6 +31,7 @@ import org.graalvm.compiler.nodeinfo.NodeCycles;
 import org.graalvm.compiler.nodeinfo.NodeInfo;
 import org.graalvm.compiler.nodes.LogicConstantNode;
 import org.graalvm.compiler.nodes.LogicNode;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.PiNode;
 import org.graalvm.compiler.nodes.UnaryOpLogicNode;
 import org.graalvm.compiler.nodes.ValueNode;
@@ -77,7 +78,7 @@ public final class IsNullNode extends UnaryOpLogicNode implements LIRLowerable,
     @Override
     public boolean verify() {
         assertTrue(getValue() != null, "is null input must not be null");
-        assertTrue(getValue().stamp() instanceof AbstractPointerStamp, "input must be a pointer not %s", getValue().stamp());
+        assertTrue(getValue().stamp(NodeView.DEFAULT) instanceof AbstractPointerStamp, "input must be a pointer not %s", getValue().stamp(NodeView.DEFAULT));
         return super.verify();
     }
 
@@ -113,7 +114,7 @@ public final class IsNullNode extends UnaryOpLogicNode implements LIRLowerable,
     @Override
     public void virtualize(VirtualizerTool tool) {
         ValueNode alias = tool.getAlias(getValue());
-        TriState fold = tryFold(alias.stamp());
+        TriState fold = tryFold(alias.stamp(NodeView.DEFAULT));
         if (fold != TriState.UNKNOWN) {
             tool.replaceWithValue(LogicConstantNode.forBoolean(fold.isTrue(), graph()));
         }
@@ -122,7 +123,7 @@ public final class IsNullNode extends UnaryOpLogicNode implements LIRLowerable,
     @Override
     public Stamp getSucceedingStampForValue(boolean negated) {
         // Ignore any more precise input stamp since canonicalization will skip through PiNodes
-        AbstractPointerStamp pointerStamp = (AbstractPointerStamp) getValue().stamp().unrestricted();
+        AbstractPointerStamp pointerStamp = (AbstractPointerStamp) getValue().stamp(NodeView.DEFAULT).unrestricted();
         return negated ? pointerStamp.asNonNull() : pointerStamp.asAlwaysNull();
     }
 
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/LeftShiftNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/LeftShiftNode.java
index f9d7cf3ed6e..98186ee15c3 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/LeftShiftNode.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/LeftShiftNode.java
@@ -31,6 +31,7 @@ import org.graalvm.compiler.graph.spi.CanonicalizerTool;
 import org.graalvm.compiler.lir.gen.ArithmeticLIRGeneratorTool;
 import org.graalvm.compiler.nodeinfo.NodeInfo;
 import org.graalvm.compiler.nodes.ConstantNode;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.ValueNode;
 import org.graalvm.compiler.nodes.spi.NodeLIRBuilderTool;
 
@@ -45,10 +46,10 @@ public final class LeftShiftNode extends ShiftNode<Shl> {
         super(TYPE, ArithmeticOpTable::getShl, x, y);
     }
 
-    public static ValueNode create(ValueNode x, ValueNode y) {
-        ArithmeticOpTable.ShiftOp<Shl> op = ArithmeticOpTable.forStamp(x.stamp()).getShl();
-        Stamp stamp = op.foldStamp(x.stamp(), (IntegerStamp) y.stamp());
-        ValueNode value = ShiftNode.canonical(op, stamp, x, y);
+    public static ValueNode create(ValueNode x, ValueNode y, NodeView view) {
+        ArithmeticOpTable.ShiftOp<Shl> op = ArithmeticOpTable.forStamp(x.stamp(view)).getShl();
+        Stamp stamp = op.foldStamp(x.stamp(view), (IntegerStamp) y.stamp(view));
+        ValueNode value = ShiftNode.canonical(op, stamp, x, y, view);
         if (value != null) {
             return value;
         }
@@ -63,7 +64,7 @@ public final class LeftShiftNode extends ShiftNode<Shl> {
             return ret;
         }
 
-        return canonical(this, getArithmeticOp(), stamp(), forX, forY);
+        return canonical(this, getArithmeticOp(), stamp(NodeView.DEFAULT), forX, forY);
     }
 
     private static ValueNode canonical(LeftShiftNode leftShiftNode, ArithmeticOpTable.ShiftOp<Shl> op, Stamp stamp, ValueNode forX, ValueNode forY) {
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/MulNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/MulNode.java
index 8f1c8879172..6f5cec5c454 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/MulNode.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/MulNode.java
@@ -35,6 +35,7 @@ import org.graalvm.compiler.graph.spi.CanonicalizerTool;
 import org.graalvm.compiler.lir.gen.ArithmeticLIRGeneratorTool;
 import org.graalvm.compiler.nodeinfo.NodeInfo;
 import org.graalvm.compiler.nodes.ConstantNode;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.ValueNode;
 import org.graalvm.compiler.nodes.spi.NodeLIRBuilderTool;
 
@@ -56,14 +57,14 @@ public class MulNode extends BinaryArithmeticNode<Mul> implements NarrowableArit
         super(c, ArithmeticOpTable::getMul, x, y);
     }
 
-    public static ValueNode create(ValueNode x, ValueNode y) {
-        BinaryOp<Mul> op = ArithmeticOpTable.forStamp(x.stamp()).getMul();
-        Stamp stamp = op.foldStamp(x.stamp(), y.stamp());
-        ConstantNode tryConstantFold = tryConstantFold(op, x, y, stamp);
+    public static ValueNode create(ValueNode x, ValueNode y, NodeView view) {
+        BinaryOp<Mul> op = ArithmeticOpTable.forStamp(x.stamp(view)).getMul();
+        Stamp stamp = op.foldStamp(x.stamp(view), y.stamp(view));
+        ConstantNode tryConstantFold = tryConstantFold(op, x, y, stamp, view);
         if (tryConstantFold != null) {
             return tryConstantFold;
         }
-        return canonical(null, op, stamp, x, y);
+        return canonical(null, op, stamp, x, y, view);
     }
 
     @Override
@@ -83,10 +84,11 @@ public class MulNode extends BinaryArithmeticNode<Mul> implements NarrowableArit
             return new MulNode(forY, forX);
         }
         BinaryOp<Mul> op = getOp(forX, forY);
-        return canonical(this, op, stamp(), forX, forY);
+        NodeView view = NodeView.from(tool);
+        return canonical(this, op, stamp(view), forX, forY, view);
     }
 
-    private static ValueNode canonical(MulNode self, BinaryOp<Mul> op, Stamp stamp, ValueNode forX, ValueNode forY) {
+    private static ValueNode canonical(MulNode self, BinaryOp<Mul> op, Stamp stamp, ValueNode forX, ValueNode forY, NodeView view) {
         if (forY.isConstant()) {
             Constant c = forY.asConstant();
             if (op.isNeutral(c)) {
@@ -95,57 +97,64 @@ public class MulNode extends BinaryArithmeticNode<Mul> implements NarrowableArit
 
             if (c instanceof PrimitiveConstant && ((PrimitiveConstant) c).getJavaKind().isNumericInteger()) {
                 long i = ((PrimitiveConstant) c).asLong();
-
-                if (i == 0) {
-                    return ConstantNode.forIntegerStamp(stamp, 0);
-                } else if (i == 1) {
-                    return forX;
-                } else if (i == -1) {
-                    return NegateNode.create(forX);
-                } else if (i > 0) {
-                    if (CodeUtil.isPowerOf2(i)) {
-                        return new LeftShiftNode(forX, ConstantNode.forInt(CodeUtil.log2(i)));
-                    } else if (CodeUtil.isPowerOf2(i - 1)) {
-                        return AddNode.create(new LeftShiftNode(forX, ConstantNode.forInt(CodeUtil.log2(i - 1))), forX);
-                    } else if (CodeUtil.isPowerOf2(i + 1)) {
-                        return SubNode.create(new LeftShiftNode(forX, ConstantNode.forInt(CodeUtil.log2(i + 1))), forX);
-                    } else {
-                        int bitCount = Long.bitCount(i);
-                        long highestBitValue = Long.highestOneBit(i);
-                        if (bitCount == 2) {
-                            // e.g., 0b1000_0010
-                            long lowerBitValue = i - highestBitValue;
-                            assert highestBitValue > 0 && lowerBitValue > 0;
-                            ValueNode left = new LeftShiftNode(forX, ConstantNode.forInt(CodeUtil.log2(highestBitValue)));
-                            ValueNode right = lowerBitValue == 1 ? forX : new LeftShiftNode(forX, ConstantNode.forInt(CodeUtil.log2(lowerBitValue)));
-                            return AddNode.create(left, right);
-                        } else {
-                            // e.g., 0b1111_1101
-                            int shiftToRoundUpToPowerOf2 = CodeUtil.log2(highestBitValue) + 1;
-                            long subValue = (1 << shiftToRoundUpToPowerOf2) - i;
-                            if (CodeUtil.isPowerOf2(subValue) && shiftToRoundUpToPowerOf2 < ((IntegerStamp) stamp).getBits()) {
-                                assert CodeUtil.log2(subValue) >= 1;
-                                ValueNode left = new LeftShiftNode(forX, ConstantNode.forInt(shiftToRoundUpToPowerOf2));
-                                ValueNode right = new LeftShiftNode(forX, ConstantNode.forInt(CodeUtil.log2(subValue)));
-                                return SubNode.create(left, right);
-                            }
-                        }
-                    }
-                } else if (i < 0) {
-                    if (CodeUtil.isPowerOf2(-i)) {
-                        return NegateNode.create(LeftShiftNode.create(forX, ConstantNode.forInt(CodeUtil.log2(-i))));
-                    }
+                ValueNode result = canonical(stamp, forX, i, view);
+                if (result != null) {
+                    return result;
                 }
             }
 
             if (op.isAssociative()) {
                 // canonicalize expressions like "(a * 1) * 2"
-                return reassociate(self != null ? self : (MulNode) new MulNode(forX, forY).maybeCommuteInputs(), ValueNode.isConstantPredicate(), forX, forY);
+                return reassociate(self != null ? self : (MulNode) new MulNode(forX, forY).maybeCommuteInputs(), ValueNode.isConstantPredicate(), forX, forY, view);
             }
         }
         return self != null ? self : new MulNode(forX, forY).maybeCommuteInputs();
     }
 
+    public static ValueNode canonical(Stamp stamp, ValueNode forX, long i, NodeView view) {
+        if (i == 0) {
+            return ConstantNode.forIntegerStamp(stamp, 0);
+        } else if (i == 1) {
+            return forX;
+        } else if (i == -1) {
+            return NegateNode.create(forX, view);
+        } else if (i > 0) {
+            if (CodeUtil.isPowerOf2(i)) {
+                return new LeftShiftNode(forX, ConstantNode.forInt(CodeUtil.log2(i)));
+            } else if (CodeUtil.isPowerOf2(i - 1)) {
+                return AddNode.create(new LeftShiftNode(forX, ConstantNode.forInt(CodeUtil.log2(i - 1))), forX, view);
+            } else if (CodeUtil.isPowerOf2(i + 1)) {
+                return SubNode.create(new LeftShiftNode(forX, ConstantNode.forInt(CodeUtil.log2(i + 1))), forX, view);
+            } else {
+                int bitCount = Long.bitCount(i);
+                long highestBitValue = Long.highestOneBit(i);
+                if (bitCount == 2) {
+                    // e.g., 0b1000_0010
+                    long lowerBitValue = i - highestBitValue;
+                    assert highestBitValue > 0 && lowerBitValue > 0;
+                    ValueNode left = new LeftShiftNode(forX, ConstantNode.forInt(CodeUtil.log2(highestBitValue)));
+                    ValueNode right = lowerBitValue == 1 ? forX : new LeftShiftNode(forX, ConstantNode.forInt(CodeUtil.log2(lowerBitValue)));
+                    return AddNode.create(left, right, view);
+                } else {
+                    // e.g., 0b1111_1101
+                    int shiftToRoundUpToPowerOf2 = CodeUtil.log2(highestBitValue) + 1;
+                    long subValue = (1 << shiftToRoundUpToPowerOf2) - i;
+                    if (CodeUtil.isPowerOf2(subValue) && shiftToRoundUpToPowerOf2 < ((IntegerStamp) stamp).getBits()) {
+                        assert CodeUtil.log2(subValue) >= 1;
+                        ValueNode left = new LeftShiftNode(forX, ConstantNode.forInt(shiftToRoundUpToPowerOf2));
+                        ValueNode right = new LeftShiftNode(forX, ConstantNode.forInt(CodeUtil.log2(subValue)));
+                        return SubNode.create(left, right, view);
+                    }
+                }
+            }
+        } else if (i < 0) {
+            if (CodeUtil.isPowerOf2(-i)) {
+                return NegateNode.create(LeftShiftNode.create(forX, ConstantNode.forInt(CodeUtil.log2(-i)), view), view);
+            }
+        }
+        return null;
+    }
+
     @Override
     public void generate(NodeLIRBuilderTool nodeValueMap, ArithmeticLIRGeneratorTool gen) {
         Value op1 = nodeValueMap.operand(getX());
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/NarrowNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/NarrowNode.java
index 766c6f854a9..e417e65cb88 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/NarrowNode.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/NarrowNode.java
@@ -34,6 +34,7 @@ import org.graalvm.compiler.graph.NodeClass;
 import org.graalvm.compiler.graph.spi.CanonicalizerTool;
 import org.graalvm.compiler.lir.gen.ArithmeticLIRGeneratorTool;
 import org.graalvm.compiler.nodeinfo.NodeInfo;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.ValueNode;
 import org.graalvm.compiler.nodes.spi.NodeLIRBuilderTool;
 
@@ -48,21 +49,21 @@ public final class NarrowNode extends IntegerConvertNode<Narrow, SignExtend> {
     public static final NodeClass<NarrowNode> TYPE = NodeClass.create(NarrowNode.class);
 
     public NarrowNode(ValueNode input, int resultBits) {
-        this(input, PrimitiveStamp.getBits(input.stamp()), resultBits);
-        assert 0 < resultBits && resultBits <= PrimitiveStamp.getBits(input.stamp());
+        this(input, PrimitiveStamp.getBits(input.stamp(NodeView.DEFAULT)), resultBits);
+        assert 0 < resultBits && resultBits <= PrimitiveStamp.getBits(input.stamp(NodeView.DEFAULT));
     }
 
     public NarrowNode(ValueNode input, int inputBits, int resultBits) {
         super(TYPE, ArithmeticOpTable::getNarrow, ArithmeticOpTable::getSignExtend, inputBits, resultBits, input);
     }
 
-    public static ValueNode create(ValueNode input, int resultBits) {
-        return create(input, PrimitiveStamp.getBits(input.stamp()), resultBits);
+    public static ValueNode create(ValueNode input, int resultBits, NodeView view) {
+        return create(input, PrimitiveStamp.getBits(input.stamp(view)), resultBits, view);
     }
 
-    public static ValueNode create(ValueNode input, int inputBits, int resultBits) {
-        IntegerConvertOp<Narrow> signExtend = ArithmeticOpTable.forStamp(input.stamp()).getNarrow();
-        ValueNode synonym = findSynonym(signExtend, input, inputBits, resultBits, signExtend.foldStamp(inputBits, resultBits, input.stamp()));
+    public static ValueNode create(ValueNode input, int inputBits, int resultBits, NodeView view) {
+        IntegerConvertOp<Narrow> signExtend = ArithmeticOpTable.forStamp(input.stamp(view)).getNarrow();
+        ValueNode synonym = findSynonym(signExtend, input, inputBits, resultBits, signExtend.foldStamp(inputBits, resultBits, input.stamp(view)));
         if (synonym != null) {
             return synonym;
         } else {
@@ -77,6 +78,7 @@ public final class NarrowNode extends IntegerConvertNode<Narrow, SignExtend> {
 
     @Override
     public ValueNode canonical(CanonicalizerTool tool, ValueNode forValue) {
+        NodeView view = NodeView.from(tool);
         ValueNode ret = super.canonical(tool, forValue);
         if (ret != this) {
             return ret;
@@ -108,7 +110,7 @@ public final class NarrowNode extends IntegerConvertNode<Narrow, SignExtend> {
                 if (other instanceof SignExtendNode) {
                     // sxxx -(sign-extend)-> ssssssss sssssxxx -(narrow)-> sssssxxx
                     // ==> sxxx -(sign-extend)-> sssssxxx
-                    return SignExtendNode.create(other.getValue(), other.getInputBits(), getResultBits());
+                    return SignExtendNode.create(other.getValue(), other.getInputBits(), getResultBits(), view);
                 } else if (other instanceof ZeroExtendNode) {
                     // xxxx -(zero-extend)-> 00000000 00000xxx -(narrow)-> 0000xxxx
                     // ==> xxxx -(zero-extend)-> 0000xxxx
@@ -117,13 +119,13 @@ public final class NarrowNode extends IntegerConvertNode<Narrow, SignExtend> {
             }
         } else if (forValue instanceof AndNode) {
             AndNode andNode = (AndNode) forValue;
-            IntegerStamp yStamp = (IntegerStamp) andNode.getY().stamp();
-            IntegerStamp xStamp = (IntegerStamp) andNode.getX().stamp();
+            IntegerStamp yStamp = (IntegerStamp) andNode.getY().stamp(view);
+            IntegerStamp xStamp = (IntegerStamp) andNode.getX().stamp(view);
             long relevantMask = CodeUtil.mask(this.getResultBits());
             if ((relevantMask & yStamp.downMask()) == relevantMask) {
-                return create(andNode.getX(), this.getResultBits());
+                return create(andNode.getX(), this.getResultBits(), view);
             } else if ((relevantMask & xStamp.downMask()) == relevantMask) {
-                return create(andNode.getY(), this.getResultBits());
+                return create(andNode.getY(), this.getResultBits(), view);
             }
         }
 
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/NegateNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/NegateNode.java
index ff563c7a94d..0ee81cc2bf8 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/NegateNode.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/NegateNode.java
@@ -33,6 +33,7 @@ import org.graalvm.compiler.graph.NodeClass;
 import org.graalvm.compiler.graph.spi.CanonicalizerTool;
 import org.graalvm.compiler.lir.gen.ArithmeticLIRGeneratorTool;
 import org.graalvm.compiler.nodeinfo.NodeInfo;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.ValueNode;
 import org.graalvm.compiler.nodes.spi.NodeLIRBuilderTool;
 import org.graalvm.compiler.nodes.spi.StampInverter;
@@ -49,8 +50,8 @@ public final class NegateNode extends UnaryArithmeticNode<Neg> implements Narrow
         super(TYPE, ArithmeticOpTable::getNeg, value);
     }
 
-    public static ValueNode create(ValueNode value) {
-        ValueNode synonym = findSynonym(value);
+    public static ValueNode create(ValueNode value, NodeView view) {
+        ValueNode synonym = findSynonym(value, view);
         if (synonym != null) {
             return synonym;
         }
@@ -66,8 +67,8 @@ public final class NegateNode extends UnaryArithmeticNode<Neg> implements Narrow
         return this;
     }
 
-    protected static ValueNode findSynonym(ValueNode forValue) {
-        ArithmeticOpTable.UnaryOp<Neg> negOp = ArithmeticOpTable.forStamp(forValue.stamp()).getNeg();
+    protected static ValueNode findSynonym(ValueNode forValue, NodeView view) {
+        ArithmeticOpTable.UnaryOp<Neg> negOp = ArithmeticOpTable.forStamp(forValue.stamp(view)).getNeg();
         ValueNode synonym = UnaryArithmeticNode.findSynonym(forValue, negOp);
         if (synonym != null) {
             return synonym;
@@ -75,9 +76,9 @@ public final class NegateNode extends UnaryArithmeticNode<Neg> implements Narrow
         if (forValue instanceof NegateNode) {
             return ((NegateNode) forValue).getValue();
         }
-        if (forValue instanceof SubNode && !(forValue.stamp() instanceof FloatStamp)) {
+        if (forValue instanceof SubNode && !(forValue.stamp(view) instanceof FloatStamp)) {
             SubNode sub = (SubNode) forValue;
-            return SubNode.create(sub.getY(), sub.getX());
+            return SubNode.create(sub.getY(), sub.getX(), view);
         }
         return null;
     }
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/NormalizeCompareNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/NormalizeCompareNode.java
index 049ff137986..6faad2746c4 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/NormalizeCompareNode.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/NormalizeCompareNode.java
@@ -35,6 +35,7 @@ import org.graalvm.compiler.nodeinfo.NodeInfo;
 import org.graalvm.compiler.nodes.ConstantNode;
 import org.graalvm.compiler.nodes.LogicConstantNode;
 import org.graalvm.compiler.nodes.LogicNode;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.ValueNode;
 
 import jdk.vm.ci.meta.ConstantReflectionProvider;
@@ -86,7 +87,8 @@ public final class NormalizeCompareNode extends BinaryNode implements IterableNo
 
     @Override
     public ValueNode canonical(CanonicalizerTool tool, ValueNode forX, ValueNode forY) {
-        ValueNode result = tryConstantFold(x, y, isUnorderedLess, stamp().getStackKind(), tool.getConstantReflection());
+        NodeView view = NodeView.from(tool);
+        ValueNode result = tryConstantFold(x, y, isUnorderedLess, stamp(view).getStackKind(), tool.getConstantReflection());
         if (result != null) {
             return result;
         }
@@ -100,7 +102,7 @@ public final class NormalizeCompareNode extends BinaryNode implements IterableNo
 
     @Override
     public Stamp foldStamp(Stamp stampX, Stamp stampY) {
-        return stamp();
+        return stamp(NodeView.DEFAULT);
     }
 
     public boolean isUnorderedLess() {
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/NotNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/NotNode.java
index 0fbfd223295..3e79b87c572 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/NotNode.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/NotNode.java
@@ -45,20 +45,31 @@ public final class NotNode extends UnaryArithmeticNode<Not> implements Arithmeti
 
     public static final NodeClass<NotNode> TYPE = NodeClass.create(NotNode.class);
 
-    public NotNode(ValueNode x) {
+    protected NotNode(ValueNode x) {
         super(TYPE, ArithmeticOpTable::getNot, x);
     }
 
+    public static ValueNode create(ValueNode x) {
+        return canonicalize(null, x);
+    }
+
     @Override
     public ValueNode canonical(CanonicalizerTool tool, ValueNode forValue) {
         ValueNode ret = super.canonical(tool, forValue);
         if (ret != this) {
             return ret;
         }
-        if (forValue instanceof NotNode) {
-            return ((NotNode) forValue).getValue();
+        return canonicalize(this, forValue);
+    }
+
+    private static ValueNode canonicalize(NotNode node, ValueNode x) {
+        if (x instanceof NotNode) {
+            return ((NotNode) x).getValue();
         }
-        return this;
+        if (node != null) {
+            return node;
+        }
+        return new NotNode(x);
     }
 
     @Override
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/ObjectEqualsNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/ObjectEqualsNode.java
index 06e156a8daf..9a9f2f402fb 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/ObjectEqualsNode.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/ObjectEqualsNode.java
@@ -34,6 +34,7 @@ import org.graalvm.compiler.nodeinfo.NodeInfo;
 import org.graalvm.compiler.nodes.ConstantNode;
 import org.graalvm.compiler.nodes.LogicConstantNode;
 import org.graalvm.compiler.nodes.LogicNode;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.ValueNode;
 import org.graalvm.compiler.nodes.extended.GetClassNode;
 import org.graalvm.compiler.nodes.java.InstanceOfNode;
@@ -58,16 +59,16 @@ public final class ObjectEqualsNode extends PointerEqualsNode implements Virtual
 
     public ObjectEqualsNode(ValueNode x, ValueNode y) {
         super(TYPE, x, y);
-        assert x.stamp() instanceof AbstractObjectStamp;
-        assert y.stamp() instanceof AbstractObjectStamp;
+        assert x.stamp(NodeView.DEFAULT) instanceof AbstractObjectStamp;
+        assert y.stamp(NodeView.DEFAULT) instanceof AbstractObjectStamp;
     }
 
-    public static LogicNode create(ValueNode x, ValueNode y, ConstantReflectionProvider constantReflection) {
+    public static LogicNode create(ValueNode x, ValueNode y, ConstantReflectionProvider constantReflection, NodeView view) {
         LogicNode result = CompareNode.tryConstantFold(Condition.EQ, x, y, constantReflection, false);
         if (result != null) {
             return result;
         } else {
-            result = findSynonym(x, y);
+            result = findSynonym(x, y, view);
             if (result != null) {
                 return result;
             }
@@ -75,17 +76,18 @@ public final class ObjectEqualsNode extends PointerEqualsNode implements Virtual
         }
     }
 
-    public static LogicNode create(ConstantReflectionProvider constantReflection, MetaAccessProvider metaAccess, OptionValues options, ValueNode x, ValueNode y) {
-        LogicNode result = OP.canonical(constantReflection, metaAccess, options, null, Condition.EQ, false, x, y);
+    public static LogicNode create(ConstantReflectionProvider constantReflection, MetaAccessProvider metaAccess, OptionValues options, ValueNode x, ValueNode y, NodeView view) {
+        LogicNode result = OP.canonical(constantReflection, metaAccess, options, null, Condition.EQ, false, x, y, view);
         if (result != null) {
             return result;
         }
-        return create(x, y, constantReflection);
+        return create(x, y, constantReflection, view);
     }
 
     @Override
     public ValueNode canonical(CanonicalizerTool tool, ValueNode forX, ValueNode forY) {
-        ValueNode value = OP.canonical(tool.getConstantReflection(), tool.getMetaAccess(), tool.getOptions(), tool.smallestCompareWidth(), Condition.EQ, false, forX, forY);
+        NodeView view = NodeView.from(tool);
+        ValueNode value = OP.canonical(tool.getConstantReflection(), tool.getMetaAccess(), tool.getOptions(), tool.smallestCompareWidth(), Condition.EQ, false, forX, forY, view);
         if (value != null) {
             return value;
         }
@@ -96,25 +98,25 @@ public final class ObjectEqualsNode extends PointerEqualsNode implements Virtual
 
         @Override
         protected LogicNode canonicalizeSymmetricConstant(ConstantReflectionProvider constantReflection, MetaAccessProvider metaAccess, OptionValues options, Integer smallestCompareWidth,
-                        Condition condition, Constant constant, ValueNode nonConstant, boolean mirrored, boolean unorderedIsTrue) {
+                        Condition condition, Constant constant, ValueNode nonConstant, boolean mirrored, boolean unorderedIsTrue, NodeView view) {
             ResolvedJavaType type = constantReflection.asJavaType(constant);
             if (type != null && nonConstant instanceof GetClassNode) {
                 GetClassNode getClassNode = (GetClassNode) nonConstant;
                 ValueNode object = getClassNode.getObject();
-                assert ((ObjectStamp) object.stamp()).nonNull();
+                assert ((ObjectStamp) object.stamp(view)).nonNull();
                 if (!type.isPrimitive() && (type.isConcrete() || type.isArray())) {
                     return InstanceOfNode.create(TypeReference.createExactTrusted(type), object);
                 }
                 return LogicConstantNode.forBoolean(false);
             }
-            return super.canonicalizeSymmetricConstant(constantReflection, metaAccess, options, smallestCompareWidth, condition, constant, nonConstant, mirrored, unorderedIsTrue);
+            return super.canonicalizeSymmetricConstant(constantReflection, metaAccess, options, smallestCompareWidth, condition, constant, nonConstant, mirrored, unorderedIsTrue, view);
         }
 
         @Override
-        protected CompareNode duplicateModified(ValueNode newX, ValueNode newY, boolean unorderedIsTrue) {
-            if (newX.stamp() instanceof ObjectStamp && newY.stamp() instanceof ObjectStamp) {
+        protected CompareNode duplicateModified(ValueNode newX, ValueNode newY, boolean unorderedIsTrue, NodeView view) {
+            if (newX.stamp(view) instanceof ObjectStamp && newY.stamp(view) instanceof ObjectStamp) {
                 return new ObjectEqualsNode(newX, newY);
-            } else if (newX.stamp() instanceof AbstractPointerStamp && newY.stamp() instanceof AbstractPointerStamp) {
+            } else if (newX.stamp(view) instanceof AbstractPointerStamp && newY.stamp(view) instanceof AbstractPointerStamp) {
                 return new PointerEqualsNode(newX, newY);
             }
             throw GraalError.shouldNotReachHere();
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/OrNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/OrNode.java
index 417ee0d101d..65ab3a01f23 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/OrNode.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/OrNode.java
@@ -33,6 +33,7 @@ import org.graalvm.compiler.graph.spi.CanonicalizerTool;
 import org.graalvm.compiler.lir.gen.ArithmeticLIRGeneratorTool;
 import org.graalvm.compiler.nodeinfo.NodeInfo;
 import org.graalvm.compiler.nodes.ConstantNode;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.ValueNode;
 import org.graalvm.compiler.nodes.spi.NodeLIRBuilderTool;
 import org.graalvm.compiler.nodes.util.GraphUtil;
@@ -50,27 +51,28 @@ public final class OrNode extends BinaryArithmeticNode<Or> implements BinaryComm
         super(TYPE, ArithmeticOpTable::getOr, x, y);
     }
 
-    public static ValueNode create(ValueNode x, ValueNode y) {
-        BinaryOp<Or> op = ArithmeticOpTable.forStamp(x.stamp()).getOr();
-        Stamp stamp = op.foldStamp(x.stamp(), y.stamp());
-        ConstantNode tryConstantFold = tryConstantFold(op, x, y, stamp);
+    public static ValueNode create(ValueNode x, ValueNode y, NodeView view) {
+        BinaryOp<Or> op = ArithmeticOpTable.forStamp(x.stamp(view)).getOr();
+        Stamp stamp = op.foldStamp(x.stamp(view), y.stamp(view));
+        ConstantNode tryConstantFold = tryConstantFold(op, x, y, stamp, view);
         if (tryConstantFold != null) {
             return tryConstantFold;
         }
-        return canonical(null, op, stamp, x, y);
+        return canonical(null, op, stamp, x, y, view);
     }
 
     @Override
     public ValueNode canonical(CanonicalizerTool tool, ValueNode forX, ValueNode forY) {
+        NodeView view = NodeView.from(tool);
         ValueNode ret = super.canonical(tool, forX, forY);
         if (ret != this) {
             return ret;
         }
 
-        return canonical(this, getOp(forX, forY), stamp(), forX, forY);
+        return canonical(this, getOp(forX, forY), stamp(view), forX, forY, view);
     }
 
-    private static ValueNode canonical(OrNode self, BinaryOp<Or> op, Stamp stamp, ValueNode forX, ValueNode forY) {
+    private static ValueNode canonical(OrNode self, BinaryOp<Or> op, Stamp stamp, ValueNode forX, ValueNode forY, NodeView view) {
         if (GraphUtil.unproxify(forX) == GraphUtil.unproxify(forY)) {
             return forX;
         }
@@ -90,10 +92,10 @@ public final class OrNode extends BinaryArithmeticNode<Or> implements BinaryComm
                     return ConstantNode.forIntegerStamp(stamp, mask);
                 }
             }
-            return reassociate(self != null ? self : (OrNode) new OrNode(forX, forY).maybeCommuteInputs(), ValueNode.isConstantPredicate(), forX, forY);
+            return reassociate(self != null ? self : (OrNode) new OrNode(forX, forY).maybeCommuteInputs(), ValueNode.isConstantPredicate(), forX, forY, view);
         }
         if (forX instanceof NotNode && forY instanceof NotNode) {
-            return new NotNode(AndNode.create(((NotNode) forX).getValue(), ((NotNode) forY).getValue()));
+            return new NotNode(AndNode.create(((NotNode) forX).getValue(), ((NotNode) forY).getValue(), view));
         }
         return self != null ? self : new OrNode(forX, forY).maybeCommuteInputs();
     }
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/PointerEqualsNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/PointerEqualsNode.java
index 798982d25e4..d1e399d1c13 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/PointerEqualsNode.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/PointerEqualsNode.java
@@ -35,6 +35,7 @@ import org.graalvm.compiler.graph.spi.CanonicalizerTool;
 import org.graalvm.compiler.nodeinfo.NodeInfo;
 import org.graalvm.compiler.nodes.LogicConstantNode;
 import org.graalvm.compiler.nodes.LogicNode;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.ValueNode;
 import org.graalvm.compiler.nodes.extended.LoadHubNode;
 import org.graalvm.compiler.nodes.extended.LoadMethodNode;
@@ -56,8 +57,8 @@ public class PointerEqualsNode extends CompareNode implements BinaryCommutative<
         this(TYPE, x, y);
     }
 
-    public static LogicNode create(ValueNode x, ValueNode y) {
-        LogicNode result = findSynonym(x, y);
+    public static LogicNode create(ValueNode x, ValueNode y, NodeView view) {
+        LogicNode result = findSynonym(x, y, view);
         if (result != null) {
             return result;
         }
@@ -66,13 +67,14 @@ public class PointerEqualsNode extends CompareNode implements BinaryCommutative<
 
     protected PointerEqualsNode(NodeClass<? extends PointerEqualsNode> c, ValueNode x, ValueNode y) {
         super(c, Condition.EQ, false, x, y);
-        assert x.stamp() instanceof AbstractPointerStamp;
-        assert y.stamp() instanceof AbstractPointerStamp;
+        assert x.stamp(NodeView.DEFAULT) instanceof AbstractPointerStamp;
+        assert y.stamp(NodeView.DEFAULT) instanceof AbstractPointerStamp;
     }
 
     @Override
     public Node canonical(CanonicalizerTool tool, ValueNode forX, ValueNode forY) {
-        ValueNode value = OP.canonical(tool.getConstantReflection(), tool.getMetaAccess(), tool.getOptions(), tool.smallestCompareWidth(), Condition.EQ, false, forX, forY);
+        NodeView view = NodeView.from(tool);
+        ValueNode value = OP.canonical(tool.getConstantReflection(), tool.getMetaAccess(), tool.getOptions(), tool.smallestCompareWidth(), Condition.EQ, false, forX, forY, view);
         if (value != null) {
             return value;
         }
@@ -111,31 +113,31 @@ public class PointerEqualsNode extends CompareNode implements BinaryCommutative<
 
         @Override
         public LogicNode canonical(ConstantReflectionProvider constantReflection, MetaAccessProvider metaAccess, OptionValues options, Integer smallestCompareWidth, Condition condition,
-                        boolean unorderedIsTrue, ValueNode forX, ValueNode forY) {
-            LogicNode result = findSynonym(forX, forY);
+                        boolean unorderedIsTrue, ValueNode forX, ValueNode forY, NodeView view) {
+            LogicNode result = findSynonym(forX, forY, view);
             if (result != null) {
                 return result;
             }
             if (isAlwaysFailingVirtualDispatchTest(condition, forX, forY)) {
                 return LogicConstantNode.contradiction();
             }
-            return super.canonical(constantReflection, metaAccess, options, smallestCompareWidth, condition, unorderedIsTrue, forX, forY);
+            return super.canonical(constantReflection, metaAccess, options, smallestCompareWidth, condition, unorderedIsTrue, forX, forY, view);
         }
 
         @Override
-        protected CompareNode duplicateModified(ValueNode newX, ValueNode newY, boolean unorderedIsTrue) {
+        protected CompareNode duplicateModified(ValueNode newX, ValueNode newY, boolean unorderedIsTrue, NodeView view) {
             return new PointerEqualsNode(newX, newY);
         }
     }
 
-    public static LogicNode findSynonym(ValueNode forX, ValueNode forY) {
+    public static LogicNode findSynonym(ValueNode forX, ValueNode forY, NodeView view) {
         if (GraphUtil.unproxify(forX) == GraphUtil.unproxify(forY)) {
             return LogicConstantNode.tautology();
-        } else if (forX.stamp().alwaysDistinct(forY.stamp())) {
+        } else if (forX.stamp(view).alwaysDistinct(forY.stamp(view))) {
             return LogicConstantNode.contradiction();
-        } else if (((AbstractPointerStamp) forX.stamp()).alwaysNull()) {
+        } else if (((AbstractPointerStamp) forX.stamp(view)).alwaysNull()) {
             return IsNullNode.create(forY);
-        } else if (((AbstractPointerStamp) forY.stamp()).alwaysNull()) {
+        } else if (((AbstractPointerStamp) forY.stamp(view)).alwaysNull()) {
             return IsNullNode.create(forX);
         } else {
             return null;
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/ReinterpretNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/ReinterpretNode.java
index dc5e60d39d0..b77e3c02102 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/ReinterpretNode.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/ReinterpretNode.java
@@ -38,6 +38,7 @@ import org.graalvm.compiler.graph.spi.CanonicalizerTool;
 import org.graalvm.compiler.lir.gen.ArithmeticLIRGeneratorTool;
 import org.graalvm.compiler.nodeinfo.NodeInfo;
 import org.graalvm.compiler.nodes.ConstantNode;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.ValueNode;
 import org.graalvm.compiler.nodes.spi.ArithmeticLIRLowerable;
 import org.graalvm.compiler.nodes.spi.NodeLIRBuilderTool;
@@ -56,16 +57,24 @@ public final class ReinterpretNode extends UnaryNode implements ArithmeticLIRLow
 
     public static final NodeClass<ReinterpretNode> TYPE = NodeClass.create(ReinterpretNode.class);
 
-    public ReinterpretNode(JavaKind to, ValueNode value) {
+    protected ReinterpretNode(JavaKind to, ValueNode value) {
         this(StampFactory.forKind(to), value);
     }
 
-    public ReinterpretNode(Stamp to, ValueNode value) {
-        super(TYPE, getReinterpretStamp(to, value.stamp()), value);
+    protected ReinterpretNode(Stamp to, ValueNode value) {
+        super(TYPE, getReinterpretStamp(to, value.stamp(NodeView.DEFAULT)), value);
         assert to instanceof ArithmeticStamp;
     }
 
-    private SerializableConstant evalConst(SerializableConstant c) {
+    public static ValueNode create(JavaKind to, ValueNode value, NodeView view) {
+        return create(StampFactory.forKind(to), value, view);
+    }
+
+    public static ValueNode create(Stamp to, ValueNode value, NodeView view) {
+        return canonical(null, to, value, view);
+    }
+
+    private static SerializableConstant evalConst(Stamp stamp, SerializableConstant c) {
         /*
          * We don't care about byte order here. Either would produce the correct result.
          */
@@ -73,7 +82,7 @@ public final class ReinterpretNode extends UnaryNode implements ArithmeticLIRLow
         c.serialize(buffer);
 
         buffer.rewind();
-        SerializableConstant ret = ((ArithmeticStamp) stamp()).deserialize(buffer);
+        SerializableConstant ret = ((ArithmeticStamp) stamp).deserialize(buffer);
 
         assert !buffer.hasRemaining();
         return ret;
@@ -81,17 +90,22 @@ public final class ReinterpretNode extends UnaryNode implements ArithmeticLIRLow
 
     @Override
     public ValueNode canonical(CanonicalizerTool tool, ValueNode forValue) {
+        NodeView view = NodeView.from(tool);
+        return canonical(this, this.stamp(view), forValue, view);
+    }
+
+    public static ValueNode canonical(ReinterpretNode node, Stamp forStamp, ValueNode forValue, NodeView view) {
         if (forValue.isConstant()) {
-            return ConstantNode.forConstant(stamp(), evalConst((SerializableConstant) forValue.asConstant()), null);
+            return ConstantNode.forConstant(forStamp, evalConst(forStamp, (SerializableConstant) forValue.asConstant()), null);
         }
-        if (stamp().isCompatible(forValue.stamp())) {
+        if (forStamp.isCompatible(forValue.stamp(view))) {
             return forValue;
         }
         if (forValue instanceof ReinterpretNode) {
             ReinterpretNode reinterpret = (ReinterpretNode) forValue;
-            return new ReinterpretNode(stamp(), reinterpret.getValue());
+            return new ReinterpretNode(forStamp, reinterpret.getValue());
         }
-        return this;
+        return node != null ? node : new ReinterpretNode(forStamp, forValue);
     }
 
     /**
@@ -269,12 +283,12 @@ public final class ReinterpretNode extends UnaryNode implements ArithmeticLIRLow
 
     @Override
     public boolean inferStamp() {
-        return updateStamp(getReinterpretStamp(stamp(), getValue().stamp()));
+        return updateStamp(getReinterpretStamp(stamp(NodeView.DEFAULT), getValue().stamp(NodeView.DEFAULT)));
     }
 
     @Override
     public void generate(NodeLIRBuilderTool builder, ArithmeticLIRGeneratorTool gen) {
-        LIRKind kind = builder.getLIRGeneratorTool().getLIRKind(stamp());
+        LIRKind kind = builder.getLIRGeneratorTool().getLIRKind(stamp(NodeView.DEFAULT));
         builder.setResult(this, gen.emitReinterpret(kind, builder.operand(getValue())));
     }
 
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/RemNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/RemNode.java
index 3ef7eebb6b8..54201e79071 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/RemNode.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/RemNode.java
@@ -25,10 +25,14 @@ package org.graalvm.compiler.nodes.calc;
 import static org.graalvm.compiler.nodeinfo.NodeCycles.CYCLES_32;
 
 import org.graalvm.compiler.core.common.type.ArithmeticOpTable;
+import org.graalvm.compiler.core.common.type.ArithmeticOpTable.BinaryOp;
 import org.graalvm.compiler.core.common.type.ArithmeticOpTable.BinaryOp.Rem;
+import org.graalvm.compiler.core.common.type.Stamp;
 import org.graalvm.compiler.graph.NodeClass;
 import org.graalvm.compiler.lir.gen.ArithmeticLIRGeneratorTool;
 import org.graalvm.compiler.nodeinfo.NodeInfo;
+import org.graalvm.compiler.nodes.ConstantNode;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.ValueNode;
 import org.graalvm.compiler.nodes.spi.Lowerable;
 import org.graalvm.compiler.nodes.spi.LoweringTool;
@@ -39,7 +43,7 @@ public class RemNode extends BinaryArithmeticNode<Rem> implements Lowerable {
 
     public static final NodeClass<RemNode> TYPE = NodeClass.create(RemNode.class);
 
-    public RemNode(ValueNode x, ValueNode y) {
+    protected RemNode(ValueNode x, ValueNode y) {
         this(TYPE, x, y);
     }
 
@@ -47,6 +51,16 @@ public class RemNode extends BinaryArithmeticNode<Rem> implements Lowerable {
         super(c, ArithmeticOpTable::getRem, x, y);
     }
 
+    public static ValueNode create(ValueNode forX, ValueNode forY, NodeView view) {
+        BinaryOp<Rem> op = ArithmeticOpTable.forStamp(forX.stamp(view)).getRem();
+        Stamp stamp = op.foldStamp(forX.stamp(view), forY.stamp(view));
+        ConstantNode tryConstantFold = tryConstantFold(op, forX, forY, stamp, view);
+        if (tryConstantFold != null) {
+            return tryConstantFold;
+        }
+        return new RemNode(forX, forY);
+    }
+
     @Override
     public void lower(LoweringTool tool) {
         tool.getLowerer().lower(this, tool);
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/RightShiftNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/RightShiftNode.java
index 7b5661b698b..621cb865afc 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/RightShiftNode.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/RightShiftNode.java
@@ -31,6 +31,7 @@ import org.graalvm.compiler.graph.spi.CanonicalizerTool;
 import org.graalvm.compiler.lir.gen.ArithmeticLIRGeneratorTool;
 import org.graalvm.compiler.nodeinfo.NodeInfo;
 import org.graalvm.compiler.nodes.ConstantNode;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.ValueNode;
 import org.graalvm.compiler.nodes.spi.NodeLIRBuilderTool;
 
@@ -45,30 +46,31 @@ public final class RightShiftNode extends ShiftNode<Shr> {
         super(TYPE, ArithmeticOpTable::getShr, x, y);
     }
 
-    public static ValueNode create(ValueNode x, ValueNode y) {
-        ArithmeticOpTable.ShiftOp<Shr> op = ArithmeticOpTable.forStamp(x.stamp()).getShr();
-        Stamp stamp = op.foldStamp(x.stamp(), (IntegerStamp) y.stamp());
-        ValueNode value = ShiftNode.canonical(op, stamp, x, y);
+    public static ValueNode create(ValueNode x, ValueNode y, NodeView view) {
+        ArithmeticOpTable.ShiftOp<Shr> op = ArithmeticOpTable.forStamp(x.stamp(view)).getShr();
+        Stamp stamp = op.foldStamp(x.stamp(view), (IntegerStamp) y.stamp(view));
+        ValueNode value = ShiftNode.canonical(op, stamp, x, y, view);
         if (value != null) {
             return value;
         }
 
-        return canonical(null, op, stamp, x, y);
+        return canonical(null, op, stamp, x, y, view);
     }
 
     @Override
     public ValueNode canonical(CanonicalizerTool tool, ValueNode forX, ValueNode forY) {
+        NodeView view = NodeView.from(tool);
         ValueNode ret = super.canonical(tool, forX, forY);
         if (ret != this) {
             return ret;
         }
 
-        return canonical(this, getArithmeticOp(), stamp(), forX, forY);
+        return canonical(this, getArithmeticOp(), stamp(view), forX, forY, view);
     }
 
-    private static ValueNode canonical(RightShiftNode rightShiftNode, ArithmeticOpTable.ShiftOp<Shr> op, Stamp stamp, ValueNode forX, ValueNode forY) {
+    private static ValueNode canonical(RightShiftNode rightShiftNode, ArithmeticOpTable.ShiftOp<Shr> op, Stamp stamp, ValueNode forX, ValueNode forY, NodeView view) {
         RightShiftNode self = rightShiftNode;
-        if (forX.stamp() instanceof IntegerStamp && ((IntegerStamp) forX.stamp()).isPositive()) {
+        if (forX.stamp(view) instanceof IntegerStamp && ((IntegerStamp) forX.stamp(view)).isPositive()) {
             return new UnsignedRightShiftNode(forX, forY);
         }
 
@@ -87,8 +89,8 @@ public final class RightShiftNode extends ShiftNode<Shr> {
                     if (other instanceof RightShiftNode) {
                         int total = amount + otherAmount;
                         if (total != (total & mask)) {
-                            assert other.getX().stamp() instanceof IntegerStamp;
-                            IntegerStamp istamp = (IntegerStamp) other.getX().stamp();
+                            assert other.getX().stamp(view) instanceof IntegerStamp;
+                            IntegerStamp istamp = (IntegerStamp) other.getX().stamp(view);
 
                             if (istamp.isPositive()) {
                                 return ConstantNode.forIntegerKind(stamp.getStackKind(), 0);
@@ -131,7 +133,7 @@ public final class RightShiftNode extends ShiftNode<Shr> {
              * are all equal to the sign bit of the input. That's equivalent to the condition that
              * the input is in the signed range of the narrow type.
              */
-            IntegerStamp inputStamp = (IntegerStamp) getX().stamp();
+            IntegerStamp inputStamp = (IntegerStamp) getX().stamp(NodeView.DEFAULT);
             return CodeUtil.minValue(resultBits) <= inputStamp.lowerBound() && inputStamp.upperBound() <= CodeUtil.maxValue(resultBits);
         } else {
             return false;
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/ShiftNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/ShiftNode.java
index 1e997bc5b8d..062a861f828 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/ShiftNode.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/ShiftNode.java
@@ -37,6 +37,7 @@ import org.graalvm.compiler.graph.spi.CanonicalizerTool;
 import org.graalvm.compiler.nodeinfo.NodeInfo;
 import org.graalvm.compiler.nodes.ArithmeticOperation;
 import org.graalvm.compiler.nodes.ConstantNode;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.ValueNode;
 import org.graalvm.compiler.nodes.spi.ArithmeticLIRLowerable;
 
@@ -64,13 +65,13 @@ public abstract class ShiftNode<OP> extends BinaryNode implements ArithmeticOper
      * @param s the second input value
      */
     protected ShiftNode(NodeClass<? extends ShiftNode<OP>> c, SerializableShiftFunction<OP> getOp, ValueNode x, ValueNode s) {
-        super(c, getOp.apply(ArithmeticOpTable.forStamp(x.stamp())).foldStamp(x.stamp(), (IntegerStamp) s.stamp()), x, s);
-        assert ((IntegerStamp) s.stamp()).getBits() == 32;
+        super(c, getOp.apply(ArithmeticOpTable.forStamp(x.stamp(NodeView.DEFAULT))).foldStamp(x.stamp(NodeView.DEFAULT), (IntegerStamp) s.stamp(NodeView.DEFAULT)), x, s);
+        assert ((IntegerStamp) s.stamp(NodeView.DEFAULT)).getBits() == 32;
         this.getOp = getOp;
     }
 
     protected final ShiftOp<OP> getOp(ValueNode forValue) {
-        return getOp.apply(ArithmeticOpTable.forStamp(forValue.stamp()));
+        return getOp.apply(ArithmeticOpTable.forStamp(forValue.stamp(NodeView.DEFAULT)));
     }
 
     @Override
@@ -85,14 +86,16 @@ public abstract class ShiftNode<OP> extends BinaryNode implements ArithmeticOper
 
     @Override
     public ValueNode canonical(CanonicalizerTool tool, ValueNode forX, ValueNode forY) {
-        ValueNode valueNode = canonical(getOp(forX), stamp(), forX, forY);
+        NodeView view = NodeView.from(tool);
+        ValueNode valueNode = canonical(getOp(forX), stamp(NodeView.DEFAULT), forX, forY, view);
         if (valueNode != null) {
             return valueNode;
         }
         return this;
     }
 
-    public static <OP> ValueNode canonical(ShiftOp<OP> op, Stamp stamp, ValueNode forX, ValueNode forY) {
+    @SuppressWarnings("unused")
+    public static <OP> ValueNode canonical(ShiftOp<OP> op, Stamp stamp, ValueNode forX, ValueNode forY, NodeView view) {
         if (forX.isConstant() && forY.isConstant()) {
             JavaConstant amount = forY.asJavaConstant();
             assert amount.getJavaKind() == JavaKind.Int;
@@ -102,7 +105,7 @@ public abstract class ShiftNode<OP> extends BinaryNode implements ArithmeticOper
     }
 
     public int getShiftAmountMask() {
-        return getArithmeticOp().getShiftAmountMask(stamp());
+        return getArithmeticOp().getShiftAmountMask(stamp(NodeView.DEFAULT));
     }
 
     @Override
@@ -117,7 +120,7 @@ public abstract class ShiftNode<OP> extends BinaryNode implements ArithmeticOper
          * amount. We can narrow only if (y & wideMask) == (y & narrowMask) for all possible values
          * of y.
          */
-        IntegerStamp yStamp = (IntegerStamp) getY().stamp();
+        IntegerStamp yStamp = (IntegerStamp) getY().stamp(NodeView.DEFAULT);
         return (yStamp.upMask() & (wideMask & ~narrowMask)) == 0;
     }
 }
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/SignExtendNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/SignExtendNode.java
index 65c4a7050e5..a603eda3934 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/SignExtendNode.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/SignExtendNode.java
@@ -34,6 +34,7 @@ import org.graalvm.compiler.graph.NodeClass;
 import org.graalvm.compiler.graph.spi.CanonicalizerTool;
 import org.graalvm.compiler.lir.gen.ArithmeticLIRGeneratorTool;
 import org.graalvm.compiler.nodeinfo.NodeInfo;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.ValueNode;
 import org.graalvm.compiler.nodes.spi.NodeLIRBuilderTool;
 
@@ -46,25 +47,25 @@ public final class SignExtendNode extends IntegerConvertNode<SignExtend, Narrow>
     public static final NodeClass<SignExtendNode> TYPE = NodeClass.create(SignExtendNode.class);
 
     public SignExtendNode(ValueNode input, int resultBits) {
-        this(input, PrimitiveStamp.getBits(input.stamp()), resultBits);
-        assert 0 < PrimitiveStamp.getBits(input.stamp()) && PrimitiveStamp.getBits(input.stamp()) <= resultBits;
+        this(input, PrimitiveStamp.getBits(input.stamp(NodeView.DEFAULT)), resultBits);
+        assert 0 < PrimitiveStamp.getBits(input.stamp(NodeView.DEFAULT)) && PrimitiveStamp.getBits(input.stamp(NodeView.DEFAULT)) <= resultBits;
     }
 
     public SignExtendNode(ValueNode input, int inputBits, int resultBits) {
         super(TYPE, ArithmeticOpTable::getSignExtend, ArithmeticOpTable::getNarrow, inputBits, resultBits, input);
     }
 
-    public static ValueNode create(ValueNode input, int resultBits) {
-        return create(input, PrimitiveStamp.getBits(input.stamp()), resultBits);
+    public static ValueNode create(ValueNode input, int resultBits, NodeView view) {
+        return create(input, PrimitiveStamp.getBits(input.stamp(view)), resultBits, view);
     }
 
-    public static ValueNode create(ValueNode input, int inputBits, int resultBits) {
-        IntegerConvertOp<SignExtend> signExtend = ArithmeticOpTable.forStamp(input.stamp()).getSignExtend();
-        ValueNode synonym = findSynonym(signExtend, input, inputBits, resultBits, signExtend.foldStamp(inputBits, resultBits, input.stamp()));
+    public static ValueNode create(ValueNode input, int inputBits, int resultBits, NodeView view) {
+        IntegerConvertOp<SignExtend> signExtend = ArithmeticOpTable.forStamp(input.stamp(view)).getSignExtend();
+        ValueNode synonym = findSynonym(signExtend, input, inputBits, resultBits, signExtend.foldStamp(inputBits, resultBits, input.stamp(view)));
         if (synonym != null) {
             return synonym;
         }
-        return canonical(null, input, inputBits, resultBits);
+        return canonical(null, input, inputBits, resultBits, view);
     }
 
     @Override
@@ -74,35 +75,36 @@ public final class SignExtendNode extends IntegerConvertNode<SignExtend, Narrow>
 
     @Override
     public ValueNode canonical(CanonicalizerTool tool, ValueNode forValue) {
+        NodeView view = NodeView.from(tool);
         ValueNode ret = super.canonical(tool, forValue);
         if (ret != this) {
             return ret;
         }
 
-        return canonical(this, forValue, getInputBits(), getResultBits());
+        return canonical(this, forValue, getInputBits(), getResultBits(), view);
     }
 
-    private static ValueNode canonical(SignExtendNode self, ValueNode forValue, int inputBits, int resultBits) {
+    private static ValueNode canonical(SignExtendNode self, ValueNode forValue, int inputBits, int resultBits, NodeView view) {
         if (forValue instanceof SignExtendNode) {
             // sxxx -(sign-extend)-> ssss sxxx -(sign-extend)-> ssssssss sssssxxx
             // ==> sxxx -(sign-extend)-> ssssssss sssssxxx
             SignExtendNode other = (SignExtendNode) forValue;
-            return SignExtendNode.create(other.getValue(), other.getInputBits(), resultBits);
+            return SignExtendNode.create(other.getValue(), other.getInputBits(), resultBits, view);
         } else if (forValue instanceof ZeroExtendNode) {
             ZeroExtendNode other = (ZeroExtendNode) forValue;
             if (other.getResultBits() > other.getInputBits()) {
                 // sxxx -(zero-extend)-> 0000 sxxx -(sign-extend)-> 00000000 0000sxxx
                 // ==> sxxx -(zero-extend)-> 00000000 0000sxxx
-                return ZeroExtendNode.create(other.getValue(), other.getInputBits(), resultBits);
+                return ZeroExtendNode.create(other.getValue(), other.getInputBits(), resultBits, view);
             }
         }
 
-        if (forValue.stamp() instanceof IntegerStamp) {
-            IntegerStamp inputStamp = (IntegerStamp) forValue.stamp();
+        if (forValue.stamp(view) instanceof IntegerStamp) {
+            IntegerStamp inputStamp = (IntegerStamp) forValue.stamp(view);
             if ((inputStamp.upMask() & (1L << (inputBits - 1))) == 0L) {
                 // 0xxx -(sign-extend)-> 0000 0xxx
                 // ==> 0xxx -(zero-extend)-> 0000 0xxx
-                return ZeroExtendNode.create(forValue, inputBits, resultBits);
+                return ZeroExtendNode.create(forValue, inputBits, resultBits, view);
             }
         }
 
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/SignedDivNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/SignedDivNode.java
index c8ba664abad..dc206008f60 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/SignedDivNode.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/SignedDivNode.java
@@ -24,10 +24,12 @@ package org.graalvm.compiler.nodes.calc;
 
 import org.graalvm.compiler.core.common.type.IntegerStamp;
 import org.graalvm.compiler.core.common.type.PrimitiveStamp;
+import org.graalvm.compiler.core.common.type.Stamp;
 import org.graalvm.compiler.graph.NodeClass;
 import org.graalvm.compiler.graph.spi.CanonicalizerTool;
 import org.graalvm.compiler.nodeinfo.NodeInfo;
 import org.graalvm.compiler.nodes.ConstantNode;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.ValueNode;
 import org.graalvm.compiler.nodes.spi.LIRLowerable;
 import org.graalvm.compiler.nodes.spi.NodeLIRBuilderTool;
@@ -39,31 +41,42 @@ public class SignedDivNode extends IntegerDivRemNode implements LIRLowerable {
 
     public static final NodeClass<SignedDivNode> TYPE = NodeClass.create(SignedDivNode.class);
 
-    public SignedDivNode(ValueNode x, ValueNode y) {
+    protected SignedDivNode(ValueNode x, ValueNode y) {
         this(TYPE, x, y);
     }
 
     protected SignedDivNode(NodeClass<? extends SignedDivNode> c, ValueNode x, ValueNode y) {
-        super(c, IntegerStamp.OPS.getDiv().foldStamp(x.stamp(), y.stamp()), Op.DIV, Type.SIGNED, x, y);
+        super(c, IntegerStamp.OPS.getDiv().foldStamp(x.stamp(NodeView.DEFAULT), y.stamp(NodeView.DEFAULT)), Op.DIV, Type.SIGNED, x, y);
+    }
+
+    public static ValueNode create(ValueNode x, ValueNode y, NodeView view) {
+        return canonical(null, x, y, view);
     }
 
     @Override
     public boolean inferStamp() {
-        return updateStamp(IntegerStamp.OPS.getDiv().foldStamp(getX().stamp(), getY().stamp()));
+        return updateStamp(IntegerStamp.OPS.getDiv().foldStamp(getX().stamp(NodeView.DEFAULT), getY().stamp(NodeView.DEFAULT)));
     }
 
     @Override
     public ValueNode canonical(CanonicalizerTool tool, ValueNode forX, ValueNode forY) {
+        NodeView view = NodeView.from(tool);
+        return canonical(this, forX, forY, view);
+    }
+
+    public static ValueNode canonical(SignedDivNode self, ValueNode forX, ValueNode forY, NodeView view) {
+        Stamp predictedStamp = IntegerStamp.OPS.getDiv().foldStamp(forX.stamp(NodeView.DEFAULT), forY.stamp(NodeView.DEFAULT));
+        Stamp stamp = self != null ? self.stamp(view) : predictedStamp;
         if (forX.isConstant() && forY.isConstant()) {
-            @SuppressWarnings("hiding")
             long y = forY.asJavaConstant().asLong();
             if (y == 0) {
-                return this; // this will trap, can not canonicalize
+                return self != null ? self : new SignedDivNode(forX, forY); // this will trap, can
+                                                                            // not canonicalize
             }
-            return ConstantNode.forIntegerStamp(stamp(), forX.asJavaConstant().asLong() / y);
+            return ConstantNode.forIntegerStamp(stamp, forX.asJavaConstant().asLong() / y);
         } else if (forY.isConstant()) {
             long c = forY.asJavaConstant().asLong();
-            ValueNode v = canonical(forX, c);
+            ValueNode v = canonical(forX, c, view);
             if (v != null) {
                 return v;
             }
@@ -74,47 +87,47 @@ public class SignedDivNode extends IntegerDivRemNode implements LIRLowerable {
             SubNode integerSubNode = (SubNode) forX;
             if (integerSubNode.getY() instanceof SignedRemNode) {
                 SignedRemNode integerRemNode = (SignedRemNode) integerSubNode.getY();
-                if (integerSubNode.stamp().isCompatible(this.stamp()) && integerRemNode.stamp().isCompatible(this.stamp()) && integerSubNode.getX() == integerRemNode.getX() &&
+                if (integerSubNode.stamp(view).isCompatible(stamp) && integerRemNode.stamp(view).isCompatible(stamp) && integerSubNode.getX() == integerRemNode.getX() &&
                                 forY == integerRemNode.getY()) {
                     SignedDivNode sd = new SignedDivNode(integerSubNode.getX(), forY);
-                    sd.stateBefore = this.stateBefore;
+                    sd.stateBefore = self != null ? self.stateBefore : null;
                     return sd;
                 }
             }
         }
 
-        if (next() instanceof SignedDivNode) {
-            NodeClass<?> nodeClass = getNodeClass();
-            if (next().getClass() == this.getClass() && nodeClass.equalInputs(this, next()) && valueEquals(next())) {
-                return next();
+        if (self != null && self.next() instanceof SignedDivNode) {
+            NodeClass<?> nodeClass = self.getNodeClass();
+            if (self.next().getClass() == self.getClass() && nodeClass.equalInputs(self, self.next()) && self.valueEquals(self.next())) {
+                return self.next();
             }
         }
 
-        return this;
+        return self != null ? self : new SignedDivNode(forX, forY);
     }
 
-    public static ValueNode canonical(ValueNode forX, long c) {
+    public static ValueNode canonical(ValueNode forX, long c, NodeView view) {
         if (c == 1) {
             return forX;
         }
         if (c == -1) {
-            return NegateNode.create(forX);
+            return NegateNode.create(forX, view);
         }
         long abs = Math.abs(c);
-        if (CodeUtil.isPowerOf2(abs) && forX.stamp() instanceof IntegerStamp) {
+        if (CodeUtil.isPowerOf2(abs) && forX.stamp(view) instanceof IntegerStamp) {
             ValueNode dividend = forX;
-            IntegerStamp stampX = (IntegerStamp) forX.stamp();
+            IntegerStamp stampX = (IntegerStamp) forX.stamp(view);
             int log2 = CodeUtil.log2(abs);
             // no rounding if dividend is positive or if its low bits are always 0
             if (stampX.canBeNegative() || (stampX.upMask() & (abs - 1)) != 0) {
-                int bits = PrimitiveStamp.getBits(forX.stamp());
+                int bits = PrimitiveStamp.getBits(forX.stamp(view));
                 RightShiftNode sign = new RightShiftNode(forX, ConstantNode.forInt(bits - 1));
                 UnsignedRightShiftNode round = new UnsignedRightShiftNode(sign, ConstantNode.forInt(bits - log2));
-                dividend = BinaryArithmeticNode.add(dividend, round);
+                dividend = BinaryArithmeticNode.add(dividend, round, view);
             }
             RightShiftNode shift = new RightShiftNode(dividend, ConstantNode.forInt(log2));
             if (c < 0) {
-                return NegateNode.create(shift);
+                return NegateNode.create(shift, view);
             }
             return shift;
         }
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/SignedRemNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/SignedRemNode.java
index 4dbfd845b54..432cb047cd2 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/SignedRemNode.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/SignedRemNode.java
@@ -23,10 +23,12 @@
 package org.graalvm.compiler.nodes.calc;
 
 import org.graalvm.compiler.core.common.type.IntegerStamp;
+import org.graalvm.compiler.core.common.type.Stamp;
 import org.graalvm.compiler.graph.NodeClass;
 import org.graalvm.compiler.graph.spi.CanonicalizerTool;
 import org.graalvm.compiler.nodeinfo.NodeInfo;
 import org.graalvm.compiler.nodes.ConstantNode;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.ValueNode;
 import org.graalvm.compiler.nodes.spi.LIRLowerable;
 import org.graalvm.compiler.nodes.spi.NodeLIRBuilderTool;
@@ -38,52 +40,63 @@ public class SignedRemNode extends IntegerDivRemNode implements LIRLowerable {
 
     public static final NodeClass<SignedRemNode> TYPE = NodeClass.create(SignedRemNode.class);
 
-    public SignedRemNode(ValueNode x, ValueNode y) {
+    protected SignedRemNode(ValueNode x, ValueNode y) {
         this(TYPE, x, y);
     }
 
     protected SignedRemNode(NodeClass<? extends SignedRemNode> c, ValueNode x, ValueNode y) {
-        super(c, IntegerStamp.OPS.getRem().foldStamp(x.stamp(), y.stamp()), Op.REM, Type.SIGNED, x, y);
+        super(c, IntegerStamp.OPS.getRem().foldStamp(x.stamp(NodeView.DEFAULT), y.stamp(NodeView.DEFAULT)), Op.REM, Type.SIGNED, x, y);
+    }
+
+    public static ValueNode create(ValueNode x, ValueNode y, NodeView view) {
+        Stamp stamp = IntegerStamp.OPS.getRem().foldStamp(x.stamp(view), y.stamp(view));
+        return canonical(null, x, y, stamp, view);
     }
 
     @Override
     public boolean inferStamp() {
-        return updateStamp(IntegerStamp.OPS.getRem().foldStamp(getX().stamp(), getY().stamp()));
+        return updateStamp(IntegerStamp.OPS.getRem().foldStamp(getX().stamp(NodeView.DEFAULT), getY().stamp(NodeView.DEFAULT)));
     }
 
     @Override
     public ValueNode canonical(CanonicalizerTool tool, ValueNode forX, ValueNode forY) {
+        NodeView view = NodeView.from(tool);
+        return canonical(this, forX, forY, stamp(view), view);
+    }
+
+    private static ValueNode canonical(SignedRemNode self, ValueNode forX, ValueNode forY, Stamp stamp, NodeView view) {
         if (forX.isConstant() && forY.isConstant()) {
-            @SuppressWarnings("hiding")
             long y = forY.asJavaConstant().asLong();
             if (y == 0) {
-                return this; // this will trap, can not canonicalize
+                return self != null ? self : new SignedRemNode(forX, forY); // this will trap, can
+                                                                            // not canonicalize
             }
-            return ConstantNode.forIntegerStamp(stamp(), forX.asJavaConstant().asLong() % y);
-        } else if (forY.isConstant() && forX.stamp() instanceof IntegerStamp && forY.stamp() instanceof IntegerStamp) {
+            return ConstantNode.forIntegerStamp(stamp, forX.asJavaConstant().asLong() % y);
+        } else if (forY.isConstant() && forX.stamp(view) instanceof IntegerStamp && forY.stamp(view) instanceof IntegerStamp) {
             long constY = forY.asJavaConstant().asLong();
-            IntegerStamp xStamp = (IntegerStamp) forX.stamp();
-            IntegerStamp yStamp = (IntegerStamp) forY.stamp();
+            IntegerStamp xStamp = (IntegerStamp) forX.stamp(view);
+            IntegerStamp yStamp = (IntegerStamp) forY.stamp(view);
             if (constY < 0 && constY != CodeUtil.minValue(yStamp.getBits())) {
-                return new SignedRemNode(forX, ConstantNode.forIntegerStamp(yStamp, -constY)).canonical(tool);
+                Stamp newStamp = IntegerStamp.OPS.getRem().foldStamp(forX.stamp(view), forY.stamp(view));
+                return canonical(null, forX, ConstantNode.forIntegerStamp(yStamp, -constY), newStamp, view);
             }
 
             if (constY == 1) {
-                return ConstantNode.forIntegerStamp(stamp(), 0);
+                return ConstantNode.forIntegerStamp(stamp, 0);
             } else if (CodeUtil.isPowerOf2(constY)) {
                 if (xStamp.isPositive()) {
                     // x & (y - 1)
-                    return new AndNode(forX, ConstantNode.forIntegerStamp(stamp(), constY - 1));
+                    return new AndNode(forX, ConstantNode.forIntegerStamp(stamp, constY - 1));
                 } else if (xStamp.isNegative()) {
                     // -((-x) & (y - 1))
-                    return new NegateNode(new AndNode(new NegateNode(forX), ConstantNode.forIntegerStamp(stamp(), constY - 1)));
+                    return new NegateNode(new AndNode(new NegateNode(forX), ConstantNode.forIntegerStamp(stamp, constY - 1)));
                 } else {
                     // x - ((x / y) << log2(y))
-                    return SubNode.create(forX, LeftShiftNode.create(SignedDivNode.canonical(forX, constY), ConstantNode.forInt(CodeUtil.log2(constY))));
+                    return SubNode.create(forX, LeftShiftNode.create(SignedDivNode.canonical(forX, constY, view), ConstantNode.forInt(CodeUtil.log2(constY)), view), view);
                 }
             }
         }
-        return this;
+        return self != null ? self : new SignedRemNode(forX, forY);
     }
 
     @Override
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/SqrtNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/SqrtNode.java
index 8b400619d2a..ae80f135ca4 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/SqrtNode.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/SqrtNode.java
@@ -30,6 +30,8 @@ import org.graalvm.compiler.core.common.type.ArithmeticOpTable.UnaryOp.Sqrt;
 import org.graalvm.compiler.graph.NodeClass;
 import org.graalvm.compiler.lir.gen.ArithmeticLIRGeneratorTool;
 import org.graalvm.compiler.nodeinfo.NodeInfo;
+import org.graalvm.compiler.nodes.ConstantNode;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.ValueNode;
 import org.graalvm.compiler.nodes.spi.ArithmeticLIRLowerable;
 import org.graalvm.compiler.nodes.spi.NodeLIRBuilderTool;
@@ -42,10 +44,18 @@ public final class SqrtNode extends UnaryArithmeticNode<Sqrt> implements Arithme
 
     public static final NodeClass<SqrtNode> TYPE = NodeClass.create(SqrtNode.class);
 
-    public SqrtNode(ValueNode x) {
+    protected SqrtNode(ValueNode x) {
         super(TYPE, ArithmeticOpTable::getSqrt, x);
     }
 
+    public static ValueNode create(ValueNode x, NodeView view) {
+        if (x.isConstant()) {
+            ArithmeticOpTable.UnaryOp<Sqrt> op = ArithmeticOpTable.forStamp(x.stamp(view)).getSqrt();
+            return ConstantNode.forPrimitive(op.foldStamp(x.stamp(view)), op.foldConstant(x.asConstant()));
+        }
+        return new SqrtNode(x);
+    }
+
     @Override
     public void generate(NodeLIRBuilderTool nodeValueMap, ArithmeticLIRGeneratorTool gen) {
         nodeValueMap.setResult(this, gen.emitMathSqrt(nodeValueMap.operand(getValue())));
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/SubNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/SubNode.java
index bfeca0be95c..f6fe0ab5e4c 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/SubNode.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/SubNode.java
@@ -33,6 +33,7 @@ import org.graalvm.compiler.graph.spi.CanonicalizerTool;
 import org.graalvm.compiler.lir.gen.ArithmeticLIRGeneratorTool;
 import org.graalvm.compiler.nodeinfo.NodeInfo;
 import org.graalvm.compiler.nodes.ConstantNode;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.ValueNode;
 import org.graalvm.compiler.nodes.spi.NodeLIRBuilderTool;
 import org.graalvm.compiler.nodes.util.GraphUtil;
@@ -53,20 +54,20 @@ public class SubNode extends BinaryArithmeticNode<Sub> implements NarrowableArit
         super(c, ArithmeticOpTable::getSub, x, y);
     }
 
-    public static ValueNode create(ValueNode x, ValueNode y) {
-        BinaryOp<Sub> op = ArithmeticOpTable.forStamp(x.stamp()).getSub();
-        Stamp stamp = op.foldStamp(x.stamp(), y.stamp());
-        ConstantNode tryConstantFold = tryConstantFold(op, x, y, stamp);
+    public static ValueNode create(ValueNode x, ValueNode y, NodeView view) {
+        BinaryOp<Sub> op = ArithmeticOpTable.forStamp(x.stamp(view)).getSub();
+        Stamp stamp = op.foldStamp(x.stamp(view), y.stamp(view));
+        ConstantNode tryConstantFold = tryConstantFold(op, x, y, stamp, view);
         if (tryConstantFold != null) {
             return tryConstantFold;
         }
-        return canonical(null, op, stamp, x, y);
+        return canonical(null, op, stamp, x, y, view);
     }
 
-    private static ValueNode canonical(SubNode subNode, BinaryOp<Sub> op, Stamp stamp, ValueNode forX, ValueNode forY) {
+    private static ValueNode canonical(SubNode subNode, BinaryOp<Sub> op, Stamp stamp, ValueNode forX, ValueNode forY, NodeView view) {
         SubNode self = subNode;
         if (GraphUtil.unproxify(forX) == GraphUtil.unproxify(forY)) {
-            Constant zero = op.getZero(forX.stamp());
+            Constant zero = op.getZero(forX.stamp(view));
             if (zero != null) {
                 return ConstantNode.forPrimitive(stamp, zero);
             }
@@ -87,18 +88,18 @@ public class SubNode extends BinaryArithmeticNode<Sub> implements NarrowableArit
                 SubNode x = (SubNode) forX;
                 if (x.getX() == forY) {
                     // (a - b) - a
-                    return NegateNode.create(x.getY());
+                    return NegateNode.create(x.getY(), view);
                 }
             }
             if (forY instanceof AddNode) {
                 AddNode y = (AddNode) forY;
                 if (y.getX() == forX) {
                     // a - (a + b)
-                    return NegateNode.create(y.getY());
+                    return NegateNode.create(y.getY(), view);
                 }
                 if (y.getY() == forX) {
                     // b - (a + b)
-                    return NegateNode.create(y.getX());
+                    return NegateNode.create(y.getX(), view);
                 }
             } else if (forY instanceof SubNode) {
                 SubNode y = (SubNode) forY;
@@ -114,7 +115,7 @@ public class SubNode extends BinaryArithmeticNode<Sub> implements NarrowableArit
                 return forX;
             }
             if (associative && self != null) {
-                ValueNode reassociated = reassociate(self, ValueNode.isConstantPredicate(), forX, forY);
+                ValueNode reassociated = reassociate(self, ValueNode.isConstantPredicate(), forX, forY, view);
                 if (reassociated != self) {
                     return reassociated;
                 }
@@ -124,7 +125,7 @@ public class SubNode extends BinaryArithmeticNode<Sub> implements NarrowableArit
                 if (i < 0 || ((IntegerStamp) StampFactory.forKind(forY.getStackKind())).contains(-i)) {
                     // Adding a negative is more friendly to the backend since adds are
                     // commutative, so prefer add when it fits.
-                    return BinaryArithmeticNode.add(forX, ConstantNode.forIntegerStamp(stamp, -i));
+                    return BinaryArithmeticNode.add(forX, ConstantNode.forIntegerStamp(stamp, -i), view);
                 }
             }
         } else if (forX.isConstant()) {
@@ -135,30 +136,28 @@ public class SubNode extends BinaryArithmeticNode<Sub> implements NarrowableArit
                  * have to test for the neutral element of +, because we are doing this
                  * transformation: 0 - x == (-x) + 0 == -x.
                  */
-                return NegateNode.create(forY);
+                return NegateNode.create(forY, view);
             }
             if (associative && self != null) {
-                return reassociate(self, ValueNode.isConstantPredicate(), forX, forY);
+                return reassociate(self, ValueNode.isConstantPredicate(), forX, forY, view);
             }
         }
         if (forY instanceof NegateNode) {
-            return BinaryArithmeticNode.add(forX, ((NegateNode) forY).getValue());
+            return BinaryArithmeticNode.add(forX, ((NegateNode) forY).getValue(), view);
         }
-        if (self == null) {
-            self = new SubNode(forX, forY);
-        }
-        return self;
+        return self != null ? self : new SubNode(forX, forY);
     }
 
     @Override
     public ValueNode canonical(CanonicalizerTool tool, ValueNode forX, ValueNode forY) {
+        NodeView view = NodeView.from(tool);
         ValueNode ret = super.canonical(tool, forX, forY);
         if (ret != this) {
             return ret;
         }
 
         BinaryOp<Sub> op = getOp(forX, forY);
-        return canonical(this, op, stamp, forX, forY);
+        return canonical(this, op, stamp, forX, forY, view);
     }
 
     @Override
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/UnaryArithmeticNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/UnaryArithmeticNode.java
index 7f3d019888b..d2f0418d6d8 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/UnaryArithmeticNode.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/UnaryArithmeticNode.java
@@ -33,6 +33,7 @@ import org.graalvm.compiler.graph.spi.CanonicalizerTool;
 import org.graalvm.compiler.nodeinfo.NodeInfo;
 import org.graalvm.compiler.nodes.ArithmeticOperation;
 import org.graalvm.compiler.nodes.ConstantNode;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.ValueNode;
 import org.graalvm.compiler.nodes.spi.ArithmeticLIRLowerable;
 
@@ -47,12 +48,12 @@ public abstract class UnaryArithmeticNode<OP> extends UnaryNode implements Arith
     protected final SerializableUnaryFunction<OP> getOp;
 
     protected UnaryArithmeticNode(NodeClass<? extends UnaryArithmeticNode<OP>> c, SerializableUnaryFunction<OP> getOp, ValueNode value) {
-        super(c, getOp.apply(ArithmeticOpTable.forStamp(value.stamp())).foldStamp(value.stamp()), value);
+        super(c, getOp.apply(ArithmeticOpTable.forStamp(value.stamp(NodeView.DEFAULT))).foldStamp(value.stamp(NodeView.DEFAULT)), value);
         this.getOp = getOp;
     }
 
     protected final UnaryOp<OP> getOp(ValueNode forValue) {
-        return getOp.apply(ArithmeticOpTable.forStamp(forValue.stamp()));
+        return getOp.apply(ArithmeticOpTable.forStamp(forValue.stamp(NodeView.DEFAULT)));
     }
 
     @Override
@@ -62,7 +63,7 @@ public abstract class UnaryArithmeticNode<OP> extends UnaryNode implements Arith
 
     @Override
     public Stamp foldStamp(Stamp newStamp) {
-        assert newStamp.isCompatible(getValue().stamp());
+        assert newStamp.isCompatible(getValue().stamp(NodeView.DEFAULT));
         return getOp(getValue()).foldStamp(newStamp);
     }
 
@@ -77,7 +78,7 @@ public abstract class UnaryArithmeticNode<OP> extends UnaryNode implements Arith
 
     protected static <OP> ValueNode findSynonym(ValueNode forValue, UnaryOp<OP> op) {
         if (forValue.isConstant()) {
-            return ConstantNode.forPrimitive(op.foldStamp(forValue.stamp()), op.foldConstant(forValue.asConstant()));
+            return ConstantNode.forPrimitive(op.foldStamp(forValue.stamp(NodeView.DEFAULT)), op.foldConstant(forValue.asConstant()));
         }
         return null;
     }
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/UnaryNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/UnaryNode.java
index 66a8ee32342..e09f50788a2 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/UnaryNode.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/UnaryNode.java
@@ -28,6 +28,7 @@ import org.graalvm.compiler.core.common.type.Stamp;
 import org.graalvm.compiler.graph.NodeClass;
 import org.graalvm.compiler.graph.spi.Canonicalizable;
 import org.graalvm.compiler.nodeinfo.NodeInfo;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.ValueNode;
 
 /**
@@ -63,7 +64,7 @@ public abstract class UnaryNode extends FloatingNode implements Canonicalizable.
 
     @Override
     public boolean inferStamp() {
-        return updateStamp(foldStamp(value.stamp()));
+        return updateStamp(foldStamp(value.stamp(NodeView.DEFAULT)));
     }
 
     /**
@@ -74,6 +75,6 @@ public abstract class UnaryNode extends FloatingNode implements Canonicalizable.
      * @param newStamp
      */
     public Stamp foldStamp(Stamp newStamp) {
-        return stamp();
+        return stamp(NodeView.DEFAULT);
     }
 }
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/UnpackEndianHalfNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/UnpackEndianHalfNode.java
index ad7cfdc9932..dadd326bf12 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/UnpackEndianHalfNode.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/UnpackEndianHalfNode.java
@@ -31,6 +31,7 @@ import org.graalvm.compiler.graph.spi.CanonicalizerTool;
 import org.graalvm.compiler.nodeinfo.NodeCycles;
 import org.graalvm.compiler.nodeinfo.NodeInfo;
 import org.graalvm.compiler.nodes.ConstantNode;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.ValueNode;
 import org.graalvm.compiler.nodes.spi.Lowerable;
 import org.graalvm.compiler.nodes.spi.LoweringTool;
@@ -52,7 +53,8 @@ public final class UnpackEndianHalfNode extends UnaryNode implements Lowerable {
         this.firstHalf = firstHalf;
     }
 
-    public static ValueNode create(ValueNode value, boolean firstHalf) {
+    @SuppressWarnings("unused")
+    public static ValueNode create(ValueNode value, boolean firstHalf, NodeView view) {
         if (value.isConstant() && value.asConstant().isDefaultForKind()) {
             return ConstantNode.defaultForKind(JavaKind.Int);
         }
@@ -84,7 +86,7 @@ public final class UnpackEndianHalfNode extends UnaryNode implements Lowerable {
         if ((byteOrder == ByteOrder.BIG_ENDIAN) == firstHalf) {
             result = graph().unique(new UnsignedRightShiftNode(result, ConstantNode.forInt(32, graph())));
         }
-        result = IntegerConvertNode.convert(result, StampFactory.forKind(JavaKind.Int), graph());
+        result = IntegerConvertNode.convert(result, StampFactory.forKind(JavaKind.Int), graph(), NodeView.DEFAULT);
         replaceAtUsagesAndDelete(result);
     }
 }
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/UnsignedDivNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/UnsignedDivNode.java
index 313407800a8..792e4388a5d 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/UnsignedDivNode.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/UnsignedDivNode.java
@@ -23,10 +23,12 @@
 package org.graalvm.compiler.nodes.calc;
 
 import org.graalvm.compiler.core.common.type.IntegerStamp;
+import org.graalvm.compiler.core.common.type.Stamp;
 import org.graalvm.compiler.graph.NodeClass;
 import org.graalvm.compiler.graph.spi.CanonicalizerTool;
 import org.graalvm.compiler.nodeinfo.NodeInfo;
 import org.graalvm.compiler.nodes.ConstantNode;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.ValueNode;
 import org.graalvm.compiler.nodes.spi.LIRLowerable;
 import org.graalvm.compiler.nodes.spi.NodeLIRBuilderTool;
@@ -43,18 +45,30 @@ public class UnsignedDivNode extends IntegerDivRemNode implements LIRLowerable {
     }
 
     protected UnsignedDivNode(NodeClass<? extends UnsignedDivNode> c, ValueNode x, ValueNode y) {
-        super(c, x.stamp().unrestricted(), Op.DIV, Type.UNSIGNED, x, y);
+        super(c, x.stamp(NodeView.DEFAULT).unrestricted(), Op.DIV, Type.UNSIGNED, x, y);
+    }
+
+    public static ValueNode create(ValueNode x, ValueNode y, NodeView view) {
+        Stamp stamp = x.stamp(view).unrestricted();
+        return canonical(null, x, y, stamp, view);
     }
 
     @Override
     public ValueNode canonical(CanonicalizerTool tool, ValueNode forX, ValueNode forY) {
-        int bits = ((IntegerStamp) stamp()).getBits();
+        NodeView view = NodeView.from(tool);
+        return canonical(this, forX, forY, stamp(view), view);
+    }
+
+    @SuppressWarnings("unused")
+    private static ValueNode canonical(UnsignedDivNode self, ValueNode forX, ValueNode forY, Stamp stamp, NodeView view) {
+        int bits = ((IntegerStamp) stamp).getBits();
         if (forX.isConstant() && forY.isConstant()) {
             long yConst = CodeUtil.zeroExtend(forY.asJavaConstant().asLong(), bits);
             if (yConst == 0) {
-                return this; // this will trap, cannot canonicalize
+                return self != null ? self : new UnsignedDivNode(forX, forY); // this will trap,
+                                                                              // cannot canonicalize
             }
-            return ConstantNode.forIntegerStamp(stamp(), Long.divideUnsigned(CodeUtil.zeroExtend(forX.asJavaConstant().asLong(), bits), yConst));
+            return ConstantNode.forIntegerStamp(stamp, Long.divideUnsigned(CodeUtil.zeroExtend(forX.asJavaConstant().asLong(), bits), yConst));
         } else if (forY.isConstant()) {
             long c = CodeUtil.zeroExtend(forY.asJavaConstant().asLong(), bits);
             if (c == 1) {
@@ -64,7 +78,7 @@ public class UnsignedDivNode extends IntegerDivRemNode implements LIRLowerable {
                 return new UnsignedRightShiftNode(forX, ConstantNode.forInt(CodeUtil.log2(c)));
             }
         }
-        return this;
+        return self != null ? self : new UnsignedDivNode(forX, forY);
     }
 
     @Override
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/UnsignedRemNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/UnsignedRemNode.java
index 849324c4263..bfba2c43283 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/UnsignedRemNode.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/UnsignedRemNode.java
@@ -23,10 +23,12 @@
 package org.graalvm.compiler.nodes.calc;
 
 import org.graalvm.compiler.core.common.type.IntegerStamp;
+import org.graalvm.compiler.core.common.type.Stamp;
 import org.graalvm.compiler.graph.NodeClass;
 import org.graalvm.compiler.graph.spi.CanonicalizerTool;
 import org.graalvm.compiler.nodeinfo.NodeInfo;
 import org.graalvm.compiler.nodes.ConstantNode;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.ValueNode;
 import org.graalvm.compiler.nodes.spi.LIRLowerable;
 import org.graalvm.compiler.nodes.spi.NodeLIRBuilderTool;
@@ -43,27 +45,39 @@ public class UnsignedRemNode extends IntegerDivRemNode implements LIRLowerable {
     }
 
     protected UnsignedRemNode(NodeClass<? extends UnsignedRemNode> c, ValueNode x, ValueNode y) {
-        super(c, x.stamp().unrestricted(), Op.REM, Type.UNSIGNED, x, y);
+        super(c, x.stamp(NodeView.DEFAULT).unrestricted(), Op.REM, Type.UNSIGNED, x, y);
+    }
+
+    public static ValueNode create(ValueNode x, ValueNode y, NodeView view) {
+        Stamp stamp = x.stamp(view).unrestricted();
+        return canonical(null, x, y, stamp, view);
     }
 
     @Override
     public ValueNode canonical(CanonicalizerTool tool, ValueNode forX, ValueNode forY) {
-        int bits = ((IntegerStamp) stamp()).getBits();
+        NodeView view = NodeView.from(tool);
+        return canonical(this, forX, forY, stamp(view), view);
+    }
+
+    @SuppressWarnings("unused")
+    public static ValueNode canonical(UnsignedRemNode self, ValueNode forX, ValueNode forY, Stamp stamp, NodeView view) {
+        int bits = ((IntegerStamp) stamp).getBits();
         if (forX.isConstant() && forY.isConstant()) {
             long yConst = CodeUtil.zeroExtend(forY.asJavaConstant().asLong(), bits);
             if (yConst == 0) {
-                return this; // this will trap, cannot canonicalize
+                return self != null ? self : new UnsignedRemNode(forX, forY); // this will trap,
+                                                                              // cannot canonicalize
             }
-            return ConstantNode.forIntegerStamp(stamp(), Long.remainderUnsigned(CodeUtil.zeroExtend(forX.asJavaConstant().asLong(), bits), yConst));
+            return ConstantNode.forIntegerStamp(stamp, Long.remainderUnsigned(CodeUtil.zeroExtend(forX.asJavaConstant().asLong(), bits), yConst));
         } else if (forY.isConstant()) {
             long c = CodeUtil.zeroExtend(forY.asJavaConstant().asLong(), bits);
             if (c == 1) {
-                return ConstantNode.forIntegerStamp(stamp(), 0);
+                return ConstantNode.forIntegerStamp(stamp, 0);
             } else if (CodeUtil.isPowerOf2(c)) {
-                return new AndNode(forX, ConstantNode.forIntegerStamp(stamp(), c - 1));
+                return new AndNode(forX, ConstantNode.forIntegerStamp(stamp, c - 1));
             }
         }
-        return this;
+        return self != null ? self : new UnsignedRemNode(forX, forY);
     }
 
     @Override
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/UnsignedRightShiftNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/UnsignedRightShiftNode.java
index 7845b059401..7edf7d4ec20 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/UnsignedRightShiftNode.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/UnsignedRightShiftNode.java
@@ -25,11 +25,13 @@ package org.graalvm.compiler.nodes.calc;
 import org.graalvm.compiler.core.common.type.ArithmeticOpTable;
 import org.graalvm.compiler.core.common.type.ArithmeticOpTable.ShiftOp.UShr;
 import org.graalvm.compiler.core.common.type.IntegerStamp;
+import org.graalvm.compiler.core.common.type.Stamp;
 import org.graalvm.compiler.graph.NodeClass;
 import org.graalvm.compiler.graph.spi.CanonicalizerTool;
 import org.graalvm.compiler.lir.gen.ArithmeticLIRGeneratorTool;
 import org.graalvm.compiler.nodeinfo.NodeInfo;
 import org.graalvm.compiler.nodes.ConstantNode;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.ValueNode;
 import org.graalvm.compiler.nodes.spi.NodeLIRBuilderTool;
 
@@ -44,17 +46,34 @@ public final class UnsignedRightShiftNode extends ShiftNode<UShr> {
         super(TYPE, ArithmeticOpTable::getUShr, x, y);
     }
 
+    public static ValueNode create(ValueNode x, ValueNode y, NodeView view) {
+        ArithmeticOpTable.ShiftOp<UShr> op = ArithmeticOpTable.forStamp(x.stamp(view)).getUShr();
+        Stamp stamp = op.foldStamp(x.stamp(view), (IntegerStamp) y.stamp(view));
+        ValueNode value = ShiftNode.canonical(op, stamp, x, y, view);
+        if (value != null) {
+            return value;
+        }
+
+        return canonical(null, op, stamp, x, y, view);
+    }
+
     @Override
     public ValueNode canonical(CanonicalizerTool tool, ValueNode forX, ValueNode forY) {
+        NodeView view = NodeView.from(tool);
         ValueNode ret = super.canonical(tool, forX, forY);
         if (ret != this) {
             return ret;
         }
 
+        return canonical(this, this.getArithmeticOp(), this.stamp(view), forX, forY, view);
+    }
+
+    @SuppressWarnings("unused")
+    private static ValueNode canonical(UnsignedRightShiftNode node, ArithmeticOpTable.ShiftOp<UShr> op, Stamp stamp, ValueNode forX, ValueNode forY, NodeView view) {
         if (forY.isConstant()) {
             int amount = forY.asJavaConstant().asInt();
             int originalAmout = amount;
-            int mask = getShiftAmountMask();
+            int mask = op.getShiftAmountMask(stamp);
             amount &= mask;
             if (amount == 0) {
                 return forX;
@@ -66,14 +85,14 @@ public final class UnsignedRightShiftNode extends ShiftNode<UShr> {
                     if (other instanceof UnsignedRightShiftNode) {
                         int total = amount + otherAmount;
                         if (total != (total & mask)) {
-                            return ConstantNode.forIntegerKind(getStackKind(), 0);
+                            return ConstantNode.forIntegerKind(stamp.getStackKind(), 0);
                         }
                         return new UnsignedRightShiftNode(other.getX(), ConstantNode.forInt(total));
                     } else if (other instanceof LeftShiftNode && otherAmount == amount) {
-                        if (getStackKind() == JavaKind.Long) {
+                        if (stamp.getStackKind() == JavaKind.Long) {
                             return new AndNode(other.getX(), ConstantNode.forLong(-1L >>> amount));
                         } else {
-                            assert getStackKind() == JavaKind.Int;
+                            assert stamp.getStackKind() == JavaKind.Int;
                             return new AndNode(other.getX(), ConstantNode.forInt(-1 >>> amount));
                         }
                     }
@@ -83,7 +102,11 @@ public final class UnsignedRightShiftNode extends ShiftNode<UShr> {
                 return new UnsignedRightShiftNode(forX, ConstantNode.forInt(amount));
             }
         }
-        return this;
+
+        if (node != null) {
+            return node;
+        }
+        return new UnsignedRightShiftNode(forX, forY);
     }
 
     @Override
@@ -98,7 +121,7 @@ public final class UnsignedRightShiftNode extends ShiftNode<UShr> {
              * For unsigned right shifts, the narrow can be done before the shift if the cut off
              * bits are all zero.
              */
-            IntegerStamp inputStamp = (IntegerStamp) getX().stamp();
+            IntegerStamp inputStamp = (IntegerStamp) getX().stamp(NodeView.DEFAULT);
             return (inputStamp.upMask() & ~(resultBits - 1)) == 0;
         } else {
             return false;
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/XorNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/XorNode.java
index 93d33ada9f2..a204f24d93c 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/XorNode.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/XorNode.java
@@ -33,6 +33,7 @@ import org.graalvm.compiler.graph.spi.CanonicalizerTool;
 import org.graalvm.compiler.lir.gen.ArithmeticLIRGeneratorTool;
 import org.graalvm.compiler.nodeinfo.NodeInfo;
 import org.graalvm.compiler.nodes.ConstantNode;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.ValueNode;
 import org.graalvm.compiler.nodes.spi.NodeLIRBuilderTool;
 import org.graalvm.compiler.nodes.util.GraphUtil;
@@ -48,17 +49,17 @@ public final class XorNode extends BinaryArithmeticNode<Xor> implements BinaryCo
 
     public XorNode(ValueNode x, ValueNode y) {
         super(TYPE, ArithmeticOpTable::getXor, x, y);
-        assert x.stamp().isCompatible(y.stamp());
+        assert x.stamp(NodeView.DEFAULT).isCompatible(y.stamp(NodeView.DEFAULT));
     }
 
-    public static ValueNode create(ValueNode x, ValueNode y) {
-        BinaryOp<Xor> op = ArithmeticOpTable.forStamp(x.stamp()).getXor();
-        Stamp stamp = op.foldStamp(x.stamp(), y.stamp());
-        ConstantNode tryConstantFold = tryConstantFold(op, x, y, stamp);
+    public static ValueNode create(ValueNode x, ValueNode y, NodeView view) {
+        BinaryOp<Xor> op = ArithmeticOpTable.forStamp(x.stamp(view)).getXor();
+        Stamp stamp = op.foldStamp(x.stamp(view), y.stamp(view));
+        ConstantNode tryConstantFold = tryConstantFold(op, x, y, stamp, view);
         if (tryConstantFold != null) {
             return tryConstantFold;
         }
-        return canonical(null, op, stamp, x, y);
+        return canonical(null, op, stamp, x, y, view);
     }
 
     @Override
@@ -68,12 +69,13 @@ public final class XorNode extends BinaryArithmeticNode<Xor> implements BinaryCo
             return ret;
         }
 
-        return canonical(this, getOp(forX, forY), stamp(), forX, forY);
+        NodeView view = NodeView.from(tool);
+        return canonical(this, getOp(forX, forY), stamp(NodeView.DEFAULT), forX, forY, view);
     }
 
-    private static ValueNode canonical(XorNode self, BinaryOp<Xor> op, Stamp stamp, ValueNode forX, ValueNode forY) {
+    private static ValueNode canonical(XorNode self, BinaryOp<Xor> op, Stamp stamp, ValueNode forX, ValueNode forY, NodeView view) {
         if (GraphUtil.unproxify(forX) == GraphUtil.unproxify(forY)) {
-            return ConstantNode.forPrimitive(stamp, op.getZero(forX.stamp()));
+            return ConstantNode.forPrimitive(stamp, op.getZero(forX.stamp(view)));
         }
         if (forX.isConstant() && !forY.isConstant()) {
             return new XorNode(forY, forX);
@@ -91,7 +93,7 @@ public final class XorNode extends BinaryArithmeticNode<Xor> implements BinaryCo
                     return new NotNode(forX);
                 }
             }
-            return reassociate(self != null ? self : (XorNode) new XorNode(forX, forY).maybeCommuteInputs(), ValueNode.isConstantPredicate(), forX, forY);
+            return reassociate(self != null ? self : (XorNode) new XorNode(forX, forY).maybeCommuteInputs(), ValueNode.isConstantPredicate(), forX, forY, view);
         }
         return self != null ? self : new XorNode(forX, forY).maybeCommuteInputs();
     }
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/ZeroExtendNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/ZeroExtendNode.java
index 83b5b3a4471..8574c816fca 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/ZeroExtendNode.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/ZeroExtendNode.java
@@ -36,6 +36,7 @@ import org.graalvm.compiler.graph.NodeClass;
 import org.graalvm.compiler.graph.spi.CanonicalizerTool;
 import org.graalvm.compiler.lir.gen.ArithmeticLIRGeneratorTool;
 import org.graalvm.compiler.nodeinfo.NodeInfo;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.ValueNode;
 import org.graalvm.compiler.nodes.spi.NodeLIRBuilderTool;
 
@@ -50,25 +51,25 @@ public final class ZeroExtendNode extends IntegerConvertNode<ZeroExtend, Narrow>
     public static final NodeClass<ZeroExtendNode> TYPE = NodeClass.create(ZeroExtendNode.class);
 
     public ZeroExtendNode(ValueNode input, int resultBits) {
-        this(input, PrimitiveStamp.getBits(input.stamp()), resultBits);
-        assert 0 < PrimitiveStamp.getBits(input.stamp()) && PrimitiveStamp.getBits(input.stamp()) <= resultBits;
+        this(input, PrimitiveStamp.getBits(input.stamp(NodeView.DEFAULT)), resultBits);
+        assert 0 < PrimitiveStamp.getBits(input.stamp(NodeView.DEFAULT)) && PrimitiveStamp.getBits(input.stamp(NodeView.DEFAULT)) <= resultBits;
     }
 
     public ZeroExtendNode(ValueNode input, int inputBits, int resultBits) {
         super(TYPE, ArithmeticOpTable::getZeroExtend, ArithmeticOpTable::getNarrow, inputBits, resultBits, input);
     }
 
-    public static ValueNode create(ValueNode input, int resultBits) {
-        return create(input, PrimitiveStamp.getBits(input.stamp()), resultBits);
+    public static ValueNode create(ValueNode input, int resultBits, NodeView view) {
+        return create(input, PrimitiveStamp.getBits(input.stamp(view)), resultBits, view);
     }
 
-    public static ValueNode create(ValueNode input, int inputBits, int resultBits) {
-        IntegerConvertOp<ZeroExtend> signExtend = ArithmeticOpTable.forStamp(input.stamp()).getZeroExtend();
-        ValueNode synonym = findSynonym(signExtend, input, inputBits, resultBits, signExtend.foldStamp(inputBits, resultBits, input.stamp()));
+    public static ValueNode create(ValueNode input, int inputBits, int resultBits, NodeView view) {
+        IntegerConvertOp<ZeroExtend> signExtend = ArithmeticOpTable.forStamp(input.stamp(view)).getZeroExtend();
+        ValueNode synonym = findSynonym(signExtend, input, inputBits, resultBits, signExtend.foldStamp(inputBits, resultBits, input.stamp(view)));
         if (synonym != null) {
             return synonym;
         }
-        return canonical(null, input, inputBits, resultBits);
+        return canonical(null, input, inputBits, resultBits, view);
     }
 
     @Override
@@ -91,15 +92,16 @@ public final class ZeroExtendNode extends IntegerConvertNode<ZeroExtend, Narrow>
 
     @Override
     public ValueNode canonical(CanonicalizerTool tool, ValueNode forValue) {
+        NodeView view = NodeView.from(tool);
         ValueNode ret = super.canonical(tool, forValue);
         if (ret != this) {
             return ret;
         }
 
-        return canonical(this, forValue, getInputBits(), getResultBits());
+        return canonical(this, forValue, getInputBits(), getResultBits(), view);
     }
 
-    private static ValueNode canonical(ZeroExtendNode zeroExtendNode, ValueNode forValue, int inputBits, int resultBits) {
+    private static ValueNode canonical(ZeroExtendNode zeroExtendNode, ValueNode forValue, int inputBits, int resultBits, NodeView view) {
         ZeroExtendNode self = zeroExtendNode;
         if (forValue instanceof ZeroExtendNode) {
             // xxxx -(zero-extend)-> 0000 xxxx -(zero-extend)-> 00000000 0000xxxx
@@ -109,20 +111,20 @@ public final class ZeroExtendNode extends IntegerConvertNode<ZeroExtend, Narrow>
         }
         if (forValue instanceof NarrowNode) {
             NarrowNode narrow = (NarrowNode) forValue;
-            Stamp inputStamp = narrow.getValue().stamp();
+            Stamp inputStamp = narrow.getValue().stamp(view);
             if (inputStamp instanceof IntegerStamp) {
                 IntegerStamp istamp = (IntegerStamp) inputStamp;
-                long mask = CodeUtil.mask(PrimitiveStamp.getBits(narrow.stamp()));
+                long mask = CodeUtil.mask(PrimitiveStamp.getBits(narrow.stamp(view)));
 
                 if ((istamp.upMask() & ~mask) == 0) {
                     // The original value cannot change because of the narrow and zero extend.
 
                     if (istamp.getBits() < resultBits) {
                         // Need to keep the zero extend, skip the narrow.
-                        return create(narrow.getValue(), resultBits);
+                        return create(narrow.getValue(), resultBits, view);
                     } else if (istamp.getBits() > resultBits) {
                         // Need to keep the narrow, skip the zero extend.
-                        return NarrowNode.create(narrow.getValue(), resultBits);
+                        return NarrowNode.create(narrow.getValue(), resultBits, view);
                     } else {
                         assert istamp.getBits() == resultBits;
                         // Just return the original value.
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/debug/BlackholeNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/debug/BlackholeNode.java
index 300ac616bae..0dbce573cab 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/debug/BlackholeNode.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/debug/BlackholeNode.java
@@ -44,6 +44,10 @@ public final class BlackholeNode extends FixedWithNextNode implements LIRLowerab
         this.value = value;
     }
 
+    public ValueNode getValue() {
+        return value;
+    }
+
     @Override
     public void generate(NodeLIRBuilderTool gen) {
         gen.getLIRGeneratorTool().emitBlackhole(gen.operand(value));
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/debug/OpaqueNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/debug/OpaqueNode.java
index 62b564f2c38..cf5ee474356 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/debug/OpaqueNode.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/debug/OpaqueNode.java
@@ -27,6 +27,7 @@ import static org.graalvm.compiler.nodeinfo.NodeSize.SIZE_0;
 
 import org.graalvm.compiler.graph.NodeClass;
 import org.graalvm.compiler.nodeinfo.NodeInfo;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.ValueNode;
 import org.graalvm.compiler.nodes.calc.FloatingNode;
 import org.graalvm.compiler.nodes.spi.LIRLowerable;
@@ -39,7 +40,7 @@ public final class OpaqueNode extends FloatingNode implements LIRLowerable {
     @Input protected ValueNode value;
 
     public OpaqueNode(ValueNode value) {
-        super(TYPE, value.stamp().unrestricted());
+        super(TYPE, value.stamp(NodeView.DEFAULT).unrestricted());
         this.value = value;
     }
 
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/extended/BoxNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/extended/BoxNode.java
index 38376fbf6cb..02cb3b8f607 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/extended/BoxNode.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/extended/BoxNode.java
@@ -34,6 +34,7 @@ import org.graalvm.compiler.graph.spi.CanonicalizerTool;
 import org.graalvm.compiler.nodeinfo.NodeCycles;
 import org.graalvm.compiler.nodeinfo.NodeInfo;
 import org.graalvm.compiler.nodes.FixedWithNextNode;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.ValueNode;
 import org.graalvm.compiler.nodes.java.MonitorIdNode;
 import org.graalvm.compiler.nodes.spi.Lowerable;
@@ -90,7 +91,7 @@ public class BoxNode extends FixedWithNextNode implements VirtualizableAllocatio
     }
 
     protected VirtualBoxingNode createVirtualBoxingNode() {
-        return new VirtualBoxingNode(StampTool.typeOrNull(stamp()), boxingKind);
+        return new VirtualBoxingNode(StampTool.typeOrNull(stamp(NodeView.DEFAULT)), boxingKind);
     }
 
     @Override
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/extended/BranchProbabilityNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/extended/BranchProbabilityNode.java
index cc3e63d14f8..e6f436d5729 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/extended/BranchProbabilityNode.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/extended/BranchProbabilityNode.java
@@ -34,6 +34,7 @@ import org.graalvm.compiler.graph.spi.SimplifierTool;
 import org.graalvm.compiler.nodeinfo.NodeInfo;
 import org.graalvm.compiler.nodes.FixedGuardNode;
 import org.graalvm.compiler.nodes.IfNode;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.ReturnNode;
 import org.graalvm.compiler.nodes.ValueNode;
 import org.graalvm.compiler.nodes.calc.ConditionalNode;
@@ -67,7 +68,7 @@ public final class BranchProbabilityNode extends FloatingNode implements Simplif
     @Input ValueNode condition;
 
     public BranchProbabilityNode(ValueNode probability, ValueNode condition) {
-        super(TYPE, condition.stamp());
+        super(TYPE, condition.stamp(NodeView.DEFAULT));
         this.probability = probability;
         this.condition = condition;
     }
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/extended/FixedValueAnchorNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/extended/FixedValueAnchorNode.java
index b490e846ba1..6fef3a662d9 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/extended/FixedValueAnchorNode.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/extended/FixedValueAnchorNode.java
@@ -29,6 +29,7 @@ import org.graalvm.compiler.core.common.type.Stamp;
 import org.graalvm.compiler.graph.NodeClass;
 import org.graalvm.compiler.nodeinfo.NodeInfo;
 import org.graalvm.compiler.nodes.FixedWithNextNode;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.ValueNode;
 import org.graalvm.compiler.nodes.spi.LIRLowerable;
 import org.graalvm.compiler.nodes.spi.NodeLIRBuilderTool;
@@ -46,7 +47,7 @@ public class FixedValueAnchorNode extends FixedWithNextNode implements LIRLowera
     }
 
     protected FixedValueAnchorNode(NodeClass<? extends FixedValueAnchorNode> c, ValueNode object) {
-        super(c, object.stamp());
+        super(c, object.stamp(NodeView.DEFAULT));
         this.object = object;
     }
 
@@ -63,7 +64,7 @@ public class FixedValueAnchorNode extends FixedWithNextNode implements LIRLowera
     @Override
     public boolean inferStamp() {
         if (predefinedStamp == null) {
-            return updateStamp(object.stamp());
+            return updateStamp(object.stamp(NodeView.DEFAULT));
         } else {
             return false;
         }
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/extended/GetClassNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/extended/GetClassNode.java
index 15f73937b12..43f17e9462e 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/extended/GetClassNode.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/extended/GetClassNode.java
@@ -32,6 +32,7 @@ import org.graalvm.compiler.graph.spi.Canonicalizable;
 import org.graalvm.compiler.graph.spi.CanonicalizerTool;
 import org.graalvm.compiler.nodeinfo.NodeInfo;
 import org.graalvm.compiler.nodes.ConstantNode;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.ValueNode;
 import org.graalvm.compiler.nodes.calc.FloatingNode;
 import org.graalvm.compiler.nodes.spi.Lowerable;
@@ -60,7 +61,7 @@ public final class GetClassNode extends FloatingNode implements Lowerable, Canon
     public GetClassNode(Stamp stamp, ValueNode object) {
         super(TYPE, stamp);
         this.object = object;
-        assert ((ObjectStamp) object.stamp()).nonNull();
+        assert ((ObjectStamp) object.stamp(NodeView.DEFAULT)).nonNull();
     }
 
     @Override
@@ -68,9 +69,9 @@ public final class GetClassNode extends FloatingNode implements Lowerable, Canon
         tool.getLowerer().lower(this, tool);
     }
 
-    public static ValueNode tryFold(MetaAccessProvider metaAccess, ConstantReflectionProvider constantReflection, ValueNode object) {
-        if (metaAccess != null && object != null && object.stamp() instanceof ObjectStamp) {
-            ObjectStamp objectStamp = (ObjectStamp) object.stamp();
+    public static ValueNode tryFold(MetaAccessProvider metaAccess, ConstantReflectionProvider constantReflection, NodeView view, ValueNode object) {
+        if (metaAccess != null && object != null && object.stamp(view) instanceof ObjectStamp) {
+            ObjectStamp objectStamp = (ObjectStamp) object.stamp(view);
             if (objectStamp.isExactType()) {
                 return ConstantNode.forConstant(constantReflection.asJavaClass(objectStamp.type()), metaAccess);
             }
@@ -80,7 +81,8 @@ public final class GetClassNode extends FloatingNode implements Lowerable, Canon
 
     @Override
     public ValueNode canonical(CanonicalizerTool tool) {
-        ValueNode folded = tryFold(tool.getMetaAccess(), tool.getConstantReflection(), getObject());
+        NodeView view = NodeView.from(tool);
+        ValueNode folded = tryFold(tool.getMetaAccess(), tool.getConstantReflection(), view, getObject());
         return folded == null ? this : folded;
     }
 
@@ -90,7 +92,7 @@ public final class GetClassNode extends FloatingNode implements Lowerable, Canon
         if (alias instanceof VirtualObjectNode) {
             VirtualObjectNode virtual = (VirtualObjectNode) alias;
             Constant javaClass = tool.getConstantReflectionProvider().asJavaClass(virtual.type());
-            tool.replaceWithValue(ConstantNode.forConstant(stamp(), javaClass, tool.getMetaAccessProvider(), graph()));
+            tool.replaceWithValue(ConstantNode.forConstant(stamp(NodeView.DEFAULT), javaClass, tool.getMetaAccessProvider(), graph()));
         }
     }
 }
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/extended/IntegerSwitchNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/extended/IntegerSwitchNode.java
index 75afb6f506f..d63cd4d311b 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/extended/IntegerSwitchNode.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/extended/IntegerSwitchNode.java
@@ -43,6 +43,7 @@ import org.graalvm.compiler.nodes.FixedGuardNode;
 import org.graalvm.compiler.nodes.FixedNode;
 import org.graalvm.compiler.nodes.FixedWithNextNode;
 import org.graalvm.compiler.nodes.LogicNode;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.ValueNode;
 import org.graalvm.compiler.nodes.calc.IntegerBelowNode;
 import org.graalvm.compiler.nodes.java.LoadIndexedNode;
@@ -70,7 +71,7 @@ public final class IntegerSwitchNode extends SwitchNode implements LIRLowerable,
         assert keySuccessors.length == keys.length + 1;
         assert keySuccessors.length == keyProbabilities.length;
         this.keys = keys;
-        assert value.stamp() instanceof PrimitiveStamp && value.stamp().getStackKind().isNumericInteger();
+        assert value.stamp(NodeView.DEFAULT) instanceof PrimitiveStamp && value.stamp(NodeView.DEFAULT).getStackKind().isNumericInteger();
         assert assertSorted();
     }
 
@@ -135,6 +136,7 @@ public final class IntegerSwitchNode extends SwitchNode implements LIRLowerable,
 
     @Override
     public void simplify(SimplifierTool tool) {
+        NodeView view = NodeView.from(tool);
         if (blockSuccessorCount() == 1) {
             tool.addToWorkList(defaultSuccessor());
             graph().removeSplitPropagate(this, defaultSuccessor());
@@ -142,7 +144,7 @@ public final class IntegerSwitchNode extends SwitchNode implements LIRLowerable,
             killOtherSuccessors(tool, successorIndexAtKey(value().asJavaConstant().asInt()));
         } else if (tryOptimizeEnumSwitch(tool)) {
             return;
-        } else if (tryRemoveUnreachableKeys(tool, value().stamp())) {
+        } else if (tryRemoveUnreachableKeys(tool, value().stamp(view))) {
             return;
         }
     }
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/extended/LoadHubNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/extended/LoadHubNode.java
index 1000a9e0ca9..5e19233ac26 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/extended/LoadHubNode.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/extended/LoadHubNode.java
@@ -35,6 +35,7 @@ import org.graalvm.compiler.graph.spi.Canonicalizable;
 import org.graalvm.compiler.graph.spi.CanonicalizerTool;
 import org.graalvm.compiler.nodeinfo.NodeInfo;
 import org.graalvm.compiler.nodes.ConstantNode;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.ValueNode;
 import org.graalvm.compiler.nodes.calc.FloatingNode;
 import org.graalvm.compiler.nodes.spi.Lowerable;
@@ -61,8 +62,8 @@ public final class LoadHubNode extends FloatingNode implements Lowerable, Canoni
     }
 
     private static Stamp hubStamp(StampProvider stampProvider, ValueNode value) {
-        assert value.stamp() instanceof ObjectStamp;
-        return stampProvider.createHubStamp(((ObjectStamp) value.stamp()));
+        assert value.stamp(NodeView.DEFAULT) instanceof ObjectStamp;
+        return stampProvider.createHubStamp(((ObjectStamp) value.stamp(NodeView.DEFAULT)));
     }
 
     public static ValueNode create(ValueNode value, StampProvider stampProvider, MetaAccessProvider metaAccess, ConstantReflectionProvider constantReflection) {
@@ -91,9 +92,10 @@ public final class LoadHubNode extends FloatingNode implements Lowerable, Canoni
     @Override
     public ValueNode canonical(CanonicalizerTool tool) {
         if (!GeneratePIC.getValue(tool.getOptions())) {
+            NodeView view = NodeView.from(tool);
             MetaAccessProvider metaAccess = tool.getMetaAccess();
             ValueNode curValue = getValue();
-            ValueNode newNode = findSynonym(curValue, stamp(), metaAccess, tool.getConstantReflection());
+            ValueNode newNode = findSynonym(curValue, stamp(view), metaAccess, tool.getConstantReflection());
             if (newNode != null) {
                 return newNode;
             }
@@ -117,7 +119,7 @@ public final class LoadHubNode extends FloatingNode implements Lowerable, Canoni
             ValueNode alias = tool.getAlias(getValue());
             TypeReference type = StampTool.typeReferenceOrNull(alias);
             if (type != null && type.isExact()) {
-                tool.replaceWithValue(ConstantNode.forConstant(stamp(), tool.getConstantReflectionProvider().asObjectHub(type.getType()), tool.getMetaAccessProvider(), graph()));
+                tool.replaceWithValue(ConstantNode.forConstant(stamp(NodeView.DEFAULT), tool.getConstantReflectionProvider().asObjectHub(type.getType()), tool.getMetaAccessProvider(), graph()));
             }
         }
     }
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/extended/LoadMethodNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/extended/LoadMethodNode.java
index 85f0c218414..c0b12be3443 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/extended/LoadMethodNode.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/extended/LoadMethodNode.java
@@ -35,6 +35,7 @@ import org.graalvm.compiler.graph.spi.CanonicalizerTool;
 import org.graalvm.compiler.nodeinfo.NodeInfo;
 import org.graalvm.compiler.nodes.ConstantNode;
 import org.graalvm.compiler.nodes.FixedWithNextNode;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.ValueNode;
 import org.graalvm.compiler.nodes.spi.Lowerable;
 import org.graalvm.compiler.nodes.spi.LoweringTool;
@@ -96,8 +97,9 @@ public final class LoadMethodNode extends FixedWithNextNode implements Lowerable
                 Assumptions assumptions = graph().getAssumptions();
                 AssumptionResult<ResolvedJavaMethod> resolvedMethod = type.getType().findUniqueConcreteMethod(method);
                 if (resolvedMethod != null && resolvedMethod.canRecordTo(assumptions) && !type.getType().isInterface() && method.getDeclaringClass().isAssignableFrom(type.getType())) {
+                    NodeView view = NodeView.from(tool);
                     resolvedMethod.recordTo(assumptions);
-                    return ConstantNode.forConstant(stamp(), resolvedMethod.getResult().getEncoding(), tool.getMetaAccess());
+                    return ConstantNode.forConstant(stamp(view), resolvedMethod.getResult().getEncoding(), tool.getMetaAccess());
                 }
             }
         }
@@ -123,9 +125,9 @@ public final class LoadMethodNode extends FixedWithNextNode implements Lowerable
              * This really represent a misuse of LoadMethod since we're loading from a class which
              * isn't known to implement the original method but for now at least fold it away.
              */
-            return ConstantNode.forConstant(stamp(), JavaConstant.NULL_POINTER, null);
+            return ConstantNode.forConstant(stamp(NodeView.DEFAULT), JavaConstant.NULL_POINTER, null);
         } else {
-            return ConstantNode.forConstant(stamp(), newMethod.getEncoding(), tool.getMetaAccess());
+            return ConstantNode.forConstant(stamp(NodeView.DEFAULT), newMethod.getEncoding(), tool.getMetaAccess());
         }
     }
 
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/extended/RawLoadNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/extended/RawLoadNode.java
index de715c7e2c3..6638e3f0d14 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/extended/RawLoadNode.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/extended/RawLoadNode.java
@@ -34,6 +34,7 @@ import org.graalvm.compiler.graph.spi.Canonicalizable;
 import org.graalvm.compiler.graph.spi.CanonicalizerTool;
 import org.graalvm.compiler.nodeinfo.NodeInfo;
 import org.graalvm.compiler.nodes.ConstantNode;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.ValueNode;
 import org.graalvm.compiler.nodes.calc.ReinterpretNode;
 import org.graalvm.compiler.nodes.java.LoadFieldNode;
@@ -103,14 +104,14 @@ public class RawLoadNode extends UnsafeAccessNode implements Lowerable, Virtuali
                     JavaKind entryKind = virtual.entryKind(entryIndex);
                     if (entry.getStackKind() == getStackKind() || entryKind == accessKind()) {
 
-                        if (!(entry.stamp().isCompatible(stamp()))) {
-                            if (entry.stamp() instanceof PrimitiveStamp && stamp instanceof PrimitiveStamp) {
+                        if (!(entry.stamp(NodeView.DEFAULT).isCompatible(stamp(NodeView.DEFAULT)))) {
+                            if (entry.stamp(NodeView.DEFAULT) instanceof PrimitiveStamp && stamp instanceof PrimitiveStamp) {
                                 PrimitiveStamp p1 = (PrimitiveStamp) stamp;
-                                PrimitiveStamp p2 = (PrimitiveStamp) entry.stamp();
+                                PrimitiveStamp p2 = (PrimitiveStamp) entry.stamp(NodeView.DEFAULT);
                                 int width1 = p1.getBits();
                                 int width2 = p2.getBits();
                                 if (width1 == width2) {
-                                    Node replacement = new ReinterpretNode(p2, entry);
+                                    Node replacement = ReinterpretNode.create(p2, entry, NodeView.DEFAULT);
                                     tool.replaceWith((ValueNode) replacement);
                                     return;
                                 } else {
@@ -141,11 +142,12 @@ public class RawLoadNode extends UnsafeAccessNode implements Lowerable, Virtuali
                     if (arrayConstant != null) {
                         int stableDimension = objectConstant.getStableDimension();
                         if (stableDimension > 0) {
+                            NodeView view = NodeView.from(tool);
                             long constantOffset = offset().asJavaConstant().asLong();
-                            Constant constant = stamp().readConstant(tool.getConstantReflection().getMemoryAccessProvider(), arrayConstant, constantOffset);
+                            Constant constant = stamp(view).readConstant(tool.getConstantReflection().getMemoryAccessProvider(), arrayConstant, constantOffset);
                             boolean isDefaultStable = objectConstant.isDefaultStable();
                             if (constant != null && (isDefaultStable || !constant.isDefaultForKind())) {
-                                return ConstantNode.forConstant(stamp(), constant, stableDimension - 1, isDefaultStable, tool.getMetaAccess());
+                                return ConstantNode.forConstant(stamp(view), constant, stableDimension - 1, isDefaultStable, tool.getMetaAccess());
                             }
                         }
                     }
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/extended/SwitchNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/extended/SwitchNode.java
index 4f9e076d4d6..14ae73e47ef 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/extended/SwitchNode.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/extended/SwitchNode.java
@@ -46,6 +46,7 @@ import org.graalvm.compiler.nodeinfo.NodeInfo;
 import org.graalvm.compiler.nodeinfo.NodeSize;
 import org.graalvm.compiler.nodes.AbstractBeginNode;
 import org.graalvm.compiler.nodes.ControlSplitNode;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.ValueNode;
 
 import jdk.vm.ci.meta.Constant;
@@ -79,7 +80,8 @@ public abstract class SwitchNode extends ControlSplitNode {
      */
     protected SwitchNode(NodeClass<? extends SwitchNode> c, ValueNode value, AbstractBeginNode[] successors, int[] keySuccessors, double[] keyProbabilities) {
         super(c, StampFactory.forVoid());
-        assert value.stamp().getStackKind().isNumericInteger() || value.stamp() instanceof AbstractPointerStamp : value.stamp() + " key not supported by SwitchNode";
+        assert value.stamp(NodeView.DEFAULT).getStackKind().isNumericInteger() || value.stamp(NodeView.DEFAULT) instanceof AbstractPointerStamp : value.stamp(NodeView.DEFAULT) +
+                        " key not supported by SwitchNode";
         assert keySuccessors.length == keyProbabilities.length;
         this.successors = new NodeSuccessorList<>(this, successors);
         this.value = value;
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/graphbuilderconf/GraphBuilderContext.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/graphbuilderconf/GraphBuilderContext.java
index 09b4cc72c89..4fbbcef8ce8 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/graphbuilderconf/GraphBuilderContext.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/graphbuilderconf/GraphBuilderContext.java
@@ -38,6 +38,7 @@ import org.graalvm.compiler.nodes.CallTargetNode.InvokeKind;
 import org.graalvm.compiler.nodes.ConstantNode;
 import org.graalvm.compiler.nodes.FixedGuardNode;
 import org.graalvm.compiler.nodes.LogicNode;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.PiNode;
 import org.graalvm.compiler.nodes.StateSplit;
 import org.graalvm.compiler.nodes.ValueNode;
@@ -118,7 +119,7 @@ public interface GraphBuilderContext extends GraphBuilderTool {
     }
 
     default ValueNode addNonNullCast(ValueNode value) {
-        AbstractPointerStamp valueStamp = (AbstractPointerStamp) value.stamp();
+        AbstractPointerStamp valueStamp = (AbstractPointerStamp) value.stamp(NodeView.DEFAULT);
         if (valueStamp.nonNull()) {
             return value;
         } else {
@@ -277,7 +278,7 @@ public interface GraphBuilderContext extends GraphBuilderTool {
     default ValueNode nullCheckedValue(ValueNode value, DeoptimizationAction action) {
         if (!StampTool.isPointerNonNull(value)) {
             LogicNode condition = getGraph().unique(IsNullNode.create(value));
-            ObjectStamp receiverStamp = (ObjectStamp) value.stamp();
+            ObjectStamp receiverStamp = (ObjectStamp) value.stamp(NodeView.DEFAULT);
             Stamp stamp = receiverStamp.join(objectNonNull());
             FixedGuardNode fixedGuard = append(new FixedGuardNode(condition, NullCheckException, action, true));
             ValueNode nonNullReceiver = getGraph().addOrUniqueWithInputs(PiNode.create(value, stamp, fixedGuard));
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/java/AbstractCompareAndSwapNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/java/AbstractCompareAndSwapNode.java
index cff46027cdb..332c51053ed 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/java/AbstractCompareAndSwapNode.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/java/AbstractCompareAndSwapNode.java
@@ -30,6 +30,7 @@ import org.graalvm.compiler.graph.NodeClass;
 import org.graalvm.compiler.nodeinfo.InputType;
 import org.graalvm.compiler.nodeinfo.NodeInfo;
 import org.graalvm.compiler.nodes.FrameState;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.StateSplit;
 import org.graalvm.compiler.nodes.ValueNode;
 import org.graalvm.compiler.nodes.memory.FixedAccessNode;
@@ -88,6 +89,6 @@ public abstract class AbstractCompareAndSwapNode extends FixedAccessNode impleme
 
     @Override
     public Stamp getAccessStamp() {
-        return expectedValue.stamp().meet(newValue.stamp()).unrestricted();
+        return expectedValue.stamp(NodeView.DEFAULT).meet(newValue.stamp(NodeView.DEFAULT)).unrestricted();
     }
 }
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/java/DynamicNewInstanceNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/java/DynamicNewInstanceNode.java
index fd1409e2184..0ad6b6b4ad3 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/java/DynamicNewInstanceNode.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/java/DynamicNewInstanceNode.java
@@ -34,6 +34,7 @@ import org.graalvm.compiler.graph.spi.Canonicalizable;
 import org.graalvm.compiler.graph.spi.CanonicalizerTool;
 import org.graalvm.compiler.nodeinfo.NodeInfo;
 import org.graalvm.compiler.nodes.FrameState;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.ValueNode;
 
 import jdk.vm.ci.meta.MetaAccessProvider;
@@ -59,7 +60,7 @@ public class DynamicNewInstanceNode extends AbstractNewObjectNode implements Can
     protected DynamicNewInstanceNode(NodeClass<? extends DynamicNewInstanceNode> c, ValueNode clazz, boolean fillContents, FrameState stateBefore) {
         super(c, StampFactory.objectNonNull(), fillContents, stateBefore);
         this.clazz = clazz;
-        assert ((ObjectStamp) clazz.stamp()).nonNull();
+        assert ((ObjectStamp) clazz.stamp(NodeView.DEFAULT)).nonNull();
     }
 
     public ValueNode getInstanceType() {
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/java/ExceptionObjectNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/java/ExceptionObjectNode.java
index d97d138f211..c00e7e842f5 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/java/ExceptionObjectNode.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/java/ExceptionObjectNode.java
@@ -34,6 +34,7 @@ import org.graalvm.compiler.nodes.AbstractBeginNode;
 import org.graalvm.compiler.nodes.BeginStateSplitNode;
 import org.graalvm.compiler.nodes.InvokeWithExceptionNode;
 import org.graalvm.compiler.nodes.KillingBeginNode;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.StructuredGraph;
 import org.graalvm.compiler.nodes.memory.MemoryCheckpoint;
 import org.graalvm.compiler.nodes.spi.Lowerable;
@@ -79,7 +80,7 @@ public final class ExceptionObjectNode extends BeginStateSplitNode implements Lo
              */
             LocationIdentity locationsKilledByInvoke = ((InvokeWithExceptionNode) predecessor()).getLocationIdentity();
             AbstractBeginNode entry = graph().add(KillingBeginNode.create(locationsKilledByInvoke));
-            LoadExceptionObjectNode loadException = graph().add(new LoadExceptionObjectNode(stamp()));
+            LoadExceptionObjectNode loadException = graph().add(new LoadExceptionObjectNode(stamp(NodeView.DEFAULT)));
 
             loadException.setStateAfter(stateAfter());
             replaceAtUsages(InputType.Value, loadException);
@@ -93,7 +94,7 @@ public final class ExceptionObjectNode extends BeginStateSplitNode implements Lo
     @Override
     public boolean verify() {
         assertTrue(stateAfter() != null, "an exception handler needs a frame state");
-        assertTrue(stateAfter().stackSize() == 1 && stateAfter().stackAt(0).stamp().getStackKind() == JavaKind.Object,
+        assertTrue(stateAfter().stackSize() == 1 && stateAfter().stackAt(0).stamp(NodeView.DEFAULT).getStackKind() == JavaKind.Object,
                         "an exception handler's frame state must have only the exception on the stack");
         return super.verify();
     }
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/java/InstanceOfNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/java/InstanceOfNode.java
index e63cffefe88..1aac395df8a 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/java/InstanceOfNode.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/java/InstanceOfNode.java
@@ -36,6 +36,7 @@ import org.graalvm.compiler.nodeinfo.NodeInfo;
 import org.graalvm.compiler.nodes.LogicConstantNode;
 import org.graalvm.compiler.nodes.LogicNegationNode;
 import org.graalvm.compiler.nodes.LogicNode;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.UnaryOpLogicNode;
 import org.graalvm.compiler.nodes.ValueNode;
 import org.graalvm.compiler.nodes.calc.IsNullNode;
@@ -92,7 +93,7 @@ public class InstanceOfNode extends UnaryOpLogicNode implements Lowerable, Virtu
     }
 
     public static LogicNode createHelper(ObjectStamp checkedStamp, ValueNode object, JavaTypeProfile profile, AnchoringNode anchor) {
-        LogicNode synonym = findSynonym(checkedStamp, object);
+        LogicNode synonym = findSynonym(checkedStamp, object, NodeView.DEFAULT);
         if (synonym != null) {
             return synonym;
         } else {
@@ -107,7 +108,8 @@ public class InstanceOfNode extends UnaryOpLogicNode implements Lowerable, Virtu
 
     @Override
     public ValueNode canonical(CanonicalizerTool tool, ValueNode forValue) {
-        LogicNode synonym = findSynonym(checkedStamp, forValue);
+        NodeView view = NodeView.from(tool);
+        LogicNode synonym = findSynonym(checkedStamp, forValue, view);
         if (synonym != null) {
             return synonym;
         } else {
@@ -115,8 +117,8 @@ public class InstanceOfNode extends UnaryOpLogicNode implements Lowerable, Virtu
         }
     }
 
-    public static LogicNode findSynonym(ObjectStamp checkedStamp, ValueNode object) {
-        ObjectStamp inputStamp = (ObjectStamp) object.stamp();
+    public static LogicNode findSynonym(ObjectStamp checkedStamp, ValueNode object, NodeView view) {
+        ObjectStamp inputStamp = (ObjectStamp) object.stamp(view);
         ObjectStamp joinedStamp = (ObjectStamp) checkedStamp.join(inputStamp);
 
         if (joinedStamp.isEmpty()) {
@@ -158,7 +160,7 @@ public class InstanceOfNode extends UnaryOpLogicNode implements Lowerable, Virtu
     @Override
     public void virtualize(VirtualizerTool tool) {
         ValueNode alias = tool.getAlias(getValue());
-        TriState fold = tryFold(alias.stamp());
+        TriState fold = tryFold(alias.stamp(NodeView.DEFAULT));
         if (fold != TriState.UNKNOWN) {
             tool.replaceWithValue(LogicConstantNode.forBoolean(fold.isTrue(), graph()));
         }
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/java/LoadFieldNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/java/LoadFieldNode.java
index 5ff55d2a65f..56f1ea939e0 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/java/LoadFieldNode.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/java/LoadFieldNode.java
@@ -36,6 +36,7 @@ import org.graalvm.compiler.nodeinfo.NodeCycles;
 import org.graalvm.compiler.nodeinfo.NodeInfo;
 import org.graalvm.compiler.nodes.ConstantNode;
 import org.graalvm.compiler.nodes.DeoptimizeNode;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.PhiNode;
 import org.graalvm.compiler.nodes.ValueNode;
 import org.graalvm.compiler.nodes.ValuePhiNode;
@@ -98,7 +99,8 @@ public final class LoadFieldNode extends AccessFieldNode implements Canonicaliza
 
     @Override
     public ValueNode canonical(CanonicalizerTool tool, ValueNode forObject) {
-        if (tool.allUsagesAvailable() && hasNoUsages() && !isVolatile() && (isStatic() || StampTool.isPointerNonNull(forObject.stamp()))) {
+        NodeView view = NodeView.from(tool);
+        if (tool.allUsagesAvailable() && hasNoUsages() && !isVolatile() && (isStatic() || StampTool.isPointerNonNull(forObject.stamp(view)))) {
             return null;
         }
         return canonical(this, StampPair.create(stamp, uncheckedStamp), forObject, field, tool.getConstantFieldProvider(),
@@ -178,10 +180,10 @@ public final class LoadFieldNode extends AccessFieldNode implements Canonicaliza
             int fieldIndex = ((VirtualInstanceNode) alias).fieldIndex(field());
             if (fieldIndex != -1) {
                 ValueNode entry = tool.getEntry((VirtualObjectNode) alias, fieldIndex);
-                if (stamp.isCompatible(entry.stamp())) {
+                if (stamp.isCompatible(entry.stamp(NodeView.DEFAULT))) {
                     tool.replaceWith(entry);
                 } else {
-                    assert stamp().getStackKind() == JavaKind.Int && (entry.stamp().getStackKind() == JavaKind.Long || entry.getStackKind() == JavaKind.Double ||
+                    assert stamp(NodeView.DEFAULT).getStackKind() == JavaKind.Int && (entry.stamp(NodeView.DEFAULT).getStackKind() == JavaKind.Long || entry.getStackKind() == JavaKind.Double ||
                                     entry.getStackKind() == JavaKind.Illegal) : "Can only allow different stack kind two slot marker writes on one stot fields.";
                 }
             }
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/java/LoadIndexedNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/java/LoadIndexedNode.java
index dc2a857c5fa..cb731818fae 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/java/LoadIndexedNode.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/java/LoadIndexedNode.java
@@ -35,6 +35,7 @@ import org.graalvm.compiler.graph.spi.Canonicalizable;
 import org.graalvm.compiler.graph.spi.CanonicalizerTool;
 import org.graalvm.compiler.nodeinfo.NodeInfo;
 import org.graalvm.compiler.nodes.ConstantNode;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.ValueNode;
 import org.graalvm.compiler.nodes.spi.Virtualizable;
 import org.graalvm.compiler.nodes.spi.VirtualizerTool;
@@ -92,7 +93,7 @@ public class LoadIndexedNode extends AccessIndexedNode implements Virtualizable,
 
     private static JavaKind determinePreciseArrayElementType(ValueNode array, JavaKind kind) {
         if (kind == JavaKind.Byte) {
-            ResolvedJavaType javaType = ((ObjectStamp) array.stamp()).type();
+            ResolvedJavaType javaType = ((ObjectStamp) array.stamp(NodeView.DEFAULT)).type();
             if (javaType != null && javaType.isArray() && javaType.getComponentType() != null && javaType.getComponentType().getJavaKind() == JavaKind.Boolean) {
                 return JavaKind.Boolean;
             }
@@ -114,10 +115,10 @@ public class LoadIndexedNode extends AccessIndexedNode implements Virtualizable,
             int idx = indexValue.isConstant() ? indexValue.asJavaConstant().asInt() : -1;
             if (idx >= 0 && idx < virtual.entryCount()) {
                 ValueNode entry = tool.getEntry(virtual, idx);
-                if (stamp.isCompatible(entry.stamp())) {
+                if (stamp.isCompatible(entry.stamp(NodeView.DEFAULT))) {
                     tool.replaceWith(entry);
                 } else {
-                    assert stamp().getStackKind() == JavaKind.Int && (entry.stamp().getStackKind() == JavaKind.Long || entry.getStackKind() == JavaKind.Double ||
+                    assert stamp(NodeView.DEFAULT).getStackKind() == JavaKind.Int && (entry.stamp(NodeView.DEFAULT).getStackKind() == JavaKind.Long || entry.getStackKind() == JavaKind.Double ||
                                     entry.getStackKind() == JavaKind.Illegal) : "Can only allow different stack kind two slot marker writes on one stot fields.";
                 }
             }
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/java/LogicCompareAndSwapNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/java/LogicCompareAndSwapNode.java
index 46e49c5f606..8cc692a0d7d 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/java/LogicCompareAndSwapNode.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/java/LogicCompareAndSwapNode.java
@@ -30,6 +30,7 @@ import org.graalvm.compiler.core.common.type.StampFactory;
 import org.graalvm.compiler.graph.NodeClass;
 import org.graalvm.compiler.lir.gen.LIRGeneratorTool;
 import org.graalvm.compiler.nodeinfo.NodeInfo;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.ValueNode;
 import org.graalvm.compiler.nodes.memory.address.AddressNode;
 import org.graalvm.compiler.nodes.spi.NodeLIRBuilderTool;
@@ -58,11 +59,11 @@ public final class LogicCompareAndSwapNode extends AbstractCompareAndSwapNode {
 
     @Override
     public void generate(NodeLIRBuilderTool gen) {
-        assert getNewValue().stamp().isCompatible(getExpectedValue().stamp());
+        assert getNewValue().stamp(NodeView.DEFAULT).isCompatible(getExpectedValue().stamp(NodeView.DEFAULT));
         assert !this.canDeoptimize();
         LIRGeneratorTool tool = gen.getLIRGeneratorTool();
 
-        LIRKind resultKind = tool.getLIRKind(stamp());
+        LIRKind resultKind = tool.getLIRKind(stamp(NodeView.DEFAULT));
         Value trueResult = tool.emitConstant(resultKind, JavaConstant.TRUE);
         Value falseResult = tool.emitConstant(resultKind, JavaConstant.FALSE);
         Value result = tool.emitLogicCompareAndSwap(gen.operand(getAddress()), gen.operand(getExpectedValue()), gen.operand(getNewValue()), trueResult, falseResult);
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/java/LoweredAtomicReadAndWriteNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/java/LoweredAtomicReadAndWriteNode.java
index dfd3c83419c..f477655786b 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/java/LoweredAtomicReadAndWriteNode.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/java/LoweredAtomicReadAndWriteNode.java
@@ -31,6 +31,7 @@ import org.graalvm.compiler.core.common.type.Stamp;
 import org.graalvm.compiler.graph.NodeClass;
 import org.graalvm.compiler.nodeinfo.NodeInfo;
 import org.graalvm.compiler.nodes.FrameState;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.StateSplit;
 import org.graalvm.compiler.nodes.ValueNode;
 import org.graalvm.compiler.nodes.memory.FixedAccessNode;
@@ -55,7 +56,7 @@ public final class LoweredAtomicReadAndWriteNode extends FixedAccessNode impleme
     @OptionalInput(State) FrameState stateAfter;
 
     public LoweredAtomicReadAndWriteNode(AddressNode address, LocationIdentity location, ValueNode newValue, BarrierType barrierType) {
-        super(TYPE, address, location, newValue.stamp().unrestricted(), barrierType);
+        super(TYPE, address, location, newValue.stamp(NodeView.DEFAULT).unrestricted(), barrierType);
         this.newValue = newValue;
     }
 
@@ -93,6 +94,6 @@ public final class LoweredAtomicReadAndWriteNode extends FixedAccessNode impleme
 
     @Override
     public Stamp getAccessStamp() {
-        return stamp();
+        return stamp(NodeView.DEFAULT);
     }
 }
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/java/MethodCallTargetNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/java/MethodCallTargetNode.java
index c1e3ce64825..105f433f859 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/java/MethodCallTargetNode.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/java/MethodCallTargetNode.java
@@ -37,6 +37,7 @@ import org.graalvm.compiler.nodes.CallTargetNode;
 import org.graalvm.compiler.nodes.FixedGuardNode;
 import org.graalvm.compiler.nodes.Invoke;
 import org.graalvm.compiler.nodes.LogicNode;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.PiNode;
 import org.graalvm.compiler.nodes.StructuredGraph;
 import org.graalvm.compiler.nodes.ValueNode;
@@ -132,7 +133,7 @@ public class MethodCallTargetNode extends CallTargetNode implements IterableNode
             return targetMethod;
         }
 
-        return devirtualizeCall(invokeKind, targetMethod, contextType, receiver.graph().getAssumptions(), receiver.stamp());
+        return devirtualizeCall(invokeKind, targetMethod, contextType, receiver.graph().getAssumptions(), receiver.stamp(NodeView.DEFAULT));
     }
 
     public static ResolvedJavaMethod devirtualizeCall(InvokeKind invokeKind, ResolvedJavaMethod targetMethod, ResolvedJavaType contextType, Assumptions assumptions, Stamp receiverStamp) {
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/java/NewArrayNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/java/NewArrayNode.java
index a62e08e333d..89fd3ddda60 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/java/NewArrayNode.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/java/NewArrayNode.java
@@ -37,6 +37,7 @@ import org.graalvm.compiler.nodes.ConstantNode;
 import org.graalvm.compiler.nodes.FixedGuardNode;
 import org.graalvm.compiler.nodes.FrameState;
 import org.graalvm.compiler.nodes.LogicNode;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.ValueNode;
 import org.graalvm.compiler.nodes.calc.CompareNode;
 import org.graalvm.compiler.nodes.spi.VirtualizableAllocation;
@@ -119,7 +120,8 @@ public class NewArrayNode extends AbstractNewArrayNode implements VirtualizableA
     @Override
     public void simplify(SimplifierTool tool) {
         if (hasNoUsages()) {
-            Stamp lengthStamp = length().stamp();
+            NodeView view = NodeView.from(tool);
+            Stamp lengthStamp = length().stamp(view);
             if (lengthStamp instanceof IntegerStamp) {
                 IntegerStamp lengthIntegerStamp = (IntegerStamp) lengthStamp;
                 if (lengthIntegerStamp.isPositive()) {
@@ -130,7 +132,7 @@ public class NewArrayNode extends AbstractNewArrayNode implements VirtualizableA
             // Should be areFrameStatesAtSideEffects but currently SVM will complain about
             // RuntimeConstraint
             if (graph().getGuardsStage().allowsFloatingGuards()) {
-                LogicNode lengthNegativeCondition = CompareNode.createCompareNode(graph(), Condition.LT, length(), ConstantNode.forInt(0, graph()), tool.getConstantReflection());
+                LogicNode lengthNegativeCondition = CompareNode.createCompareNode(graph(), Condition.LT, length(), ConstantNode.forInt(0, graph()), tool.getConstantReflection(), view);
                 // we do not have a non-deopting path for that at the moment so action=None.
                 FixedGuardNode guard = graph().add(new FixedGuardNode(lengthNegativeCondition, DeoptimizationReason.RuntimeConstraint, DeoptimizationAction.None, true));
                 graph().replaceFixedWithFixed(this, guard);
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/java/RawMonitorEnterNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/java/RawMonitorEnterNode.java
index 59d3d000ff8..9870bbb9994 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/java/RawMonitorEnterNode.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/java/RawMonitorEnterNode.java
@@ -29,6 +29,7 @@ import org.graalvm.compiler.core.common.type.ObjectStamp;
 import org.graalvm.compiler.graph.IterableNodeType;
 import org.graalvm.compiler.graph.NodeClass;
 import org.graalvm.compiler.nodeinfo.NodeInfo;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.ValueNode;
 import org.graalvm.compiler.nodes.extended.MonitorEnter;
 import org.graalvm.compiler.nodes.memory.MemoryCheckpoint;
@@ -56,7 +57,7 @@ public final class RawMonitorEnterNode extends AccessMonitorNode implements Virt
 
     public RawMonitorEnterNode(ValueNode object, ValueNode hub, MonitorIdNode monitorId) {
         super(TYPE, object, monitorId);
-        assert ((ObjectStamp) object.stamp()).nonNull();
+        assert ((ObjectStamp) object.stamp(NodeView.DEFAULT)).nonNull();
         this.hub = hub;
     }
 
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/java/RegisterFinalizerNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/java/RegisterFinalizerNode.java
index c1d2d996ac5..7898cc85688 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/java/RegisterFinalizerNode.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/java/RegisterFinalizerNode.java
@@ -37,6 +37,7 @@ import org.graalvm.compiler.nodeinfo.NodeInfo;
 import org.graalvm.compiler.nodes.AbstractStateSplit;
 import org.graalvm.compiler.nodes.DeoptimizingNode;
 import org.graalvm.compiler.nodes.FrameState;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.ValueNode;
 import org.graalvm.compiler.nodes.spi.LIRLowerable;
 import org.graalvm.compiler.nodes.spi.NodeLIRBuilderTool;
@@ -87,7 +88,7 @@ public final class RegisterFinalizerNode extends AbstractStateSplit implements C
      * that must be registered with the runtime upon object initialization.
      */
     public static boolean mayHaveFinalizer(ValueNode object, Assumptions assumptions) {
-        ObjectStamp objectStamp = (ObjectStamp) object.stamp();
+        ObjectStamp objectStamp = (ObjectStamp) object.stamp(NodeView.DEFAULT);
         if (objectStamp.isExactType()) {
             return objectStamp.type().hasFinalizer();
         } else if (objectStamp.type() != null) {
@@ -102,7 +103,8 @@ public final class RegisterFinalizerNode extends AbstractStateSplit implements C
 
     @Override
     public ValueNode canonical(CanonicalizerTool tool, ValueNode forValue) {
-        if (!(forValue.stamp() instanceof ObjectStamp)) {
+        NodeView view = NodeView.from(tool);
+        if (!(forValue.stamp(view) instanceof ObjectStamp)) {
             return this;
         }
         if (!mayHaveFinalizer(forValue, graph().getAssumptions())) {
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/java/TypeSwitchNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/java/TypeSwitchNode.java
index 7bc851ad1e4..36223bde288 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/java/TypeSwitchNode.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/java/TypeSwitchNode.java
@@ -37,6 +37,7 @@ import org.graalvm.compiler.nodeinfo.NodeInfo;
 import org.graalvm.compiler.nodes.AbstractBeginNode;
 import org.graalvm.compiler.nodes.ConstantNode;
 import org.graalvm.compiler.nodes.FixedWithNextNode;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.ValueNode;
 import org.graalvm.compiler.nodes.extended.LoadHubNode;
 import org.graalvm.compiler.nodes.extended.SwitchNode;
@@ -64,7 +65,7 @@ public final class TypeSwitchNode extends SwitchNode implements LIRLowerable, Si
         assert successors.length <= keys.length + 1;
         assert keySuccessors.length == keyProbabilities.length;
         this.keys = keys;
-        assert value.stamp() instanceof AbstractPointerStamp;
+        assert value.stamp(NodeView.DEFAULT) instanceof AbstractPointerStamp;
         assert assertKeys();
 
         hubs = new Constant[keys.length];
@@ -123,6 +124,7 @@ public final class TypeSwitchNode extends SwitchNode implements LIRLowerable, Si
 
     @Override
     public void simplify(SimplifierTool tool) {
+        NodeView view = NodeView.from(tool);
         if (value() instanceof ConstantNode) {
             Constant constant = value().asConstant();
 
@@ -139,8 +141,8 @@ public final class TypeSwitchNode extends SwitchNode implements LIRLowerable, Si
             }
             killOtherSuccessors(tool, survivingEdge);
         }
-        if (value() instanceof LoadHubNode && ((LoadHubNode) value()).getValue().stamp() instanceof ObjectStamp) {
-            ObjectStamp objectStamp = (ObjectStamp) ((LoadHubNode) value()).getValue().stamp();
+        if (value() instanceof LoadHubNode && ((LoadHubNode) value()).getValue().stamp(view) instanceof ObjectStamp) {
+            ObjectStamp objectStamp = (ObjectStamp) ((LoadHubNode) value()).getValue().stamp(view);
             if (objectStamp.type() != null) {
                 int validKeys = 0;
                 for (int i = 0; i < keyCount(); i++) {
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/java/UnsafeCompareAndSwapNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/java/UnsafeCompareAndSwapNode.java
index 951201be41f..4bbdb302829 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/java/UnsafeCompareAndSwapNode.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/java/UnsafeCompareAndSwapNode.java
@@ -30,6 +30,7 @@ import static org.graalvm.compiler.nodeinfo.NodeSize.SIZE_8;
 import org.graalvm.compiler.core.common.type.StampFactory;
 import org.graalvm.compiler.graph.NodeClass;
 import org.graalvm.compiler.nodeinfo.NodeInfo;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.ValueNode;
 import org.graalvm.compiler.nodes.memory.AbstractMemoryCheckpoint;
 import org.graalvm.compiler.nodes.memory.MemoryCheckpoint;
@@ -57,7 +58,7 @@ public final class UnsafeCompareAndSwapNode extends AbstractMemoryCheckpoint imp
 
     public UnsafeCompareAndSwapNode(ValueNode object, ValueNode offset, ValueNode expected, ValueNode newValue, JavaKind valueKind, LocationIdentity locationIdentity) {
         super(TYPE, StampFactory.forKind(JavaKind.Boolean.getStackKind()));
-        assert expected.stamp().isCompatible(newValue.stamp());
+        assert expected.stamp(NodeView.DEFAULT).isCompatible(newValue.stamp(NodeView.DEFAULT));
         this.object = object;
         this.offset = offset;
         this.expected = expected;
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/java/ValueCompareAndSwapNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/java/ValueCompareAndSwapNode.java
index 8f8b9536b5b..5125954d93c 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/java/ValueCompareAndSwapNode.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/java/ValueCompareAndSwapNode.java
@@ -28,6 +28,7 @@ import static org.graalvm.compiler.nodeinfo.NodeSize.SIZE_8;
 import org.graalvm.compiler.graph.NodeClass;
 import org.graalvm.compiler.lir.gen.LIRGeneratorTool;
 import org.graalvm.compiler.nodeinfo.NodeInfo;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.ValueNode;
 import org.graalvm.compiler.nodes.memory.address.AddressNode;
 import org.graalvm.compiler.nodes.spi.NodeLIRBuilderTool;
@@ -46,12 +47,12 @@ public final class ValueCompareAndSwapNode extends AbstractCompareAndSwapNode {
     }
 
     public ValueCompareAndSwapNode(AddressNode address, ValueNode expectedValue, ValueNode newValue, LocationIdentity location, BarrierType barrierType) {
-        super(TYPE, address, location, expectedValue, newValue, barrierType, expectedValue.stamp().meet(newValue.stamp()).unrestricted());
+        super(TYPE, address, location, expectedValue, newValue, barrierType, expectedValue.stamp(NodeView.DEFAULT).meet(newValue.stamp(NodeView.DEFAULT)).unrestricted());
     }
 
     @Override
     public void generate(NodeLIRBuilderTool gen) {
-        assert getNewValue().stamp().isCompatible(getExpectedValue().stamp());
+        assert getNewValue().stamp(NodeView.DEFAULT).isCompatible(getExpectedValue().stamp(NodeView.DEFAULT));
         LIRGeneratorTool tool = gen.getLIRGeneratorTool();
         assert !this.canDeoptimize();
         gen.setResult(this, tool.emitValueCompareAndSwap(gen.operand(getAddress()), gen.operand(getExpectedValue()), gen.operand(getNewValue())));
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/memory/Access.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/memory/Access.java
index c7aac9dcc52..f277b0ad1f0 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/memory/Access.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/memory/Access.java
@@ -30,6 +30,8 @@ public interface Access extends GuardedNode, HeapAccess {
 
     AddressNode getAddress();
 
+    void setAddress(AddressNode address);
+
     LocationIdentity getLocationIdentity();
 
     boolean canNullCheck();
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/memory/FixedAccessNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/memory/FixedAccessNode.java
index 06b3a7f5d2c..9df482b446b 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/memory/FixedAccessNode.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/memory/FixedAccessNode.java
@@ -54,6 +54,7 @@ public abstract class FixedAccessNode extends DeoptimizingFixedWithNextNode impl
         return address;
     }
 
+    @Override
     public void setAddress(AddressNode address) {
         updateUsages(this.address, address);
         this.address = address;
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/memory/FloatingAccessNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/memory/FloatingAccessNode.java
index 894b6f62691..43dd02ca8d3 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/memory/FloatingAccessNode.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/memory/FloatingAccessNode.java
@@ -58,6 +58,12 @@ public abstract class FloatingAccessNode extends FloatingGuardedNode implements
         return address;
     }
 
+    @Override
+    public void setAddress(AddressNode address) {
+        updateUsages(this.address, address);
+        this.address = address;
+    }
+
     @Override
     public LocationIdentity getLocationIdentity() {
         return location;
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/memory/FloatingReadNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/memory/FloatingReadNode.java
index 4d2705da114..40dc67fd9a6 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/memory/FloatingReadNode.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/memory/FloatingReadNode.java
@@ -35,6 +35,7 @@ import org.graalvm.compiler.graph.NodeClass;
 import org.graalvm.compiler.graph.spi.Canonicalizable;
 import org.graalvm.compiler.graph.spi.CanonicalizerTool;
 import org.graalvm.compiler.nodeinfo.NodeInfo;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.ValueNodeUtil;
 import org.graalvm.compiler.nodes.ValuePhiNode;
 import org.graalvm.compiler.nodes.extended.GuardingNode;
@@ -65,8 +66,8 @@ public final class FloatingReadNode extends FloatingAccessNode implements LIRLow
         this.lastLocationAccess = lastLocationAccess;
 
         // The input to floating reads must be always non-null or have at least a guard.
-        assert guard != null || !(address.getBase().stamp() instanceof ObjectStamp) || address.getBase() instanceof ValuePhiNode ||
-                        ((ObjectStamp) address.getBase().stamp()).nonNull() : address.getBase();
+        assert guard != null || !(address.getBase().stamp(NodeView.DEFAULT) instanceof ObjectStamp) || address.getBase() instanceof ValuePhiNode ||
+                        ((ObjectStamp) address.getBase().stamp(NodeView.DEFAULT)).nonNull() : address.getBase();
     }
 
     @Override
@@ -82,7 +83,7 @@ public final class FloatingReadNode extends FloatingAccessNode implements LIRLow
 
     @Override
     public void generate(NodeLIRBuilderTool gen) {
-        LIRKind readKind = gen.getLIRGeneratorTool().getLIRKind(stamp());
+        LIRKind readKind = gen.getLIRGeneratorTool().getLIRKind(stamp(NodeView.DEFAULT));
         gen.setResult(this, gen.getLIRGeneratorTool().getArithmetic().emitLoad(readKind, gen.operand(address), null));
     }
 
@@ -106,7 +107,7 @@ public final class FloatingReadNode extends FloatingAccessNode implements LIRLow
     @Override
     public FixedAccessNode asFixedNode() {
         try (DebugCloseable position = withNodeSourcePosition()) {
-            ReadNode result = graph().add(new ReadNode(getAddress(), getLocationIdentity(), stamp(), getBarrierType()));
+            ReadNode result = graph().add(new ReadNode(getAddress(), getLocationIdentity(), stamp(NodeView.DEFAULT), getBarrierType()));
             result.setGuard(getGuard());
             return result;
         }
@@ -121,6 +122,6 @@ public final class FloatingReadNode extends FloatingAccessNode implements LIRLow
 
     @Override
     public Stamp getAccessStamp() {
-        return stamp();
+        return stamp(NodeView.DEFAULT);
     }
 }
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/memory/ReadNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/memory/ReadNode.java
index aac2c80c812..fbe26e2653f 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/memory/ReadNode.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/memory/ReadNode.java
@@ -39,6 +39,7 @@ import org.graalvm.compiler.nodeinfo.NodeInfo;
 import org.graalvm.compiler.nodes.CanonicalizableLocation;
 import org.graalvm.compiler.nodes.ConstantNode;
 import org.graalvm.compiler.nodes.FrameState;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.ValueNode;
 import org.graalvm.compiler.nodes.extended.GuardingNode;
 import org.graalvm.compiler.nodes.memory.address.AddressNode;
@@ -94,7 +95,7 @@ public class ReadNode extends FloatableAccessNode implements LIRLowerableAccess,
     @Override
     public FloatingAccessNode asFloatingNode(MemoryNode lastLocationAccess) {
         try (DebugCloseable position = withNodeSourcePosition()) {
-            return graph().unique(new FloatingReadNode(getAddress(), getLocationIdentity(), lastLocationAccess, stamp(), getGuard(), getBarrierType()));
+            return graph().unique(new FloatingReadNode(getAddress(), getLocationIdentity(), lastLocationAccess, stamp(NodeView.DEFAULT), getGuard(), getBarrierType()));
         }
     }
 
@@ -104,6 +105,7 @@ public class ReadNode extends FloatableAccessNode implements LIRLowerableAccess,
     }
 
     public static ValueNode canonicalizeRead(ValueNode read, AddressNode address, LocationIdentity locationIdentity, CanonicalizerTool tool) {
+        NodeView view = NodeView.from(tool);
         MetaAccessProvider metaAccess = tool.getMetaAccess();
         if (tool.canonicalizeReads() && address instanceof OffsetAddressNode) {
             OffsetAddressNode objAddress = (OffsetAddressNode) address;
@@ -112,10 +114,10 @@ public class ReadNode extends FloatableAccessNode implements LIRLowerableAccess,
                 long displacement = objAddress.getOffset().asJavaConstant().asLong();
                 int stableDimension = ((ConstantNode) object).getStableDimension();
                 if (locationIdentity.isImmutable() || stableDimension > 0) {
-                    Constant constant = read.stamp().readConstant(tool.getConstantReflection().getMemoryAccessProvider(), object.asConstant(), displacement);
+                    Constant constant = read.stamp(view).readConstant(tool.getConstantReflection().getMemoryAccessProvider(), object.asConstant(), displacement);
                     boolean isDefaultStable = locationIdentity.isImmutable() || ((ConstantNode) object).isDefaultStable();
                     if (constant != null && (isDefaultStable || !constant.isDefaultForKind())) {
-                        return ConstantNode.forConstant(read.stamp(), constant, Math.max(stableDimension - 1, 0), isDefaultStable, metaAccess);
+                        return ConstantNode.forConstant(read.stamp(view), constant, Math.max(stableDimension - 1, 0), isDefaultStable, metaAccess);
                     }
                 }
             }
@@ -128,7 +130,7 @@ public class ReadNode extends FloatableAccessNode implements LIRLowerableAccess,
             if (locationIdentity instanceof CanonicalizableLocation) {
                 CanonicalizableLocation canonicalize = (CanonicalizableLocation) locationIdentity;
                 ValueNode result = canonicalize.canonicalizeRead(read, address, object, tool);
-                assert result != null && result.stamp().isCompatible(read.stamp());
+                assert result != null && result.stamp(view).isCompatible(read.stamp(view));
                 return result;
             }
 
@@ -148,6 +150,6 @@ public class ReadNode extends FloatableAccessNode implements LIRLowerableAccess,
 
     @Override
     public Stamp getAccessStamp() {
-        return stamp();
+        return stamp(NodeView.DEFAULT);
     }
 }
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/memory/WriteNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/memory/WriteNode.java
index bcce27b1a65..a2ecb533fd8 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/memory/WriteNode.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/memory/WriteNode.java
@@ -29,6 +29,7 @@ import org.graalvm.compiler.graph.NodeClass;
 import org.graalvm.compiler.graph.spi.Canonicalizable;
 import org.graalvm.compiler.graph.spi.CanonicalizerTool;
 import org.graalvm.compiler.nodeinfo.NodeInfo;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.ValueNode;
 import org.graalvm.compiler.nodes.memory.address.AddressNode;
 import org.graalvm.compiler.nodes.spi.NodeLIRBuilderTool;
@@ -52,7 +53,7 @@ public class WriteNode extends AbstractWriteNode implements LIRLowerableAccess,
 
     @Override
     public void generate(NodeLIRBuilderTool gen) {
-        LIRKind writeKind = gen.getLIRGeneratorTool().getLIRKind(value().stamp());
+        LIRKind writeKind = gen.getLIRGeneratorTool().getLIRKind(value().stamp(NodeView.DEFAULT));
         gen.getLIRGeneratorTool().getArithmetic().emitStore(writeKind, gen.operand(address), gen.operand(value()), gen.state(this));
     }
 
@@ -63,7 +64,7 @@ public class WriteNode extends AbstractWriteNode implements LIRLowerableAccess,
 
     @Override
     public Stamp getAccessStamp() {
-        return value().stamp();
+        return value().stamp(NodeView.DEFAULT);
     }
 
     @Override
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/memory/address/OffsetAddressNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/memory/address/OffsetAddressNode.java
index 345bef6cd99..2c73ce4537e 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/memory/address/OffsetAddressNode.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/memory/address/OffsetAddressNode.java
@@ -24,6 +24,7 @@ package org.graalvm.compiler.nodes.memory.address;
 
 import org.graalvm.compiler.core.common.type.AbstractPointerStamp;
 import org.graalvm.compiler.core.common.type.IntegerStamp;
+import org.graalvm.compiler.core.common.type.PrimitiveStamp;
 import org.graalvm.compiler.core.common.type.Stamp;
 import org.graalvm.compiler.graph.Node;
 import org.graalvm.compiler.graph.NodeClass;
@@ -31,6 +32,8 @@ import org.graalvm.compiler.graph.spi.Canonicalizable;
 import org.graalvm.compiler.graph.spi.CanonicalizerTool;
 import org.graalvm.compiler.nodeinfo.InputType;
 import org.graalvm.compiler.nodeinfo.NodeInfo;
+import org.graalvm.compiler.nodes.ConstantNode;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.ValueNode;
 import org.graalvm.compiler.nodes.calc.AddNode;
 import org.graalvm.compiler.nodes.calc.BinaryArithmeticNode;
@@ -54,8 +57,12 @@ public class OffsetAddressNode extends AddressNode implements Canonicalizable {
         this.base = base;
         this.offset = offset;
 
-        assert base != null && (base.stamp() instanceof AbstractPointerStamp || IntegerStamp.getBits(base.stamp()) == 64) &&
-                        offset != null && IntegerStamp.getBits(offset.stamp()) == 64 : "both values must have 64 bits";
+        assert base != null && (base.stamp(NodeView.DEFAULT) instanceof AbstractPointerStamp || IntegerStamp.getBits(base.stamp(NodeView.DEFAULT)) == 64) &&
+                        offset != null && IntegerStamp.getBits(offset.stamp(NodeView.DEFAULT)) == 64 : "both values must have 64 bits";
+    }
+
+    public static OffsetAddressNode create(ValueNode base) {
+        return new OffsetAddressNode(base, ConstantNode.forIntegerBits(PrimitiveStamp.getBits(base.stamp(NodeView.DEFAULT)), 0));
     }
 
     @Override
@@ -66,7 +73,7 @@ public class OffsetAddressNode extends AddressNode implements Canonicalizable {
     public void setBase(ValueNode base) {
         updateUsages(this.base, base);
         this.base = base;
-        assert base != null && (base.stamp() instanceof AbstractPointerStamp || IntegerStamp.getBits(base.stamp()) == 64);
+        assert base != null && (base.stamp(NodeView.DEFAULT) instanceof AbstractPointerStamp || IntegerStamp.getBits(base.stamp(NodeView.DEFAULT)) == 64);
     }
 
     public ValueNode getOffset() {
@@ -76,18 +83,16 @@ public class OffsetAddressNode extends AddressNode implements Canonicalizable {
     public void setOffset(ValueNode offset) {
         updateUsages(this.offset, offset);
         this.offset = offset;
-        assert offset != null && IntegerStamp.getBits(offset.stamp()) == 64;
+        assert offset != null && IntegerStamp.getBits(offset.stamp(NodeView.DEFAULT)) == 64;
     }
 
     @Override
     public Node canonical(CanonicalizerTool tool) {
-        if (base instanceof RawAddressNode) {
-            // The RawAddressNode is redundant, just directly use its input as base.
-            return new OffsetAddressNode(((RawAddressNode) base).getAddress(), offset);
-        } else if (base instanceof OffsetAddressNode) {
+        if (base instanceof OffsetAddressNode) {
+            NodeView view = NodeView.from(tool);
             // Rewrite (&base[offset1])[offset2] to base[offset1 + offset2].
             OffsetAddressNode b = (OffsetAddressNode) base;
-            return new OffsetAddressNode(b.getBase(), BinaryArithmeticNode.add(b.getOffset(), this.getOffset()));
+            return new OffsetAddressNode(b.getBase(), BinaryArithmeticNode.add(b.getOffset(), this.getOffset(), view));
         } else if (base instanceof AddNode) {
             AddNode add = (AddNode) base;
             if (add.getY().isConstant()) {
@@ -102,7 +107,7 @@ public class OffsetAddressNode extends AddressNode implements Canonicalizable {
 
     @Override
     public long getMaxConstantDisplacement() {
-        Stamp curStamp = offset.stamp();
+        Stamp curStamp = offset.stamp(NodeView.DEFAULT);
         if (curStamp instanceof IntegerStamp) {
             IntegerStamp integerStamp = (IntegerStamp) curStamp;
             if (integerStamp.lowerBound() >= 0) {
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/memory/address/RawAddressNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/memory/address/RawAddressNode.java
deleted file mode 100644
index 3a68bf45e04..00000000000
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/memory/address/RawAddressNode.java
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * Copyright (c) 2015, 2015, 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 org.graalvm.compiler.nodes.memory.address;
-
-import org.graalvm.compiler.graph.NodeClass;
-import org.graalvm.compiler.nodeinfo.InputType;
-import org.graalvm.compiler.nodeinfo.NodeInfo;
-import org.graalvm.compiler.nodes.ValueNode;
-
-/**
- * Convert a word-sized integer to a raw address.
- */
-@NodeInfo(allowedUsageTypes = InputType.Association)
-public class RawAddressNode extends AddressNode {
-    public static final NodeClass<RawAddressNode> TYPE = NodeClass.create(RawAddressNode.class);
-
-    @Input ValueNode address;
-
-    public RawAddressNode(ValueNode address) {
-        super(TYPE);
-        this.address = address;
-    }
-
-    public ValueNode getAddress() {
-        return address;
-    }
-
-    public void setAddress(ValueNode address) {
-        updateUsages(this.address, address);
-        this.address = address;
-    }
-
-    @Override
-    public ValueNode getBase() {
-        return address;
-    }
-
-    @Override
-    public long getMaxConstantDisplacement() {
-        return 0;
-    }
-
-    @Override
-    public ValueNode getIndex() {
-        return null;
-    }
-}
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/type/StampTool.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/type/StampTool.java
index dec36504dde..027b21f3c99 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/type/StampTool.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/type/StampTool.java
@@ -30,6 +30,7 @@ import org.graalvm.compiler.core.common.type.IntegerStamp;
 import org.graalvm.compiler.core.common.type.Stamp;
 import org.graalvm.compiler.core.common.type.StampFactory;
 import org.graalvm.compiler.core.common.type.TypeReference;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.ValueNode;
 
 import jdk.vm.ci.code.CodeUtil;
@@ -61,9 +62,9 @@ public class StampTool {
             ValueNode nextValue = iterator.next();
             if (nextValue != selfValue) {
                 if (stamp == null) {
-                    stamp = nextValue.stamp();
+                    stamp = nextValue.stamp(NodeView.DEFAULT);
                 } else {
-                    stamp = stamp.meet(nextValue.stamp());
+                    stamp = stamp.meet(nextValue.stamp(NodeView.DEFAULT));
                 }
             }
         }
@@ -132,7 +133,7 @@ public class StampTool {
      * @return true if this node represents a legal object value which is known to be always null
      */
     public static boolean isPointerAlwaysNull(ValueNode node) {
-        return isPointerAlwaysNull(node.stamp());
+        return isPointerAlwaysNull(node.stamp(NodeView.DEFAULT));
     }
 
     /**
@@ -158,7 +159,7 @@ public class StampTool {
      * @return true if this node represents a legal object value which is known to never be null
      */
     public static boolean isPointerNonNull(ValueNode node) {
-        return isPointerNonNull(node.stamp());
+        return isPointerNonNull(node.stamp(NodeView.DEFAULT));
     }
 
     /**
@@ -184,11 +185,11 @@ public class StampTool {
      * @return the Java type this value has if it is a legal Object type, null otherwise
      */
     public static TypeReference typeReferenceOrNull(ValueNode node) {
-        return typeReferenceOrNull(node.stamp());
+        return typeReferenceOrNull(node.stamp(NodeView.DEFAULT));
     }
 
     public static ResolvedJavaType typeOrNull(ValueNode node) {
-        return typeOrNull(node.stamp());
+        return typeOrNull(node.stamp(NodeView.DEFAULT));
     }
 
     public static ResolvedJavaType typeOrNull(Stamp stamp) {
@@ -210,7 +211,7 @@ public class StampTool {
     }
 
     public static ResolvedJavaType typeOrNull(ValueNode node, MetaAccessProvider metaAccess) {
-        return typeOrNull(node.stamp(), metaAccess);
+        return typeOrNull(node.stamp(NodeView.DEFAULT), metaAccess);
     }
 
     /**
@@ -242,7 +243,7 @@ public class StampTool {
      * @return true if this node represents a legal object value whose Java type is known exactly
      */
     public static boolean isExactType(ValueNode node) {
-        return isExactType(node.stamp());
+        return isExactType(node.stamp(NodeView.DEFAULT));
     }
 
     /**
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/util/GraphUtil.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/util/GraphUtil.java
index 4990cc79e70..fcb46500de5 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/util/GraphUtil.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/util/GraphUtil.java
@@ -55,6 +55,7 @@ import org.graalvm.compiler.nodes.GuardNode;
 import org.graalvm.compiler.nodes.LoopBeginNode;
 import org.graalvm.compiler.nodes.LoopEndNode;
 import org.graalvm.compiler.nodes.LoopExitNode;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.PhiNode;
 import org.graalvm.compiler.nodes.PiNode;
 import org.graalvm.compiler.nodes.ProxyNode;
@@ -652,7 +653,7 @@ public class GraphUtil {
         ValueNode n = node;
         while (n instanceof PiNode) {
             PiNode piNode = (PiNode) n;
-            ObjectStamp originalStamp = (ObjectStamp) piNode.getOriginalNode().stamp();
+            ObjectStamp originalStamp = (ObjectStamp) piNode.getOriginalNode().stamp(NodeView.DEFAULT);
             if (originalStamp.nonNull()) {
                 n = piNode.getOriginalNode();
             } else {
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/virtual/CommitAllocationNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/virtual/CommitAllocationNode.java
index 0e6839c3120..222dd1d7ed4 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/virtual/CommitAllocationNode.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/virtual/CommitAllocationNode.java
@@ -44,6 +44,7 @@ import org.graalvm.compiler.nodeinfo.NodeInfo;
 import org.graalvm.compiler.nodeinfo.NodeSize;
 import org.graalvm.compiler.nodeinfo.Verbosity;
 import org.graalvm.compiler.nodes.FixedWithNextNode;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.ValueNode;
 import org.graalvm.compiler.nodes.java.AbstractNewObjectNode;
 import org.graalvm.compiler.nodes.java.MonitorIdNode;
@@ -111,7 +112,7 @@ public final class CommitAllocationNode extends FixedWithNextNode implements Vir
     public void lower(LoweringTool tool) {
         for (int i = 0; i < virtualObjects.size(); i++) {
             if (ensureVirtual.get(i)) {
-                EnsureVirtualizedNode.ensureVirtualFailure(this, virtualObjects.get(i).stamp());
+                EnsureVirtualizedNode.ensureVirtualFailure(this, virtualObjects.get(i).stamp(NodeView.DEFAULT));
             }
         }
         tool.getLowerer().lower(this, tool);
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/virtual/EnsureVirtualizedNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/virtual/EnsureVirtualizedNode.java
index a24ccf926fd..13ec603f708 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/virtual/EnsureVirtualizedNode.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/virtual/EnsureVirtualizedNode.java
@@ -35,6 +35,7 @@ import org.graalvm.compiler.nodes.AbstractEndNode;
 import org.graalvm.compiler.nodes.FixedNode;
 import org.graalvm.compiler.nodes.FixedWithNextNode;
 import org.graalvm.compiler.nodes.Invoke;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.ValueNode;
 import org.graalvm.compiler.nodes.java.StoreFieldNode;
 import org.graalvm.compiler.nodes.spi.Lowerable;
@@ -76,7 +77,7 @@ public final class EnsureVirtualizedNode extends FixedWithNextNode implements Vi
 
     @Override
     public void lower(LoweringTool tool) {
-        ensureVirtualFailure(this, object.stamp());
+        ensureVirtualFailure(this, object.stamp(NodeView.DEFAULT));
     }
 
     public static void ensureVirtualFailure(Node location, Stamp stamp) {
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/AddressLoweringByUsePhase.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/AddressLoweringByUsePhase.java
index c6e8632e5e7..6c7f3c8ce15 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/AddressLoweringByUsePhase.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/AddressLoweringByUsePhase.java
@@ -23,10 +23,10 @@
  */
 package org.graalvm.compiler.phases.common;
 
-import jdk.vm.ci.meta.JavaKind;
 import org.graalvm.compiler.core.common.type.Stamp;
 import org.graalvm.compiler.core.common.type.StampFactory;
 import org.graalvm.compiler.graph.Node;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.PrefetchAllocateNode;
 import org.graalvm.compiler.nodes.StructuredGraph;
 import org.graalvm.compiler.nodes.ValueNode;
@@ -36,10 +36,11 @@ import org.graalvm.compiler.nodes.memory.FloatingReadNode;
 import org.graalvm.compiler.nodes.memory.ReadNode;
 import org.graalvm.compiler.nodes.memory.address.AddressNode;
 import org.graalvm.compiler.nodes.memory.address.OffsetAddressNode;
-import org.graalvm.compiler.nodes.memory.address.RawAddressNode;
 import org.graalvm.compiler.nodes.util.GraphUtil;
 import org.graalvm.compiler.phases.Phase;
 
+import jdk.vm.ci.meta.JavaKind;
+
 /**
  * Created by adinn on 09/05/17.
  */
@@ -66,22 +67,22 @@ public class AddressLoweringByUsePhase extends Phase {
             AddressNode lowered;
             if (node instanceof ReadNode) {
                 ReadNode readNode = (ReadNode) node;
-                Stamp stamp = readNode.stamp();
+                Stamp stamp = readNode.stamp(NodeView.DEFAULT);
                 address = readNode.getAddress();
                 lowered = lowering.lower(readNode, stamp, address);
             } else if (node instanceof JavaReadNode) {
                 JavaReadNode javaReadNode = (JavaReadNode) node;
-                Stamp stamp = javaReadNode.stamp();
+                Stamp stamp = javaReadNode.stamp(NodeView.DEFAULT);
                 address = javaReadNode.getAddress();
                 lowered = lowering.lower(javaReadNode, stamp, address);
             } else if (node instanceof FloatingReadNode) {
                 FloatingReadNode floatingReadNode = (FloatingReadNode) node;
-                Stamp stamp = floatingReadNode.stamp();
+                Stamp stamp = floatingReadNode.stamp(NodeView.DEFAULT);
                 address = floatingReadNode.getAddress();
                 lowered = lowering.lower(floatingReadNode, stamp, address);
             } else if (node instanceof AbstractWriteNode) {
                 AbstractWriteNode abstractWriteNode = (AbstractWriteNode) node;
-                Stamp stamp = abstractWriteNode.value().stamp();
+                Stamp stamp = abstractWriteNode.value().stamp(NodeView.DEFAULT);
                 address = abstractWriteNode.getAddress();
                 lowered = lowering.lower(abstractWriteNode, stamp, address);
             } else if (node instanceof PrefetchAllocateNode) {
@@ -108,7 +109,7 @@ public class AddressLoweringByUsePhase extends Phase {
         // now replace any remaining unlowered address nodes
         for (Node node : graph.getNodes()) {
             AddressNode lowered;
-            if (node instanceof RawAddressNode || node instanceof OffsetAddressNode) {
+            if (node instanceof OffsetAddressNode) {
                 AddressNode address = (AddressNode) node;
                 lowered = lowering.lower(address);
             } else {
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/AddressLoweringPhase.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/AddressLoweringPhase.java
index dbdada7b0ce..333fbccb610 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/AddressLoweringPhase.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/AddressLoweringPhase.java
@@ -27,16 +27,12 @@ import org.graalvm.compiler.nodes.StructuredGraph;
 import org.graalvm.compiler.nodes.ValueNode;
 import org.graalvm.compiler.nodes.memory.address.AddressNode;
 import org.graalvm.compiler.nodes.memory.address.OffsetAddressNode;
-import org.graalvm.compiler.nodes.memory.address.RawAddressNode;
 import org.graalvm.compiler.nodes.util.GraphUtil;
 import org.graalvm.compiler.phases.Phase;
 
 public class AddressLoweringPhase extends Phase {
 
     public abstract static class AddressLowering {
-
-        public abstract AddressNode lower(ValueNode address);
-
         public abstract AddressNode lower(ValueNode base, ValueNode offset);
     }
 
@@ -51,10 +47,7 @@ public class AddressLoweringPhase extends Phase {
     protected void run(StructuredGraph graph) {
         for (Node node : graph.getNodes()) {
             AddressNode lowered;
-            if (node instanceof RawAddressNode) {
-                RawAddressNode address = (RawAddressNode) node;
-                lowered = lowering.lower(address.getAddress());
-            } else if (node instanceof OffsetAddressNode) {
+            if (node instanceof OffsetAddressNode) {
                 OffsetAddressNode address = (OffsetAddressNode) node;
                 lowered = lowering.lower(address.getBase(), address.getOffset());
             } else {
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/CanonicalizerPhase.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/CanonicalizerPhase.java
index 232fd32c383..957984e2242 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/CanonicalizerPhase.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/CanonicalizerPhase.java
@@ -23,6 +23,7 @@
 package org.graalvm.compiler.phases.common;
 
 import org.graalvm.compiler.core.common.spi.ConstantFieldProvider;
+import org.graalvm.compiler.core.common.type.Stamp;
 import org.graalvm.compiler.debug.CounterKey;
 import org.graalvm.compiler.debug.DebugCloseable;
 import org.graalvm.compiler.debug.DebugContext;
@@ -44,6 +45,7 @@ import org.graalvm.compiler.nodes.ConstantNode;
 import org.graalvm.compiler.nodes.ControlSinkNode;
 import org.graalvm.compiler.nodes.FixedNode;
 import org.graalvm.compiler.nodes.FixedWithNextNode;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.StartNode;
 import org.graalvm.compiler.nodes.StructuredGraph;
 import org.graalvm.compiler.nodes.ValueNode;
@@ -153,6 +155,10 @@ public class CanonicalizerPhase extends BasePhase<PhaseContext> {
         new Instance(context, workingSet, newNodesMark).apply(graph, dumpGraph);
     }
 
+    public NodeView getNodeView() {
+        return NodeView.DEFAULT;
+    }
+
     private final class Instance extends Phase {
 
         private final Mark newNodesMark;
@@ -260,9 +266,9 @@ public class CanonicalizerPhase extends BasePhase<PhaseContext> {
             if (node instanceof ValueNode) {
                 ValueNode valueNode = (ValueNode) node;
                 boolean improvedStamp = tryInferStamp(valueNode);
-                Constant constant = valueNode.stamp().asConstant();
+                Constant constant = valueNode.stamp(NodeView.DEFAULT).asConstant();
                 if (constant != null && !(node instanceof ConstantNode)) {
-                    ConstantNode stampConstant = ConstantNode.forConstant(valueNode.stamp(), constant, context.getMetaAccess(), graph);
+                    ConstantNode stampConstant = ConstantNode.forConstant(valueNode.stamp(NodeView.DEFAULT), constant, context.getMetaAccess(), graph);
                     debug.log("Canonicalizer: constant stamp replaces %1s with %1s", valueNode, stampConstant);
                     valueNode.replaceAtUsages(InputType.Value, stampConstant);
                     GraphUtil.tryKillUnused(valueNode);
@@ -442,14 +448,16 @@ public class CanonicalizerPhase extends BasePhase<PhaseContext> {
             return false;
         }
 
-        private final class Tool implements SimplifierTool {
+        private final class Tool implements SimplifierTool, NodeView {
 
             private final Assumptions assumptions;
             private final OptionValues options;
+            private NodeView nodeView;
 
             Tool(Assumptions assumptions, OptionValues options) {
                 this.assumptions = assumptions;
                 this.options = options;
+                this.nodeView = getNodeView();
             }
 
             @Override
@@ -513,6 +521,11 @@ public class CanonicalizerPhase extends BasePhase<PhaseContext> {
             public OptionValues getOptions() {
                 return options;
             }
+
+            @Override
+            public Stamp stamp(ValueNode node) {
+                return nodeView.stamp(node);
+            }
         }
     }
 
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/ConditionalEliminationPhase.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/ConditionalEliminationPhase.java
index 3530b938204..0f1c4206191 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/ConditionalEliminationPhase.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/ConditionalEliminationPhase.java
@@ -58,6 +58,7 @@ import org.graalvm.compiler.nodes.IfNode;
 import org.graalvm.compiler.nodes.LogicNode;
 import org.graalvm.compiler.nodes.LoopExitNode;
 import org.graalvm.compiler.nodes.MergeNode;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.ParameterNode;
 import org.graalvm.compiler.nodes.PiNode;
 import org.graalvm.compiler.nodes.ProxyNode;
@@ -414,7 +415,7 @@ public class ConditionalEliminationPhase extends BasePhase<PhaseContext> {
                     Stamp bestPossibleStamp = null;
                     for (int i = 0; i < phi.valueCount(); ++i) {
                         ValueNode valueAt = phi.valueAt(i);
-                        Stamp curBestStamp = valueAt.stamp();
+                        Stamp curBestStamp = valueAt.stamp(NodeView.DEFAULT);
                         InfoElement infoElement = phiInfoElements.get(merge.forwardEndAt(i));
                         if (infoElement != null) {
                             curBestStamp = curBestStamp.join(infoElement.getStamp());
@@ -427,7 +428,7 @@ public class ConditionalEliminationPhase extends BasePhase<PhaseContext> {
                         }
                     }
 
-                    Stamp oldStamp = phi.stamp();
+                    Stamp oldStamp = phi.stamp(NodeView.DEFAULT);
                     if (oldStamp.tryImproveWith(bestPossibleStamp) != null) {
 
                         // Need to be careful to not run into stamp update cycles with the iterative
@@ -460,7 +461,7 @@ public class ConditionalEliminationPhase extends BasePhase<PhaseContext> {
                             ValuePhiNode newPhi = graph.addWithoutUnique(new ValuePhiNode(bestPossibleStamp, merge));
                             for (int i = 0; i < phi.valueCount(); ++i) {
                                 ValueNode valueAt = phi.valueAt(i);
-                                if (bestPossibleStamp.meet(valueAt.stamp()).equals(bestPossibleStamp)) {
+                                if (bestPossibleStamp.meet(valueAt.stamp(NodeView.DEFAULT)).equals(bestPossibleStamp)) {
                                     // Pi not required here.
                                 } else {
                                     InfoElement infoElement = phiInfoElements.get(merge.forwardEndAt(i));
@@ -494,7 +495,7 @@ public class ConditionalEliminationPhase extends BasePhase<PhaseContext> {
                     InfoElement infoElement = this.getInfoElements(valueAt);
                     while (infoElement != null) {
                         Stamp newStamp = infoElement.getStamp();
-                        if (phi.stamp().tryImproveWith(newStamp) != null) {
+                        if (phi.stamp(NodeView.DEFAULT).tryImproveWith(newStamp) != null) {
                             if (mergeMap == null) {
                                 mergeMap = EconomicMap.create();
                                 mergeMaps.put(merge, mergeMap);
@@ -547,7 +548,7 @@ public class ConditionalEliminationPhase extends BasePhase<PhaseContext> {
                              * It's equivalent to or'ing in the mask value since those values are
                              * known to be set.
                              */
-                            BinaryOp<Or> op = ArithmeticOpTable.forStamp(x.stamp()).getOr();
+                            BinaryOp<Or> op = ArithmeticOpTable.forStamp(x.stamp(NodeView.DEFAULT)).getOr();
                             IntegerStamp newStampX = (IntegerStamp) op.foldStamp(getSafeStamp(andX), getOtherSafeStamp(y));
                             registerNewStamp(andX, newStampX, guard);
                         }
@@ -580,7 +581,7 @@ public class ConditionalEliminationPhase extends BasePhase<PhaseContext> {
                 if (y.isConstant()) {
                     InfoElement infoElement = getInfoElements(x);
                     while (infoElement != null) {
-                        Stamp result = binary.foldStamp(infoElement.stamp, y.stamp());
+                        Stamp result = binary.foldStamp(infoElement.stamp, y.stamp(NodeView.DEFAULT));
                         if (result != null) {
                             return Pair.create(infoElement, result);
                         }
@@ -597,7 +598,7 @@ public class ConditionalEliminationPhase extends BasePhase<PhaseContext> {
          * registered info elements is in the same chain of pi nodes.
          */
         private static Stamp getSafeStamp(ValueNode x) {
-            return x.stamp();
+            return x.stamp(NodeView.DEFAULT);
         }
 
         /**
@@ -610,9 +611,9 @@ public class ConditionalEliminationPhase extends BasePhase<PhaseContext> {
          */
         private static Stamp getOtherSafeStamp(ValueNode x) {
             if (x.isConstant()) {
-                return x.stamp();
+                return x.stamp(NodeView.DEFAULT);
             }
-            return x.stamp().unrestricted();
+            return x.stamp(NodeView.DEFAULT).unrestricted();
         }
 
         /**
@@ -777,7 +778,7 @@ public class ConditionalEliminationPhase extends BasePhase<PhaseContext> {
                 ValueNode y = binaryOpLogicNode.getY();
                 infoElement = getInfoElements(x);
                 while (infoElement != null) {
-                    TriState result = binaryOpLogicNode.tryFold(infoElement.getStamp(), y.stamp());
+                    TriState result = binaryOpLogicNode.tryFold(infoElement.getStamp(), y.stamp(NodeView.DEFAULT));
                     if (result.isKnown()) {
                         return rewireGuards(infoElement.getGuard(), result.toBoolean(), infoElement.getProxifiedInput(), infoElement.getStamp(), rewireGuardFunction);
                     }
@@ -787,7 +788,7 @@ public class ConditionalEliminationPhase extends BasePhase<PhaseContext> {
                 if (y.isConstant()) {
                     Pair<InfoElement, Stamp> foldResult = recursiveFoldStampFromInfo(x);
                     if (foldResult != null) {
-                        TriState result = binaryOpLogicNode.tryFold(foldResult.getRight(), y.stamp());
+                        TriState result = binaryOpLogicNode.tryFold(foldResult.getRight(), y.stamp(NodeView.DEFAULT));
                         if (result.isKnown()) {
                             return rewireGuards(foldResult.getLeft().getGuard(), result.toBoolean(), foldResult.getLeft().getProxifiedInput(), foldResult.getRight(), rewireGuardFunction);
                         }
@@ -795,7 +796,7 @@ public class ConditionalEliminationPhase extends BasePhase<PhaseContext> {
                 } else {
                     infoElement = getInfoElements(y);
                     while (infoElement != null) {
-                        TriState result = binaryOpLogicNode.tryFold(x.stamp(), infoElement.getStamp());
+                        TriState result = binaryOpLogicNode.tryFold(x.stamp(NodeView.DEFAULT), infoElement.getStamp());
                         if (result.isKnown()) {
                             return rewireGuards(infoElement.getGuard(), result.toBoolean(), infoElement.getProxifiedInput(), infoElement.getStamp(), rewireGuardFunction);
                         }
@@ -815,8 +816,8 @@ public class ConditionalEliminationPhase extends BasePhase<PhaseContext> {
                     if (binary.getY().isConstant()) {
                         infoElement = getInfoElements(binary.getX());
                         while (infoElement != null) {
-                            Stamp newStampX = binary.foldStamp(infoElement.getStamp(), binary.getY().stamp());
-                            TriState result = binaryOpLogicNode.tryFold(newStampX, y.stamp());
+                            Stamp newStampX = binary.foldStamp(infoElement.getStamp(), binary.getY().stamp(NodeView.DEFAULT));
+                            TriState result = binaryOpLogicNode.tryFold(newStampX, y.stamp(NodeView.DEFAULT));
                             if (result.isKnown()) {
                                 return rewireGuards(infoElement.getGuard(), result.toBoolean(), infoElement.getProxifiedInput(), newStampX, rewireGuardFunction);
                             }
@@ -834,7 +835,7 @@ public class ConditionalEliminationPhase extends BasePhase<PhaseContext> {
                              * It's equivalent to or'ing in the mask value since those values are
                              * known to be set.
                              */
-                            BinaryOp<Or> op = ArithmeticOpTable.forStamp(x.stamp()).getOr();
+                            BinaryOp<Or> op = ArithmeticOpTable.forStamp(x.stamp(NodeView.DEFAULT)).getOr();
                             IntegerStamp newStampX = (IntegerStamp) op.foldStamp(getSafeStamp(and.getX()), getOtherSafeStamp(y));
                             if (foldPendingTest(thisGuard, and.getX(), newStampX, rewireGuardFunction)) {
                                 return true;
@@ -899,7 +900,7 @@ public class ConditionalEliminationPhase extends BasePhase<PhaseContext> {
                 do {
                     counterStampsRegistered.increment(debug);
                     debug.log("\t Saving stamp for node %s stamp %s guarded by %s", value, stamp, guard);
-                    assert value instanceof LogicNode || stamp.isCompatible(value.stamp()) : stamp + " vs. " + value.stamp() + " (" + value + ")";
+                    assert value instanceof LogicNode || stamp.isCompatible(value.stamp(NodeView.DEFAULT)) : stamp + " vs. " + value.stamp(NodeView.DEFAULT) + " (" + value + ")";
                     map.setAndGrow(value, new InfoElement(stamp, guard, proxiedValue, map.getAndGrow(value)));
                     undoOperations.push(value);
                     if (value instanceof StampInverter) {
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/ExpandLogicPhase.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/ExpandLogicPhase.java
index 81caff4a201..889c11cb67e 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/ExpandLogicPhase.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/ExpandLogicPhase.java
@@ -35,6 +35,7 @@ import org.graalvm.compiler.nodes.EndNode;
 import org.graalvm.compiler.nodes.IfNode;
 import org.graalvm.compiler.nodes.LogicNode;
 import org.graalvm.compiler.nodes.MergeNode;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.ShortCircuitOrNode;
 import org.graalvm.compiler.nodes.StructuredGraph;
 import org.graalvm.compiler.nodes.ValueNode;
@@ -68,15 +69,15 @@ public class ExpandLogicPhase extends Phase {
         StructuredGraph graph = normalize.graph();
         ValueNode x = normalize.getX();
         ValueNode y = normalize.getY();
-        if (x.stamp() instanceof FloatStamp) {
-            equalComp = graph.addOrUniqueWithInputs(FloatEqualsNode.create(x, y));
-            lessComp = graph.addOrUniqueWithInputs(FloatLessThanNode.create(x, y, normalize.isUnorderedLess()));
+        if (x.stamp(NodeView.DEFAULT) instanceof FloatStamp) {
+            equalComp = graph.addOrUniqueWithInputs(FloatEqualsNode.create(x, y, NodeView.DEFAULT));
+            lessComp = graph.addOrUniqueWithInputs(FloatLessThanNode.create(x, y, normalize.isUnorderedLess(), NodeView.DEFAULT));
         } else {
-            equalComp = graph.addOrUniqueWithInputs(IntegerEqualsNode.create(x, y));
-            lessComp = graph.addOrUniqueWithInputs(IntegerLessThanNode.create(x, y));
+            equalComp = graph.addOrUniqueWithInputs(IntegerEqualsNode.create(x, y, NodeView.DEFAULT));
+            lessComp = graph.addOrUniqueWithInputs(IntegerLessThanNode.create(x, y, NodeView.DEFAULT));
         }
 
-        Stamp stamp = normalize.stamp();
+        Stamp stamp = normalize.stamp(NodeView.DEFAULT);
         ConditionalNode equalValue = graph.unique(
                         new ConditionalNode(equalComp, ConstantNode.forIntegerStamp(stamp, 0, graph), ConstantNode.forIntegerStamp(stamp, 1, graph)));
         ConditionalNode value = graph.unique(new ConditionalNode(lessComp, ConstantNode.forIntegerStamp(stamp, -1, graph), equalValue));
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/FixReadsPhase.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/FixReadsPhase.java
index b1379d9b0ca..0d183bdeb0e 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/FixReadsPhase.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/FixReadsPhase.java
@@ -42,6 +42,7 @@ import org.graalvm.compiler.nodes.EndNode;
 import org.graalvm.compiler.nodes.IfNode;
 import org.graalvm.compiler.nodes.LogicNode;
 import org.graalvm.compiler.nodes.MergeNode;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.PhiNode;
 import org.graalvm.compiler.nodes.PiNode;
 import org.graalvm.compiler.nodes.StructuredGraph;
@@ -115,7 +116,7 @@ public class FixReadsPhase extends BasePhase<LowTierContext> {
                 replaceCurrent(fixedAccess);
             } else if (node instanceof PiNode) {
                 PiNode piNode = (PiNode) node;
-                if (piNode.stamp().isCompatible(piNode.getOriginalNode().stamp())) {
+                if (piNode.stamp(NodeView.DEFAULT).isCompatible(piNode.getOriginalNode().stamp(NodeView.DEFAULT))) {
                     // Pi nodes are no longer necessary at this point.
                     piNode.replaceAndDelete(piNode.getOriginalNode());
                 }
@@ -178,7 +179,7 @@ public class FixReadsPhase extends BasePhase<LowTierContext> {
                                 }
                                 counterConstantInputReplacements.increment(node.getDebug());
                                 ConstantNode stampConstant = ConstantNode.forConstant(bestStamp, constant, metaAccess, graph);
-                                assert stampConstant.stamp().isCompatible(valueNode.stamp());
+                                assert stampConstant.stamp(NodeView.DEFAULT).isCompatible(valueNode.stamp(NodeView.DEFAULT));
                                 replaceInput(p, node, stampConstant);
                                 replacements++;
                             }
@@ -258,7 +259,7 @@ public class FixReadsPhase extends BasePhase<LowTierContext> {
                                 bestStamp = bestStamp.meet(currentEndMap.get(phi));
                             }
 
-                            if (!bestStamp.equals(phi.stamp())) {
+                            if (!bestStamp.equals(phi.stamp(NodeView.DEFAULT))) {
                                 endMap.put(phi, bestStamp);
                             }
                         }
@@ -293,7 +294,7 @@ public class FixReadsPhase extends BasePhase<LowTierContext> {
                                     bestStamp = bestStamp.meet(otherEndsStamp);
                                 }
 
-                                if (nodeWithNewStamp.stamp().tryImproveWith(bestStamp) == null) {
+                                if (nodeWithNewStamp.stamp(NodeView.DEFAULT).tryImproveWith(bestStamp) == null) {
                                     // No point in registering the stamp.
                                 } else {
                                     endMap.put(nodeWithNewStamp, bestStamp);
@@ -462,7 +463,7 @@ public class FixReadsPhase extends BasePhase<LowTierContext> {
             ValueNode originalNode = value;
             StampElement currentStamp = stampMap.getAndGrow(originalNode);
             if (currentStamp == null) {
-                return value.stamp();
+                return value.stamp(NodeView.DEFAULT);
             }
             return currentStamp.getStamp();
         }
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/NonNullParametersPhase.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/NonNullParametersPhase.java
index a25a2b8ad62..e1094687706 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/NonNullParametersPhase.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/NonNullParametersPhase.java
@@ -25,6 +25,7 @@ package org.graalvm.compiler.phases.common;
 import org.graalvm.compiler.core.common.type.ObjectStamp;
 import org.graalvm.compiler.core.common.type.Stamp;
 import org.graalvm.compiler.core.common.type.StampFactory;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.ParameterNode;
 import org.graalvm.compiler.nodes.StructuredGraph;
 import org.graalvm.compiler.phases.Phase;
@@ -39,8 +40,8 @@ public class NonNullParametersPhase extends Phase {
     protected void run(StructuredGraph graph) {
         Stamp nonNull = StampFactory.objectNonNull();
         for (ParameterNode param : graph.getNodes(ParameterNode.TYPE)) {
-            if (param.stamp() instanceof ObjectStamp) {
-                ObjectStamp paramStamp = (ObjectStamp) param.stamp();
+            if (param.stamp(NodeView.DEFAULT) instanceof ObjectStamp) {
+                ObjectStamp paramStamp = (ObjectStamp) param.stamp(NodeView.DEFAULT);
                 param.setStamp(paramStamp.join(nonNull));
             }
         }
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/ProfileCompiledMethodsPhase.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/ProfileCompiledMethodsPhase.java
index c395bcca3fc..1f632ea2faf 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/ProfileCompiledMethodsPhase.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/ProfileCompiledMethodsPhase.java
@@ -160,7 +160,7 @@ public class ProfileCompiledMethodsPhase extends Phase {
             return 10;
         } else if (node instanceof Access) {
             return 2;
-        } else if (node instanceof LogicNode || node instanceof ConvertNode || node instanceof BinaryNode || node instanceof NotNode) {
+        } else if (node instanceof LogicNode || node instanceof ConvertNode || node instanceof NotNode) {
             return 1;
         } else if (node instanceof IntegerDivRemNode || node instanceof FloatDivNode || node instanceof RemNode) {
             return 10;
@@ -168,7 +168,7 @@ public class ProfileCompiledMethodsPhase extends Phase {
             return 3;
         } else if (node instanceof Invoke) {
             return 5;
-        } else if (node instanceof IfNode || node instanceof SafepointNode) {
+        } else if (node instanceof IfNode || node instanceof SafepointNode || node instanceof BinaryNode) {
             return 1;
         } else if (node instanceof SwitchNode) {
             return node.successors().count();
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/inlining/InliningUtil.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/inlining/InliningUtil.java
index 2711f097b43..f0db2b4df29 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/inlining/InliningUtil.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/inlining/InliningUtil.java
@@ -69,6 +69,7 @@ import org.graalvm.compiler.nodes.InvokeWithExceptionNode;
 import org.graalvm.compiler.nodes.KillingBeginNode;
 import org.graalvm.compiler.nodes.LogicNode;
 import org.graalvm.compiler.nodes.MergeNode;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.ParameterNode;
 import org.graalvm.compiler.nodes.PhiNode;
 import org.graalvm.compiler.nodes.PiNode;
@@ -827,7 +828,7 @@ public class InliningUtil extends ValueMergeUtil {
         if (newReceiver.getStackKind() == JavaKind.Object) {
 
             if (invoke.getInvokeKind() == InvokeKind.Special) {
-                Stamp paramStamp = newReceiver.stamp();
+                Stamp paramStamp = newReceiver.stamp(NodeView.DEFAULT);
                 Stamp stamp = paramStamp.join(StampFactory.object(TypeReference.create(graph.getAssumptions(), callTarget.targetMethod().getDeclaringClass())));
                 if (!stamp.equals(paramStamp)) {
                     // The verifier and previous optimizations guarantee unconditionally that the
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/inlining/info/MultiTypeGuardInlineInfo.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/inlining/info/MultiTypeGuardInlineInfo.java
index f01aec572b2..c117547930f 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/inlining/info/MultiTypeGuardInlineInfo.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/inlining/info/MultiTypeGuardInlineInfo.java
@@ -39,6 +39,7 @@ import org.graalvm.compiler.nodes.FrameState;
 import org.graalvm.compiler.nodes.Invoke;
 import org.graalvm.compiler.nodes.InvokeWithExceptionNode;
 import org.graalvm.compiler.nodes.MergeNode;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.PhiNode;
 import org.graalvm.compiler.nodes.PiNode;
 import org.graalvm.compiler.nodes.StructuredGraph;
@@ -191,7 +192,7 @@ public class MultiTypeGuardInlineInfo extends AbstractInlineInfo {
 
         PhiNode returnValuePhi = null;
         if (invoke.asNode().getStackKind() != JavaKind.Void) {
-            returnValuePhi = graph.addWithoutUnique(new ValuePhiNode(invoke.asNode().stamp().unrestricted(), returnMerge));
+            returnValuePhi = graph.addWithoutUnique(new ValuePhiNode(invoke.asNode().stamp(NodeView.DEFAULT).unrestricted(), returnMerge));
         }
 
         AbstractMergeNode exceptionMerge = null;
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/inlining/info/TypeGuardInlineInfo.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/inlining/info/TypeGuardInlineInfo.java
index 969458fedbb..1127d5d76fc 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/inlining/info/TypeGuardInlineInfo.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/inlining/info/TypeGuardInlineInfo.java
@@ -29,6 +29,7 @@ import org.graalvm.compiler.nodes.ConstantNode;
 import org.graalvm.compiler.nodes.FixedGuardNode;
 import org.graalvm.compiler.nodes.Invoke;
 import org.graalvm.compiler.nodes.LogicNode;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.StructuredGraph;
 import org.graalvm.compiler.nodes.ValueNode;
 import org.graalvm.compiler.nodes.calc.CompareNode;
@@ -111,9 +112,9 @@ public class TypeGuardInlineInfo extends AbstractInlineInfo {
     private void createGuard(StructuredGraph graph, Providers providers) {
         ValueNode nonNullReceiver = InliningUtil.nonNullReceiver(invoke);
         LoadHubNode receiverHub = graph.unique(new LoadHubNode(providers.getStampProvider(), nonNullReceiver));
-        ConstantNode typeHub = ConstantNode.forConstant(receiverHub.stamp(), providers.getConstantReflection().asObjectHub(type), providers.getMetaAccess(), graph);
+        ConstantNode typeHub = ConstantNode.forConstant(receiverHub.stamp(NodeView.DEFAULT), providers.getConstantReflection().asObjectHub(type), providers.getMetaAccess(), graph);
 
-        LogicNode typeCheck = CompareNode.createCompareNode(graph, Condition.EQ, receiverHub, typeHub, providers.getConstantReflection());
+        LogicNode typeCheck = CompareNode.createCompareNode(graph, Condition.EQ, receiverHub, typeHub, providers.getConstantReflection(), NodeView.DEFAULT);
         FixedGuardNode guard = graph.add(new FixedGuardNode(typeCheck, DeoptimizationReason.TypeCheckedInliningViolated, DeoptimizationAction.InvalidateReprofile));
         assert invoke.predecessor() != null;
 
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/inlining/info/elem/InlineableGraph.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/inlining/info/elem/InlineableGraph.java
index 35d076543a6..317a0556b58 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/inlining/info/elem/InlineableGraph.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/inlining/info/elem/InlineableGraph.java
@@ -33,6 +33,7 @@ import org.graalvm.compiler.graph.Node;
 import org.graalvm.compiler.graph.NodeInputList;
 import org.graalvm.compiler.nodes.ConstantNode;
 import org.graalvm.compiler.nodes.Invoke;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.ParameterNode;
 import org.graalvm.compiler.nodes.StructuredGraph;
 import org.graalvm.compiler.nodes.StructuredGraph.AllowAssumptions;
@@ -120,8 +121,8 @@ public class InlineableGraph implements Inlineable {
     }
 
     private static Stamp improvedStamp(ValueNode arg, ParameterNode param) {
-        Stamp joinedStamp = param.stamp().join(arg.stamp());
-        if (joinedStamp == null || joinedStamp.equals(param.stamp())) {
+        Stamp joinedStamp = param.stamp(NodeView.DEFAULT).join(arg.stamp(NodeView.DEFAULT));
+        if (joinedStamp == null || joinedStamp.equals(param.stamp(NodeView.DEFAULT))) {
             return null;
         }
         return joinedStamp;
@@ -162,7 +163,7 @@ public class InlineableGraph implements Inlineable {
                     parameterUsages = trackParameterUsages(param, parameterUsages);
                     // collect param usages before replacing the param
                     param.replaceAtUsagesAndDelete(graph.unique(
-                                    ConstantNode.forConstant(arg.stamp(), constant.getValue(), constant.getStableDimension(), constant.isDefaultStable(), context.getMetaAccess())));
+                                    ConstantNode.forConstant(arg.stamp(NodeView.DEFAULT), constant.getValue(), constant.getStableDimension(), constant.isDefaultStable(), context.getMetaAccess())));
                     // param-node gone, leaving a gap in the sequence given by param.index()
                 } else {
                     Stamp impro = improvedStamp(arg, param);
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/inlining/walker/InliningData.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/inlining/walker/InliningData.java
index 56475ba11e9..ed6a1208f27 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/inlining/walker/InliningData.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/inlining/walker/InliningData.java
@@ -42,6 +42,7 @@ import org.graalvm.compiler.graph.Graph;
 import org.graalvm.compiler.graph.Node;
 import org.graalvm.compiler.nodes.CallTargetNode;
 import org.graalvm.compiler.nodes.Invoke;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.ParameterNode;
 import org.graalvm.compiler.nodes.StructuredGraph;
 import org.graalvm.compiler.nodes.ValueNode;
@@ -192,10 +193,10 @@ public class InliningData {
         assert callTarget.invokeKind().isIndirect();
 
         ResolvedJavaType holder = targetMethod.getDeclaringClass();
-        if (!(callTarget.receiver().stamp() instanceof ObjectStamp)) {
+        if (!(callTarget.receiver().stamp(NodeView.DEFAULT) instanceof ObjectStamp)) {
             return null;
         }
-        ObjectStamp receiverStamp = (ObjectStamp) callTarget.receiver().stamp();
+        ObjectStamp receiverStamp = (ObjectStamp) callTarget.receiver().stamp(NodeView.DEFAULT);
         if (receiverStamp.alwaysNull()) {
             // Don't inline if receiver is known to be null
             return null;
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases/src/org/graalvm/compiler/phases/graph/InferStamps.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases/src/org/graalvm/compiler/phases/graph/InferStamps.java
index 1bd1ad5ea20..85281e1a19c 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases/src/org/graalvm/compiler/phases/graph/InferStamps.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases/src/org/graalvm/compiler/phases/graph/InferStamps.java
@@ -24,6 +24,7 @@ package org.graalvm.compiler.phases.graph;
 
 import org.graalvm.compiler.core.common.type.ObjectStamp;
 import org.graalvm.compiler.graph.Node;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.StructuredGraph;
 import org.graalvm.compiler.nodes.ValueNode;
 import org.graalvm.compiler.nodes.ValuePhiNode;
@@ -50,9 +51,9 @@ public class InferStamps {
         for (Node n : graph.getNodes()) {
             if (n instanceof ValuePhiNode) {
                 ValueNode node = (ValueNode) n;
-                if (node.stamp() instanceof ObjectStamp) {
-                    assert node.stamp().hasValues() : "We assume all Phi and Proxy stamps are legal before the analysis";
-                    node.setStamp(node.stamp().empty());
+                if (node.stamp(NodeView.DEFAULT) instanceof ObjectStamp) {
+                    assert node.stamp(NodeView.DEFAULT).hasValues() : "We assume all Phi and Proxy stamps are legal before the analysis";
+                    node.setStamp(node.stamp(NodeView.DEFAULT).empty());
                 }
             }
         }
@@ -71,7 +72,7 @@ public class InferStamps {
             for (Node n : graph.getNodes()) {
                 if (n instanceof ValueNode) {
                     ValueNode node = (ValueNode) n;
-                    if (node.stamp() instanceof ObjectStamp) {
+                    if (node.stamp(NodeView.DEFAULT) instanceof ObjectStamp) {
                         stampChanged |= node.inferStamp();
                     }
                 }
@@ -90,7 +91,8 @@ public class InferStamps {
         for (Node n : graph.getNodes()) {
             if (n instanceof ValuePhiNode) {
                 ValueNode node = (ValueNode) n;
-                assert node.stamp().hasValues() : "Stamp is empty after analysis. This is not necessarily an error, but a condition that we want to investigate (and then maybe relax or remove the assertion).";
+                assert node.stamp(
+                                NodeView.DEFAULT).hasValues() : "Stamp is empty after analysis. This is not necessarily an error, but a condition that we want to investigate (and then maybe relax or remove the assertion).";
             }
         }
         return true;
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases/src/org/graalvm/compiler/phases/util/ValueMergeUtil.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases/src/org/graalvm/compiler/phases/util/ValueMergeUtil.java
index 7ed4b1821f9..8b4d18381cd 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases/src/org/graalvm/compiler/phases/util/ValueMergeUtil.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases/src/org/graalvm/compiler/phases/util/ValueMergeUtil.java
@@ -29,6 +29,7 @@ import org.graalvm.compiler.nodes.AbstractMergeNode;
 import org.graalvm.compiler.nodes.ControlSinkNode;
 import org.graalvm.compiler.nodes.EndNode;
 import org.graalvm.compiler.nodes.FixedWithNextNode;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.PhiNode;
 import org.graalvm.compiler.nodes.ReturnNode;
 import org.graalvm.compiler.nodes.UnwindNode;
@@ -52,7 +53,7 @@ public class ValueMergeUtil {
                     singleResult = result;
                 } else if (phiResult == null) {
                     /* Found a second result value, so create phi node. */
-                    phiResult = merge.graph().addWithoutUnique(new ValuePhiNode(result.stamp().unrestricted(), merge));
+                    phiResult = merge.graph().addWithoutUnique(new ValuePhiNode(result.stamp(NodeView.DEFAULT).unrestricted(), merge));
                     for (int i = 0; i < merge.forwardEndCount(); i++) {
                         phiResult.addInput(singleResult);
                     }
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases/src/org/graalvm/compiler/phases/verify/VerifyDebugUsage.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases/src/org/graalvm/compiler/phases/verify/VerifyDebugUsage.java
index c2cefd1cfdf..e195ad1875b 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases/src/org/graalvm/compiler/phases/verify/VerifyDebugUsage.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases/src/org/graalvm/compiler/phases/verify/VerifyDebugUsage.java
@@ -38,6 +38,7 @@ import org.graalvm.compiler.graph.Node;
 import org.graalvm.compiler.graph.NodeInputList;
 import org.graalvm.compiler.nodes.CallTargetNode;
 import org.graalvm.compiler.nodes.Invoke;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.StructuredGraph;
 import org.graalvm.compiler.nodes.ValueNode;
 import org.graalvm.compiler.nodes.java.MethodCallTargetNode;
@@ -234,7 +235,7 @@ public class VerifyDebugUsage extends VerifyPhase<PhaseContext> {
 
     protected void verifyDumpObjectParameter(StructuredGraph callerGraph, MethodCallTargetNode debugCallTarget, ValueNode arg, ResolvedJavaMethod verifiedCallee, Integer dumpLevel)
                     throws org.graalvm.compiler.phases.VerifyPhase.VerificationError {
-        ResolvedJavaType argType = ((ObjectStamp) arg.stamp()).type();
+        ResolvedJavaType argType = ((ObjectStamp) arg.stamp(NodeView.DEFAULT)).type();
         if (metaAccess.lookupJavaType(Graph.class).isAssignableFrom(argType)) {
             verifyStructuredGraphDumping(callerGraph, debugCallTarget, verifiedCallee, dumpLevel);
         }
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases/src/org/graalvm/compiler/phases/verify/VerifyUsageWithEquals.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases/src/org/graalvm/compiler/phases/verify/VerifyUsageWithEquals.java
index 30563849c2d..e4e14911d82 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases/src/org/graalvm/compiler/phases/verify/VerifyUsageWithEquals.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases/src/org/graalvm/compiler/phases/verify/VerifyUsageWithEquals.java
@@ -24,6 +24,7 @@ package org.graalvm.compiler.phases.verify;
 
 import org.graalvm.compiler.core.common.type.ObjectStamp;
 import org.graalvm.compiler.nodes.Invoke;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.ParameterNode;
 import org.graalvm.compiler.nodes.StructuredGraph;
 import org.graalvm.compiler.nodes.ValueNode;
@@ -80,7 +81,7 @@ public class VerifyUsageWithEquals extends VerifyPhase<PhaseContext> {
      * Determines whether the type of {@code node} is assignable to the {@link #restrictedClass}.
      */
     private boolean isAssignableToRestrictedType(ValueNode node, MetaAccessProvider metaAccess) {
-        if (node.stamp() instanceof ObjectStamp) {
+        if (node.stamp(NodeView.DEFAULT) instanceof ObjectStamp) {
             ResolvedJavaType restrictedType = metaAccess.lookupJavaType(restrictedClass);
             ResolvedJavaType nodeType = StampTool.typeOrNull(node);
             if (nodeType == null && node instanceof LoadFieldNode) {
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases/src/org/graalvm/compiler/phases/verify/VerifyVirtualizableUsage.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases/src/org/graalvm/compiler/phases/verify/VerifyVirtualizableUsage.java
index ace130325ce..1b29868ba06 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases/src/org/graalvm/compiler/phases/verify/VerifyVirtualizableUsage.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases/src/org/graalvm/compiler/phases/verify/VerifyVirtualizableUsage.java
@@ -28,6 +28,7 @@ import org.graalvm.compiler.graph.Graph;
 import org.graalvm.compiler.graph.Node;
 import org.graalvm.compiler.graph.NodeInputList;
 import org.graalvm.compiler.nodes.ConstantNode;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.StructuredGraph;
 import org.graalvm.compiler.nodes.ValueNode;
 import org.graalvm.compiler.nodes.java.MethodCallTargetNode;
@@ -81,7 +82,7 @@ public class VerifyVirtualizableUsage extends VerifyPhase<PhaseContext> {
         int i = 0;
         for (Node arg : arguments) {
             if (i >= startIdx) {
-                Stamp argStamp = ((ValueNode) arg).stamp();
+                Stamp argStamp = ((ValueNode) arg).stamp(NodeView.DEFAULT);
                 if (argStamp instanceof ObjectStamp) {
                     ObjectStamp objectStamp = (ObjectStamp) argStamp;
                     ResolvedJavaType argStampType = objectStamp.type();
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.printer/src/org/graalvm/compiler/printer/BinaryGraphPrinter.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.printer/src/org/graalvm/compiler/printer/BinaryGraphPrinter.java
index f40d78c6fc5..d5dc2508341 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.printer/src/org/graalvm/compiler/printer/BinaryGraphPrinter.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.printer/src/org/graalvm/compiler/printer/BinaryGraphPrinter.java
@@ -92,7 +92,7 @@ public class BinaryGraphPrinter implements
 
     @Override
     public void beginGroup(DebugContext debug, String name, String shortName, ResolvedJavaMethod method, int bci, Map<Object, Object> properties) throws IOException {
-        output.beginGroup(new GraphInfo(debug, null), name, shortName, method, bci, properties);
+        output.beginGroup(new GraphInfo(debug, null), name, shortName, method, bci, DebugContext.addVersionProperties(properties));
     }
 
     @Override
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.aarch64/src/org/graalvm/compiler/replacements/aarch64/AArch64CountLeadingZerosNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.aarch64/src/org/graalvm/compiler/replacements/aarch64/AArch64CountLeadingZerosNode.java
index 9d5463b6b37..6568ecc9198 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.aarch64/src/org/graalvm/compiler/replacements/aarch64/AArch64CountLeadingZerosNode.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.aarch64/src/org/graalvm/compiler/replacements/aarch64/AArch64CountLeadingZerosNode.java
@@ -33,6 +33,7 @@ import org.graalvm.compiler.lir.aarch64.AArch64ArithmeticLIRGeneratorTool;
 import org.graalvm.compiler.lir.gen.ArithmeticLIRGeneratorTool;
 import org.graalvm.compiler.nodeinfo.NodeInfo;
 import org.graalvm.compiler.nodes.ConstantNode;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.ValueNode;
 import org.graalvm.compiler.nodes.calc.UnaryNode;
 import org.graalvm.compiler.nodes.spi.ArithmeticLIRLowerable;
@@ -48,7 +49,7 @@ public final class AArch64CountLeadingZerosNode extends UnaryNode implements Ari
     public static final NodeClass<AArch64CountLeadingZerosNode> TYPE = NodeClass.create(AArch64CountLeadingZerosNode.class);
 
     public AArch64CountLeadingZerosNode(ValueNode value) {
-        super(TYPE, computeStamp(value.stamp(), value), value);
+        super(TYPE, computeStamp(value.stamp(NodeView.DEFAULT), value), value);
     }
 
     @Override
@@ -57,7 +58,7 @@ public final class AArch64CountLeadingZerosNode extends UnaryNode implements Ari
     }
 
     private static Stamp computeStamp(Stamp newStamp, ValueNode theValue) {
-        assert newStamp.isCompatible(theValue.stamp());
+        assert newStamp.isCompatible(theValue.stamp(NodeView.DEFAULT));
         assert theValue.getStackKind() == JavaKind.Int || theValue.getStackKind() == JavaKind.Long;
         return StampTool.stampForLeadingZeros((IntegerStamp) newStamp);
     }
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.aarch64/src/org/graalvm/compiler/replacements/aarch64/AArch64CountTrailingZerosNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.aarch64/src/org/graalvm/compiler/replacements/aarch64/AArch64CountTrailingZerosNode.java
index ac7d674b0e1..54e7e653b6f 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.aarch64/src/org/graalvm/compiler/replacements/aarch64/AArch64CountTrailingZerosNode.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.aarch64/src/org/graalvm/compiler/replacements/aarch64/AArch64CountTrailingZerosNode.java
@@ -33,6 +33,7 @@ import org.graalvm.compiler.lir.aarch64.AArch64ArithmeticLIRGeneratorTool;
 import org.graalvm.compiler.lir.gen.ArithmeticLIRGeneratorTool;
 import org.graalvm.compiler.nodeinfo.NodeInfo;
 import org.graalvm.compiler.nodes.ConstantNode;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.ValueNode;
 import org.graalvm.compiler.nodes.calc.UnaryNode;
 import org.graalvm.compiler.nodes.spi.ArithmeticLIRLowerable;
@@ -50,7 +51,7 @@ public final class AArch64CountTrailingZerosNode extends UnaryNode implements Ar
     public static final NodeClass<AArch64CountTrailingZerosNode> TYPE = NodeClass.create(AArch64CountTrailingZerosNode.class);
 
     public AArch64CountTrailingZerosNode(ValueNode value) {
-        super(TYPE, computeStamp(value.stamp(), value), value);
+        super(TYPE, computeStamp(value.stamp(NodeView.DEFAULT), value), value);
         assert value.getStackKind() == JavaKind.Int || value.getStackKind() == JavaKind.Long;
     }
 
@@ -60,7 +61,7 @@ public final class AArch64CountTrailingZerosNode extends UnaryNode implements Ar
     }
 
     static Stamp computeStamp(Stamp newStamp, ValueNode value) {
-        assert newStamp.isCompatible(value.stamp());
+        assert newStamp.isCompatible(value.stamp(NodeView.DEFAULT));
         IntegerStamp valueStamp = (IntegerStamp) newStamp;
         return StampTool.stampForTrailingZeros(valueStamp);
     }
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.aarch64/src/org/graalvm/compiler/replacements/aarch64/AArch64FloatArithmeticSnippets.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.aarch64/src/org/graalvm/compiler/replacements/aarch64/AArch64FloatArithmeticSnippets.java
index 339af5d5dc3..ad5dbccd87f 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.aarch64/src/org/graalvm/compiler/replacements/aarch64/AArch64FloatArithmeticSnippets.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.aarch64/src/org/graalvm/compiler/replacements/aarch64/AArch64FloatArithmeticSnippets.java
@@ -32,6 +32,7 @@ import org.graalvm.compiler.debug.DebugHandlersFactory;
 import org.graalvm.compiler.graph.Node.NodeIntrinsic;
 import org.graalvm.compiler.graph.NodeClass;
 import org.graalvm.compiler.nodeinfo.NodeInfo;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.StructuredGraph;
 import org.graalvm.compiler.nodes.ValueNode;
 import org.graalvm.compiler.nodes.calc.RemNode;
@@ -63,7 +64,7 @@ public class AArch64FloatArithmeticSnippets extends SnippetTemplate.AbstractTemp
     }
 
     public void lower(RemNode node, LoweringTool tool) {
-        JavaKind kind = node.stamp().getStackKind();
+        JavaKind kind = node.stamp(NodeView.DEFAULT).getStackKind();
         assert kind == JavaKind.Float || kind == JavaKind.Double;
         if (node instanceof SafeNode) {
             // We already introduced the necessary checks, nothing to do.
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.aarch64/src/org/graalvm/compiler/replacements/aarch64/AArch64IntegerArithmeticSnippets.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.aarch64/src/org/graalvm/compiler/replacements/aarch64/AArch64IntegerArithmeticSnippets.java
index b45c2e4272f..cf6ba186a14 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.aarch64/src/org/graalvm/compiler/replacements/aarch64/AArch64IntegerArithmeticSnippets.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.aarch64/src/org/graalvm/compiler/replacements/aarch64/AArch64IntegerArithmeticSnippets.java
@@ -31,6 +31,7 @@ import org.graalvm.compiler.graph.Node.NodeIntrinsic;
 import org.graalvm.compiler.graph.NodeClass;
 import org.graalvm.compiler.nodeinfo.NodeInfo;
 import org.graalvm.compiler.nodes.DeoptimizeNode;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.StructuredGraph;
 import org.graalvm.compiler.nodes.ValueNode;
 import org.graalvm.compiler.nodes.calc.FixedBinaryNode;
@@ -84,7 +85,7 @@ public class AArch64IntegerArithmeticSnippets extends AbstractTemplates implemen
     }
 
     public void lower(FixedBinaryNode node, LoweringTool tool) {
-        JavaKind kind = node.stamp().getStackKind();
+        JavaKind kind = node.stamp(NodeView.DEFAULT).getStackKind();
         assert kind == JavaKind.Int || kind == JavaKind.Long;
         SnippetTemplate.SnippetInfo snippet;
         if (node instanceof SafeNode) {
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.aarch64/src/org/graalvm/compiler/replacements/aarch64/AArch64ReadNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.aarch64/src/org/graalvm/compiler/replacements/aarch64/AArch64ReadNode.java
index a9eec477b0c..1baec57dac2 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.aarch64/src/org/graalvm/compiler/replacements/aarch64/AArch64ReadNode.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.aarch64/src/org/graalvm/compiler/replacements/aarch64/AArch64ReadNode.java
@@ -33,6 +33,7 @@ import org.graalvm.compiler.graph.NodeClass;
 import org.graalvm.compiler.lir.aarch64.AArch64AddressValue;
 import org.graalvm.compiler.nodeinfo.NodeInfo;
 import org.graalvm.compiler.nodes.FrameState;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.StructuredGraph;
 import org.graalvm.compiler.nodes.ValueNode;
 import org.graalvm.compiler.nodes.calc.SignExtendNode;
@@ -66,7 +67,7 @@ public class AArch64ReadNode extends ReadNode {
         AArch64LIRGenerator lirgen = (AArch64LIRGenerator) gen.getLIRGeneratorTool();
         AArch64ArithmeticLIRGenerator arithgen = (AArch64ArithmeticLIRGenerator) lirgen.getArithmetic();
         AArch64Kind readKind = (AArch64Kind) lirgen.getLIRKind(accessStamp).getPlatformKind();
-        int resultBits = ((IntegerStamp) stamp()).getBits();
+        int resultBits = ((IntegerStamp) stamp(NodeView.DEFAULT)).getBits();
         gen.setResult(this, arithgen.emitExtendMemory(isSigned, readKind, resultBits, (AArch64AddressValue) gen.operand(getAddress()), gen.state(this)));
     }
 
@@ -86,7 +87,7 @@ public class AArch64ReadNode extends ReadNode {
 
         AddressNode address = readNode.getAddress();
         LocationIdentity location = readNode.getLocationIdentity();
-        Stamp stamp = usage.stamp();
+        Stamp stamp = usage.stamp(NodeView.DEFAULT);
         GuardingNode guard = readNode.getGuard();
         BarrierType barrierType = readNode.getBarrierType();
         boolean nullCheck = readNode.getNullCheck();
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.amd64/src/org/graalvm/compiler/replacements/amd64/AMD64CountLeadingZerosNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.amd64/src/org/graalvm/compiler/replacements/amd64/AMD64CountLeadingZerosNode.java
index 56938d2c930..160ba55bba8 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.amd64/src/org/graalvm/compiler/replacements/amd64/AMD64CountLeadingZerosNode.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.amd64/src/org/graalvm/compiler/replacements/amd64/AMD64CountLeadingZerosNode.java
@@ -33,6 +33,7 @@ import org.graalvm.compiler.lir.amd64.AMD64ArithmeticLIRGeneratorTool;
 import org.graalvm.compiler.lir.gen.ArithmeticLIRGeneratorTool;
 import org.graalvm.compiler.nodeinfo.NodeInfo;
 import org.graalvm.compiler.nodes.ConstantNode;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.ValueNode;
 import org.graalvm.compiler.nodes.calc.UnaryNode;
 import org.graalvm.compiler.nodes.spi.ArithmeticLIRLowerable;
@@ -50,7 +51,7 @@ public final class AMD64CountLeadingZerosNode extends UnaryNode implements Arith
     public static final NodeClass<AMD64CountLeadingZerosNode> TYPE = NodeClass.create(AMD64CountLeadingZerosNode.class);
 
     public AMD64CountLeadingZerosNode(ValueNode value) {
-        super(TYPE, computeStamp(value.stamp(), value), value);
+        super(TYPE, computeStamp(value.stamp(NodeView.DEFAULT), value), value);
         assert value.getStackKind() == JavaKind.Int || value.getStackKind() == JavaKind.Long;
     }
 
@@ -60,7 +61,7 @@ public final class AMD64CountLeadingZerosNode extends UnaryNode implements Arith
     }
 
     private static Stamp computeStamp(Stamp newStamp, ValueNode theValue) {
-        assert newStamp.isCompatible(theValue.stamp());
+        assert newStamp.isCompatible(theValue.stamp(NodeView.DEFAULT));
         assert theValue.getStackKind() == JavaKind.Int || theValue.getStackKind() == JavaKind.Long;
         return StampTool.stampForLeadingZeros((IntegerStamp) newStamp);
     }
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.amd64/src/org/graalvm/compiler/replacements/amd64/AMD64CountTrailingZerosNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.amd64/src/org/graalvm/compiler/replacements/amd64/AMD64CountTrailingZerosNode.java
index 3c17918d5bf..d9314d4948f 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.amd64/src/org/graalvm/compiler/replacements/amd64/AMD64CountTrailingZerosNode.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.amd64/src/org/graalvm/compiler/replacements/amd64/AMD64CountTrailingZerosNode.java
@@ -33,6 +33,7 @@ import org.graalvm.compiler.lir.amd64.AMD64ArithmeticLIRGeneratorTool;
 import org.graalvm.compiler.lir.gen.ArithmeticLIRGeneratorTool;
 import org.graalvm.compiler.nodeinfo.NodeInfo;
 import org.graalvm.compiler.nodes.ConstantNode;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.ValueNode;
 import org.graalvm.compiler.nodes.calc.UnaryNode;
 import org.graalvm.compiler.nodes.spi.ArithmeticLIRLowerable;
@@ -50,7 +51,7 @@ public final class AMD64CountTrailingZerosNode extends UnaryNode implements Arit
     public static final NodeClass<AMD64CountTrailingZerosNode> TYPE = NodeClass.create(AMD64CountTrailingZerosNode.class);
 
     public AMD64CountTrailingZerosNode(ValueNode value) {
-        super(TYPE, computeStamp(value.stamp(), value), value);
+        super(TYPE, computeStamp(value.stamp(NodeView.DEFAULT), value), value);
         assert value.getStackKind() == JavaKind.Int || value.getStackKind() == JavaKind.Long;
     }
 
@@ -60,7 +61,7 @@ public final class AMD64CountTrailingZerosNode extends UnaryNode implements Arit
     }
 
     static Stamp computeStamp(Stamp newStamp, ValueNode value) {
-        assert newStamp.isCompatible(value.stamp());
+        assert newStamp.isCompatible(value.stamp(NodeView.DEFAULT));
         IntegerStamp valueStamp = (IntegerStamp) newStamp;
         return StampTool.stampForTrailingZeros(valueStamp);
     }
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.amd64/src/org/graalvm/compiler/replacements/amd64/AMD64RoundNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.amd64/src/org/graalvm/compiler/replacements/amd64/AMD64RoundNode.java
index 4a980b0cbb7..54674b55e53 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.amd64/src/org/graalvm/compiler/replacements/amd64/AMD64RoundNode.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.amd64/src/org/graalvm/compiler/replacements/amd64/AMD64RoundNode.java
@@ -34,6 +34,7 @@ import org.graalvm.compiler.lir.amd64.AMD64ArithmeticLIRGeneratorTool.RoundingMo
 import org.graalvm.compiler.lir.gen.ArithmeticLIRGeneratorTool;
 import org.graalvm.compiler.nodeinfo.NodeInfo;
 import org.graalvm.compiler.nodes.ConstantNode;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.ValueNode;
 import org.graalvm.compiler.nodes.calc.UnaryNode;
 import org.graalvm.compiler.nodes.spi.ArithmeticLIRLowerable;
@@ -52,7 +53,7 @@ public final class AMD64RoundNode extends UnaryNode implements ArithmeticLIRLowe
     private final RoundingMode mode;
 
     public AMD64RoundNode(ValueNode value, RoundingMode mode) {
-        super(TYPE, roundStamp((FloatStamp) value.stamp(), mode), value);
+        super(TYPE, roundStamp((FloatStamp) value.stamp(NodeView.DEFAULT), mode), value);
         this.mode = mode;
     }
 
@@ -83,7 +84,7 @@ public final class AMD64RoundNode extends UnaryNode implements ArithmeticLIRLowe
 
     @Override
     public Stamp foldStamp(Stamp newStamp) {
-        assert newStamp.isCompatible(getValue().stamp());
+        assert newStamp.isCompatible(getValue().stamp(NodeView.DEFAULT));
         return roundStamp((FloatStamp) newStamp, mode);
     }
 
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.test/src/org/graalvm/compiler/replacements/test/BitOpNodesTest.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.test/src/org/graalvm/compiler/replacements/test/BitOpNodesTest.java
index 2dd8252aeb9..2aeda14b2ed 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.test/src/org/graalvm/compiler/replacements/test/BitOpNodesTest.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.test/src/org/graalvm/compiler/replacements/test/BitOpNodesTest.java
@@ -22,6 +22,7 @@
  */
 package org.graalvm.compiler.replacements.test;
 
+import org.graalvm.compiler.nodes.NodeView;
 import org.junit.Assert;
 import org.junit.Assume;
 import org.junit.Test;
@@ -83,7 +84,7 @@ public class BitOpNodesTest extends GraalCompilerTest {
         boolean isSparc = arch instanceof SPARC;
         Assume.assumeTrue("Only works on hardware with popcnt at the moment", isAmd64WithPopCount || isSparc);
         ValueNode result = parseAndInline("bitCountIntSnippet");
-        Assert.assertEquals(StampFactory.forInteger(JavaKind.Int, 8, 24), result.stamp());
+        Assert.assertEquals(StampFactory.forInteger(JavaKind.Int, 8, 24), result.stamp(NodeView.DEFAULT));
     }
 
     public static int bitCountIntEmptySnippet(int v) {
@@ -97,7 +98,7 @@ public class BitOpNodesTest extends GraalCompilerTest {
         boolean isSparc = arch instanceof SPARC;
         Assume.assumeTrue("Only works on hardware with popcnt at the moment", isAmd64WithPopCount || isSparc);
         ValueNode result = parseAndInline("bitCountIntEmptySnippet");
-        Assert.assertEquals(StampFactory.forInteger(JavaKind.Int, 0, 24), result.stamp());
+        Assert.assertEquals(StampFactory.forInteger(JavaKind.Int, 0, 24), result.stamp(NodeView.DEFAULT));
     }
 
     @Test
@@ -117,7 +118,7 @@ public class BitOpNodesTest extends GraalCompilerTest {
         boolean isSparc = arch instanceof SPARC;
         Assume.assumeTrue("Only works on hardware with popcnt at the moment", isAmd64WithPopCount || isSparc);
         ValueNode result = parseAndInline("bitCountLongSnippet");
-        Assert.assertEquals(StampFactory.forInteger(JavaKind.Int, 8, 40), result.stamp());
+        Assert.assertEquals(StampFactory.forInteger(JavaKind.Int, 8, 40), result.stamp(NodeView.DEFAULT));
     }
 
     public static int bitCountLongEmptySnippet(long v) {
@@ -131,7 +132,7 @@ public class BitOpNodesTest extends GraalCompilerTest {
         boolean isSparc = arch instanceof SPARC;
         Assume.assumeTrue("Only works on hardware with popcnt at the moment", isAmd64WithPopCount || isSparc);
         ValueNode result = parseAndInline("bitCountLongEmptySnippet");
-        Assert.assertEquals(StampFactory.forInteger(JavaKind.Int, 0, 40), result.stamp());
+        Assert.assertEquals(StampFactory.forInteger(JavaKind.Int, 0, 40), result.stamp(NodeView.DEFAULT));
     }
 
     /*
@@ -155,7 +156,7 @@ public class BitOpNodesTest extends GraalCompilerTest {
     @Test
     public void testScanForwardInt() {
         ValueNode result = parseAndInline("scanForwardIntSnippet");
-        Assert.assertEquals(StampFactory.forInteger(JavaKind.Int, 4, 8), result.stamp());
+        Assert.assertEquals(StampFactory.forInteger(JavaKind.Int, 4, 8), result.stamp(NodeView.DEFAULT));
     }
 
     public static int scanForwardLongConstantSnippet() {
@@ -175,7 +176,7 @@ public class BitOpNodesTest extends GraalCompilerTest {
     @Test
     public void testScanForwardLong() {
         ValueNode result = parseAndInline("scanForwardLongSnippet");
-        Assert.assertEquals(StampFactory.forInteger(JavaKind.Int, 24, 32), result.stamp());
+        Assert.assertEquals(StampFactory.forInteger(JavaKind.Int, 24, 32), result.stamp(NodeView.DEFAULT));
     }
 
     public static int scanForwardLongEmptySnippet(long v) {
@@ -187,7 +188,7 @@ public class BitOpNodesTest extends GraalCompilerTest {
     @Test
     public void testScanForwardLongEmpty() {
         ValueNode result = parseAndInline("scanForwardLongEmptySnippet");
-        Assert.assertEquals(StampFactory.forInteger(JavaKind.Int, 24, 64), result.stamp());
+        Assert.assertEquals(StampFactory.forInteger(JavaKind.Int, 24, 64), result.stamp(NodeView.DEFAULT));
     }
 
     /*
@@ -213,7 +214,7 @@ public class BitOpNodesTest extends GraalCompilerTest {
         /* This test isn't valid unless the BitScanReverseNode intrinsic is used. */
         ValueNode result = parseAndInline("scanReverseIntSnippet", BitScanReverseNode.class);
         if (result != null) {
-            Assert.assertEquals(StampFactory.forInteger(JavaKind.Int, 16, 20), result.stamp());
+            Assert.assertEquals(StampFactory.forInteger(JavaKind.Int, 16, 20), result.stamp(NodeView.DEFAULT));
         }
     }
 
@@ -238,7 +239,7 @@ public class BitOpNodesTest extends GraalCompilerTest {
         /* This test isn't valid unless the BitScanReverseNode intrinsic is used. */
         ValueNode result = parseAndInline("scanReverseLongSnippet", BitScanReverseNode.class);
         if (result != null) {
-            Assert.assertEquals(StampFactory.forInteger(JavaKind.Int, 48, 64), result.stamp());
+            Assert.assertEquals(StampFactory.forInteger(JavaKind.Int, 48, 64), result.stamp(NodeView.DEFAULT));
         }
     }
 
@@ -253,7 +254,7 @@ public class BitOpNodesTest extends GraalCompilerTest {
         /* This test isn't valid unless the BitScanReverseNode intrinsic is used. */
         ValueNode result = parseAndInline("scanReverseLongEmptySnippet", BitScanReverseNode.class);
         if (result != null) {
-            Assert.assertEquals(StampFactory.forInteger(JavaKind.Int, 24, 64), result.stamp());
+            Assert.assertEquals(StampFactory.forInteger(JavaKind.Int, 24, 64), result.stamp(NodeView.DEFAULT));
         }
     }
 
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.test/src/org/graalvm/compiler/replacements/test/IntegerExactFoldTest.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.test/src/org/graalvm/compiler/replacements/test/IntegerExactFoldTest.java
index c833aec2258..1ab1abb7052 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.test/src/org/graalvm/compiler/replacements/test/IntegerExactFoldTest.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.test/src/org/graalvm/compiler/replacements/test/IntegerExactFoldTest.java
@@ -32,6 +32,7 @@ import org.graalvm.compiler.core.common.type.IntegerStamp;
 import org.graalvm.compiler.core.common.type.StampFactory;
 import org.graalvm.compiler.core.test.GraalCompilerTest;
 import org.graalvm.compiler.graph.Node;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.ParameterNode;
 import org.graalvm.compiler.nodes.PiNode;
 import org.graalvm.compiler.nodes.ReturnNode;
@@ -95,7 +96,7 @@ public class IntegerExactFoldTest extends GraalCompilerTest {
         ValueNode node = findNode(graph);
         boolean overflowExpected = node instanceof IntegerExactArithmeticNode;
 
-        IntegerStamp resultStamp = (IntegerStamp) node.stamp();
+        IntegerStamp resultStamp = (IntegerStamp) node.stamp(NodeView.DEFAULT);
         operation.verifyOverflow(lowerBoundA, upperBoundA, lowerBoundB, upperBoundB, bits, overflowExpected, resultStamp);
     }
 
@@ -120,7 +121,7 @@ public class IntegerExactFoldTest extends GraalCompilerTest {
         ValueNode node = findNode(graph);
         boolean overflowExpected = node instanceof IntegerExactArithmeticSplitNode;
 
-        IntegerStamp resultStamp = (IntegerStamp) node.stamp();
+        IntegerStamp resultStamp = (IntegerStamp) node.stamp(NodeView.DEFAULT);
         operation.verifyOverflow(lowerBoundA, upperBoundA, lowerBoundB, upperBoundB, bits, overflowExpected, resultStamp);
     }
 
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.test/src/org/graalvm/compiler/replacements/test/MethodSubstitutionTest.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.test/src/org/graalvm/compiler/replacements/test/MethodSubstitutionTest.java
index cc02a21f970..f99d7adb323 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.test/src/org/graalvm/compiler/replacements/test/MethodSubstitutionTest.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.test/src/org/graalvm/compiler/replacements/test/MethodSubstitutionTest.java
@@ -31,6 +31,7 @@ import org.graalvm.compiler.graph.Node;
 import org.graalvm.compiler.nodes.Invoke;
 import org.graalvm.compiler.nodes.StructuredGraph;
 import org.graalvm.compiler.nodes.StructuredGraph.AllowAssumptions;
+import org.graalvm.compiler.nodes.java.MethodCallTargetNode;
 import org.graalvm.compiler.nodes.spi.LoweringTool;
 import org.graalvm.compiler.phases.common.CanonicalizerPhase;
 import org.graalvm.compiler.phases.common.DeadCodeEliminationPhase;
@@ -50,8 +51,12 @@ import jdk.vm.ci.meta.ResolvedJavaMethod;
  */
 public abstract class MethodSubstitutionTest extends GraalCompilerTest {
 
-    @SuppressWarnings("try")
     protected StructuredGraph testGraph(final String snippet) {
+        return testGraph(snippet, null);
+    }
+
+    @SuppressWarnings("try")
+    protected StructuredGraph testGraph(final String snippet, String name) {
         DebugContext debug = getDebugContext();
         try (DebugContext.Scope s = debug.scope("MethodSubstitutionTest", getResolvedJavaMethod(snippet))) {
             StructuredGraph graph = parseEager(snippet, AllowAssumptions.YES, debug);
@@ -69,7 +74,20 @@ public abstract class MethodSubstitutionTest extends GraalCompilerTest {
                 new LoweringPhase(new CanonicalizerPhase(), LoweringTool.StandardLoweringStage.MID_TIER).apply(graph, context);
             }
             assertNotInGraph(graph, MacroNode.class);
-            assertNotInGraph(graph, Invoke.class);
+            if (name != null) {
+                for (Node node : graph.getNodes()) {
+                    if (node instanceof Invoke) {
+                        Invoke invoke = (Invoke) node;
+                        if (invoke.callTarget() instanceof MethodCallTargetNode) {
+                            MethodCallTargetNode call = (MethodCallTargetNode) invoke.callTarget();
+                            assertTrue(!call.targetMethod().getName().equals(name), "Unexpected invoke of intrinsic %s", call.targetMethod());
+                        }
+                    }
+
+                }
+            } else {
+                assertNotInGraph(graph, Invoke.class);
+            }
             return graph;
         } catch (Throwable e) {
             throw debug.handle(e);
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.test/src/org/graalvm/compiler/replacements/test/MonitorTest.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.test/src/org/graalvm/compiler/replacements/test/MonitorTest.java
index 090470f0c59..fd20ab92a7d 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.test/src/org/graalvm/compiler/replacements/test/MonitorTest.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.test/src/org/graalvm/compiler/replacements/test/MonitorTest.java
@@ -221,7 +221,8 @@ public class MonitorTest extends GraalCompilerTest {
      * Reproduces issue reported in https://github.com/graalvm/graal-core/issues/201. The stamp in
      * the PiNode returned by {@link BoxingSnippets#longValueOf} was overwritten when the node was
      * subsequently canonicalized because {@code PiNode.computeValue()} ignored the
-     * {@link ValueNode#stamp()} field and used the {@code PiNode.piStamp} field.
+     * {@link ValueNode#stamp(org.graalvm.compiler.nodes.NodeView)} field and used the
+     * {@code PiNode.piStamp} field.
      */
     @Test
     public void test8() {
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.test/src/org/graalvm/compiler/replacements/test/ObjectAccessTest.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.test/src/org/graalvm/compiler/replacements/test/ObjectAccessTest.java
index 95e0f45c7e2..7eb29a16c78 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.test/src/org/graalvm/compiler/replacements/test/ObjectAccessTest.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.test/src/org/graalvm/compiler/replacements/test/ObjectAccessTest.java
@@ -24,6 +24,7 @@ package org.graalvm.compiler.replacements.test;
 
 import org.graalvm.compiler.api.replacements.Snippet;
 import org.graalvm.compiler.nodes.NamedLocationIdentity;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.ReturnNode;
 import org.graalvm.compiler.nodes.StructuredGraph;
 import org.graalvm.compiler.nodes.StructuredGraph.AllowAssumptions;
@@ -93,7 +94,7 @@ public class ObjectAccessTest extends SnippetsTest {
 
     private static void assertRead(StructuredGraph graph, JavaKind kind, boolean indexConvert, LocationIdentity locationIdentity) {
         JavaReadNode read = (JavaReadNode) graph.start().next();
-        Assert.assertEquals(kind.getStackKind(), read.stamp().getStackKind());
+        Assert.assertEquals(kind.getStackKind(), read.stamp(NodeView.DEFAULT).getStackKind());
 
         OffsetAddressNode address = (OffsetAddressNode) read.getAddress();
         Assert.assertEquals(graph.getParameter(0), address.getBase());
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.test/src/org/graalvm/compiler/replacements/test/PointerTest.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.test/src/org/graalvm/compiler/replacements/test/PointerTest.java
index 9ebfb868398..48d6f19f8c3 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.test/src/org/graalvm/compiler/replacements/test/PointerTest.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.test/src/org/graalvm/compiler/replacements/test/PointerTest.java
@@ -24,6 +24,7 @@ package org.graalvm.compiler.replacements.test;
 
 import org.graalvm.compiler.api.replacements.Snippet;
 import org.graalvm.compiler.nodes.NamedLocationIdentity;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.ReturnNode;
 import org.graalvm.compiler.nodes.StructuredGraph;
 import org.graalvm.compiler.nodes.StructuredGraph.AllowAssumptions;
@@ -105,12 +106,12 @@ public class PointerTest extends SnippetsTest {
         WordCastNode cast = (WordCastNode) graph.start().next();
 
         JavaReadNode read = (JavaReadNode) cast.next();
-        Assert.assertEquals(kind.getStackKind(), read.stamp().getStackKind());
+        Assert.assertEquals(kind.getStackKind(), read.stamp(NodeView.DEFAULT).getStackKind());
 
         OffsetAddressNode address = (OffsetAddressNode) read.getAddress();
         Assert.assertEquals(cast, address.getBase());
         Assert.assertEquals(graph.getParameter(0), cast.getInput());
-        Assert.assertEquals(target.wordJavaKind, cast.stamp().getStackKind());
+        Assert.assertEquals(target.wordJavaKind, cast.stamp(NodeView.DEFAULT).getStackKind());
 
         Assert.assertEquals(locationIdentity, read.getLocationIdentity());
 
@@ -137,7 +138,7 @@ public class PointerTest extends SnippetsTest {
         OffsetAddressNode address = (OffsetAddressNode) write.getAddress();
         Assert.assertEquals(cast, address.getBase());
         Assert.assertEquals(graph.getParameter(0), cast.getInput());
-        Assert.assertEquals(target.wordJavaKind, cast.stamp().getStackKind());
+        Assert.assertEquals(target.wordJavaKind, cast.stamp(NodeView.DEFAULT).getStackKind());
 
         Assert.assertEquals(locationIdentity, write.getLocationIdentity());
 
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.test/src/org/graalvm/compiler/replacements/test/SystemArrayCopyTest.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.test/src/org/graalvm/compiler/replacements/test/SystemArrayCopyTest.java
new file mode 100644
index 00000000000..ee74c619b04
--- /dev/null
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements.test/src/org/graalvm/compiler/replacements/test/SystemArrayCopyTest.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright (c) 2014, 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package org.graalvm.compiler.replacements.test;
+
+import jdk.vm.ci.code.InstalledCode;
+import jdk.vm.ci.meta.JavaConstant;
+import jdk.vm.ci.meta.JavaType;
+import jdk.vm.ci.meta.ResolvedJavaMethod;
+import org.graalvm.compiler.core.test.GraalCompilerTest;
+import org.graalvm.compiler.nodes.ConstantNode;
+import org.graalvm.compiler.nodes.ParameterNode;
+import org.graalvm.compiler.nodes.StructuredGraph;
+import org.graalvm.compiler.options.OptionValues;
+import org.graalvm.compiler.phases.PhaseSuite;
+import org.graalvm.compiler.phases.tiers.HighTierContext;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+import java.util.ArrayList;
+import java.util.Collection;
+
+import static java.lang.reflect.Modifier.isStatic;
+
+@RunWith(Parameterized.class)
+public class SystemArrayCopyTest extends GraalCompilerTest {
+
+    @Parameter(0) public Object src;
+    @Parameter(1) public Object dst;
+    @Parameter(2) public int len;
+    @Parameter(3) public String name;
+
+    @Parameters(name = "{3}")
+    public static Collection<Object[]> data() {
+        Object[] srcs = {new int[4], new double[4], new Integer[4], new Number[4], new String[4], new Object[]{"Graal", 0, 0, 0}, new Object()};
+        Object[] dsts = {new int[4], new Number[4]};
+        int[] lens = {-1, 0, 2, 8};
+
+        ArrayList<Object[]> ret = new ArrayList<>(srcs.length * dsts.length * lens.length);
+        for (Object src : srcs) {
+            for (Object dst : dsts) {
+                for (int length : lens) {
+                    ret.add(new Object[]{src, dst, length, src.getClass().getSimpleName() + ", 0, " + dst.getClass().getSimpleName() + ", 0, " + length});
+                }
+            }
+        }
+        return ret;
+    }
+
+    public static void testArrayCopySnippet(Object src, Object dst, int length) {
+        System.arraycopy(src, 0, dst, 0, length);
+    }
+
+    private static final int PARAMETER_LENGTH = 3;
+    private Object[] argsToBind;
+
+    @Test
+    public void testArrayCopy() {
+        ResolvedJavaMethod method = getResolvedJavaMethod("testArrayCopySnippet");
+        Object receiver = method.isStatic() ? null : this;
+        Object[] args = {src, dst, len};
+
+        Result expect = executeExpected(method, receiver, args);
+        testAgainstExpected(method, expect, receiver, args);
+
+        // test composition of constant binding
+        for (int i = 1; i < (1 << PARAMETER_LENGTH); i++) {
+            argsToBind = new Object[PARAMETER_LENGTH];
+            for (int j = 0; j < PARAMETER_LENGTH; j++) {
+                if ((i & (1 << j)) != 0) {
+                    argsToBind[j] = args[j];
+                }
+            }
+            testAgainstExpected(method, expect, receiver, args);
+        }
+    }
+
+    @Override
+    protected StructuredGraph parse(StructuredGraph.Builder builder, PhaseSuite<HighTierContext> graphBuilderSuite) {
+        StructuredGraph graph = super.parse(builder, graphBuilderSuite);
+        if (argsToBind != null) {
+            ResolvedJavaMethod m = graph.method();
+            Object receiver = isStatic(m.getModifiers()) ? null : this;
+            Object[] args = argsWithReceiver(receiver, argsToBind);
+            JavaType[] parameterTypes = m.toParameterTypes();
+            assert parameterTypes.length == args.length;
+            for (ParameterNode param : graph.getNodes(ParameterNode.TYPE)) {
+                int index = param.index();
+                if (args[index] != null) {
+                    JavaConstant c = getSnippetReflection().forBoxed(parameterTypes[index].getJavaKind(), args[index]);
+                    ConstantNode replacement = ConstantNode.forConstant(c, getMetaAccess(), graph);
+                    param.replaceAtUsages(replacement);
+                }
+            }
+        }
+        return graph;
+    }
+
+    @Override
+    protected InstalledCode getCode(ResolvedJavaMethod method, StructuredGraph graph, boolean forceCompile, boolean installAsDefault, OptionValues options) {
+        return super.getCode(method, graph, true, installAsDefault, options);
+    }
+
+}
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/DefaultJavaLoweringProvider.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/DefaultJavaLoweringProvider.java
index dd0746320f3..4bfcadde273 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/DefaultJavaLoweringProvider.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/DefaultJavaLoweringProvider.java
@@ -58,6 +58,7 @@ import org.graalvm.compiler.nodes.FieldLocationIdentity;
 import org.graalvm.compiler.nodes.FixedNode;
 import org.graalvm.compiler.nodes.LogicNode;
 import org.graalvm.compiler.nodes.NamedLocationIdentity;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.PiNode;
 import org.graalvm.compiler.nodes.StructuredGraph;
 import org.graalvm.compiler.nodes.ValueNode;
@@ -113,7 +114,6 @@ import org.graalvm.compiler.nodes.memory.ReadNode;
 import org.graalvm.compiler.nodes.memory.WriteNode;
 import org.graalvm.compiler.nodes.memory.address.AddressNode;
 import org.graalvm.compiler.nodes.memory.address.OffsetAddressNode;
-import org.graalvm.compiler.nodes.memory.address.RawAddressNode;
 import org.graalvm.compiler.nodes.spi.Lowerable;
 import org.graalvm.compiler.nodes.spi.LoweringProvider;
 import org.graalvm.compiler.nodes.spi.LoweringTool;
@@ -247,7 +247,7 @@ public abstract class DefaultJavaLoweringProvider implements LoweringProvider {
                     indexOfSnippets.lower(node, tool);
                 }
             };
-            SnippetLowerableMemoryNode snippetLower = new SnippetLowerableMemoryNode(lowering, NamedLocationIdentity.getArrayLocation(JavaKind.Char), n.stamp(), n.toArgumentArray());
+            SnippetLowerableMemoryNode snippetLower = new SnippetLowerableMemoryNode(lowering, NamedLocationIdentity.getArrayLocation(JavaKind.Char), n.stamp(NodeView.DEFAULT), n.toArgumentArray());
             n.graph().add(snippetLower);
             n.graph().replaceFixedWithFixed(n, snippetLower);
         }
@@ -347,7 +347,7 @@ public abstract class DefaultJavaLoweringProvider implements LoweringProvider {
         ResolvedJavaField field = loadField.field();
         ValueNode object = loadField.isStatic() ? staticFieldBase(graph, field) : loadField.object();
         object = createNullCheckedValue(object, loadField, tool);
-        Stamp loadStamp = loadStamp(loadField.stamp(), getStorageKind(field));
+        Stamp loadStamp = loadStamp(loadField.stamp(NodeView.DEFAULT), getStorageKind(field));
 
         AddressNode address = createFieldAddress(graph, object, field);
         assert address != null : "Field that is loaded must not be eliminated: " + field.getDeclaringClass().toJavaName(true) + "." + field.getName();
@@ -419,7 +419,7 @@ public abstract class DefaultJavaLoweringProvider implements LoweringProvider {
         ValueNode array = loadIndexed.array();
         array = createNullCheckedValue(array, loadIndexed, tool);
         JavaKind elementKind = loadIndexed.elementKind();
-        Stamp loadStamp = loadStamp(loadIndexed.stamp(), elementKind);
+        Stamp loadStamp = loadStamp(loadIndexed.stamp(NodeView.DEFAULT), elementKind);
 
         GuardingNode boundsCheck = getBoundsCheck(loadIndexed, array, tool);
         AddressNode address = createArrayIndexAddress(graph, array, elementKind, loadIndexed.index(), boundsCheck);
@@ -575,7 +575,7 @@ public abstract class DefaultJavaLoweringProvider implements LoweringProvider {
 
     protected AddressNode createUnsafeAddress(StructuredGraph graph, ValueNode object, ValueNode offset) {
         if (object.isConstant() && object.asConstant().isDefaultForKind()) {
-            return graph.unique(new RawAddressNode(offset));
+            return graph.addOrUniqueWithInputs(OffsetAddressNode.create(offset));
         } else {
             return graph.unique(new OffsetAddressNode(object, offset));
         }
@@ -584,7 +584,7 @@ public abstract class DefaultJavaLoweringProvider implements LoweringProvider {
     protected ReadNode createUnsafeRead(StructuredGraph graph, RawLoadNode load, GuardingNode guard) {
         boolean compressible = load.accessKind() == JavaKind.Object;
         JavaKind readKind = load.accessKind();
-        Stamp loadStamp = loadStamp(load.stamp(), readKind, compressible);
+        Stamp loadStamp = loadStamp(load.stamp(NodeView.DEFAULT), readKind, compressible);
         AddressNode address = createUnsafeAddress(graph, load.object(), load.offset());
         ReadNode memoryRead = graph.add(new ReadNode(address, load.getLocationIdentity(), loadStamp, BarrierType.NONE));
         if (guard == null) {
@@ -603,8 +603,8 @@ public abstract class DefaultJavaLoweringProvider implements LoweringProvider {
         StructuredGraph graph = load.graph();
         JavaKind readKind = load.getKind();
         assert readKind != JavaKind.Object;
-        Stamp loadStamp = loadStamp(load.stamp(), readKind, false);
-        AddressNode address = graph.unique(new RawAddressNode(load.getAddress()));
+        Stamp loadStamp = loadStamp(load.stamp(NodeView.DEFAULT), readKind, false);
+        AddressNode address = graph.addOrUniqueWithInputs(OffsetAddressNode.create(load.getAddress()));
         ReadNode memoryRead = graph.add(new ReadNode(address, load.getLocationIdentity(), loadStamp, BarrierType.NONE));
         // An unsafe read must not float otherwise it may float above
         // a test guaranteeing the read is safe.
@@ -639,7 +639,7 @@ public abstract class DefaultJavaLoweringProvider implements LoweringProvider {
         assert store.getValue().getStackKind() != JavaKind.Object;
         JavaKind valueKind = store.getKind();
         ValueNode value = implicitStoreConvert(graph, valueKind, store.getValue(), false);
-        AddressNode address = graph.unique(new RawAddressNode(store.getAddress()));
+        AddressNode address = graph.addOrUniqueWithInputs(OffsetAddressNode.create(store.getAddress()));
         WriteNode write = graph.add(new WriteNode(address, store.getLocationIdentity(), value, BarrierType.NONE));
         write.setStateAfter(store.stateAfter());
         graph.replaceFixedWithFixed(store, write);
@@ -648,7 +648,7 @@ public abstract class DefaultJavaLoweringProvider implements LoweringProvider {
     protected void lowerJavaReadNode(JavaReadNode read) {
         StructuredGraph graph = read.graph();
         JavaKind valueKind = read.getReadKind();
-        Stamp loadStamp = loadStamp(read.stamp(), valueKind, read.isCompressible());
+        Stamp loadStamp = loadStamp(read.stamp(NodeView.DEFAULT), valueKind, read.isCompressible());
 
         ReadNode memoryRead = graph.add(new ReadNode(read.getAddress(), read.getLocationIdentity(), loadStamp, read.getBarrierType()));
         GuardingNode guard = read.getGuard();
@@ -1041,7 +1041,7 @@ public abstract class DefaultJavaLoweringProvider implements LoweringProvider {
             arrayLength = arrayLength.isAlive() ? arrayLength : graph.addOrUniqueWithInputs(arrayLength);
         }
 
-        LogicNode boundsCheck = IntegerBelowNode.create(n.index(), arrayLength);
+        LogicNode boundsCheck = IntegerBelowNode.create(n.index(), arrayLength, NodeView.DEFAULT);
         if (boundsCheck.isTautology()) {
             return null;
         }
@@ -1060,7 +1060,7 @@ public abstract class DefaultJavaLoweringProvider implements LoweringProvider {
         if (nullCheck == null) {
             return object;
         }
-        return before.graph().maybeAddOrUnique(PiNode.create(object, (object.stamp()).join(StampFactory.objectNonNull()), (ValueNode) nullCheck));
+        return before.graph().maybeAddOrUnique(PiNode.create(object, (object.stamp(NodeView.DEFAULT)).join(StampFactory.objectNonNull()), (ValueNode) nullCheck));
     }
 
     @Override
@@ -1069,10 +1069,10 @@ public abstract class DefaultJavaLoweringProvider implements LoweringProvider {
         ValueNode offset = ((OffsetAddressNode) address).getOffset();
 
         int base = arrayBaseOffset(elementKind);
-        ValueNode scaledIndex = graph.unique(new SubNode(offset, ConstantNode.forIntegerStamp(offset.stamp(), base, graph)));
+        ValueNode scaledIndex = graph.unique(new SubNode(offset, ConstantNode.forIntegerStamp(offset.stamp(NodeView.DEFAULT), base, graph)));
 
         int shift = CodeUtil.log2(arrayScalingFactor(elementKind));
         ValueNode ret = graph.unique(new RightShiftNode(scaledIndex, ConstantNode.forInt(shift, graph)));
-        return IntegerConvertNode.convert(ret, StampFactory.forKind(JavaKind.Int), graph);
+        return IntegerConvertNode.convert(ret, StampFactory.forKind(JavaKind.Int), graph, NodeView.DEFAULT);
     }
 }
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/GraphKit.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/GraphKit.java
index 99d6ff7d413..6ccd8d4f91d 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/GraphKit.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/GraphKit.java
@@ -50,6 +50,7 @@ import org.graalvm.compiler.nodes.InvokeWithExceptionNode;
 import org.graalvm.compiler.nodes.KillingBeginNode;
 import org.graalvm.compiler.nodes.LogicNode;
 import org.graalvm.compiler.nodes.MergeNode;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.StructuredGraph;
 import org.graalvm.compiler.nodes.ValueNode;
 import org.graalvm.compiler.nodes.calc.FloatingNode;
@@ -271,12 +272,12 @@ public class GraphKit implements GraphBuilderTool {
         int argIndex = 0;
         if (!isStatic) {
             JavaKind expected = asKind(method.getDeclaringClass());
-            JavaKind actual = args[argIndex++].stamp().getStackKind();
+            JavaKind actual = args[argIndex++].stamp(NodeView.DEFAULT).getStackKind();
             assert expected == actual : graph + ": wrong kind of value for receiver argument of call to " + method + " [" + actual + " != " + expected + "]";
         }
         for (int i = 0; i != signature.getParameterCount(false); i++) {
             JavaKind expected = asKind(signature.getParameterType(i, method.getDeclaringClass())).getStackKind();
-            JavaKind actual = args[argIndex++].stamp().getStackKind();
+            JavaKind actual = args[argIndex++].stamp(NodeView.DEFAULT).getStackKind();
             if (expected != actual) {
                 throw new AssertionError(graph + ": wrong kind of value for argument " + i + " of call to " + method + " [" + actual + " != " + expected + "]");
             }
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/InstanceOfSnippetsTemplates.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/InstanceOfSnippetsTemplates.java
index bea1cd94df3..0f0440ddaf0 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/InstanceOfSnippetsTemplates.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/InstanceOfSnippetsTemplates.java
@@ -36,6 +36,7 @@ import org.graalvm.compiler.nodes.FixedGuardNode;
 import org.graalvm.compiler.nodes.IfNode;
 import org.graalvm.compiler.nodes.LogicConstantNode;
 import org.graalvm.compiler.nodes.LogicNode;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.PhiNode;
 import org.graalvm.compiler.nodes.ShortCircuitOrNode;
 import org.graalvm.compiler.nodes.StructuredGraph;
@@ -183,7 +184,7 @@ public abstract class InstanceOfSnippetsTemplates extends AbstractTemplates {
             }
             if (condition == null || (!(condition instanceof CompareNode)) || ((CompareNode) condition).getY() != testValue) {
                 // Re-use previously generated condition if the trueValue for the test is the same
-                condition = createCompareNode(result.graph(), Condition.EQ, result, testValue, null);
+                condition = createCompareNode(result.graph(), Condition.EQ, result, testValue, null, NodeView.DEFAULT);
             }
             return condition;
         }
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/MethodHandlePlugin.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/MethodHandlePlugin.java
index b7027451717..48ebe3e562e 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/MethodHandlePlugin.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/MethodHandlePlugin.java
@@ -83,6 +83,10 @@ public class MethodHandlePlugin implements NodePlugin {
                     // As such, it needs to recursively inline everything.
                     inlineEverything = args.length != argumentsList.size();
                 }
+                if (inlineEverything && !callTarget.targetMethod().hasBytecodes()) {
+                    // we need to force-inline but we can not, leave the invoke as-is
+                    return false;
+                }
                 b.handleReplacedInvoke(invoke.getInvokeKind(), callTarget.targetMethod(), argumentsList.toArray(new ValueNode[argumentsList.size()]), inlineEverything);
             }
             return true;
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/PEGraphDecoder.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/PEGraphDecoder.java
index 1509b364545..aa52ee525d8 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/PEGraphDecoder.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/PEGraphDecoder.java
@@ -63,6 +63,7 @@ import org.graalvm.compiler.nodes.IfNode;
 import org.graalvm.compiler.nodes.Invoke;
 import org.graalvm.compiler.nodes.InvokeWithExceptionNode;
 import org.graalvm.compiler.nodes.MergeNode;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.ParameterNode;
 import org.graalvm.compiler.nodes.ReturnNode;
 import org.graalvm.compiler.nodes.SimplifyingGraphDecoder;
@@ -583,7 +584,7 @@ public abstract class PEGraphDecoder extends SimplifyingGraphDecoder {
             return callTarget.targetMethod();
         }
 
-        SpecialCallTargetCacheKey key = new SpecialCallTargetCacheKey(callTarget.invokeKind(), callTarget.targetMethod(), invokeData.contextType, callTarget.receiver().stamp());
+        SpecialCallTargetCacheKey key = new SpecialCallTargetCacheKey(callTarget.invokeKind(), callTarget.targetMethod(), invokeData.contextType, callTarget.receiver().stamp(NodeView.DEFAULT));
         Object specialCallTarget = specialCallTargetCache.get(key);
         if (specialCallTarget == null) {
             specialCallTarget = MethodCallTargetNode.devirtualizeCall(key.invokeKind, key.targetMethod, key.contextType, graph.getAssumptions(),
@@ -1066,7 +1067,7 @@ public abstract class PEGraphDecoder extends SimplifyingGraphDecoder {
                 assert !methodScope.isInlinedMethod();
                 GraphBuilderContext graphBuilderContext = new PENonAppendGraphBuilderContext(methodScope, null);
                 Node result = parameterPlugin.interceptParameter(graphBuilderContext, param.index(),
-                                StampPair.create(param.stamp(), param.uncheckedStamp()));
+                                StampPair.create(param.stamp(NodeView.DEFAULT), param.uncheckedStamp()));
                 if (result != null) {
                     return result;
                 }
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/SnippetTemplate.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/SnippetTemplate.java
index 067fde637bd..45258eac938 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/SnippetTemplate.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/SnippetTemplate.java
@@ -83,6 +83,7 @@ import org.graalvm.compiler.nodes.FixedWithNextNode;
 import org.graalvm.compiler.nodes.FrameState;
 import org.graalvm.compiler.nodes.LoopBeginNode;
 import org.graalvm.compiler.nodes.MergeNode;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.ParameterNode;
 import org.graalvm.compiler.nodes.PhiNode;
 import org.graalvm.compiler.nodes.PiNode.Placeholder;
@@ -750,7 +751,7 @@ public class SnippetTemplate {
                         nodeReplacements.put(parameter, placeholder);
                         placeholders[i] = placeholder;
                     } else if (args.info.isNonNullParameter(i)) {
-                        parameter.setStamp(parameter.stamp().join(StampFactory.objectNonNull()));
+                        parameter.setStamp(parameter.stamp(NodeView.DEFAULT).join(StampFactory.objectNonNull()));
                     }
                 }
             }
@@ -785,7 +786,8 @@ public class SnippetTemplate {
                             if (usage instanceof LoadIndexedNode) {
                                 LoadIndexedNode loadIndexed = (LoadIndexedNode) usage;
                                 debug.dump(DebugContext.INFO_LEVEL, snippetCopy, "Before replacing %s", loadIndexed);
-                                LoadSnippetVarargParameterNode loadSnippetParameter = snippetCopy.add(new LoadSnippetVarargParameterNode(params, loadIndexed.index(), loadIndexed.stamp()));
+                                LoadSnippetVarargParameterNode loadSnippetParameter = snippetCopy.add(
+                                                new LoadSnippetVarargParameterNode(params, loadIndexed.index(), loadIndexed.stamp(NodeView.DEFAULT)));
                                 snippetCopy.replaceFixedWithFixed(loadIndexed, loadSnippetParameter);
                                 debug.dump(DebugContext.INFO_LEVEL, snippetCopy, "After replacing %s", loadIndexed);
                             } else if (usage instanceof StoreIndexedNode) {
@@ -829,7 +831,7 @@ public class SnippetTemplate {
             for (Node node : snippetCopy.getNodes()) {
                 if (node instanceof ValueNode) {
                     ValueNode valueNode = (ValueNode) node;
-                    if (valueNode.stamp() == PlaceholderStamp.singleton()) {
+                    if (valueNode.stamp(NodeView.DEFAULT) == PlaceholderStamp.singleton()) {
                         curPlaceholderStampedNodes.add(valueNode);
                     }
                 }
@@ -1502,7 +1504,7 @@ public class SnippetTemplate {
     private void updateStamps(ValueNode replacee, UnmodifiableEconomicMap<Node, Node> duplicates) {
         for (ValueNode node : placeholderStampedNodes) {
             ValueNode dup = (ValueNode) duplicates.get(node);
-            Stamp replaceeStamp = replacee.stamp();
+            Stamp replaceeStamp = replacee.stamp(NodeView.DEFAULT);
             if (node instanceof Placeholder) {
                 Placeholder placeholderDup = (Placeholder) dup;
                 placeholderDup.makeReplacement(replaceeStamp);
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/StandardGraphBuilderPlugins.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/StandardGraphBuilderPlugins.java
index 9bd73d2d322..ba074f3a9c4 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/StandardGraphBuilderPlugins.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/StandardGraphBuilderPlugins.java
@@ -54,6 +54,7 @@ import org.graalvm.compiler.nodes.ConstantNode;
 import org.graalvm.compiler.nodes.DeoptimizeNode;
 import org.graalvm.compiler.nodes.FixedGuardNode;
 import org.graalvm.compiler.nodes.LogicNode;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.StructuredGraph;
 import org.graalvm.compiler.nodes.ValueNode;
 import org.graalvm.compiler.nodes.calc.AbsNode;
@@ -296,14 +297,14 @@ public class StandardGraphBuilderPlugins {
         r.register2("divideUnsigned", type, type, new InvocationPlugin() {
             @Override
             public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode dividend, ValueNode divisor) {
-                b.push(kind, b.append(new UnsignedDivNode(dividend, divisor).canonical(null)));
+                b.push(kind, b.append(UnsignedDivNode.create(dividend, divisor, NodeView.DEFAULT)));
                 return true;
             }
         });
         r.register2("remainderUnsigned", type, type, new InvocationPlugin() {
             @Override
             public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode dividend, ValueNode divisor) {
-                b.push(kind, b.append(new UnsignedRemNode(dividend, divisor).canonical(null)));
+                b.push(kind, b.append(UnsignedRemNode.create(dividend, divisor, NodeView.DEFAULT)));
                 return true;
             }
         });
@@ -344,14 +345,14 @@ public class StandardGraphBuilderPlugins {
         r.register1("floatToRawIntBits", float.class, new InvocationPlugin() {
             @Override
             public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode value) {
-                b.push(JavaKind.Int, b.append(new ReinterpretNode(JavaKind.Int, value).canonical(null)));
+                b.push(JavaKind.Int, b.append(ReinterpretNode.create(JavaKind.Int, value, NodeView.DEFAULT)));
                 return true;
             }
         });
         r.register1("intBitsToFloat", int.class, new InvocationPlugin() {
             @Override
             public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode value) {
-                b.push(JavaKind.Float, b.append(new ReinterpretNode(JavaKind.Float, value).canonical(null)));
+                b.push(JavaKind.Float, b.append(ReinterpretNode.create(JavaKind.Float, value, NodeView.DEFAULT)));
                 return true;
             }
         });
@@ -362,14 +363,14 @@ public class StandardGraphBuilderPlugins {
         r.register1("doubleToRawLongBits", double.class, new InvocationPlugin() {
             @Override
             public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode value) {
-                b.push(JavaKind.Long, b.append(new ReinterpretNode(JavaKind.Long, value).canonical(null)));
+                b.push(JavaKind.Long, b.append(ReinterpretNode.create(JavaKind.Long, value, NodeView.DEFAULT)));
                 return true;
             }
         });
         r.register1("longBitsToDouble", long.class, new InvocationPlugin() {
             @Override
             public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode value) {
-                b.push(JavaKind.Double, b.append(new ReinterpretNode(JavaKind.Double, value).canonical(null)));
+                b.push(JavaKind.Double, b.append(ReinterpretNode.create(JavaKind.Double, value, NodeView.DEFAULT)));
                 return true;
             }
         });
@@ -421,7 +422,7 @@ public class StandardGraphBuilderPlugins {
         r.register1("sqrt", Double.TYPE, new InvocationPlugin() {
             @Override
             public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode value) {
-                b.push(JavaKind.Double, b.append(new SqrtNode(value).canonical(null)));
+                b.push(JavaKind.Double, b.append(SqrtNode.create(value, NodeView.DEFAULT)));
                 return true;
             }
         });
@@ -470,7 +471,7 @@ public class StandardGraphBuilderPlugins {
                 cond = cond.negate();
             }
 
-            LogicNode compare = CompareNode.createCompareNode(graph, b.getConstantReflection(), b.getMetaAccess(), b.getOptions(), null, cond, lhs, rhs);
+            LogicNode compare = CompareNode.createCompareNode(graph, b.getConstantReflection(), b.getMetaAccess(), b.getOptions(), null, cond, lhs, rhs, NodeView.DEFAULT);
             b.addPush(JavaKind.Boolean, new ConditionalNode(compare, trueValue, falseValue));
             return true;
         }
@@ -521,7 +522,7 @@ public class StandardGraphBuilderPlugins {
             @Override
             public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver) {
                 ValueNode object = receiver.get();
-                ValueNode folded = GetClassNode.tryFold(b.getMetaAccess(), b.getConstantReflection(), GraphUtil.originalValue(object));
+                ValueNode folded = GetClassNode.tryFold(b.getMetaAccess(), b.getConstantReflection(), NodeView.DEFAULT, GraphUtil.originalValue(object));
                 if (folded != null) {
                     b.addPush(JavaKind.Object, folded);
                 } else {
@@ -892,7 +893,8 @@ public class StandardGraphBuilderPlugins {
                         } else if (falseCount == 0 || trueCount == 0) {
                             boolean expected = falseCount == 0;
                             LogicNode condition = b.addWithInputs(
-                                            IntegerEqualsNode.create(b.getConstantReflection(), b.getMetaAccess(), b.getOptions(), null, result, b.add(ConstantNode.forBoolean(!expected))));
+                                            IntegerEqualsNode.create(b.getConstantReflection(), b.getMetaAccess(), b.getOptions(), null, result, b.add(ConstantNode.forBoolean(!expected)),
+                                                            NodeView.DEFAULT));
                             b.append(new FixedGuardNode(condition, DeoptimizationReason.UnreachedCode, DeoptimizationAction.InvalidateReprofile, true));
                             newResult = b.add(ConstantNode.forBoolean(expected));
                         } else {
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/classfile/Classfile.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/classfile/Classfile.java
index d627cc4f68b..c983a1b466c 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/classfile/Classfile.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/classfile/Classfile.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2017, 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
@@ -46,8 +46,8 @@ public class Classfile {
     private final ResolvedJavaType type;
     private final List<ClassfileBytecode> codeAttributes;
 
-    private static final int MAJOR_VERSION_JAVA7 = 51;
-    private static final int MAJOR_VERSION_JAVA9 = 53;
+    private static final int MAJOR_VERSION_JAVA_MIN = 51;
+    private static final int MAJOR_VERSION_JAVA_MAX = 54;
     private static final int MAGIC = 0xCAFEBABE;
 
     /**
@@ -65,7 +65,7 @@ public class Classfile {
 
         int minor = stream.readUnsignedShort();
         int major = stream.readUnsignedShort();
-        if (major < MAJOR_VERSION_JAVA7 || major > MAJOR_VERSION_JAVA9) {
+        if (major < MAJOR_VERSION_JAVA_MIN || major > MAJOR_VERSION_JAVA_MAX) {
             throw new UnsupportedClassVersionError("Unsupported class file version: " + major + "." + minor);
         }
 
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/ArrayEqualsNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/ArrayEqualsNode.java
index a08ff3182cc..9f62f4d66bb 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/ArrayEqualsNode.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/ArrayEqualsNode.java
@@ -35,6 +35,7 @@ import org.graalvm.compiler.nodeinfo.NodeSize;
 import org.graalvm.compiler.nodes.ConstantNode;
 import org.graalvm.compiler.nodes.FixedWithNextNode;
 import org.graalvm.compiler.nodes.NamedLocationIdentity;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.ValueNode;
 import org.graalvm.compiler.nodes.ValueNodeUtil;
 import org.graalvm.compiler.nodes.memory.MemoryAccess;
@@ -173,7 +174,7 @@ public final class ArrayEqualsNode extends FixedWithNextNode implements LIRLower
                             allEqual = false;
                         }
                     }
-                    if (entry1.stamp().alwaysDistinct(entry2.stamp())) {
+                    if (entry1.stamp(NodeView.DEFAULT).alwaysDistinct(entry2.stamp(NodeView.DEFAULT))) {
                         // the contents are different
                         tool.replaceWithValue(ConstantNode.forBoolean(false, graph()));
                         return;
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/BasicArrayCopyNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/BasicArrayCopyNode.java
index 0c5e6a17724..a8c9eca51f7 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/BasicArrayCopyNode.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/BasicArrayCopyNode.java
@@ -37,6 +37,7 @@ import org.graalvm.compiler.nodes.ConstantNode;
 import org.graalvm.compiler.nodes.DeoptimizingNode;
 import org.graalvm.compiler.nodes.FrameState;
 import org.graalvm.compiler.nodes.NamedLocationIdentity;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.ValueNode;
 import org.graalvm.compiler.nodes.java.LoadIndexedNode;
 import org.graalvm.compiler.nodes.memory.AbstractMemoryCheckpoint;
@@ -164,8 +165,8 @@ public class BasicArrayCopyNode extends AbstractMemoryCheckpoint implements Virt
      * Returns true if this copy doesn't require store checks. Trivially true for primitive arrays.
      */
     public boolean isExact() {
-        ResolvedJavaType srcType = StampTool.typeOrNull(getSource().stamp());
-        ResolvedJavaType destType = StampTool.typeOrNull(getDestination().stamp());
+        ResolvedJavaType srcType = StampTool.typeOrNull(getSource().stamp(NodeView.DEFAULT));
+        ResolvedJavaType destType = StampTool.typeOrNull(getDestination().stamp(NodeView.DEFAULT));
         if (srcType == null || !srcType.isArray() || destType == null || !destType.isArray()) {
             return false;
         }
@@ -173,7 +174,7 @@ public class BasicArrayCopyNode extends AbstractMemoryCheckpoint implements Virt
             return true;
         }
 
-        if (StampTool.isExactType(getDestination().stamp())) {
+        if (StampTool.isExactType(getDestination().stamp(NodeView.DEFAULT))) {
             if (destType != null && destType.isAssignableFrom(srcType)) {
                 return true;
             }
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/BasicObjectCloneNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/BasicObjectCloneNode.java
index 9261266c0eb..a053795a24f 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/BasicObjectCloneNode.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/BasicObjectCloneNode.java
@@ -34,6 +34,7 @@ import org.graalvm.compiler.graph.NodeClass;
 import org.graalvm.compiler.nodeinfo.NodeCycles;
 import org.graalvm.compiler.nodeinfo.NodeInfo;
 import org.graalvm.compiler.nodes.CallTargetNode.InvokeKind;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.ValueNode;
 import org.graalvm.compiler.nodes.java.LoadFieldNode;
 import org.graalvm.compiler.nodes.java.MonitorIdNode;
@@ -65,7 +66,7 @@ public abstract class BasicObjectCloneNode extends MacroStateSplitNode implement
     }
 
     protected Stamp computeStamp(ValueNode object) {
-        Stamp objectStamp = object.stamp();
+        Stamp objectStamp = object.stamp(NodeView.DEFAULT);
         if (objectStamp instanceof ObjectStamp) {
             objectStamp = objectStamp.join(StampFactory.objectNonNull());
         }
@@ -116,7 +117,7 @@ public abstract class BasicObjectCloneNode extends MacroStateSplitNode implement
                 tool.replaceWithVirtual(newVirtual);
             }
         } else {
-            ResolvedJavaType type = getConcreteType(originalAlias.stamp());
+            ResolvedJavaType type = getConcreteType(originalAlias.stamp(NodeView.DEFAULT));
             if (type != null && !type.isArray()) {
                 VirtualInstanceNode newVirtual = createVirtualInstanceNode(type, true);
                 ResolvedJavaField[] fields = newVirtual.getFields();
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/BinaryMathIntrinsicNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/BinaryMathIntrinsicNode.java
index 2e68bc7e6b8..e2fad1dfccc 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/BinaryMathIntrinsicNode.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/BinaryMathIntrinsicNode.java
@@ -35,6 +35,7 @@ import org.graalvm.compiler.graph.spi.CanonicalizerTool;
 import org.graalvm.compiler.lir.gen.ArithmeticLIRGeneratorTool;
 import org.graalvm.compiler.nodeinfo.NodeInfo;
 import org.graalvm.compiler.nodes.ConstantNode;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.ValueNode;
 import org.graalvm.compiler.nodes.calc.BinaryNode;
 import org.graalvm.compiler.nodes.calc.FloatDivNode;
@@ -86,13 +87,13 @@ public final class BinaryMathIntrinsicNode extends BinaryNode implements Arithme
 
     @Override
     public Stamp foldStamp(Stamp stampX, Stamp stampY) {
-        return stamp();
+        return stamp(NodeView.DEFAULT);
     }
 
     protected BinaryMathIntrinsicNode(ValueNode forX, ValueNode forY, BinaryOperation op) {
         super(TYPE, StampFactory.forKind(JavaKind.Double), forX, forY);
-        assert forX.stamp() instanceof FloatStamp && PrimitiveStamp.getBits(forX.stamp()) == 64;
-        assert forY.stamp() instanceof FloatStamp && PrimitiveStamp.getBits(forY.stamp()) == 64;
+        assert forX.stamp(NodeView.DEFAULT) instanceof FloatStamp && PrimitiveStamp.getBits(forX.stamp(NodeView.DEFAULT)) == 64;
+        assert forY.stamp(NodeView.DEFAULT) instanceof FloatStamp && PrimitiveStamp.getBits(forY.stamp(NodeView.DEFAULT)) == 64;
         this.operation = op;
     }
 
@@ -118,6 +119,7 @@ public final class BinaryMathIntrinsicNode extends BinaryNode implements Arithme
 
     @Override
     public ValueNode canonical(CanonicalizerTool tool, ValueNode forX, ValueNode forY) {
+        NodeView view = NodeView.from(tool);
         ValueNode c = tryConstantFold(forX, forY, getOperation());
         if (c != null) {
             return c;
@@ -150,8 +152,8 @@ public final class BinaryMathIntrinsicNode extends BinaryNode implements Arithme
             }
 
             // x**0.5 = sqrt(x)
-            if (yValue == 0.5D && x.stamp() instanceof FloatStamp && ((FloatStamp) x.stamp()).lowerBound() >= 0.0D) {
-                return new SqrtNode(x);
+            if (yValue == 0.5D && x.stamp(view) instanceof FloatStamp && ((FloatStamp) x.stamp(view)).lowerBound() >= 0.0D) {
+                return SqrtNode.create(x, view);
             }
         }
         return this;
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/BitCountNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/BitCountNode.java
index 864e9b569f7..a1868016324 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/BitCountNode.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/BitCountNode.java
@@ -33,6 +33,7 @@ import org.graalvm.compiler.graph.spi.CanonicalizerTool;
 import org.graalvm.compiler.lir.gen.ArithmeticLIRGeneratorTool;
 import org.graalvm.compiler.nodeinfo.NodeInfo;
 import org.graalvm.compiler.nodes.ConstantNode;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.ValueNode;
 import org.graalvm.compiler.nodes.calc.UnaryNode;
 import org.graalvm.compiler.nodes.spi.ArithmeticLIRLowerable;
@@ -48,7 +49,7 @@ public final class BitCountNode extends UnaryNode implements ArithmeticLIRLowera
     public static final NodeClass<BitCountNode> TYPE = NodeClass.create(BitCountNode.class);
 
     public BitCountNode(ValueNode value) {
-        super(TYPE, computeStamp(value.stamp(), value), value);
+        super(TYPE, computeStamp(value.stamp(NodeView.DEFAULT), value), value);
         assert value.getStackKind() == JavaKind.Int || value.getStackKind() == JavaKind.Long;
     }
 
@@ -59,7 +60,7 @@ public final class BitCountNode extends UnaryNode implements ArithmeticLIRLowera
     }
 
     static Stamp computeStamp(Stamp newStamp, ValueNode theValue) {
-        assert newStamp.isCompatible(theValue.stamp());
+        assert newStamp.isCompatible(theValue.stamp(NodeView.DEFAULT));
         IntegerStamp valueStamp = (IntegerStamp) newStamp;
         assert (valueStamp.downMask() & CodeUtil.mask(valueStamp.getBits())) == valueStamp.downMask();
         assert (valueStamp.upMask() & CodeUtil.mask(valueStamp.getBits())) == valueStamp.upMask();
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/BitScanForwardNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/BitScanForwardNode.java
index 80d3d8d61a6..b9d1f50056a 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/BitScanForwardNode.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/BitScanForwardNode.java
@@ -34,6 +34,7 @@ import org.graalvm.compiler.graph.spi.CanonicalizerTool;
 import org.graalvm.compiler.lir.gen.ArithmeticLIRGeneratorTool;
 import org.graalvm.compiler.nodeinfo.NodeInfo;
 import org.graalvm.compiler.nodes.ConstantNode;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.ValueNode;
 import org.graalvm.compiler.nodes.calc.UnaryNode;
 import org.graalvm.compiler.nodes.spi.ArithmeticLIRLowerable;
@@ -53,13 +54,13 @@ public final class BitScanForwardNode extends UnaryNode implements ArithmeticLIR
     public static final NodeClass<BitScanForwardNode> TYPE = NodeClass.create(BitScanForwardNode.class);
 
     public BitScanForwardNode(ValueNode value) {
-        super(TYPE, StampFactory.forInteger(JavaKind.Int, 0, ((PrimitiveStamp) value.stamp()).getBits()), value);
+        super(TYPE, StampFactory.forInteger(JavaKind.Int, 0, ((PrimitiveStamp) value.stamp(NodeView.DEFAULT)).getBits()), value);
         assert value.getStackKind() == JavaKind.Int || value.getStackKind() == JavaKind.Long;
     }
 
     @Override
     public Stamp foldStamp(Stamp newStamp) {
-        assert newStamp.isCompatible(getValue().stamp());
+        assert newStamp.isCompatible(getValue().stamp(NodeView.DEFAULT));
         IntegerStamp valueStamp = (IntegerStamp) newStamp;
         int min;
         int max;
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/BitScanReverseNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/BitScanReverseNode.java
index bf9be6929fb..9dc9ac98ca4 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/BitScanReverseNode.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/BitScanReverseNode.java
@@ -34,6 +34,7 @@ import org.graalvm.compiler.graph.spi.CanonicalizerTool;
 import org.graalvm.compiler.lir.gen.ArithmeticLIRGeneratorTool;
 import org.graalvm.compiler.nodeinfo.NodeInfo;
 import org.graalvm.compiler.nodes.ConstantNode;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.ValueNode;
 import org.graalvm.compiler.nodes.calc.UnaryNode;
 import org.graalvm.compiler.nodes.spi.ArithmeticLIRLowerable;
@@ -53,13 +54,13 @@ public final class BitScanReverseNode extends UnaryNode implements ArithmeticLIR
     public static final NodeClass<BitScanReverseNode> TYPE = NodeClass.create(BitScanReverseNode.class);
 
     public BitScanReverseNode(ValueNode value) {
-        super(TYPE, StampFactory.forInteger(JavaKind.Int, 0, ((PrimitiveStamp) value.stamp()).getBits()), value);
+        super(TYPE, StampFactory.forInteger(JavaKind.Int, 0, ((PrimitiveStamp) value.stamp(NodeView.DEFAULT)).getBits()), value);
         assert value.getStackKind() == JavaKind.Int || value.getStackKind() == JavaKind.Long;
     }
 
     @Override
     public Stamp foldStamp(Stamp newStamp) {
-        assert newStamp.isCompatible(getValue().stamp());
+        assert newStamp.isCompatible(getValue().stamp(NodeView.DEFAULT));
         IntegerStamp valueStamp = (IntegerStamp) newStamp;
         int min;
         int max;
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/MethodHandleNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/MethodHandleNode.java
index 604a9987ea8..bc78260f0c4 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/MethodHandleNode.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/MethodHandleNode.java
@@ -44,6 +44,7 @@ import org.graalvm.compiler.nodes.FixedWithNextNode;
 import org.graalvm.compiler.nodes.GuardNode;
 import org.graalvm.compiler.nodes.InvokeNode;
 import org.graalvm.compiler.nodes.LogicNode;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.PiNode;
 import org.graalvm.compiler.nodes.StructuredGraph;
 import org.graalvm.compiler.nodes.ValueNode;
@@ -263,7 +264,7 @@ public final class MethodHandleNode extends MacroStateSplitNode implements Simpl
                 // Try to get the most accurate receiver type
                 if (intrinsicMethod == IntrinsicMethod.LINK_TO_VIRTUAL || intrinsicMethod == IntrinsicMethod.LINK_TO_INTERFACE) {
                     ValueNode receiver = getReceiver(originalArguments);
-                    TypeReference receiverType = StampTool.typeReferenceOrNull(receiver.stamp());
+                    TypeReference receiverType = StampTool.typeReferenceOrNull(receiver.stamp(NodeView.DEFAULT));
                     if (receiverType != null) {
                         concreteMethod = receiverType.getType().findUniqueConcreteMethod(target);
                     }
@@ -317,7 +318,7 @@ public final class MethodHandleNode extends MacroStateSplitNode implements Simpl
              * type information anyway.
              */
             if (targetType != null && !targetType.getType().isPrimitive() && !argument.getStackKind().isPrimitive()) {
-                ResolvedJavaType argumentType = StampTool.typeOrNull(argument.stamp());
+                ResolvedJavaType argumentType = StampTool.typeOrNull(argument.stamp(NodeView.DEFAULT));
                 if (argumentType == null || (argumentType.isAssignableFrom(targetType.getType()) && !argumentType.equals(targetType.getType()))) {
                     LogicNode inst = InstanceOfNode.createAllowNull(targetType, argument, null, null);
                     assert !inst.isAlive();
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/ReadRegisterNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/ReadRegisterNode.java
index ff5d7658706..e46bf3af591 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/ReadRegisterNode.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/ReadRegisterNode.java
@@ -32,6 +32,7 @@ import org.graalvm.compiler.graph.NodeClass;
 import org.graalvm.compiler.nodeinfo.NodeInfo;
 import org.graalvm.compiler.nodeinfo.Verbosity;
 import org.graalvm.compiler.nodes.FixedWithNextNode;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.spi.LIRLowerable;
 import org.graalvm.compiler.nodes.spi.NodeLIRBuilderTool;
 
@@ -82,7 +83,7 @@ public final class ReadRegisterNode extends FixedWithNextNode implements LIRLowe
 
     @Override
     public void generate(NodeLIRBuilderTool generator) {
-        LIRKind kind = generator.getLIRGeneratorTool().getLIRKind(stamp());
+        LIRKind kind = generator.getLIRGeneratorTool().getLIRKind(stamp(NodeView.DEFAULT));
         Value result = register.asValue(kind);
         if (incoming) {
             generator.getLIRGeneratorTool().emitIncomingValues(new Value[]{result});
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/ReverseBytesNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/ReverseBytesNode.java
index 414a7632938..5ad14522c51 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/ReverseBytesNode.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/ReverseBytesNode.java
@@ -32,6 +32,7 @@ import org.graalvm.compiler.graph.NodeClass;
 import org.graalvm.compiler.graph.spi.CanonicalizerTool;
 import org.graalvm.compiler.nodeinfo.NodeInfo;
 import org.graalvm.compiler.nodes.ConstantNode;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.ValueNode;
 import org.graalvm.compiler.nodes.calc.UnaryNode;
 import org.graalvm.compiler.nodes.spi.LIRLowerable;
@@ -54,7 +55,7 @@ public final class ReverseBytesNode extends UnaryNode implements LIRLowerable {
 
     @Override
     public Stamp foldStamp(Stamp newStamp) {
-        assert newStamp.isCompatible(getValue().stamp());
+        assert newStamp.isCompatible(getValue().stamp(NodeView.DEFAULT));
         IntegerStamp valueStamp = (IntegerStamp) newStamp;
         if (getStackKind() == JavaKind.Int) {
             long mask = CodeUtil.mask(JavaKind.Int.getBitCount());
@@ -62,7 +63,7 @@ public final class ReverseBytesNode extends UnaryNode implements LIRLowerable {
         } else if (getStackKind() == JavaKind.Long) {
             return IntegerStamp.stampForMask(valueStamp.getBits(), Long.reverse(valueStamp.downMask()), Long.reverse(valueStamp.upMask()));
         } else {
-            return stamp();
+            return stamp(NodeView.DEFAULT);
         }
     }
 
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/UnaryMathIntrinsicNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/UnaryMathIntrinsicNode.java
index 390c96af042..77d2c6d94dc 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/UnaryMathIntrinsicNode.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/UnaryMathIntrinsicNode.java
@@ -36,6 +36,7 @@ import org.graalvm.compiler.graph.spi.CanonicalizerTool;
 import org.graalvm.compiler.lir.gen.ArithmeticLIRGeneratorTool;
 import org.graalvm.compiler.nodeinfo.NodeInfo;
 import org.graalvm.compiler.nodes.ConstantNode;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.ValueNode;
 import org.graalvm.compiler.nodes.calc.UnaryNode;
 import org.graalvm.compiler.nodes.spi.ArithmeticLIRLowerable;
@@ -106,8 +107,8 @@ public final class UnaryMathIntrinsicNode extends UnaryNode implements Arithmeti
     }
 
     protected UnaryMathIntrinsicNode(ValueNode value, UnaryOperation op) {
-        super(TYPE, computeStamp(value.stamp(), op), value);
-        assert value.stamp() instanceof FloatStamp && PrimitiveStamp.getBits(value.stamp()) == 64;
+        super(TYPE, computeStamp(value.stamp(NodeView.DEFAULT), op), value);
+        assert value.stamp(NodeView.DEFAULT) instanceof FloatStamp && PrimitiveStamp.getBits(value.stamp(NodeView.DEFAULT)) == 64;
         this.operation = op;
     }
 
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/arithmetic/IntegerAddExactNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/arithmetic/IntegerAddExactNode.java
index bc6ae830e95..f22225b1e7d 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/arithmetic/IntegerAddExactNode.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/arithmetic/IntegerAddExactNode.java
@@ -36,6 +36,7 @@ import org.graalvm.compiler.graph.spi.CanonicalizerTool;
 import org.graalvm.compiler.nodeinfo.NodeInfo;
 import org.graalvm.compiler.nodes.AbstractBeginNode;
 import org.graalvm.compiler.nodes.ConstantNode;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.ValueNode;
 import org.graalvm.compiler.nodes.calc.AddNode;
 import org.graalvm.compiler.nodes.spi.LoweringTool;
@@ -54,8 +55,8 @@ public final class IntegerAddExactNode extends AddNode implements IntegerExactAr
 
     public IntegerAddExactNode(ValueNode x, ValueNode y) {
         super(TYPE, x, y);
-        setStamp(x.stamp().unrestricted());
-        assert x.stamp().isCompatible(y.stamp()) && x.stamp() instanceof IntegerStamp;
+        setStamp(x.stamp(NodeView.DEFAULT).unrestricted());
+        assert x.stamp(NodeView.DEFAULT).isCompatible(y.stamp(NodeView.DEFAULT)) && x.stamp(NodeView.DEFAULT) instanceof IntegerStamp;
     }
 
     @Override
@@ -132,7 +133,7 @@ public final class IntegerAddExactNode extends AddNode implements IntegerExactAr
                 return forX;
             }
         }
-        if (!IntegerStamp.addCanOverflow((IntegerStamp) forX.stamp(), (IntegerStamp) forY.stamp())) {
+        if (!IntegerStamp.addCanOverflow((IntegerStamp) forX.stamp(NodeView.DEFAULT), (IntegerStamp) forY.stamp(NodeView.DEFAULT))) {
             return new AddNode(forX, forY).canonical(tool);
         }
         return this;
@@ -159,7 +160,7 @@ public final class IntegerAddExactNode extends AddNode implements IntegerExactAr
 
     @Override
     public IntegerExactArithmeticSplitNode createSplit(AbstractBeginNode next, AbstractBeginNode deopt) {
-        return graph().add(new IntegerAddExactSplitNode(stamp(), getX(), getY(), next, deopt));
+        return graph().add(new IntegerAddExactSplitNode(stamp(NodeView.DEFAULT), getX(), getY(), next, deopt));
     }
 
     @Override
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/arithmetic/IntegerAddExactSplitNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/arithmetic/IntegerAddExactSplitNode.java
index 43bb3993c3c..5e909f8e165 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/arithmetic/IntegerAddExactSplitNode.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/arithmetic/IntegerAddExactSplitNode.java
@@ -28,6 +28,7 @@ import org.graalvm.compiler.graph.NodeClass;
 import org.graalvm.compiler.graph.spi.SimplifierTool;
 import org.graalvm.compiler.nodeinfo.NodeInfo;
 import org.graalvm.compiler.nodes.AbstractBeginNode;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.ValueNode;
 import org.graalvm.compiler.nodes.calc.AddNode;
 import org.graalvm.compiler.nodes.spi.NodeLIRBuilderTool;
@@ -49,7 +50,8 @@ public final class IntegerAddExactSplitNode extends IntegerExactArithmeticSplitN
 
     @Override
     public void simplify(SimplifierTool tool) {
-        if (!IntegerStamp.addCanOverflow((IntegerStamp) x.stamp(), (IntegerStamp) y.stamp())) {
+        NodeView view = NodeView.from(tool);
+        if (!IntegerStamp.addCanOverflow((IntegerStamp) x.stamp(view), (IntegerStamp) y.stamp(view))) {
             tool.deleteBranch(overflowSuccessor);
             tool.addToWorkList(next);
             AddNode replacement = graph().unique(new AddNode(x, y));
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/arithmetic/IntegerMulExactNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/arithmetic/IntegerMulExactNode.java
index f4781e44123..f3656b3b5f0 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/arithmetic/IntegerMulExactNode.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/arithmetic/IntegerMulExactNode.java
@@ -31,6 +31,7 @@ import org.graalvm.compiler.graph.spi.CanonicalizerTool;
 import org.graalvm.compiler.nodeinfo.NodeInfo;
 import org.graalvm.compiler.nodes.AbstractBeginNode;
 import org.graalvm.compiler.nodes.ConstantNode;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.ValueNode;
 import org.graalvm.compiler.nodes.calc.MulNode;
 import org.graalvm.compiler.nodes.spi.LoweringTool;
@@ -48,8 +49,8 @@ public final class IntegerMulExactNode extends MulNode implements IntegerExactAr
 
     public IntegerMulExactNode(ValueNode x, ValueNode y) {
         super(TYPE, x, y);
-        setStamp(x.stamp().unrestricted());
-        assert x.stamp().isCompatible(y.stamp()) && x.stamp() instanceof IntegerStamp;
+        setStamp(x.stamp(NodeView.DEFAULT).unrestricted());
+        assert x.stamp(NodeView.DEFAULT).isCompatible(y.stamp(NodeView.DEFAULT)) && x.stamp(NodeView.DEFAULT) instanceof IntegerStamp;
     }
 
     @Override
@@ -76,10 +77,10 @@ public final class IntegerMulExactNode extends MulNode implements IntegerExactAr
                 return forX;
             }
             if (c == 0) {
-                return ConstantNode.forIntegerStamp(stamp(), 0);
+                return ConstantNode.forIntegerStamp(stamp(NodeView.DEFAULT), 0);
             }
         }
-        if (!IntegerStamp.multiplicationCanOverflow((IntegerStamp) x.stamp(), (IntegerStamp) y.stamp())) {
+        if (!IntegerStamp.multiplicationCanOverflow((IntegerStamp) x.stamp(NodeView.DEFAULT), (IntegerStamp) y.stamp(NodeView.DEFAULT))) {
             return new MulNode(x, y).canonical(tool);
         }
         return this;
@@ -104,7 +105,7 @@ public final class IntegerMulExactNode extends MulNode implements IntegerExactAr
 
     @Override
     public IntegerExactArithmeticSplitNode createSplit(AbstractBeginNode next, AbstractBeginNode deopt) {
-        return graph().add(new IntegerMulExactSplitNode(stamp(), getX(), getY(), next, deopt));
+        return graph().add(new IntegerMulExactSplitNode(stamp(NodeView.DEFAULT), getX(), getY(), next, deopt));
     }
 
     @Override
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/arithmetic/IntegerMulExactSplitNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/arithmetic/IntegerMulExactSplitNode.java
index c7d4492e53d..aff76b1d4e4 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/arithmetic/IntegerMulExactSplitNode.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/arithmetic/IntegerMulExactSplitNode.java
@@ -30,6 +30,7 @@ import org.graalvm.compiler.graph.NodeClass;
 import org.graalvm.compiler.graph.spi.SimplifierTool;
 import org.graalvm.compiler.nodeinfo.NodeInfo;
 import org.graalvm.compiler.nodes.AbstractBeginNode;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.ValueNode;
 import org.graalvm.compiler.nodes.calc.MulNode;
 import org.graalvm.compiler.nodes.spi.NodeLIRBuilderTool;
@@ -51,7 +52,8 @@ public final class IntegerMulExactSplitNode extends IntegerExactArithmeticSplitN
 
     @Override
     public void simplify(SimplifierTool tool) {
-        if (!IntegerStamp.multiplicationCanOverflow((IntegerStamp) x.stamp(), (IntegerStamp) y.stamp())) {
+        NodeView view = NodeView.from(tool);
+        if (!IntegerStamp.multiplicationCanOverflow((IntegerStamp) x.stamp(view), (IntegerStamp) y.stamp(view))) {
             tool.deleteBranch(overflowSuccessor);
             tool.addToWorkList(next);
             MulNode replacement = graph().unique(new MulNode(x, y));
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/arithmetic/IntegerMulHighNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/arithmetic/IntegerMulHighNode.java
index 2a7107ef435..13c381dc6fc 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/arithmetic/IntegerMulHighNode.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/arithmetic/IntegerMulHighNode.java
@@ -33,6 +33,7 @@ import org.graalvm.compiler.graph.spi.CanonicalizerTool;
 import org.graalvm.compiler.lir.gen.ArithmeticLIRGeneratorTool;
 import org.graalvm.compiler.nodeinfo.NodeInfo;
 import org.graalvm.compiler.nodes.ConstantNode;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.ValueNode;
 import org.graalvm.compiler.nodes.calc.BinaryArithmeticNode;
 import org.graalvm.compiler.nodes.spi.NodeLIRBuilderTool;
@@ -81,7 +82,7 @@ public final class IntegerMulHighNode extends BinaryArithmeticNode<MulHigh> impl
             if (c instanceof PrimitiveConstant && ((PrimitiveConstant) c).getJavaKind().isNumericInteger()) {
                 long i = ((PrimitiveConstant) c).asLong();
                 if (i == 0 || i == 1) {
-                    return ConstantNode.forIntegerStamp(self.stamp(), 0);
+                    return ConstantNode.forIntegerStamp(self.stamp(NodeView.DEFAULT), 0);
                 }
             }
         }
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/arithmetic/IntegerSubExactNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/arithmetic/IntegerSubExactNode.java
index 625dbf6f7d0..d9a4ede5b95 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/arithmetic/IntegerSubExactNode.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/arithmetic/IntegerSubExactNode.java
@@ -31,6 +31,7 @@ import org.graalvm.compiler.graph.spi.CanonicalizerTool;
 import org.graalvm.compiler.nodeinfo.NodeInfo;
 import org.graalvm.compiler.nodes.AbstractBeginNode;
 import org.graalvm.compiler.nodes.ConstantNode;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.ValueNode;
 import org.graalvm.compiler.nodes.calc.SubNode;
 import org.graalvm.compiler.nodes.spi.LoweringTool;
@@ -49,8 +50,8 @@ public final class IntegerSubExactNode extends SubNode implements IntegerExactAr
 
     public IntegerSubExactNode(ValueNode x, ValueNode y) {
         super(TYPE, x, y);
-        setStamp(x.stamp().unrestricted());
-        assert x.stamp().isCompatible(y.stamp()) && x.stamp() instanceof IntegerStamp;
+        setStamp(x.stamp(NodeView.DEFAULT).unrestricted());
+        assert x.stamp(NodeView.DEFAULT).isCompatible(y.stamp(NodeView.DEFAULT)) && x.stamp(NodeView.DEFAULT) instanceof IntegerStamp;
     }
 
     @Override
@@ -67,7 +68,7 @@ public final class IntegerSubExactNode extends SubNode implements IntegerExactAr
     @Override
     public ValueNode canonical(CanonicalizerTool tool, ValueNode forX, ValueNode forY) {
         if (GraphUtil.unproxify(forX) == GraphUtil.unproxify(forY)) {
-            return ConstantNode.forIntegerStamp(stamp(), 0);
+            return ConstantNode.forIntegerStamp(stamp(NodeView.DEFAULT), 0);
         }
         if (forX.isConstant() && forY.isConstant()) {
             return canonicalXYconstant(forX, forY);
@@ -77,7 +78,7 @@ public final class IntegerSubExactNode extends SubNode implements IntegerExactAr
                 return forX;
             }
         }
-        if (!IntegerStamp.subtractionCanOverflow((IntegerStamp) x.stamp(), (IntegerStamp) y.stamp())) {
+        if (!IntegerStamp.subtractionCanOverflow((IntegerStamp) x.stamp(NodeView.DEFAULT), (IntegerStamp) y.stamp(NodeView.DEFAULT))) {
             return new SubNode(x, y).canonical(tool);
         }
         return this;
@@ -102,7 +103,7 @@ public final class IntegerSubExactNode extends SubNode implements IntegerExactAr
 
     @Override
     public IntegerExactArithmeticSplitNode createSplit(AbstractBeginNode next, AbstractBeginNode deopt) {
-        return graph().add(new IntegerSubExactSplitNode(stamp(), getX(), getY(), next, deopt));
+        return graph().add(new IntegerSubExactSplitNode(stamp(NodeView.DEFAULT), getX(), getY(), next, deopt));
     }
 
     @Override
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/arithmetic/IntegerSubExactSplitNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/arithmetic/IntegerSubExactSplitNode.java
index ad6ad0f808e..acd86ca9995 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/arithmetic/IntegerSubExactSplitNode.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/arithmetic/IntegerSubExactSplitNode.java
@@ -28,6 +28,7 @@ import org.graalvm.compiler.graph.NodeClass;
 import org.graalvm.compiler.graph.spi.SimplifierTool;
 import org.graalvm.compiler.nodeinfo.NodeInfo;
 import org.graalvm.compiler.nodes.AbstractBeginNode;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.ValueNode;
 import org.graalvm.compiler.nodes.calc.SubNode;
 import org.graalvm.compiler.nodes.spi.NodeLIRBuilderTool;
@@ -49,7 +50,8 @@ public final class IntegerSubExactSplitNode extends IntegerExactArithmeticSplitN
 
     @Override
     public void simplify(SimplifierTool tool) {
-        if (!IntegerStamp.subtractionCanOverflow((IntegerStamp) x.stamp(), (IntegerStamp) y.stamp())) {
+        NodeView view = NodeView.from(tool);
+        if (!IntegerStamp.subtractionCanOverflow((IntegerStamp) x.stamp(view), (IntegerStamp) y.stamp(view))) {
             tool.deleteBranch(overflowSuccessor);
             tool.addToWorkList(next);
             SubNode replacement = graph().unique(new SubNode(x, y));
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/arithmetic/UnsignedMulHighNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/arithmetic/UnsignedMulHighNode.java
index ff0d7d1ceca..c0554681a7c 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/arithmetic/UnsignedMulHighNode.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/nodes/arithmetic/UnsignedMulHighNode.java
@@ -33,6 +33,7 @@ import org.graalvm.compiler.graph.spi.CanonicalizerTool;
 import org.graalvm.compiler.lir.gen.ArithmeticLIRGeneratorTool;
 import org.graalvm.compiler.nodeinfo.NodeInfo;
 import org.graalvm.compiler.nodes.ConstantNode;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.ValueNode;
 import org.graalvm.compiler.nodes.calc.BinaryArithmeticNode;
 import org.graalvm.compiler.nodes.spi.NodeLIRBuilderTool;
@@ -81,7 +82,7 @@ public final class UnsignedMulHighNode extends BinaryArithmeticNode<UMulHigh> im
             if (c instanceof PrimitiveConstant && ((PrimitiveConstant) c).getJavaKind().isNumericInteger()) {
                 long i = ((PrimitiveConstant) c).asLong();
                 if (i == 0 || i == 1) {
-                    return ConstantNode.forIntegerStamp(self.stamp(), 0);
+                    return ConstantNode.forIntegerStamp(self.stamp(NodeView.DEFAULT), 0);
                 }
             }
         }
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.virtual/src/org/graalvm/compiler/virtual/phases/ea/GraphEffectList.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.virtual/src/org/graalvm/compiler/virtual/phases/ea/GraphEffectList.java
index d880f8dd5cb..9dae16eef14 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.virtual/src/org/graalvm/compiler/virtual/phases/ea/GraphEffectList.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.virtual/src/org/graalvm/compiler/virtual/phases/ea/GraphEffectList.java
@@ -31,6 +31,7 @@ import org.graalvm.compiler.nodes.FixedNode;
 import org.graalvm.compiler.nodes.FixedWithNextNode;
 import org.graalvm.compiler.nodes.FrameState;
 import org.graalvm.compiler.nodes.IfNode;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.PhiNode;
 import org.graalvm.compiler.nodes.PiNode;
 import org.graalvm.compiler.nodes.ProxyNode;
@@ -228,7 +229,8 @@ public final class GraphEffectList extends EffectList {
      */
     public void replaceAtUsages(ValueNode node, ValueNode replacement, FixedNode insertBefore) {
         assert node != null && replacement != null : node + " " + replacement;
-        assert node.stamp().isCompatible(replacement.stamp()) : "Replacement node stamp not compatible " + node.stamp() + " vs " + replacement.stamp();
+        assert node.stamp(NodeView.DEFAULT).isCompatible(replacement.stamp(NodeView.DEFAULT)) : "Replacement node stamp not compatible " + node.stamp(NodeView.DEFAULT) + " vs " +
+                        replacement.stamp(NodeView.DEFAULT);
         add("replace at usages", (graph, obsoleteNodes) -> {
             assert node.isAlive();
             ValueNode replacementNode = graph.addOrUniqueWithInputs(replacement);
@@ -244,8 +246,8 @@ public final class GraphEffectList extends EffectList {
              * to improve the stamp information of the read. Such a read might later be replaced
              * with a read with a less precise stamp.
              */
-            if (!node.stamp().equals(replacementNode.stamp())) {
-                replacementNode = graph.unique(new PiNode(replacementNode, node.stamp()));
+            if (!node.stamp(NodeView.DEFAULT).equals(replacementNode.stamp(NodeView.DEFAULT))) {
+                replacementNode = graph.unique(new PiNode(replacementNode, node.stamp(NodeView.DEFAULT)));
             }
             node.replaceAtUsages(replacementNode);
             if (node instanceof FixedWithNextNode) {
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.virtual/src/org/graalvm/compiler/virtual/phases/ea/PEReadEliminationBlockState.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.virtual/src/org/graalvm/compiler/virtual/phases/ea/PEReadEliminationBlockState.java
index b4722d8f9b5..5403efad0a3 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.virtual/src/org/graalvm/compiler/virtual/phases/ea/PEReadEliminationBlockState.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.virtual/src/org/graalvm/compiler/virtual/phases/ea/PEReadEliminationBlockState.java
@@ -29,6 +29,7 @@ import org.graalvm.compiler.core.common.type.IntegerStamp;
 import org.graalvm.compiler.core.common.type.Stamp;
 import org.graalvm.compiler.debug.DebugContext;
 import org.graalvm.compiler.nodes.FieldLocationIdentity;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.ValueNode;
 import org.graalvm.compiler.nodes.virtual.AllocatedObjectNode;
 import org.graalvm.compiler.nodes.virtual.VirtualInstanceNode;
@@ -127,7 +128,7 @@ public final class PEReadEliminationBlockState extends PartialEscapeBlockState<P
             VirtualInstanceNode instance = (VirtualInstanceNode) virtual;
             for (int i = 0; i < instance.entryCount(); i++) {
                 JavaKind declaredKind = instance.field(i).getJavaKind();
-                if (declaredKind == stampToJavaKind(values.get(i).stamp())) {
+                if (declaredKind == stampToJavaKind(values.get(i).stamp(NodeView.DEFAULT))) {
                     // We won't cache unaligned field writes upon instantiation unless we add
                     // support for non-array objects in PEReadEliminationClosure.processUnsafeLoad.
                     readCache.put(new ReadCacheEntry(new FieldLocationIdentity(instance.field(i)), representation, -1, declaredKind, false), values.get(i));
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.virtual/src/org/graalvm/compiler/virtual/phases/ea/PEReadEliminationClosure.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.virtual/src/org/graalvm/compiler/virtual/phases/ea/PEReadEliminationClosure.java
index b667a19c72f..59ec8053046 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.virtual/src/org/graalvm/compiler/virtual/phases/ea/PEReadEliminationClosure.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.virtual/src/org/graalvm/compiler/virtual/phases/ea/PEReadEliminationClosure.java
@@ -39,6 +39,7 @@ import org.graalvm.compiler.nodes.FixedWithNextNode;
 import org.graalvm.compiler.nodes.LoopBeginNode;
 import org.graalvm.compiler.nodes.LoopExitNode;
 import org.graalvm.compiler.nodes.NamedLocationIdentity;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.PhiNode;
 import org.graalvm.compiler.nodes.ProxyNode;
 import org.graalvm.compiler.nodes.StructuredGraph.ScheduleResult;
@@ -189,7 +190,7 @@ public final class PEReadEliminationClosure extends PartialEscapeClosure<PEReadE
                 ValueNode object = GraphUtil.unproxify(load.object());
                 LocationIdentity location = NamedLocationIdentity.getArrayLocation(componentKind);
                 ValueNode cachedValue = state.getReadCache(object, location, index, accessKind, this);
-                assert cachedValue == null || load.stamp().isCompatible(cachedValue.stamp()) : "The RawLoadNode's stamp is not compatible with the cached value.";
+                assert cachedValue == null || load.stamp(NodeView.DEFAULT).isCompatible(cachedValue.stamp(NodeView.DEFAULT)) : "The RawLoadNode's stamp is not compatible with the cached value.";
                 if (cachedValue != null) {
                     effects.replaceAtUsages(load, cachedValue, load);
                     addScalarAlias(load, cachedValue);
@@ -399,7 +400,7 @@ public final class PEReadEliminationClosure extends PartialEscapeClosure<PEReadE
                     // e.g. unsafe loads / stores with different access kinds have different stamps
                     // although location, object and offset are the same, in this case we cannot
                     // create a phi nor can we set a common value
-                    if (otherValue == null || !value.stamp().isCompatible(otherValue.stamp())) {
+                    if (otherValue == null || !value.stamp(NodeView.DEFAULT).isCompatible(otherValue.stamp(NodeView.DEFAULT))) {
                         value = null;
                         phi = false;
                         break;
@@ -409,11 +410,11 @@ public final class PEReadEliminationClosure extends PartialEscapeClosure<PEReadE
                     }
                 }
                 if (phi) {
-                    PhiNode phiNode = getPhi(key, value.stamp().unrestricted());
+                    PhiNode phiNode = getPhi(key, value.stamp(NodeView.DEFAULT).unrestricted());
                     mergeEffects.addFloatingNode(phiNode, "mergeReadCache");
                     for (int i = 0; i < states.size(); i++) {
                         ValueNode v = states.get(i).getReadCache(key.object, key.identity, key.index, key.kind, PEReadEliminationClosure.this);
-                        assert phiNode.stamp().isCompatible(v.stamp()) : "Cannot create read elimination phi for inputs with incompatible stamps.";
+                        assert phiNode.stamp(NodeView.DEFAULT).isCompatible(v.stamp(NodeView.DEFAULT)) : "Cannot create read elimination phi for inputs with incompatible stamps.";
                         setPhiInput(phiNode, i, v);
                     }
                     newState.readCache.put(key, phiNode);
@@ -444,13 +445,13 @@ public final class PEReadEliminationClosure extends PartialEscapeClosure<PEReadE
                     ValueNode value = states.get(i).getReadCache(getPhiValueAt(phi, i), identity, index, kind, PEReadEliminationClosure.this);
                     // e.g. unsafe loads / stores with same identity and different access kinds see
                     // mergeReadCache(states)
-                    if (value == null || !values[i - 1].stamp().isCompatible(value.stamp())) {
+                    if (value == null || !values[i - 1].stamp(NodeView.DEFAULT).isCompatible(value.stamp(NodeView.DEFAULT))) {
                         return;
                     }
                     values[i] = value;
                 }
 
-                PhiNode phiNode = getPhi(new ReadCacheEntry(identity, phi, index, kind, overflowAccess), values[0].stamp().unrestricted());
+                PhiNode phiNode = getPhi(new ReadCacheEntry(identity, phi, index, kind, overflowAccess), values[0].stamp(NodeView.DEFAULT).unrestricted());
                 mergeEffects.addFloatingNode(phiNode, "mergeReadCachePhi");
                 for (int i = 0; i < values.length; i++) {
                     setPhiInput(phiNode, i, values[i]);
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.virtual/src/org/graalvm/compiler/virtual/phases/ea/PartialEscapeClosure.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.virtual/src/org/graalvm/compiler/virtual/phases/ea/PartialEscapeClosure.java
index f88b86b763b..57ec34493f5 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.virtual/src/org/graalvm/compiler/virtual/phases/ea/PartialEscapeClosure.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.virtual/src/org/graalvm/compiler/virtual/phases/ea/PartialEscapeClosure.java
@@ -49,6 +49,7 @@ import org.graalvm.compiler.nodes.FrameState;
 import org.graalvm.compiler.nodes.Invoke;
 import org.graalvm.compiler.nodes.LoopBeginNode;
 import org.graalvm.compiler.nodes.LoopExitNode;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.PhiNode;
 import org.graalvm.compiler.nodes.ProxyNode;
 import org.graalvm.compiler.nodes.StructuredGraph;
@@ -877,12 +878,12 @@ public abstract class PartialEscapeClosure<BlockT extends PartialEscapeBlockStat
                         if (phis[valueIndex] == null) {
                             ValueNode field = states[i].getObjectState(getObject.applyAsInt(i)).getEntry(valueIndex);
                             if (values[valueIndex] != field) {
-                                phis[valueIndex] = createValuePhi(values[valueIndex].stamp().unrestricted());
+                                phis[valueIndex] = createValuePhi(values[valueIndex].stamp(NodeView.DEFAULT).unrestricted());
                             }
                         }
                     }
-                    if (phis[valueIndex] != null && !phis[valueIndex].stamp().isCompatible(values[valueIndex].stamp())) {
-                        phis[valueIndex] = createValuePhi(values[valueIndex].stamp().unrestricted());
+                    if (phis[valueIndex] != null && !phis[valueIndex].stamp(NodeView.DEFAULT).isCompatible(values[valueIndex].stamp(NodeView.DEFAULT))) {
+                        phis[valueIndex] = createValuePhi(values[valueIndex].stamp(NodeView.DEFAULT).unrestricted());
                     }
                     if (twoSlotKinds != null && twoSlotKinds[valueIndex] != null) {
                         // skip an entry after a long/double value that occupies two int slots
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.virtual/src/org/graalvm/compiler/virtual/phases/ea/ReadEliminationClosure.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.virtual/src/org/graalvm/compiler/virtual/phases/ea/ReadEliminationClosure.java
index 26b8d935fc3..6e05a283695 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.virtual/src/org/graalvm/compiler/virtual/phases/ea/ReadEliminationClosure.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.virtual/src/org/graalvm/compiler/virtual/phases/ea/ReadEliminationClosure.java
@@ -35,6 +35,7 @@ import org.graalvm.compiler.graph.Node;
 import org.graalvm.compiler.nodes.FieldLocationIdentity;
 import org.graalvm.compiler.nodes.FixedWithNextNode;
 import org.graalvm.compiler.nodes.LoopExitNode;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.PhiNode;
 import org.graalvm.compiler.nodes.ProxyNode;
 import org.graalvm.compiler.nodes.ValueNode;
@@ -97,7 +98,7 @@ public final class ReadEliminationClosure extends EffectsClosure<ReadElimination
                 LoadCacheEntry identifier = new LoadCacheEntry(object, new FieldLocationIdentity(access.field()));
                 ValueNode cachedValue = state.getCacheEntry(identifier);
                 if (node instanceof LoadFieldNode) {
-                    if (cachedValue != null && access.stamp().isCompatible(cachedValue.stamp())) {
+                    if (cachedValue != null && access.stamp(NodeView.DEFAULT).isCompatible(cachedValue.stamp(NodeView.DEFAULT))) {
                         effects.replaceAtUsages(access, cachedValue, access);
                         addScalarAlias(access, cachedValue);
                         deleted = true;
@@ -196,7 +197,7 @@ public final class ReadEliminationClosure extends EffectsClosure<ReadElimination
     }
 
     private static boolean areValuesReplaceable(ValueNode originalValue, ValueNode replacementValue, boolean considerGuards) {
-        return originalValue.stamp().isCompatible(replacementValue.stamp()) &&
+        return originalValue.stamp(NodeView.DEFAULT).isCompatible(replacementValue.stamp(NodeView.DEFAULT)) &&
                         (!considerGuards || (getGuard(originalValue) == null || getGuard(originalValue) == getGuard(replacementValue)));
     }
 
@@ -269,7 +270,7 @@ public final class ReadEliminationClosure extends EffectsClosure<ReadElimination
                     // E.g. unsafe loads / stores with different access kinds have different stamps
                     // although location, object and offset are the same. In this case we cannot
                     // create a phi nor can we set a common value.
-                    if (otherValue == null || !value.stamp().isCompatible(otherValue.stamp())) {
+                    if (otherValue == null || !value.stamp(NodeView.DEFAULT).isCompatible(otherValue.stamp(NodeView.DEFAULT))) {
                         value = null;
                         phi = false;
                         break;
@@ -279,11 +280,11 @@ public final class ReadEliminationClosure extends EffectsClosure<ReadElimination
                     }
                 }
                 if (phi) {
-                    PhiNode phiNode = getCachedPhi(key, value.stamp().unrestricted());
+                    PhiNode phiNode = getCachedPhi(key, value.stamp(NodeView.DEFAULT).unrestricted());
                     mergeEffects.addFloatingNode(phiNode, "mergeReadCache");
                     for (int i = 0; i < states.size(); i++) {
                         ValueNode v = states.get(i).getCacheEntry(key);
-                        assert phiNode.stamp().isCompatible(v.stamp()) : "Cannot create read elimination phi for inputs with incompatible stamps.";
+                        assert phiNode.stamp(NodeView.DEFAULT).isCompatible(v.stamp(NodeView.DEFAULT)) : "Cannot create read elimination phi for inputs with incompatible stamps.";
                         setPhiInput(phiNode, i, v);
                     }
                     newState.addCacheEntry(key, phiNode);
@@ -315,14 +316,14 @@ public final class ReadEliminationClosure extends EffectsClosure<ReadElimination
                     ValueNode value = states.get(i).getCacheEntry(identifier.duplicateWithObject(getPhiValueAt(phi, i)));
                     // e.g. unsafe loads / stores with same identity and different access kinds see
                     // mergeReadCache(states)
-                    if (value == null || !values[i - 1].stamp().isCompatible(value.stamp())) {
+                    if (value == null || !values[i - 1].stamp(NodeView.DEFAULT).isCompatible(value.stamp(NodeView.DEFAULT))) {
                         return;
                     }
                     values[i] = value;
                 }
 
                 CacheEntry<?> newIdentifier = identifier.duplicateWithObject(phi);
-                PhiNode phiNode = getCachedPhi(newIdentifier, values[0].stamp().unrestricted());
+                PhiNode phiNode = getCachedPhi(newIdentifier, values[0].stamp(NodeView.DEFAULT).unrestricted());
                 mergeEffects.addFloatingNode(phiNode, "mergeReadCachePhi");
                 for (int i = 0; i < values.length; i++) {
                     setPhiInput(phiNode, i, values[i]);
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.virtual/src/org/graalvm/compiler/virtual/phases/ea/VirtualizerToolImpl.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.virtual/src/org/graalvm/compiler/virtual/phases/ea/VirtualizerToolImpl.java
index bae52ff242c..314d2d97dd1 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.virtual/src/org/graalvm/compiler/virtual/phases/ea/VirtualizerToolImpl.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.virtual/src/org/graalvm/compiler/virtual/phases/ea/VirtualizerToolImpl.java
@@ -33,6 +33,7 @@ import org.graalvm.compiler.graph.spi.CanonicalizerTool;
 import org.graalvm.compiler.nodes.ConstantNode;
 import org.graalvm.compiler.nodes.FixedNode;
 import org.graalvm.compiler.nodes.FixedWithNextNode;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.ValueNode;
 import org.graalvm.compiler.nodes.calc.FloatingNode;
 import org.graalvm.compiler.nodes.calc.UnpackEndianHalfNode;
@@ -179,7 +180,7 @@ class VirtualizerToolImpl implements VirtualizerTool, CanonicalizerTool {
                 } else if (oldValue.getStackKind() == JavaKind.Double || oldValue.getStackKind() == JavaKind.Long) {
                     // Splitting double word constant by storing over it with an int
                     getDebug().log(DebugContext.DETAILED_LEVEL, "virtualizing %s producing second half of double word value %s", current, oldValue);
-                    ValueNode secondHalf = UnpackEndianHalfNode.create(oldValue, false);
+                    ValueNode secondHalf = UnpackEndianHalfNode.create(oldValue, false, NodeView.DEFAULT);
                     addNode(secondHalf);
                     state.setEntry(virtual.getObjectId(), index + 1, secondHalf);
                 }
@@ -188,7 +189,7 @@ class VirtualizerToolImpl implements VirtualizerTool, CanonicalizerTool {
                 // Storing into second half of double, so replace previous value
                 ValueNode previous = getEntry(virtual, index - 1);
                 getDebug().log(DebugContext.DETAILED_LEVEL, "virtualizing %s producing first half of double word value %s", current, previous);
-                ValueNode firstHalf = UnpackEndianHalfNode.create(previous, true);
+                ValueNode firstHalf = UnpackEndianHalfNode.create(previous, true, NodeView.DEFAULT);
                 addNode(firstHalf);
                 state.setEntry(virtual.getObjectId(), index - 1, firstHalf);
             }
diff --git a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.word/src/org/graalvm/compiler/word/WordCastNode.java b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.word/src/org/graalvm/compiler/word/WordCastNode.java
index 9b43b013a6c..41abd8cc3e9 100644
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.word/src/org/graalvm/compiler/word/WordCastNode.java
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.word/src/org/graalvm/compiler/word/WordCastNode.java
@@ -38,6 +38,7 @@ import org.graalvm.compiler.lir.ConstantValue;
 import org.graalvm.compiler.nodeinfo.NodeInfo;
 import org.graalvm.compiler.nodes.ConstantNode;
 import org.graalvm.compiler.nodes.FixedWithNextNode;
+import org.graalvm.compiler.nodes.NodeView;
 import org.graalvm.compiler.nodes.ValueNode;
 import org.graalvm.compiler.nodes.spi.LIRLowerable;
 import org.graalvm.compiler.nodes.spi.NodeLIRBuilderTool;
@@ -73,17 +74,17 @@ public final class WordCastNode extends FixedWithNextNode implements LIRLowerabl
     }
 
     public static WordCastNode addressToWord(ValueNode input, JavaKind wordKind) {
-        assert input.stamp() instanceof AbstractPointerStamp;
+        assert input.stamp(NodeView.DEFAULT) instanceof AbstractPointerStamp;
         return new WordCastNode(StampFactory.forKind(wordKind), input);
     }
 
     public static WordCastNode objectToTrackedPointer(ValueNode input, JavaKind wordKind) {
-        assert input.stamp() instanceof ObjectStamp;
+        assert input.stamp(NodeView.DEFAULT) instanceof ObjectStamp;
         return new WordCastNode(StampFactory.forKind(wordKind), input, true);
     }
 
     public static WordCastNode objectToUntrackedPointer(ValueNode input, JavaKind wordKind) {
-        assert input.stamp() instanceof ObjectStamp;
+        assert input.stamp(NodeView.DEFAULT) instanceof ObjectStamp;
         return new WordCastNode(StampFactory.forKind(wordKind), input, false);
     }
 
@@ -108,13 +109,13 @@ public final class WordCastNode extends FixedWithNextNode implements LIRLowerabl
             return input;
         }
 
-        assert !stamp().isCompatible(input.stamp());
+        assert !stamp(NodeView.DEFAULT).isCompatible(input.stamp(NodeView.DEFAULT));
         if (input.isConstant()) {
             /* Null pointers are uncritical for GC, so they can be constant folded. */
             if (input.asJavaConstant().isNull()) {
-                return ConstantNode.forIntegerStamp(stamp(), 0);
+                return ConstantNode.forIntegerStamp(stamp(NodeView.DEFAULT), 0);
             } else if (input.asJavaConstant().getJavaKind().isNumericInteger() && input.asJavaConstant().asLong() == 0) {
-                return ConstantNode.forConstant(stamp(), JavaConstant.NULL_POINTER, tool.getMetaAccess());
+                return ConstantNode.forConstant(stamp(NodeView.DEFAULT), JavaConstant.NULL_POINTER, tool.getMetaAccess());
             }
         }
 
@@ -124,7 +125,7 @@ public final class WordCastNode extends FixedWithNextNode implements LIRLowerabl
     @Override
     public void generate(NodeLIRBuilderTool generator) {
         Value value = generator.operand(input);
-        ValueKind<?> kind = generator.getLIRGeneratorTool().getLIRKind(stamp());
+        ValueKind<?> kind = generator.getLIRGeneratorTool().getLIRKind(stamp(NodeView.DEFAULT));
         assert kind.getPlatformKind().getSizeInBytes() == value.getPlatformKind().getSizeInBytes();
 
         if (trackedPointer && LIRKind.isValue(kind) && !LIRKind.isValue(value)) {
diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HtmlSerialFieldWriter.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HtmlSerialFieldWriter.java
index 82bad7e9e92..e3d2aa42658 100644
--- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HtmlSerialFieldWriter.java
+++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HtmlSerialFieldWriter.java
@@ -29,6 +29,7 @@ import java.util.*;
 
 import javax.lang.model.element.TypeElement;
 import javax.lang.model.element.VariableElement;
+import javax.lang.model.type.TypeMirror;
 
 import com.sun.source.doctree.DocTree;
 
@@ -115,18 +116,10 @@ public class HtmlSerialFieldWriter extends FieldWriterImpl
         return li;
     }
 
-    /**
-     * Add the member header.
-     *
-     * @param fieldType the class document to be listed
-     * @param fieldTypeStr the string for the field type to be documented
-     * @param fieldDimensions the dimensions of the field string to be added
-     * @param fieldName name of the field to be added
-     * @param contentTree the content tree to which the member header will be added
-     */
+    @Override
     public void addMemberHeader(TypeElement fieldType, String fieldTypeStr,
             String fieldDimensions, String fieldName, Content contentTree) {
-        Content nameContent = new RawHtml(fieldName);
+        Content nameContent = new StringContent(fieldName);
         Content heading = HtmlTree.HEADING(HtmlConstants.MEMBER_HEADING, nameContent);
         contentTree.addContent(heading);
         Content pre = new HtmlTree(HtmlTag.PRE);
@@ -142,6 +135,20 @@ public class HtmlSerialFieldWriter extends FieldWriterImpl
         contentTree.addContent(pre);
     }
 
+    @Override
+    public void addMemberHeader(TypeMirror fieldType, String fieldName, Content contentTree) {
+        Content nameContent = new StringContent(fieldName);
+        Content heading = HtmlTree.HEADING(HtmlConstants.MEMBER_HEADING, nameContent);
+        contentTree.addContent(heading);
+        Content pre = new HtmlTree(HtmlTag.PRE);
+        Content fieldContent = writer.getLink(new LinkInfoImpl(
+                configuration, LinkInfoImpl.Kind.SERIAL_MEMBER, fieldType));
+        pre.addContent(fieldContent);
+        pre.addContent(" ");
+        pre.addContent(fieldName);
+        contentTree.addContent(pre);
+    }
+
     /**
      * Add the deprecated information for this member.
      *
diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/SerializedFormWriter.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/SerializedFormWriter.java
index 7fd1c581186..af9293973f7 100644
--- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/SerializedFormWriter.java
+++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/SerializedFormWriter.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2003, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2017, 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,6 +28,7 @@ package jdk.javadoc.internal.doclets.toolkit;
 import javax.lang.model.element.ExecutableElement;
 import javax.lang.model.element.TypeElement;
 import javax.lang.model.element.VariableElement;
+import javax.lang.model.type.TypeMirror;
 
 import com.sun.source.doctree.DocTree;
 import jdk.javadoc.internal.doclets.toolkit.util.DocFileIOException;
@@ -227,8 +228,8 @@ public interface SerializedFormWriter {
          * Adds the member header.
          *
          * @param fieldType the type of the field
-         * @param fieldTypeStr the type of the field in string format.  We will
-         * print this out if we can't link to the type
+         * @param fieldTypeStr the type of the field in string format, used
+         *                     only if the type cannot be linked
          * @param fieldDimensions the dimensions of the field
          * @param fieldName the name of the field
          * @param contentTree content tree to which the member header will be added
@@ -236,6 +237,15 @@ public interface SerializedFormWriter {
         public void addMemberHeader(TypeElement fieldType, String fieldTypeStr,
             String fieldDimensions, String fieldName, Content contentTree);
 
+        /**
+         * Adds the member header.
+         *
+         * @param fieldType the type of the field
+         * @param fieldName the name of the field
+         * @param contentTree content tree to which the member header will be added
+         */
+        public void addMemberHeader(TypeMirror fieldType, String fieldName, Content contentTree);
+
         /**
          * Check to see if overview details should be printed. If
          * nocomment option set or if there is no text to be printed
diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/builders/SerializedFormBuilder.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/builders/SerializedFormBuilder.java
index 042ac2bd52f..57a707a1e09 100644
--- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/builders/SerializedFormBuilder.java
+++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/builders/SerializedFormBuilder.java
@@ -450,8 +450,7 @@ public class SerializedFormBuilder extends AbstractBuilder {
     protected void buildFieldSubHeader(Content fieldsContentTree) {
         if (!utils.definesSerializableFields(currentTypeElement)) {
             VariableElement field = (VariableElement) currentMember;
-            fieldWriter.addMemberHeader(utils.asTypeElement(field.asType()),
-                    utils.getTypeName(field.asType(), false), utils.getDimension(field.asType()),
+            fieldWriter.addMemberHeader(field.asType(),
                     utils.getSimpleName(field),
                     fieldsContentTree);
         }
diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/taglets/ValueTaglet.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/taglets/ValueTaglet.java
index 9fd8d415269..bfbdc124eec 100644
--- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/taglets/ValueTaglet.java
+++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/taglets/ValueTaglet.java
@@ -25,11 +25,8 @@
 
 package jdk.javadoc.internal.doclets.toolkit.taglets;
 
-
 import javax.lang.model.element.Element;
-import javax.lang.model.element.TypeElement;
 import javax.lang.model.element.VariableElement;
-import javax.lang.model.util.Elements;
 
 import com.sun.source.doctree.DocTree;
 import jdk.javadoc.internal.doclets.toolkit.BaseConfiguration;
@@ -121,60 +118,26 @@ public class ValueTaglet extends BaseInlineTaglet {
     }
 
     /**
-     * Given the name of the field, return the corresponding VariableElement. Return null
-     * due to invalid use of value tag if the name is null or empty string and if
-     * the value tag is not used on a field.
+     * Returns the referenced field or a null if the value tag
+     * is empty or the reference is invalid.
      *
-     * @param holder the element holding the tag
-     * @param config the current configuration of the doclet.
+     * @param holder the tag holder.
+     * @param config the  configuration of the doclet.
      * @param tag the value tag.
      *
-     * @return the corresponding VariableElement. If the name is null or empty string,
-     * return field that the value tag was used in. Return null if the name is null
-     * or empty string and if the value tag is not used on a field.
+     * @return the referenced field or null.
      */
     private VariableElement getVariableElement(Element holder, BaseConfiguration config, DocTree tag) {
-        Utils utils = config.utils;
-        CommentHelper ch = utils.getCommentHelper(holder);
+        CommentHelper ch = config.utils.getCommentHelper(holder);
         String signature = ch.getReferencedSignature(tag);
 
-        if (signature == null) { // no reference
-            //Base case: no label.
-            if (utils.isVariableElement(holder)) {
-                return (VariableElement)(holder);
-            } else {
-                // If the value tag does not specify a parameter which is a valid field and
-                // it is not used within the comments of a valid field, return null.
-                 return null;
-            }
-        }
+        Element e = signature == null
+                ? holder
+                : ch.getReferencedMember(config, tag);
 
-        String[] sigValues = signature.split("#");
-        String memberName = null;
-        TypeElement te = null;
-        if (sigValues.length == 1) {
-            //Case 2:  @value in same class.
-            if (utils.isExecutableElement(holder) || utils.isVariableElement(holder)) {
-                te = utils.getEnclosingTypeElement(holder);
-            } else if (utils.isTypeElement(holder)) {
-                te = utils.getTopMostContainingTypeElement(holder);
-            }
-            memberName = sigValues[0];
-        } else {
-            //Case 3: @value in different class.
-            Elements elements = config.docEnv.getElementUtils();
-            te = elements.getTypeElement(sigValues[0]);
-            memberName = sigValues[1];
-        }
-        if (te == null) {
-            return null;
-        }
-        for (Element field : utils.getFields(te)) {
-            if (utils.getSimpleName(field).equals(memberName)) {
-                return (VariableElement)field;
-            }
-        }
-        return null;
+        return (e != null && config.utils.isVariableElement(e))
+                ? (VariableElement) e
+                : null;
     }
 
     /**
diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/util/Group.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/util/Group.java
index 2e80a742e22..710fffb6d2b 100644
--- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/util/Group.java
+++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/util/Group.java
@@ -290,7 +290,7 @@ public class Group {
             groupList.add(defaultGroupName);
         }
         for (PackageElement pkg : packages) {
-            String pkgName = pkg.isUnnamed() ? null : configuration.utils.getPackageName(pkg);
+            String pkgName = configuration.utils.getPackageName(pkg);
             String groupName = pkg.isUnnamed() ? null : elementNameGroupMap.get(pkgName);
             // if this package is not explicitly assigned to a group,
             // try matching it to group specified by regular expression
diff --git a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/util/Utils.java b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/util/Utils.java
index b6097ad81e6..8b3d4593259 100644
--- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/util/Utils.java
+++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/util/Utils.java
@@ -2365,9 +2365,6 @@ public class Utils {
 
     List<Element> getItems(Element e, boolean filter, ElementKind select) {
         List<Element> elements = new ArrayList<>();
-        // maintain backward compatibility by returning a null list, see AnnotationDocType.methods().
-        if (configuration.backwardCompatibility && e.getKind() == ANNOTATION_TYPE)
-            return elements;
         return new SimpleElementVisitor9<List<Element>, Void>() {
 
             @Override
diff --git a/src/jdk.jcmd/share/classes/sun/tools/jstat/ExpressionResolver.java b/src/jdk.jcmd/share/classes/sun/tools/jstat/ExpressionResolver.java
index 63505988b8e..58b02dc4a77 100644
--- a/src/jdk.jcmd/share/classes/sun/tools/jstat/ExpressionResolver.java
+++ b/src/jdk.jcmd/share/classes/sun/tools/jstat/ExpressionResolver.java
@@ -69,8 +69,10 @@ public class ExpressionResolver implements ExpressionEvaluator {
             // look it up
             Monitor m = vm.findByName(id.getName());
             if (m == null) {
-                System.err.println("Warning: Unresolved Symbol: "
-                                   + id.getName() + " substituted NaN");
+                if (debug) {
+                    System.err.println("Warning: Unresolved Symbol: "
+                                       + id.getName() + " substituted NaN");
+                }
                 return new Literal(Double.valueOf(Double.NaN));
             }
             if (m.getVariability() == Variability.CONSTANT) {
diff --git a/src/jdk.jdi/share/native/libdt_shmem/shmemBase.c b/src/jdk.jdi/share/native/libdt_shmem/shmemBase.c
index dc3e6956f6b..fbe15b465f7 100644
--- a/src/jdk.jdi/share/native/libdt_shmem/shmemBase.c
+++ b/src/jdk.jdi/share/native/libdt_shmem/shmemBase.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1999, 2008, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1999, 2017, 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
@@ -31,6 +31,10 @@
 #include "shmemBase.h"
 #include "jdwpTransport.h"  /* for Packet, TransportCallback */
 
+#if defined(_WIN32)
+  #define PRId64 "I64d"
+#endif
+
 #define MIN(x,y) ((x)<(y)?(x):(y))
 
 /*
@@ -537,7 +541,7 @@ openConnection(SharedMemoryTransport *transport, jlong otherPID,
         return SYS_NOMEM;
     }
 
-    sprintf(connection->name, "%s.%ld", transport->name, sysProcessGetID());
+    sprintf(connection->name, "%s.%" PRId64, transport->name, sysProcessGetID());
     error = sysSharedMemOpen(connection->name, &connection->sharedMemory,
                              &connection->shared);
     if (error != SYS_OK) {
@@ -601,7 +605,7 @@ createConnection(SharedMemoryTransport *transport, jlong otherPID,
         return SYS_NOMEM;
     }
 
-    sprintf(connection->name, "%s.%ld", transport->name, otherPID);
+    sprintf(connection->name, "%s.%" PRId64, transport->name, otherPID);
     error = sysSharedMemCreate(connection->name, sizeof(SharedMemory),
                                &connection->sharedMemory, &connection->shared);
     if (error != SYS_OK) {
diff --git a/src/jdk.jdwp.agent/share/native/libjdwp/VirtualMachineImpl.c b/src/jdk.jdwp.agent/share/native/libjdwp/VirtualMachineImpl.c
index 8e1639c5e2e..767b1b3f777 100644
--- a/src/jdk.jdwp.agent/share/native/libjdwp/VirtualMachineImpl.c
+++ b/src/jdk.jdwp.agent/share/native/libjdwp/VirtualMachineImpl.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1998, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1998, 2017, 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
@@ -839,7 +839,7 @@ writePaths(PacketOutputStream *out, char *string) {
     }
 
     pos = string;
-    for ( i = 0 ; i < npaths ; i++ ) {
+    for ( i = 0 ; i < npaths && pos != NULL; i++ ) {
         char *psPos;
         int   plen;
 
@@ -859,8 +859,6 @@ writePaths(PacketOutputStream *out, char *string) {
     jvmtiDeallocate(buf);
 }
 
-
-
 static jboolean
 classPaths(PacketInputStream *in, PacketOutputStream *out)
 {
diff --git a/src/jdk.jdwp.agent/share/native/libjdwp/error_messages.c b/src/jdk.jdwp.agent/share/native/libjdwp/error_messages.c
index d6a08c12961..0cd00d667b2 100644
--- a/src/jdk.jdwp.agent/share/native/libjdwp/error_messages.c
+++ b/src/jdk.jdwp.agent/share/native/libjdwp/error_messages.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2003, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2017, 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
@@ -71,6 +71,7 @@ vprint_message(FILE *fp, const char *prefix, const char *suffix,
 
     /* Fill buffer with single UTF-8 string */
     (void)vsnprintf((char*)utf8buf, sizeof(utf8buf), format, ap);
+    utf8buf[sizeof(utf8buf) - 1] = 0;
     len = (int)strlen((char*)utf8buf);
 
     /* Convert to platform encoding (ignore errors, dangerous area) */
diff --git a/src/jdk.jdwp.agent/share/native/libjdwp/error_messages.h b/src/jdk.jdwp.agent/share/native/libjdwp/error_messages.h
index bf3765357f8..e25908c5592 100644
--- a/src/jdk.jdwp.agent/share/native/libjdwp/error_messages.h
+++ b/src/jdk.jdwp.agent/share/native/libjdwp/error_messages.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2003, 2012, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2017, 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
@@ -47,7 +47,7 @@ const char * jdwpErrorText(jdwpError);
     #define THIS_FILE __FILE__
 #endif
 
-#define EXIT_ERROR(error,msg) \
+#define EXIT_ERROR(error, msg) \
         { \
                 print_message(stderr, "JDWP exit error ", "\n", \
                         "%s(%d): %s [%s:%d]", \
@@ -56,21 +56,21 @@ const char * jdwpErrorText(jdwpError);
                 debugInit_exit((jvmtiError)error, msg); \
         }
 
-#define JDI_ASSERT(expression)  \
-do {                            \
-    if (gdata && gdata->assertOn && !(expression)) {            \
+#define JDI_ASSERT(expression) \
+do { \
+    if (gdata && gdata->assertOn && !(expression)) { \
         jdiAssertionFailed(THIS_FILE, __LINE__, #expression); \
-    }                                           \
+    } \
 } while (0)
 
-#define JDI_ASSERT_MSG(expression, msg)  \
-do {                            \
-    if (gdata && gdata->assertOn && !(expression)) {            \
+#define JDI_ASSERT_MSG(expression, msg) \
+do { \
+    if (gdata && gdata->assertOn && !(expression)) { \
         jdiAssertionFailed(THIS_FILE, __LINE__, msg); \
-    }                                           \
+    } \
 } while (0)
 
-#define JDI_ASSERT_FAILED(msg)  \
+#define JDI_ASSERT_FAILED(msg) \
    jdiAssertionFailed(THIS_FILE, __LINE__, msg)
 
 void do_pause(void);
diff --git a/src/jdk.jdwp.agent/share/native/libjdwp/eventHandler.c b/src/jdk.jdwp.agent/share/native/libjdwp/eventHandler.c
index 2fc3d1b82d3..ad4b773448d 100644
--- a/src/jdk.jdwp.agent/share/native/libjdwp/eventHandler.c
+++ b/src/jdk.jdwp.agent/share/native/libjdwp/eventHandler.c
@@ -471,11 +471,8 @@ synthesizeUnloadEvent(void *signatureVoid, void *envVoid)
     jbyte eventSessionID = currentSessionID;
     struct bag *eventBag = eventHelper_createEventBag();
 
-    if (eventBag == NULL) {
-        /* TO DO: Report, but don't die
-         */
-        JDI_ASSERT(eventBag != NULL);
-    }
+    /* TO DO: Report null error, but don't die */
+    JDI_ASSERT(eventBag != NULL);
 
     /* Signature needs to last, so convert extra copy to
      * classname
diff --git a/src/jdk.jdwp.agent/share/native/libjdwp/invoker.c b/src/jdk.jdwp.agent/share/native/libjdwp/invoker.c
index d3d533b14a5..8e2981d9fa3 100644
--- a/src/jdk.jdwp.agent/share/native/libjdwp/invoker.c
+++ b/src/jdk.jdwp.agent/share/native/libjdwp/invoker.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1998, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1998, 2017, 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
@@ -808,7 +808,6 @@ invoker_completeInvokeRequest(jthread thread)
         mustReleaseReturnValue = request->invokeType == INVOKE_CONSTRUCTOR ||
            returnTypeTag(request->methodSignature) == JDWP_TAG(OBJECT) ||
            returnTypeTag(request->methodSignature) == JDWP_TAG(ARRAY);
-
     }
 
     /*
diff --git a/src/jdk.jdwp.agent/share/native/libjdwp/log_messages.c b/src/jdk.jdwp.agent/share/native/libjdwp/log_messages.c
index a2607d1c568..287cebb9bc9 100644
--- a/src/jdk.jdwp.agent/share/native/libjdwp/log_messages.c
+++ b/src/jdk.jdwp.agent/share/native/libjdwp/log_messages.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2003, 2005, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2017, 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
@@ -187,6 +187,7 @@ log_message_end(const char *format, ...)
             /* Construct message string. */
             va_start(ap, format);
             (void)vsnprintf(message, sizeof(message), format, ap);
+            message[sizeof(message) - 1] = 0;
             va_end(ap);
 
             get_time_stamp(datetime, sizeof(datetime));
diff --git a/src/jdk.jlink/share/classes/jdk/tools/jlink/builder/DefaultImageBuilder.java b/src/jdk.jlink/share/classes/jdk/tools/jlink/builder/DefaultImageBuilder.java
index b1f0ca1c1b4..aa3c66d44f2 100644
--- a/src/jdk.jlink/share/classes/jdk/tools/jlink/builder/DefaultImageBuilder.java
+++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/builder/DefaultImageBuilder.java
@@ -267,15 +267,17 @@ public final class DefaultImageBuilder implements ImageBuilder {
                 assert !mainClassName.isEmpty();
             }
 
-            String path = "/" + module + "/module-info.class";
-            Optional<ResourcePoolEntry> res = imageContent.findEntry(path);
-            if (!res.isPresent()) {
-                throw new IOException("module-info.class not found for " + module + " module");
-            }
-            ByteArrayInputStream stream = new ByteArrayInputStream(res.get().contentBytes());
-            Optional<String> mainClass = ModuleDescriptor.read(stream).mainClass();
-            if (mainClassName == null && mainClass.isPresent()) {
-                mainClassName = mainClass.get();
+            if (mainClassName == null) {
+                String path = "/" + module + "/module-info.class";
+                Optional<ResourcePoolEntry> res = imageContent.findEntry(path);
+                if (!res.isPresent()) {
+                    throw new IOException("module-info.class not found for " + module + " module");
+                }
+                ByteArrayInputStream stream = new ByteArrayInputStream(res.get().contentBytes());
+                Optional<String> mainClass = ModuleDescriptor.read(stream).mainClass();
+                if (mainClass.isPresent()) {
+                    mainClassName = mainClass.get();
+                }
             }
 
             if (mainClassName != null) {
diff --git a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/DirArchive.java b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/DirArchive.java
index 84fee1b4710..59205ac02ae 100644
--- a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/DirArchive.java
+++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/DirArchive.java
@@ -85,17 +85,17 @@ public class DirArchive implements Archive {
     private static final Consumer<String> noopConsumer = (String t) -> {
     };
 
-    public DirArchive(Path dirPath) {
-        this(dirPath, noopConsumer);
+    public DirArchive(Path dirPath, String moduleName) {
+        this(dirPath, moduleName, noopConsumer);
     }
 
-    public DirArchive(Path dirPath, Consumer<String> log) {
+    public DirArchive(Path dirPath, String moduleName, Consumer<String> log) {
         Objects.requireNonNull(dirPath);
         if (!Files.isDirectory(dirPath)) {
             throw new IllegalArgumentException(dirPath + " is not a directory");
         }
         chop = dirPath.toString().length() + 1;
-        this.moduleName = Objects.requireNonNull(dirPath.getFileName()).toString();
+        this.moduleName = Objects.requireNonNull(moduleName);
         this.dirPath = dirPath;
         this.log = log;
     }
diff --git a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/JlinkTask.java b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/JlinkTask.java
index 1efcffa110f..5904907cdab 100644
--- a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/JlinkTask.java
+++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/JlinkTask.java
@@ -24,6 +24,7 @@
  */
 package jdk.tools.jlink.internal;
 
+import java.io.BufferedInputStream;
 import java.io.File;
 import java.io.IOException;
 import java.io.PrintWriter;
@@ -430,7 +431,7 @@ public class JlinkTask {
                                                Set<String> roots)
     {
         if (Objects.requireNonNull(paths).isEmpty()) {
-             throw new IllegalArgumentException("Empty module path");
+             throw new IllegalArgumentException(taskHelper.getMessage("err.empty.module.path"));
         }
 
         Path[] entries = paths.toArray(new Path[0]);
@@ -447,8 +448,13 @@ public class JlinkTask {
 
             // java.base version is different than the current runtime version
             version = Runtime.Version.parse(v.toString());
-            if (Runtime.version().major() != version.major()) {
-                finder = ModulePath.of(version, true, entries);
+            if (Runtime.version().major() != version.major() ||
+                Runtime.version().minor() != version.minor()) {
+                // jlink version and java.base version do not match.
+                // We do not (yet) support this mode.
+                throw new IllegalArgumentException(taskHelper.getMessage("err.jlink.version.mismatch",
+                    Runtime.version().major(), Runtime.version().minor(),
+                    version.major(), version.minor()));
             }
         }
 
@@ -819,13 +825,29 @@ public class JlinkTask {
 
                 return modularJarArchive;
             } else if (Files.isDirectory(path)) {
-                return new DirArchive(path);
+                Path modInfoPath = path.resolve("module-info.class");
+                if (Files.isRegularFile(modInfoPath)) {
+                    return new DirArchive(path, findModuleName(modInfoPath));
+                } else {
+                    throw new IllegalArgumentException(
+                        taskHelper.getMessage("err.not.a.module.directory", path));
+                }
             } else {
                 throw new IllegalArgumentException(
                     taskHelper.getMessage("err.not.modular.format", module, path));
             }
         }
 
+        private static String findModuleName(Path modInfoPath) {
+            try (BufferedInputStream bis = new BufferedInputStream(
+                    Files.newInputStream(modInfoPath))) {
+                return ModuleDescriptor.read(bis).name();
+            } catch (IOException exp) {
+                throw new IllegalArgumentException(taskHelper.getMessage(
+                    "err.cannot.read.module.info", modInfoPath), exp);
+            }
+        }
+
         @Override
         public ExecutableImage retrieve(ImagePluginStack stack) throws IOException {
             ExecutableImage image = ImageFileCreator.create(archives, order, stack);
diff --git a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/ModuleSorter.java b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/ModuleSorter.java
index 42cbe9f0255..d01364a914b 100644
--- a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/ModuleSorter.java
+++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/ModuleSorter.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2017, 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
@@ -30,13 +30,16 @@ import jdk.tools.jlink.plugin.ResourcePoolModule;
 import jdk.tools.jlink.plugin.ResourcePoolModuleView;
 
 import java.lang.module.ModuleDescriptor;
+import java.lang.module.ModuleDescriptor.Requires;
 import java.lang.module.ModuleDescriptor.Requires.Modifier;
 
 import java.nio.ByteBuffer;
-import java.util.Deque;
+import java.util.ArrayList;
+import java.util.Comparator;
 import java.util.HashMap;
 import java.util.HashSet;
-import java.util.LinkedList;
+import java.util.LinkedHashSet;
+import java.util.List;
 import java.util.Map;
 import java.util.Set;
 import java.util.stream.Stream;
@@ -45,9 +48,8 @@ import java.util.stream.Stream;
  * Helper class to sort modules in topological order
  */
 public final class ModuleSorter {
-    private final Deque<ResourcePoolModule> nodes = new LinkedList<>();
-    private final Map<String, Set<ResourcePoolModule>> edges = new HashMap<>();
-    private final Deque<ResourcePoolModule> result = new LinkedList<>();
+    private final Map<ResourcePoolModule, Set<ResourcePoolModule>> graph = new HashMap<>();
+    private final List<ResourcePoolModule> result = new ArrayList<>();
 
     private final ResourcePoolModuleView moduleView;
 
@@ -69,11 +71,17 @@ public final class ModuleSorter {
 
     private ModuleSorter addModule(ResourcePoolModule module) {
         addNode(module);
-        readModuleDescriptor(module).requires().forEach(req -> {
+        // the module graph will be traversed in a stable order for
+        // the topological sort. So add the dependences in the module name order
+        readModuleDescriptor(module).requires()
+                                    .stream()
+                                    .sorted(Comparator.comparing(Requires::name))
+                                    .forEach(req ->
+        {
             ResourcePoolModule dep = moduleView.findModule(req.name()).orElse(null);
             if (dep != null) {
                 addNode(dep);
-                edges.get(module.name()).add(dep);
+                graph.get(module).add(dep);
             } else if (!req.modifiers().contains(Modifier.STATIC)) {
                 throw new PluginException(req.name() + " not found");
             }
@@ -82,22 +90,23 @@ public final class ModuleSorter {
     }
 
     private void addNode(ResourcePoolModule module) {
-        nodes.add(module);
-        edges.computeIfAbsent(module.name(), _n -> new HashSet<>());
+        graph.computeIfAbsent(module, _n -> new LinkedHashSet<>());
     }
 
+    /*
+     * The module graph will be traversed in a stable order
+     * (traversing the modules and their dependences in alphabetical order)
+     * so that it will produce the same result of a given module graph.
+     */
     private synchronized void build() {
-        if (!result.isEmpty() || nodes.isEmpty())
+        if (!result.isEmpty() || graph.isEmpty())
             return;
 
-        Deque<ResourcePoolModule> visited = new LinkedList<>();
-        Deque<ResourcePoolModule> done = new LinkedList<>();
-        ResourcePoolModule node;
-        while ((node = nodes.poll()) != null) {
-            if (!visited.contains(node)) {
-                visit(node, visited, done);
-            }
-        }
+        Set<ResourcePoolModule> visited = new HashSet<>();
+        Set<ResourcePoolModule> done = new HashSet<>();
+        graph.keySet().stream()
+             .sorted(Comparator.comparing(ResourcePoolModule::name))
+             .forEach(node -> visit(node, visited, done));
     }
 
     public Stream<ResourcePoolModule> sorted() {
@@ -106,19 +115,21 @@ public final class ModuleSorter {
     }
 
     private void visit(ResourcePoolModule node,
-                       Deque<ResourcePoolModule> visited,
-                       Deque<ResourcePoolModule> done) {
+                       Set<ResourcePoolModule> visited,
+                       Set<ResourcePoolModule> done) {
         if (visited.contains(node)) {
             if (!done.contains(node)) {
                 throw new IllegalArgumentException("Cyclic detected: " +
-                    node + " " + edges.get(node.name()));
+                    node + " " + graph.get(node));
             }
             return;
         }
+
+        // traverse the dependences of the given module which are
+        // also sorted in alphabetical order
         visited.add(node);
-        edges.get(node.name())
-             .forEach(x -> visit(x, visited, done));
+        graph.get(node).forEach(x -> visit(x, visited, done));
         done.add(node);
-        result.addLast(node);
+        result.add(node);
     }
 }
diff --git a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/GenerateJLIClassesPlugin.java b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/GenerateJLIClassesPlugin.java
index a5c4597efef..edcbb6fdf61 100644
--- a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/GenerateJLIClassesPlugin.java
+++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/GenerateJLIClassesPlugin.java
@@ -54,11 +54,8 @@ import jdk.tools.jlink.plugin.Plugin;
 public final class GenerateJLIClassesPlugin implements Plugin {
 
     private static final String NAME = "generate-jli-classes";
-    private static final String IGNORE_VERSION = "ignore-version";
 
     private static final String DESCRIPTION = PluginsResourceBundle.getDescription(NAME);
-    private static final String IGNORE_VERSION_WARNING = NAME + ".ignore.version.warn";
-    private static final String VERSION_MISMATCH_WARNING = NAME + ".version.mismatch.warn";
 
     private static final String DEFAULT_TRACE_FILE = "default_jli_trace.txt";
 
@@ -85,8 +82,6 @@ public final class GenerateJLIClassesPlugin implements Plugin {
 
     String mainArgument;
 
-    boolean ignoreVersion;
-
     public GenerateJLIClassesPlugin() {
     }
 
@@ -170,7 +165,6 @@ public final class GenerateJLIClassesPlugin implements Plugin {
     @Override
     public void configure(Map<String, String> config) {
         mainArgument = config.get(NAME);
-        ignoreVersion = Boolean.parseBoolean(config.get(IGNORE_VERSION));
     }
 
     public void initialize(ResourcePool in) {
@@ -208,26 +202,6 @@ public final class GenerateJLIClassesPlugin implements Plugin {
         }
     }
 
-    private boolean checkVersion(Runtime.Version linkedVersion) {
-        Runtime.Version baseVersion = Runtime.version();
-        if (baseVersion.major() != linkedVersion.major() ||
-                baseVersion.minor() != linkedVersion.minor()) {
-            return false;
-        }
-        return true;
-    }
-
-    private Runtime.Version getLinkedVersion(ResourcePool in) {
-        ModuleDescriptor.Version version = in.moduleView()
-                .findModule("java.base")
-                .get()
-                .descriptor()
-                .version()
-                .orElseThrow(() -> new PluginException("No version defined in "
-                        + "the java.base being linked"));
-         return Runtime.Version.parse(version.toString());
-    }
-
     private void readTraceConfig(Stream<String> lines) {
         // Use TreeSet/TreeMap to keep things sorted in a deterministic
         // order to avoid scrambling the layout on small changes and to
@@ -315,24 +289,6 @@ public final class GenerateJLIClassesPlugin implements Plugin {
 
     @Override
     public ResourcePool transform(ResourcePool in, ResourcePoolBuilder out) {
-        if (ignoreVersion) {
-            System.out.println(
-                    PluginsResourceBundle
-                            .getMessage(IGNORE_VERSION_WARNING));
-        } else if (!checkVersion(getLinkedVersion(in))) {
-            // The linked images are not version compatible
-            if (mainArgument != null) {
-                // Log a mismatch warning if an argument was specified
-                System.out.println(
-                        PluginsResourceBundle
-                                .getMessage(VERSION_MISMATCH_WARNING,
-                                            getLinkedVersion(in),
-                                            Runtime.version()));
-            }
-            in.transformAndCopy(entry -> entry, out);
-            return out.build();
-        }
-
         initialize(in);
         // Copy all but DMH_ENTRY to out
         in.transformAndCopy(entry -> {
diff --git a/src/jdk.jlink/share/classes/jdk/tools/jlink/resources/jlink.properties b/src/jdk.jlink/share/classes/jdk/tools/jlink/resources/jlink.properties
index 1a976a3aa57..63caa691f52 100644
--- a/src/jdk.jlink/share/classes/jdk/tools/jlink/resources/jlink.properties
+++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/resources/jlink.properties
@@ -107,6 +107,8 @@ main.extended.help.footer=\
 error.prefix=Error:
 warn.prefix=Warning:
 
+err.empty.module.path=empty module path
+err.jlink.version.mismatch=jlink version {0}.{1} does not match target java.base version {2}.{3}
 err.automatic.module:automatic module cannot be used with jlink: {0} from {1}
 err.unknown.byte.order:unknown byte order {0}
 err.launcher.main.class.empty:launcher main class name cannot be empty: {0}
@@ -133,6 +135,8 @@ err.orphan.arguments=invalid argument: {0}
 err.config.defaults=property {0} is missing from configuration
 err.config.defaults.value=wrong value in defaults property: {0}
 err.bom.generation=bom file generation failed: {0}
+err.not.a.module.directory=directory {0} does not contain module-info.class file under it
+err.cannot.read.module.info=cannot read module descriptor from {0}
 err.not.modular.format=selected module {0} ({1}) not in jmod or modular JAR format
 err.signing=signed modular JAR {0} is currently not supported,\
 \ use --ignore-signing-information to suppress error
diff --git a/src/jdk.jlink/share/classes/jdk/tools/jlink/resources/plugins.properties b/src/jdk.jlink/share/classes/jdk/tools/jlink/resources/plugins.properties
index 819c3530fc9..72815fc5be0 100644
--- a/src/jdk.jlink/share/classes/jdk/tools/jlink/resources/plugins.properties
+++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/resources/plugins.properties
@@ -75,7 +75,7 @@ where <section-name> is \"man\" or \"headers".
 exclude-jmod-section.description=\
 Specify a JMOD section to exclude
 
-generate-jli-classes.argument=@filename[:ignore-version=<true|false>]
+generate-jli-classes.argument=@filename
 
 generate-jli-classes.description=\
 Specify a file listing the java.lang.invoke classes to pre-generate. \n\
@@ -84,15 +84,6 @@ If this plugin runs on a different runtime version than the image being \n\
 created then code generation will be disabled by default to guarantee \n\
 correctness - add ignore-version=true to override this.
 
-generate-jli-classes.ignore.version.warn=\
-WARNING: --generate-jli-classes set to ignore version mismatch between \n\
-JDK running jlink and target image.
-
-generate-jli-classes.version.mismatch.warn=\
-WARNING: Pre-generation of JLI classes is only supported when linking \n\
-the same version of java.base ({0}) as the JDK running jlink ({1}), \n\
-class generation skipped - specify ignore-version to override.
-
 system-modules.argument=retainModuleTarget
 
 system-modules.description=Fast loading of module descriptors (always enabled)
diff --git a/src/jdk.jshell/share/classes/jdk/jshell/Eval.java b/src/jdk.jshell/share/classes/jdk/jshell/Eval.java
index 1e176ed4bee..30f0db98501 100644
--- a/src/jdk.jshell/share/classes/jdk/jshell/Eval.java
+++ b/src/jdk.jshell/share/classes/jdk/jshell/Eval.java
@@ -834,6 +834,8 @@ class Eval {
                         if (!toReplace.isEmpty()) {
                             replaced.addAll(toReplace);
                             replaced.stream().forEach(Unit::markForReplacement);
+                            //ensure correct classnames are set in the snippets:
+                            replaced.stream().forEach(u -> u.setWrap(ins, legit));
                         }
 
                         return toReplace.isEmpty() ? Result.SUCESS : Result.FAILURE;
diff --git a/src/jdk.management.agent/share/classes/sun/management/jmxremote/ConnectorBootstrap.java b/src/jdk.management.agent/share/classes/sun/management/jmxremote/ConnectorBootstrap.java
index dae6b95c8bf..8d2031f888b 100644
--- a/src/jdk.management.agent/share/classes/sun/management/jmxremote/ConnectorBootstrap.java
+++ b/src/jdk.management.agent/share/classes/sun/management/jmxremote/ConnectorBootstrap.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2003, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2017, 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
@@ -70,10 +70,8 @@ import javax.net.ssl.TrustManagerFactory;
 import javax.rmi.ssl.SslRMIClientSocketFactory;
 import javax.rmi.ssl.SslRMIServerSocketFactory;
 import javax.security.auth.Subject;
-
 import com.sun.jmx.remote.internal.rmi.RMIExporter;
 import com.sun.jmx.remote.security.JMXPluggableAuthenticator;
-
 import jdk.internal.agent.Agent;
 import jdk.internal.agent.AgentConfigurationError;
 import static jdk.internal.agent.AgentConfigurationError.*;
@@ -102,6 +100,7 @@ public final class ConnectorBootstrap {
         public static final String USE_REGISTRY_SSL = "false";
         public static final String USE_AUTHENTICATION = "true";
         public static final String PASSWORD_FILE_NAME = "jmxremote.password";
+        public static final String HASH_PASSWORDS = "true";
         public static final String ACCESS_FILE_NAME = "jmxremote.access";
         public static final String SSL_NEED_CLIENT_AUTH = "false";
     }
@@ -129,6 +128,8 @@ public final class ConnectorBootstrap {
                 "com.sun.management.jmxremote.authenticate";
         public static final String PASSWORD_FILE_NAME =
                 "com.sun.management.jmxremote.password.file";
+        public static final String HASH_PASSWORDS
+                = "com.sun.management.jmxremote.password.toHashes";
         public static final String ACCESS_FILE_NAME =
                 "com.sun.management.jmxremote.access.file";
         public static final String LOGIN_CONFIG_NAME =
@@ -412,6 +413,7 @@ public final class ConnectorBootstrap {
 
         String loginConfigName = null;
         String passwordFileName = null;
+        boolean shouldHashPasswords = true;
         String accessFileName = null;
 
         // Initialize settings when authentication is active
@@ -426,6 +428,11 @@ public final class ConnectorBootstrap {
                 passwordFileName =
                         props.getProperty(PropertyNames.PASSWORD_FILE_NAME,
                         getDefaultFileName(DefaultValues.PASSWORD_FILE_NAME));
+                String hashPasswords
+                        = props.getProperty(PropertyNames.HASH_PASSWORDS,
+                                DefaultValues.HASH_PASSWORDS);
+                shouldHashPasswords = Boolean.parseBoolean(hashPasswords);
+
                 checkPasswordFile(passwordFileName);
             }
 
@@ -474,7 +481,7 @@ public final class ConnectorBootstrap {
                     sslConfigFileName, enabledCipherSuitesList,
                     enabledProtocolsList, sslNeedClientAuth,
                     useAuthentication, loginConfigName,
-                    passwordFileName, accessFileName, bindAddress, jmxRmiFilter);
+                    passwordFileName, shouldHashPasswords, accessFileName, bindAddress, jmxRmiFilter);
             cs = data.jmxConnectorServer;
             url = data.jmxRemoteURL;
             config("startRemoteConnectorServer",
@@ -569,6 +576,10 @@ public final class ConnectorBootstrap {
             throw new AgentConfigurationError(PASSWORD_FILE_NOT_READABLE, passwordFileName);
         }
 
+        if(!file.canWrite() && PropertyNames.HASH_PASSWORDS.equalsIgnoreCase("true")) {
+            logger.log(Level.WARNING, "");
+        }
+
         FileSystem fs = FileSystem.open();
         try {
             if (fs.supportsFileSecurity(file)) {
@@ -729,6 +740,7 @@ public final class ConnectorBootstrap {
             boolean useAuthentication,
             String loginConfigName,
             String passwordFileName,
+            boolean shouldHashPasswords,
             String accessFileName,
             String bindAddress,
             String jmxRmiFilter)
@@ -761,6 +773,9 @@ public final class ConnectorBootstrap {
             if (passwordFileName != null) {
                 env.put("jmx.remote.x.password.file", passwordFileName);
             }
+            if (shouldHashPasswords) {
+                env.put("jmx.remote.x.password.toHashes", "true");
+            }
 
             env.put("jmx.remote.x.access.file", accessFileName);
 
diff --git a/src/jdk.management.agent/share/conf/jmxremote.password.template b/src/jdk.management.agent/share/conf/jmxremote.password.template
index 13dfd2578bb..c98a0ad253a 100644
--- a/src/jdk.management.agent/share/conf/jmxremote.password.template
+++ b/src/jdk.management.agent/share/conf/jmxremote.password.template
@@ -3,11 +3,12 @@
 #
 # o Copy this template to jmxremote.password
 # o Set the user/password entries in jmxremote.password
-# o Change the permission of jmxremote.password to read-only
-#   by the owner.
+# o Change the permission of jmxremote.password to be accessible
+#   only by the owner.
+# o The jmxremote.passwords file will be re-written by the server
+#   to replace all plain text passwords with hashed passwords when
+#   the file is read by the server.
 #
-# See below for the location of jmxremote.password file.
-# ----------------------------------------------------------------------
 
 ##############################################################
 #        Password File for Remote JMX Monitoring
@@ -24,41 +25,91 @@
 # the management config file $JRE/conf/management/management.properties
 # or by specifying a system property (See that file for details).
 
-
 ##############################################################
-#    File permissions of the jmxremote.password file
+#    File format of the jmxremote.password file
 ##############################################################
-#      Since there are cleartext passwords stored in this file,
-#      this file must be readable by ONLY the owner,
-#      otherwise the program will exit with an error.
 #
-# The file format for password and access files is syntactically the same
-# as the Properties file format.  The syntax is described in the Javadoc
-# for java.util.Properties.load.
-# Typical password file has multiple  lines, where each line is blank,
+# The file contains multiple lines where each line is blank,
 # a comment (like this one), or a password entry.
 #
+# password entry follows the below syntax
+#   role_name W [clearPassword|hashedPassword]
 #
-# A password entry consists of a role name and an associated
-# password.  The role name is any string that does not itself contain
-# spaces or tabs.  The password is again any string that does not
-# contain spaces or tabs.  Note that passwords appear in the clear in
-# this file, so it is a good idea not to use valuable passwords.
+# role_name is any string that does not itself contain spaces or tabs.
+# W = spaces or tabs
+#
+# Passwords can be specified via clear text or via a hash. Clear text password
+# is any string that does not contain spaces or tabs. Hashed passwords must
+# follow the below format.
+# hashedPassword = base64_encoded_64_byte_salt W base64_encoded_hash W hash_algorithm
+# where,
+#   base64_encoded_64_byte_salt = 64 byte random salt
+#   base64_encoded_hash = Hash_algorithm(password + salt)
+#   W = spaces or tabs
+#   hash_algorithm = Algorithm string specified using the format below
+#       https://docs.oracle.com/javase/9/docs/specs/security/standard-names.html#messagedigest-algorithms
+#       This is an optional field. If not specified, SHA3-512 will be assumed.
+#
+# If passwords are in clear, they will be overwritten by their hash if all of
+# the below criteria are met.
+#   * com.sun.management.jmxremote.password.toHashes property is set to true in
+#     management.properties file
+#   * the password file is writable
+#   * the system security policy allows writing into the password file, if a
+#     security manager is configured
+#
+# In order to change the password for a role, replace the hashed password entry
+# with a new clear text password or a new hashed password. If the new password
+# is in clear, it will be replaced with its hash when a new login attempt is made.
 #
 # A given role should have at most one entry in this file.  If a role
 # has no entry, it has no access.
 # If multiple entries are found for the same role name, then the last one
 # is used.
 #
-# In a typical installation, this file can be read by anybody on the
+# A user generated hashed password file can also be used instead of clear-text
+# password file. If generated by the user, hashed passwords must follow the
+# format specified above.
+#
+# Caution: It is recommended not to edit the password file while the
+# agent is running, as edits could be lost if a client connection triggers the
+# hashing of the password file at the same time that the file is externally modified.
+# The integrity of the file is guaranteed, but any external edits made to the
+# file during the short period between the time that the agent reads the file
+# and the time that it writes it back might get lost
+
+##############################################################
+#    File permissions of the jmxremote.password file
+##############################################################
+#       This file must be made accessible by ONLY the owner,
+#       otherwise the program will exit with an error.
+#
+# In a typical installation, this file can be accessed by anybody on the
 # local machine, and possibly by people on other machines.
-# For # security, you should either restrict the access to this file,
+# For security, you should either restrict the access to this file except for owner,
 # or specify another, less accessible file in the management config file
 # as described above.
 #
-# Following are two commented-out entries.  The "measureRole" role has
-# password "QED".  The "controlRole" role has password "R&D".
+# In order to prevent inadverent edits to the password file in the 
+# production environment, it is recommended to deploy a read-only 
+# hashed password file. The hashed entries for clear passwords can be generated 
+# in advance by running the JMX agent.
 #
-# monitorRole  QED
-# controlRole   R&D
 
+##############################################################
+#    Sample of the jmxremote.password file
+##############################################################
+# Following are two commented-out entries.  The "monitorRole" role has
+# password "QED".  The "controlRole" role has password "R&D". This is an example
+# of specifying passwords in the clear
+#
+#   monitorRole  QED
+#   controlRole  R&D
+# 
+# Once a login attempt is made, passwords will be hashed and the file will have 
+# below entries with clear passwords overwritten by their respective 
+# SHA3-512 hash
+#
+#   monitorRole trilby APzBTt34rV2l+OMbuvbnOQ4si8UZmfRCVbIY1+fAofV5CkQzXS/FDMGteQQk/R3q1wtt104qImzJEA7gCwl6dw== 4EeTdSJ7X6Imu0Mb+dWqIns7a7QPIBoM3NB/XlpMQSPSicE7PnlALVWn2pBY3Q3pGDHyAb32Hd8GUToQbUhAjA== SHA3-512
+#   controlRole roHEJSbRqSSTII4Z4+NOCV2OJaZVQ/dw153Fy2u4ILDP9XiZ426GwzCzc3RtpoqNMwqYIcfdd74xWXSMrWtGaA== w9qDsekgKn0WOVJycDyU0kLBa081zbStcCjUAVEqlfon5Sgx7XHtaodbmzpLegA1jT7Ag36T0zHaEWRHJe2fdA== SHA3-512
+# 
\ No newline at end of file
diff --git a/src/jdk.management.agent/share/conf/management.properties b/src/jdk.management.agent/share/conf/management.properties
index 3bf88daf630..1f8d86e1730 100644
--- a/src/jdk.management.agent/share/conf/management.properties
+++ b/src/jdk.management.agent/share/conf/management.properties
@@ -300,6 +300,17 @@
 # For a non-default password file location use the following line
 # com.sun.management.jmxremote.password.file=filepath
 
+#
+# ################# Hash passwords in password file ##############
+# com.sun.management.jmxremote.password.toHashes = true|false
+#      Default for this property is true.
+#      Specifies if passwords in the password file should be hashed or not.
+#      If this property is true, and if the password file is writable, and if the 
+#      system security policy allows writing into the password file,
+#      all the clear passwords in the password file will be replaced by
+#      their SHA3-512 hash when the file is read by the server
+#
+
 #
 # ################ RMI Access file location #####################
 #
diff --git a/src/jdk.rmic/share/classes/sun/tools/java/RuntimeConstants.java b/src/jdk.rmic/share/classes/sun/tools/java/RuntimeConstants.java
index 1c7ada16f06..85b903d670a 100644
--- a/src/jdk.rmic/share/classes/sun/tools/java/RuntimeConstants.java
+++ b/src/jdk.rmic/share/classes/sun/tools/java/RuntimeConstants.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1998, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1998, 2017, 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,7 +67,7 @@ public interface RuntimeConstants {
     /* Class File Constants */
     int JAVA_MAGIC                   = 0xcafebabe;
     int JAVA_MIN_SUPPORTED_VERSION   = 45;
-    int JAVA_MAX_SUPPORTED_VERSION   = 53;
+    int JAVA_MAX_SUPPORTED_VERSION   = 54;
     int JAVA_MAX_SUPPORTED_MINOR_VERSION = 0;
 
     /* Generate class file version for 1.1  by default */
diff --git a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/tools/Shell.java b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/tools/Shell.java
index b4f3a0bdf32..9722ac133d4 100644
--- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/tools/Shell.java
+++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/tools/Shell.java
@@ -295,7 +295,7 @@ public class Shell implements PartialParser {
                 } catch (final IOException ioe) {
                     // ignore
                 }
-                if (l.startsWith("#!")) {
+                if (l != null && l.startsWith("#!")) {
                     shebangFilePos = i;
                 }
                 // We're only checking the first non-option argument. If it's not a shebang file, we're in normal
diff --git a/src/utils/IdealGraphVisualizer/nbproject/platform.properties b/src/utils/IdealGraphVisualizer/nbproject/platform.properties
index c86f8f4227d..3e4aeabf98e 100644
--- a/src/utils/IdealGraphVisualizer/nbproject/platform.properties
+++ b/src/utils/IdealGraphVisualizer/nbproject/platform.properties
@@ -6,7 +6,7 @@ disabled.modules=
 nbplatform.active.dir=${suite.dir}/nbplatform
 nbplatform.default.netbeans.dest.dir=${suite.dir}/nbplatform
 nbplatform.default.harness.dir=${nbplatform.default.netbeans.dest.dir}/harness
-bootstrap.url=http://deadlock.netbeans.org/hudson/job/nbms-and-javadoc/lastSuccessfulBuild/artifact/nbbuild/netbeans/harness/tasks.jar
+bootstrap.url=http://bits.netbeans.org/dev/nbms-and-javadoc/lastSuccessfulBuild/artifact/nbbuild/netbeans/harness/tasks.jar
 autoupdate.catalog.url=http://updates.netbeans.org/netbeans/updates/7.4/uc/final/distribution/catalog.xml.gz
 suite.dir=${basedir}
 nbplatform.active=default
diff --git a/src/utils/LogCompilation/Makefile b/src/utils/LogCompilation/Makefile
index f04795e44b1..5f9ca083842 100644
--- a/src/utils/LogCompilation/Makefile
+++ b/src/utils/LogCompilation/Makefile
@@ -25,7 +25,7 @@ PKGLIST = \
 com.sun.hotspot.tools.compiler
 #END PKGLIST
 
-FILELIST = com/sun/hotspot/tools/compiler/*.java
+FILELIST = main/java/com/sun/hotspot/tools/compiler/*.java
 
 ifneq "x$(ALT_BOOTDIR)" "x"
   BOOTDIR := $(ALT_BOOTDIR)
diff --git a/src/utils/LogCompilation/README b/src/utils/LogCompilation/README
index 790173120d6..db9b20aec47 100644
--- a/src/utils/LogCompilation/README
+++ b/src/utils/LogCompilation/README
@@ -16,3 +16,10 @@ More information about the LogCompilation output can be found at
 https://wiki.openjdk.java.net/display/HotSpot/LogCompilation+overview
 https://wiki.openjdk.java.net/display/HotSpot/PrintCompilation
 https://wiki.openjdk.java.net/display/HotSpot/LogCompilation+tool
+
+The project layout is now for Maven. To build the project with Maven do:
+
+  mvn clean install
+
+The build also copies the resulting target jar to ./logc.jar for easy 
+interop with the Makefile build.
\ No newline at end of file
diff --git a/src/utils/LogCompilation/pom.xml b/src/utils/LogCompilation/pom.xml
new file mode 100644
index 00000000000..ca5c3aa0a1f
--- /dev/null
+++ b/src/utils/LogCompilation/pom.xml
@@ -0,0 +1,110 @@
+<!--
+ Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+   - Redistributions of source code must retain the above copyright
+     notice, this list of conditions and the following disclaimer.
+
+   - Redistributions in binary form must reproduce the above copyright
+     notice, this list of conditions and the following disclaimer in the
+     documentation and/or other materials provided with the distribution.
+
+   - Neither the name of Oracle nor the names of its
+     contributors may be used to endorse or promote products derived
+     from this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+ IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <groupId>com.sun.hotspot.tools.compiler</groupId>
+  <artifactId>LogCompilation</artifactId>
+  <packaging>jar</packaging>
+  <version>1.0-SNAPSHOT</version>
+  <name>LogCompilation</name>
+  <url>http://maven.apache.org</url>
+  <dependencies>
+    <dependency>
+      <groupId>junit</groupId>
+      <artifactId>junit</artifactId>
+      <version>4.8.2</version>
+      <scope>test</scope>
+    </dependency>
+  </dependencies>
+      <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-shade-plugin</artifactId>
+                <version>3.1.0</version>
+                <executions>
+                    <execution>
+                        <phase>package</phase>
+                        <goals>
+                            <goal>shade</goal>
+                        </goals>
+                        <configuration>
+                            <transformers>
+                                <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
+                                    <mainClass>com.sun.hotspot.tools.compiler.LogCompilation</mainClass>
+                                </transformer>
+                            </transformers>
+                            <createDependencyReducedPom>false</createDependencyReducedPom>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+                <configuration>
+                    <source>1.8</source>
+                    <target>1.8</target>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-release-plugin</artifactId>
+                <version>2.5.3</version>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-javadoc-plugin</artifactId>
+                <version>3.0.0-M1</version>
+            </plugin>
+            <plugin>
+                <artifactId>maven-antrun-plugin</artifactId>
+                <version>1.8</version>
+                <executions>
+                    <execution>
+                        <id>copy</id>
+                        <phase>package</phase>
+                        <configuration>
+                            <target>
+                                <copy file="${basedir}/target/${artifactId}-${version}.jar" tofile="${basedir}/logc.jar"/>
+                            </target>
+                        </configuration>
+                        <goals>
+                            <goal>run</goal>
+                        </goals>
+                    </execution> 
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
+</project>
diff --git a/src/utils/LogCompilation/src/com/sun/hotspot/tools/compiler/BasicLogEvent.java b/src/utils/LogCompilation/src/main/java/com/sun/hotspot/tools/compiler/BasicLogEvent.java
similarity index 100%
rename from src/utils/LogCompilation/src/com/sun/hotspot/tools/compiler/BasicLogEvent.java
rename to src/utils/LogCompilation/src/main/java/com/sun/hotspot/tools/compiler/BasicLogEvent.java
diff --git a/src/utils/LogCompilation/src/com/sun/hotspot/tools/compiler/CallSite.java b/src/utils/LogCompilation/src/main/java/com/sun/hotspot/tools/compiler/CallSite.java
similarity index 100%
rename from src/utils/LogCompilation/src/com/sun/hotspot/tools/compiler/CallSite.java
rename to src/utils/LogCompilation/src/main/java/com/sun/hotspot/tools/compiler/CallSite.java
diff --git a/src/utils/LogCompilation/src/com/sun/hotspot/tools/compiler/Compilation.java b/src/utils/LogCompilation/src/main/java/com/sun/hotspot/tools/compiler/Compilation.java
similarity index 100%
rename from src/utils/LogCompilation/src/com/sun/hotspot/tools/compiler/Compilation.java
rename to src/utils/LogCompilation/src/main/java/com/sun/hotspot/tools/compiler/Compilation.java
diff --git a/src/utils/LogCompilation/src/com/sun/hotspot/tools/compiler/Constants.java b/src/utils/LogCompilation/src/main/java/com/sun/hotspot/tools/compiler/Constants.java
similarity index 100%
rename from src/utils/LogCompilation/src/com/sun/hotspot/tools/compiler/Constants.java
rename to src/utils/LogCompilation/src/main/java/com/sun/hotspot/tools/compiler/Constants.java
diff --git a/src/utils/LogCompilation/src/com/sun/hotspot/tools/compiler/LogCleanupReader.java b/src/utils/LogCompilation/src/main/java/com/sun/hotspot/tools/compiler/LogCleanupReader.java
similarity index 100%
rename from src/utils/LogCompilation/src/com/sun/hotspot/tools/compiler/LogCleanupReader.java
rename to src/utils/LogCompilation/src/main/java/com/sun/hotspot/tools/compiler/LogCleanupReader.java
diff --git a/src/utils/LogCompilation/src/com/sun/hotspot/tools/compiler/LogCompilation.java b/src/utils/LogCompilation/src/main/java/com/sun/hotspot/tools/compiler/LogCompilation.java
similarity index 100%
rename from src/utils/LogCompilation/src/com/sun/hotspot/tools/compiler/LogCompilation.java
rename to src/utils/LogCompilation/src/main/java/com/sun/hotspot/tools/compiler/LogCompilation.java
diff --git a/src/utils/LogCompilation/src/com/sun/hotspot/tools/compiler/LogEvent.java b/src/utils/LogCompilation/src/main/java/com/sun/hotspot/tools/compiler/LogEvent.java
similarity index 100%
rename from src/utils/LogCompilation/src/com/sun/hotspot/tools/compiler/LogEvent.java
rename to src/utils/LogCompilation/src/main/java/com/sun/hotspot/tools/compiler/LogEvent.java
diff --git a/src/utils/LogCompilation/src/com/sun/hotspot/tools/compiler/LogParser.java b/src/utils/LogCompilation/src/main/java/com/sun/hotspot/tools/compiler/LogParser.java
similarity index 98%
rename from src/utils/LogCompilation/src/com/sun/hotspot/tools/compiler/LogParser.java
rename to src/utils/LogCompilation/src/main/java/com/sun/hotspot/tools/compiler/LogParser.java
index 04102c2e468..85ce379e16f 100644
--- a/src/utils/LogCompilation/src/com/sun/hotspot/tools/compiler/LogParser.java
+++ b/src/utils/LogCompilation/src/main/java/com/sun/hotspot/tools/compiler/LogParser.java
@@ -632,7 +632,7 @@ public class LogParser extends DefaultHandler implements ErrorHandler {
 
     /**
      * Entry point for log file parsing with a file reader.
-     * {@see #parse(String,boolean)}
+     * {@link #parse(String,boolean)}
      */
     public static ArrayList<LogEvent> parse(Reader reader, boolean cleanup) throws Exception {
         // Create the XML input factory
@@ -825,7 +825,7 @@ public class LogParser extends DefaultHandler implements ErrorHandler {
      * an {@linkplain #site initial scope} with a bogus bytecode index and the
      * right inline ID, and push the scope with the inline ID attached. Note
      * that most of late inlining processing happens in
-     * {@link #endElement()}.</li>
+     * {@link #endElement(String,String,String)}.</li>
      * <li><b>jvms:</b> record a {@linkplain Jvms JVMState}. Depending on the
      * context in which this event is encountered, this can mean adding
      * information to the currently being processed trap, lock elimination, or
@@ -1025,13 +1025,20 @@ public class LogParser extends DefaultHandler implements ErrorHandler {
                     return;
                 }
                 try {
+                    UncommonTrap unc = new UncommonTrap(Integer.parseInt(search(atts, "bci")),
+                            search(atts, "reason"),
+                            search(atts, "action"),
+                            bytecodes[current_bytecode]);
                     if (scopes.size() == 0) {
-                        reportInternalError("scope underflow");
+                        // There may be a dangling site not yet in scopes after a late_inline
+                        if (site != null) {
+                            site.add(unc);
+                        } else {
+                            reportInternalError("scope underflow");
+                        }
+                    } else {
+                        scopes.peek().add(unc);
                     }
-                    scopes.peek().add(new UncommonTrap(Integer.parseInt(search(atts, "bci")),
-                                                       search(atts, "reason"),
-                                                       search(atts, "action"),
-                                                       bytecodes[current_bytecode]));
                 } catch (Error e) {
                     e.printStackTrace();
                 }
@@ -1175,11 +1182,11 @@ public class LogParser extends DefaultHandler implements ErrorHandler {
      * {@code true} here. (It will be reset when parsing the inlined methods is
      * done; this happens for the successful case in this method as well, when
      * {@code parse} elements are processed; and for inlining failures, in
-     * {@link #startElement()}, when {@code inline_fail} elements are
+     * {@link #startElement(String,String,String,Attributes)}, when {@code inline_fail} elements are
      * processed.)</li>
      * <li><b>task:</b> perform cleanup at the end of a compilation. Note that
      * the explicit {@code task_done} event is handled in
-     * {@link #startElement()}.</li>
+     * {@link #startElement(String,String,String,Attributes)}.</li>
      * </ul>
      */
     @Override
diff --git a/src/utils/LogCompilation/src/com/sun/hotspot/tools/compiler/MakeNotEntrantEvent.java b/src/utils/LogCompilation/src/main/java/com/sun/hotspot/tools/compiler/MakeNotEntrantEvent.java
similarity index 100%
rename from src/utils/LogCompilation/src/com/sun/hotspot/tools/compiler/MakeNotEntrantEvent.java
rename to src/utils/LogCompilation/src/main/java/com/sun/hotspot/tools/compiler/MakeNotEntrantEvent.java
diff --git a/src/utils/LogCompilation/src/com/sun/hotspot/tools/compiler/Method.java b/src/utils/LogCompilation/src/main/java/com/sun/hotspot/tools/compiler/Method.java
similarity index 100%
rename from src/utils/LogCompilation/src/com/sun/hotspot/tools/compiler/Method.java
rename to src/utils/LogCompilation/src/main/java/com/sun/hotspot/tools/compiler/Method.java
diff --git a/src/utils/LogCompilation/src/com/sun/hotspot/tools/compiler/NMethod.java b/src/utils/LogCompilation/src/main/java/com/sun/hotspot/tools/compiler/NMethod.java
similarity index 100%
rename from src/utils/LogCompilation/src/com/sun/hotspot/tools/compiler/NMethod.java
rename to src/utils/LogCompilation/src/main/java/com/sun/hotspot/tools/compiler/NMethod.java
diff --git a/src/utils/LogCompilation/src/com/sun/hotspot/tools/compiler/Phase.java b/src/utils/LogCompilation/src/main/java/com/sun/hotspot/tools/compiler/Phase.java
similarity index 100%
rename from src/utils/LogCompilation/src/com/sun/hotspot/tools/compiler/Phase.java
rename to src/utils/LogCompilation/src/main/java/com/sun/hotspot/tools/compiler/Phase.java
diff --git a/src/utils/LogCompilation/src/com/sun/hotspot/tools/compiler/UncommonTrap.java b/src/utils/LogCompilation/src/main/java/com/sun/hotspot/tools/compiler/UncommonTrap.java
similarity index 100%
rename from src/utils/LogCompilation/src/com/sun/hotspot/tools/compiler/UncommonTrap.java
rename to src/utils/LogCompilation/src/main/java/com/sun/hotspot/tools/compiler/UncommonTrap.java
diff --git a/src/utils/LogCompilation/src/com/sun/hotspot/tools/compiler/UncommonTrapEvent.java b/src/utils/LogCompilation/src/main/java/com/sun/hotspot/tools/compiler/UncommonTrapEvent.java
similarity index 100%
rename from src/utils/LogCompilation/src/com/sun/hotspot/tools/compiler/UncommonTrapEvent.java
rename to src/utils/LogCompilation/src/main/java/com/sun/hotspot/tools/compiler/UncommonTrapEvent.java
diff --git a/test/hotspot/gtest/gc/g1/test_g1HeapVerifier.cpp b/test/hotspot/gtest/gc/g1/test_g1HeapVerifier.cpp
new file mode 100644
index 00000000000..9ef0f14f3e6
--- /dev/null
+++ b/test/hotspot/gtest/gc/g1/test_g1HeapVerifier.cpp
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+#include "precompiled.hpp"
+#include "gc/g1/g1HeapVerifier.hpp"
+#include "logging/logConfiguration.hpp"
+#include "unittest.hpp"
+
+TEST(G1HeapVerifier, parse) {
+  G1HeapVerifier verifier(NULL);
+
+  LogConfiguration::configure_stdout(LogLevel::Off, true, LOG_TAGS(gc, verify));
+
+  // Default is to verify everything.
+  ASSERT_TRUE(verifier.should_verify(G1HeapVerifier::G1VerifyAll));
+  ASSERT_TRUE(verifier.should_verify(G1HeapVerifier::G1VerifyYoungOnly));
+  ASSERT_TRUE(verifier.should_verify(G1HeapVerifier::G1VerifyInitialMark));
+  ASSERT_TRUE(verifier.should_verify(G1HeapVerifier::G1VerifyMixed));
+  ASSERT_TRUE(verifier.should_verify(G1HeapVerifier::G1VerifyRemark));
+  ASSERT_TRUE(verifier.should_verify(G1HeapVerifier::G1VerifyCleanup));
+  ASSERT_TRUE(verifier.should_verify(G1HeapVerifier::G1VerifyFull));
+
+  // Setting one will disable all other.
+  verifier.parse_verification_type("full");
+  ASSERT_FALSE(verifier.should_verify(G1HeapVerifier::G1VerifyAll));
+  ASSERT_FALSE(verifier.should_verify(G1HeapVerifier::G1VerifyYoungOnly));
+  ASSERT_FALSE(verifier.should_verify(G1HeapVerifier::G1VerifyInitialMark));
+  ASSERT_FALSE(verifier.should_verify(G1HeapVerifier::G1VerifyMixed));
+  ASSERT_FALSE(verifier.should_verify(G1HeapVerifier::G1VerifyRemark));
+  ASSERT_FALSE(verifier.should_verify(G1HeapVerifier::G1VerifyCleanup));
+  ASSERT_TRUE(verifier.should_verify(G1HeapVerifier::G1VerifyFull));
+
+  // Verify case sensitivity.
+  verifier.parse_verification_type("YOUNG-ONLY");
+  ASSERT_FALSE(verifier.should_verify(G1HeapVerifier::G1VerifyYoungOnly));
+  verifier.parse_verification_type("young-only");
+  ASSERT_TRUE(verifier.should_verify(G1HeapVerifier::G1VerifyYoungOnly));
+
+  // Verify perfect match
+  verifier.parse_verification_type("mixedgc");
+  ASSERT_FALSE(verifier.should_verify(G1HeapVerifier::G1VerifyMixed));
+  verifier.parse_verification_type("mixe");
+  ASSERT_FALSE(verifier.should_verify(G1HeapVerifier::G1VerifyMixed));
+  verifier.parse_verification_type("mixed");
+  ASSERT_TRUE(verifier.should_verify(G1HeapVerifier::G1VerifyMixed));
+
+  // Verify the last three
+  verifier.parse_verification_type("initial-mark");
+  verifier.parse_verification_type("remark");
+  verifier.parse_verification_type("cleanup");
+  ASSERT_TRUE(verifier.should_verify(G1HeapVerifier::G1VerifyRemark));
+  ASSERT_TRUE(verifier.should_verify(G1HeapVerifier::G1VerifyCleanup));
+
+  // Enabling all is not the same as G1VerifyAll
+  ASSERT_FALSE(verifier.should_verify(G1HeapVerifier::G1VerifyAll));
+}
diff --git a/test/hotspot/gtest/logging/test_logMessageTest.cpp b/test/hotspot/gtest/logging/test_logMessageTest.cpp
index b3c736f34fa..5540b6c5424 100644
--- a/test/hotspot/gtest/logging/test_logMessageTest.cpp
+++ b/test/hotspot/gtest/logging/test_logMessageTest.cpp
@@ -27,6 +27,7 @@
 #include "logTestUtils.inline.hpp"
 #include "logging/log.hpp"
 #include "logging/logMessage.hpp"
+#include "memory/allocation.inline.hpp"
 #include "unittest.hpp"
 #include "utilities/globalDefinitions.hpp"
 
diff --git a/test/hotspot/jtreg/Makefile b/test/hotspot/jtreg/Makefile
index f24c3b081fc..3e4ac3117d4 100644
--- a/test/hotspot/jtreg/Makefile
+++ b/test/hotspot/jtreg/Makefile
@@ -62,8 +62,12 @@ ifeq ($(findstring CYGWIN,$(UNAME_S)), CYGWIN)
   endif
 endif
 
+ifndef CONCURRENCY_FACTOR
+  CONCURRENCY_FACTOR = 1
+endif
+
 # Concurrency based on min(cores / 2, 12)
-CONCURRENCY := $(shell expr $(NUM_CORES) / 2)
+CONCURRENCY := $(shell awk 'BEGIN { printf "%.0f", $(NUM_CORES) / 2 * $(CONCURRENCY_FACTOR) }')
 ifeq ($(CONCURRENCY), 0)
   CONCURRENCY := 1
 else ifeq ($(shell expr $(CONCURRENCY) \> 12), 1)
diff --git a/test/hotspot/jtreg/ProblemList.txt b/test/hotspot/jtreg/ProblemList.txt
index 76192ac93bd..a07b9c9e792 100644
--- a/test/hotspot/jtreg/ProblemList.txt
+++ b/test/hotspot/jtreg/ProblemList.txt
@@ -65,6 +65,7 @@ gc/g1/humongousObjects/objectGraphTest/TestObjectGraphAfterGC.java 8156755 gener
 gc/survivorAlignment/TestPromotionToSurvivor.java 8129886 generic-all
 gc/g1/logging/TestG1LoggingFailure.java 8169634 generic-all
 gc/g1/humongousObjects/TestHeapCounters.java 8178918 generic-all
+gc/g1/TestVerifyGCType.java 8193067 generic-all
 gc/stress/gclocker/TestGCLockerWithG1.java 8179226 generic-all
 gc/survivorAlignment/TestPromotionFromSurvivorToTenuredAfterMinorGC.java 8177765 generic-all
 
@@ -76,6 +77,7 @@ runtime/CompressedOops/UseCompressedOops.java 8079353 generic-all
 # This test is disabled since it will stress NMT and timeout during normal testing
 runtime/NMT/MallocStressTest.java 8166548 generic-all
 runtime/SharedArchiveFile/DefaultUseWithClient.java 8154204 generic-all
+runtime/AppCDS/UseAppCDS.java 8165603 windows-all
 
 #############################################################################
 
@@ -84,12 +86,7 @@ runtime/SharedArchiveFile/DefaultUseWithClient.java 8154204 generic-all
 serviceability/jdwp/AllModulesCommandTest.java 8170541 generic-all
 serviceability/sa/sadebugd/SADebugDTest.java 8163805 generic-all
 serviceability/jvmti/ModuleAwareAgents/ClassFileLoadHook/MAAClassFileLoadHook.java 8173936 generic-all
-serviceability/sa/JhsdbThreadInfoTest.java 8184042 macosx-all
-serviceability/sa/TestInstanceKlassSize.java 8184042 macosx-all
-serviceability/sa/TestInstanceKlassSizeForInterface.java 8184042 macosx-all
-serviceability/sa/TestPrintMdo.java 8184042 macosx-all
 serviceability/sa/TestRevPtrsForInvokeDynamic.java 8191270 generic-all
-serviceability/sa/jmap-hprof/JMapHProfLargeHeapTest.java 8184042 macosx-all
 #############################################################################
 
 # :hotspot_misc
diff --git a/test/hotspot/jtreg/TEST.ROOT b/test/hotspot/jtreg/TEST.ROOT
index 69eca8ec107..c7ccaf2f4de 100644
--- a/test/hotspot/jtreg/TEST.ROOT
+++ b/test/hotspot/jtreg/TEST.ROOT
@@ -53,6 +53,7 @@ requires.properties= \
     vm.rtm.os \
     vm.aot \
     vm.cds \
+    vm.cds.custom.loaders \
     vm.graal.enabled \
     docker.support
 
diff --git a/test/hotspot/jtreg/TEST.groups b/test/hotspot/jtreg/TEST.groups
index daf4480f452..d775c50bdb9 100644
--- a/test/hotspot/jtreg/TEST.groups
+++ b/test/hotspot/jtreg/TEST.groups
@@ -189,12 +189,27 @@ hotspot_tier1_runtime = \
  -runtime/Unsafe/RangeCheck.java \
  -runtime/containers/ \
   sanity/ \
-  testlibrary_tests/TestMutuallyExclusivePlatformPredicates.java
+  testlibrary_tests/TestMutuallyExclusivePlatformPredicates.java \
+ -:hotspot_tier1_runtime_appcds_exclude
 
 hotspot_cds = \
   runtime/SharedArchiveFile/ \
   runtime/CompressedOops/
 
+# AppCDS
+# If modifying AppCDS it is also recommended to run the open hotspot_cds group
+hotspot_appcds = \
+  runtime/appcds/
+
+# A subset of AppCDS tests to be run in JPRT push
+hotspot_tier1_runtime_appcds = \
+  runtime/appcds/HelloTest.java \
+  runtime/appcds/sharedStrings/SharedStringsBasic.java \
+  runtime/appcds/ClassLoaderTest.java
+
+hotspot_tier1_runtime_appcds_exclude = \
+  runtime/appcds/ \
+  -:hotspot_tier1_runtime_appcds
 
 hotspot_tier1_serviceability = \
   serviceability/dcmd/compiler \
diff --git a/test/hotspot/jtreg/compiler/loopopts/TestSplitIfPinnedCMove.java b/test/hotspot/jtreg/compiler/loopopts/TestSplitIfPinnedCMove.java
new file mode 100644
index 00000000000..bce4fdf28c0
--- /dev/null
+++ b/test/hotspot/jtreg/compiler/loopopts/TestSplitIfPinnedCMove.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 2017, Red Hat, Inc. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/**
+ * @test
+ * @bug 8191153
+ * @summary too strong assert from 8186125
+ *
+ * @run main/othervm -XX:-BackgroundCompilation -XX:CompileCommand=dontinline,TestSplitIfPinnedCMove::not_inlined -XX:CompileOnly=TestSplitIfPinnedCMove::test TestSplitIfPinnedCMove
+ *
+ */
+
+public class TestSplitIfPinnedCMove {
+    static void not_inlined() {}
+
+    static class A {
+        A(int f) {
+            this.f = f;
+        }
+        int f;
+    }
+
+    static int test(int i1, int i3, A a1, A a2) {
+        // loops to trigger more loop optimization passes
+        int res = 0;
+        for (int i = 0; i < 2; i++) {
+            for (int j = 0; j < 2; j++) {
+                for (int k = 0; k < 2; k++) {
+                    res++;
+                }
+            }
+        }
+        // null check a1 and a2
+        res += a1.f + a2.f;
+
+        boolean f2 = false;
+        if (i1 > 0) {
+            not_inlined();
+            f2 = true;
+        }
+
+        // Should become CMoveP and be pinned here
+        res += (i3 > 0 ? a1 : a2).f;
+
+        // f2 should be split through phi with above if
+        if (f2) {
+            not_inlined();
+            res += 42;
+        }
+
+        // Another use for i3 > 0
+        if (i3 > 0) {
+            res++;
+        }
+        return res;
+    }
+
+    public static void main(String[] args) {
+        A a = new A(42);
+        for (int i = 0; i < 20_000; i++) {
+                test((i % 2) == 0 ? 0 : 1, (i % 2) == 0 ? 0 : 1, a, a);
+        }
+    }
+}
diff --git a/test/hotspot/jtreg/compiler/loopopts/UseCountedLoopSafepointsTest.java b/test/hotspot/jtreg/compiler/loopopts/UseCountedLoopSafepointsTest.java
index f23673ad651..e581f4ea3c8 100644
--- a/test/hotspot/jtreg/compiler/loopopts/UseCountedLoopSafepointsTest.java
+++ b/test/hotspot/jtreg/compiler/loopopts/UseCountedLoopSafepointsTest.java
@@ -61,7 +61,8 @@ public class UseCountedLoopSafepointsTest {
         OutputAnalyzer oa;
         try {
             oa = ProcessTools.executeTestJvm("-XX:+UnlockDiagnosticVMOptions", "-Xbootclasspath/a:.",
-                    "-XX:" + (enabled ? "+" : "-") + "UseCountedLoopSafepoints", "-XX:+WhiteBoxAPI",
+                                             "-XX:" + (enabled ? "+" : "-") + "UseCountedLoopSafepoints",
+                                             "-XX:LoopStripMiningIter=" + (enabled ? "1" : "0"), "-XX:+WhiteBoxAPI",
                     "-XX:-Inline", "-Xbatch", "-XX:+PrintIdeal", "-XX:LoopUnrollLimit=0",
                     "-XX:CompileOnly=" + UseCountedLoopSafepoints.class.getName() + "::testMethod",
                     UseCountedLoopSafepoints.class.getName());
diff --git a/test/hotspot/jtreg/compiler/profiling/TestTypeProfiling.java b/test/hotspot/jtreg/compiler/profiling/TestTypeProfiling.java
new file mode 100644
index 00000000000..5d41c83d0fa
--- /dev/null
+++ b/test/hotspot/jtreg/compiler/profiling/TestTypeProfiling.java
@@ -0,0 +1,146 @@
+/*
+ * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+ /**
+  * @test
+  * @bug 8189439
+  * @summary Parameters type profiling is not performed from aarch64 interpreter
+  * @requires vm.flavor == "server" & vm.compMode == "Xmixed" & !vm.emulatedClient & !vm.graal.enabled
+  * @library /test/lib /
+  * @build sun.hotspot.WhiteBox
+  * @run driver ClassFileInstaller sun.hotspot.WhiteBox sun.hotspot.WhiteBox$WhiteBoxPermission
+  * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI
+  *                   -XX:-BackgroundCompilation -XX:-UseOnStackReplacement
+  *                   -server -XX:-TieredCompilation -XX:TypeProfileLevel=020
+  *                    compiler.profiling.TestTypeProfiling
+  * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI
+  *                   -XX:-BackgroundCompilation -XX:-UseOnStackReplacement
+  *                   -server -XX:-TieredCompilation -XX:TypeProfileLevel=200
+  *                    compiler.profiling.TestTypeProfiling
+  */
+
+package compiler.profiling;
+
+import jdk.test.lib.Platform;
+import sun.hotspot.WhiteBox;
+import compiler.whitebox.CompilerWhiteBoxTest;
+import java.lang.reflect.Method;
+
+public class TestTypeProfiling {
+
+    public static int[] mParamTypeCheck(Object in) {
+        try {
+            return (int[]) in;
+        } catch (ClassCastException cce) {
+            return null;
+        }
+    }
+
+    static Object x2(Object src) {
+        return src;
+    }
+
+    public static int[] mRetTypeCheck(Object in) {
+        try {
+            Object out = x2(in);
+            return (int[]) out;
+        } catch (ClassCastException cce) {
+            return null;
+        }
+    }
+
+    private static final WhiteBox WHITE_BOX = WhiteBox.getWhiteBox();
+    private static final int TIERED_STOP_AT_LEVEL = WHITE_BOX.getIntxVMFlag("TieredStopAtLevel").intValue();
+
+    static boolean deoptimize(Method method, Object src_obj) throws Exception {
+        for (int i = 0; i < 10; i++) {
+            method.invoke(null, src_obj);
+            if (!WHITE_BOX.isMethodCompiled(method)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    static public void main(String[] args) throws Exception {
+        if (!Platform.isServer() || Platform.isEmulatedClient()) {
+            throw new Error("TESTBUG: Not server mode");
+        }
+        // Only execute if C2 is available
+        if (TIERED_STOP_AT_LEVEL != CompilerWhiteBoxTest.COMP_LEVEL_FULL_OPTIMIZATION) {
+            throw new RuntimeException("please enable C2");
+        }
+
+        Method method;
+        if (WHITE_BOX.getUintxVMFlag("TypeProfileLevel") == 20) {
+            method = TestTypeProfiling.class.getMethod("mRetTypeCheck", Object.class);
+        } else
+        if (WHITE_BOX.getUintxVMFlag("TypeProfileLevel") == 200) {
+            method = TestTypeProfiling.class.getMethod("mParamTypeCheck", Object.class);
+        } else {
+            throw new RuntimeException("please setup method return/params type profilation: -XX:TypeProfileLevel=020/200");
+        }
+
+        int[] src = new int[10];
+        Object src_obj = new Object();
+
+        // Warm up & make sure we collect type profiling
+        for (int i = 0; i < 20000; i++) {
+            mParamTypeCheck(src);
+            mRetTypeCheck(src);
+        }
+
+        // And make sure the method is compiled by C2
+        WHITE_BOX.enqueueMethodForCompilation(method, CompilerWhiteBoxTest.COMP_LEVEL_FULL_OPTIMIZATION);
+        if (!WHITE_BOX.isMethodCompiled(method)) {
+            throw new RuntimeException(method.getName() + " is not compiled");
+        }
+
+        // should deoptimize for speculative type check
+        if (!deoptimize(method, src_obj)) {
+            throw new RuntimeException(method.getName() + " is not deoptimized");
+        }
+
+        // compile again
+        WHITE_BOX.enqueueMethodForCompilation(method, CompilerWhiteBoxTest.COMP_LEVEL_FULL_OPTIMIZATION);
+        if (!WHITE_BOX.isMethodCompiled(method)) {
+            throw new RuntimeException(method.getName() + " is not recompiled");
+        }
+
+        // should deoptimize for actual type check
+        if (!deoptimize(method, src_obj)) {
+            throw new RuntimeException(method.getName() + " is not deoptimized (should deoptimize for actual type check)");
+        }
+
+        // compile once again
+        WHITE_BOX.enqueueMethodForCompilation(method, CompilerWhiteBoxTest.COMP_LEVEL_FULL_OPTIMIZATION);
+        if (!WHITE_BOX.isMethodCompiled(method)) {
+            throw new RuntimeException(method.getName() + " is not recompiled");
+        }
+
+        // this time new parameter type should not force deoptimization
+        if (deoptimize(method, src_obj)) {
+            throw new RuntimeException(method.getName() + " is deoptimized again");
+        }
+    }
+}
diff --git a/test/hotspot/jtreg/compiler/runtime/criticalnatives/argumentcorruption/CheckLongArgs.java b/test/hotspot/jtreg/compiler/runtime/criticalnatives/argumentcorruption/CheckLongArgs.java
index cbe0235c4e0..59acd39043e 100644
--- a/test/hotspot/jtreg/compiler/runtime/criticalnatives/argumentcorruption/CheckLongArgs.java
+++ b/test/hotspot/jtreg/compiler/runtime/criticalnatives/argumentcorruption/CheckLongArgs.java
@@ -24,7 +24,8 @@
 
 /* @test
  * @bug 8167409
- * @run main/othervm/native -Xcomp compiler.runtime.criticalnatives.argumentcorruption.CheckLongArgs
+ * @requires os.arch != "aarch64"
+ * @run main/othervm/native -Xcomp -XX:+CriticalJNINatives compiler.runtime.criticalnatives.argumentcorruption.CheckLongArgs
  */
 package compiler.runtime.criticalnatives.argumentcorruption;
 public class CheckLongArgs {
diff --git a/test/hotspot/jtreg/compiler/runtime/criticalnatives/lookup/LookUp.java b/test/hotspot/jtreg/compiler/runtime/criticalnatives/lookup/LookUp.java
index 17c9a0f9b90..20b7af1726c 100644
--- a/test/hotspot/jtreg/compiler/runtime/criticalnatives/lookup/LookUp.java
+++ b/test/hotspot/jtreg/compiler/runtime/criticalnatives/lookup/LookUp.java
@@ -24,7 +24,8 @@
 
 /* @test
  * @bug 8167408
- * @run main/othervm/native -Xcomp compiler.runtime.criticalnatives.lookup.LookUp
+ * @requires os.arch != "aarch64"
+ * @run main/othervm/native -Xcomp -XX:+CriticalJNINatives compiler.runtime.criticalnatives.lookup.LookUp
  */
 package compiler.runtime.criticalnatives.lookup;
 public class LookUp {
diff --git a/test/hotspot/jtreg/compiler/unsafe/TestLoopUnswitching.java b/test/hotspot/jtreg/compiler/unsafe/TestLoopUnswitching.java
new file mode 100644
index 00000000000..4c35789c718
--- /dev/null
+++ b/test/hotspot/jtreg/compiler/unsafe/TestLoopUnswitching.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (c) 2017, Red Hat, Inc. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/**
+ * @test
+ * @bug 8191887
+ * @summary loop cloning misses support for Opaque4 node
+ * @modules java.base/jdk.internal.misc:+open
+ *
+ * @run main/othervm -XX:-BackgroundCompilation TestLoopUnswitching
+ *
+ */
+
+import jdk.internal.misc.Unsafe;
+import java.lang.reflect.Field;
+import java.util.Arrays;
+
+public class TestLoopUnswitching {
+
+    static final jdk.internal.misc.Unsafe UNSAFE = Unsafe.getUnsafe();
+    static final long F_OFFSET;
+
+    static class A {
+        int f;
+        A(int f) {
+            this.f = f;
+        }
+    }
+
+    static {
+        try {
+            Field fField = A.class.getDeclaredField("f");
+            F_OFFSET = UNSAFE.objectFieldOffset(fField);
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    static int test1(A[] arr, boolean flag1, boolean flag2) {
+        int res = 0;
+        for (int i = 0; i < 10; i++) {
+            A a = arr[i];
+            if (flag1) { // triggers unswitching
+                res += UNSAFE.getInt(a, F_OFFSET);
+            }
+            if (flag2) {
+                // Opaque4 node here is in the loop but If is out of the loop
+                res += UNSAFE.getInt(a, F_OFFSET);
+                break;
+            }
+            res += UNSAFE.getInt(a, F_OFFSET);
+        }
+        return res;
+    }
+
+    static int test2(A[] arr, boolean flag1, boolean flag2) {
+        int res = 0;
+        for (int i = 0; i < 10; i++) {
+            A a = arr[i];
+            if (flag1) { // triggers unswitching
+                res += UNSAFE.getInt(a, F_OFFSET);
+            }
+            if (flag2) {
+                // Opaque4 node here is out of the loop but Bool is in the the loop
+                res += UNSAFE.getInt(a, F_OFFSET);
+                break;
+            }
+            res += a.f;
+        }
+        return res;
+    }
+
+    static public void main(String[] args) {
+        A[] arr = new A[1000];
+        Arrays.fill(arr, new A(0x42));
+        for (int i = 0; i < 20000; i++) {
+            test1(arr, (i%2) == 0, (i%2) == 0);
+            test2(arr, (i%2) == 0, (i%2) == 0);
+        }
+    }
+
+}
diff --git a/test/hotspot/jtreg/gc/TestAllocateHeapAt.java b/test/hotspot/jtreg/gc/TestAllocateHeapAt.java
new file mode 100644
index 00000000000..aae999ce1ea
--- /dev/null
+++ b/test/hotspot/jtreg/gc/TestAllocateHeapAt.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/* @test TestAllocateHeapAt.java
+ * @key gc
+ * @summary Test to check allocation of Java Heap with AllocateHeapAt option
+ * @library /test/lib
+ * @modules java.base/jdk.internal.misc
+ */
+
+import jdk.test.lib.JDKToolFinder;
+import jdk.test.lib.process.ProcessTools;
+import jdk.test.lib.process.OutputAnalyzer;
+import java.util.ArrayList;
+import java.util.Collections;
+
+public class TestAllocateHeapAt {
+  public static void main(String args[]) throws Exception {
+    ArrayList<String> vmOpts = new ArrayList();
+
+    String testVmOptsStr = System.getProperty("test.java.opts");
+    if (!testVmOptsStr.isEmpty()) {
+      String[] testVmOpts = testVmOptsStr.split(" ");
+      Collections.addAll(vmOpts, testVmOpts);
+    }
+    String test_dir = System.getProperty("test.dir", ".");
+    Collections.addAll(vmOpts, new String[] {"-XX:AllocateHeapAt=" + test_dir,
+                                             "-Xlog:gc+heap=info",
+                                             "-Xmx32m",
+                                             "-Xms32m",
+                                             "-version"});
+
+    System.out.print("Testing:\n" + JDKToolFinder.getJDKTool("java"));
+    for (int i = 0; i < vmOpts.size(); i += 1) {
+      System.out.print(" " + vmOpts.get(i));
+    }
+    System.out.println();
+
+    ProcessBuilder pb =
+      ProcessTools.createJavaProcessBuilder(vmOpts.toArray(new String[vmOpts.size()]));
+    OutputAnalyzer output = new OutputAnalyzer(pb.start());
+
+    System.out.println("Output:\n" + output.getOutput());
+
+    output.shouldContain("Successfully allocated Java heap at location");
+    output.shouldHaveExitValue(0);
+  }
+}
diff --git a/test/hotspot/jtreg/gc/TestAllocateHeapAtError.java b/test/hotspot/jtreg/gc/TestAllocateHeapAtError.java
new file mode 100644
index 00000000000..b7f667c1a17
--- /dev/null
+++ b/test/hotspot/jtreg/gc/TestAllocateHeapAtError.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/* @test TestAllocateHeapAtError.java
+ * @key gc
+ * @summary Test to check correct handling of non-existent directory passed to AllocateHeapAt option
+ * @library /test/lib
+ * @modules java.base/jdk.internal.misc
+ */
+
+import java.io.File;
+import jdk.test.lib.JDKToolFinder;
+import jdk.test.lib.process.ProcessTools;
+import jdk.test.lib.process.OutputAnalyzer;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.UUID;
+
+public class TestAllocateHeapAtError {
+  public static void main(String args[]) throws Exception {
+    ArrayList<String> vmOpts = new ArrayList();
+
+    String testVmOptsStr = System.getProperty("test.java.opts");
+    if (!testVmOptsStr.isEmpty()) {
+      String[] testVmOpts = testVmOptsStr.split(" ");
+      Collections.addAll(vmOpts, testVmOpts);
+    }
+    String test_dir = System.getProperty("test.dir", ".");
+
+    File f = null;
+    do {
+      f = new File(test_dir, UUID.randomUUID().toString());
+    } while(f.exists());
+
+    Collections.addAll(vmOpts, new String[] {"-XX:AllocateHeapAt=" + f.getName(),
+                                             "-Xlog:gc+heap=info",
+                                             "-Xmx32m",
+                                             "-Xms32m",
+                                             "-version"});
+
+    System.out.print("Testing:\n" + JDKToolFinder.getJDKTool("java"));
+    for (int i = 0; i < vmOpts.size(); i += 1) {
+      System.out.print(" " + vmOpts.get(i));
+    }
+    System.out.println();
+
+    ProcessBuilder pb =
+      ProcessTools.createJavaProcessBuilder(vmOpts.toArray(new String[vmOpts.size()]));
+    OutputAnalyzer output = new OutputAnalyzer(pb.start());
+
+    System.out.println("Output:\n" + output.getOutput());
+
+    output.shouldContain("Could not create file for Heap");
+    output.shouldContain("Error occurred during initialization of VM");
+    output.shouldNotHaveExitValue(0);
+  }
+}
+
diff --git a/test/hotspot/jtreg/gc/TestAllocateHeapAtMultiple.java b/test/hotspot/jtreg/gc/TestAllocateHeapAtMultiple.java
new file mode 100644
index 00000000000..492a976c1e9
--- /dev/null
+++ b/test/hotspot/jtreg/gc/TestAllocateHeapAtMultiple.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/* @test TestAllocateHeapAtMultiple.java
+ * @key gc
+ * @summary Test to check allocation of Java Heap with AllocateHeapAt option. Has multiple sub-tests to cover different code paths.
+ * @library /test/lib
+ * @modules java.base/jdk.internal.misc
+ * @requires vm.bits == "64"
+ */
+
+import jdk.test.lib.JDKToolFinder;
+import jdk.test.lib.process.ProcessTools;
+import jdk.test.lib.process.OutputAnalyzer;
+import java.util.ArrayList;
+import java.util.Collections;
+
+public class TestAllocateHeapAtMultiple {
+  public static void main(String args[]) throws Exception {
+    ArrayList<String> vmOpts = new ArrayList();
+    String[] testVmOpts = null;
+
+    String test_dir = System.getProperty("test.dir", ".");
+
+    String testVmOptsStr = System.getProperty("test.java.opts");
+    if (!testVmOptsStr.isEmpty()) {
+      testVmOpts = testVmOptsStr.split(" ");
+    }
+
+    // Extra options for each of the sub-tests
+    String[] extraOptsList = new String[] {
+      "-Xmx32m -Xms32m -XX:+UseCompressedOops",     // 1. With compressedoops enabled.
+      "-Xmx32m -Xms32m -XX:-UseCompressedOops",     // 2. With compressedoops disabled.
+      "-Xmx32m -Xms32m -XX:HeapBaseMinAddress=3g",  // 3. With user specified HeapBaseMinAddress.
+      "-Xmx4g -Xms4g",                              // 4. With larger heap size (UnscaledNarrowOop not possible).
+      "-Xmx4g -Xms4g -XX:+UseLargePages",           // 5. Set UseLargePages.
+      "-Xmx4g -Xms4g -XX:+UseNUMA"                  // 6. Set UseNUMA.
+    };
+
+    for(String extraOpts : extraOptsList) {
+      vmOpts.clear();
+      if(testVmOpts != null) {
+        Collections.addAll(vmOpts, testVmOpts);
+      }
+      // Add extra options specific to the sub-test.
+      String[] extraOptsArray = extraOpts.split(" ");
+      if(extraOptsArray != null) {
+        Collections.addAll(vmOpts, extraOptsArray);
+      }
+      // Add common options
+      Collections.addAll(vmOpts, new String[] {"-XX:AllocateHeapAt=" + test_dir,
+                                               "-Xlog:gc+heap=info",
+                                               "-version"});
+
+      System.out.print("Testing:\n" + JDKToolFinder.getJDKTool("java"));
+      for (int i = 0; i < vmOpts.size(); i += 1) {
+        System.out.print(" " + vmOpts.get(i));
+      }
+      System.out.println();
+
+      ProcessBuilder pb =
+        ProcessTools.createJavaProcessBuilder(vmOpts.toArray(new String[vmOpts.size()]));
+      OutputAnalyzer output = new OutputAnalyzer(pb.start());
+
+      System.out.println("Output:\n" + output.getOutput());
+
+      output.shouldContain("Successfully allocated Java heap at location");
+      output.shouldHaveExitValue(0);
+    }
+  }
+}
diff --git a/test/hotspot/jtreg/gc/TestFullGCALot.java b/test/hotspot/jtreg/gc/TestFullGCALot.java
index 19ac9641cd2..90d05f9253b 100644
--- a/test/hotspot/jtreg/gc/TestFullGCALot.java
+++ b/test/hotspot/jtreg/gc/TestFullGCALot.java
@@ -24,7 +24,7 @@
 /*
  * @test TestFullGCALot
  * @key gc
- * @bug 4187687
+ * @bug 4187687 8187819
  * @summary Ensure no access violation when using FullGCALot
  * @requires vm.debug
  * @run main/othervm -XX:NewSize=10m -XX:+FullGCALot -XX:FullGCALotInterval=120 TestFullGCALot
diff --git a/test/hotspot/jtreg/gc/TestGenerationPerfCounter.java b/test/hotspot/jtreg/gc/TestGenerationPerfCounter.java
new file mode 100644
index 00000000000..d9b4c94c7fb
--- /dev/null
+++ b/test/hotspot/jtreg/gc/TestGenerationPerfCounter.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import static jdk.test.lib.Asserts.*;
+import gc.testlibrary.PerfCounter;
+import gc.testlibrary.PerfCounters;
+
+
+/* @test TestGenerationPerfCounter
+ * @bug 8080345
+ * @requires vm.gc=="null"
+ * @library /test/lib /
+ * @summary Tests that the sun.gc.policy.generations returns 2 for all GCs.
+ * @modules java.base/jdk.internal.misc
+ *          java.compiler
+ *          java.management/sun.management
+ *          jdk.internal.jvmstat/sun.jvmstat.monitor
+ * @run main/othervm -XX:+UsePerfData -XX:+UseSerialGC TestGenerationPerfCounter
+ * @run main/othervm -XX:+UsePerfData -XX:+UseParallelGC TestGenerationPerfCounter
+ * @run main/othervm -XX:+UsePerfData -XX:+UseG1GC TestGenerationPerfCounter
+ * @run main/othervm -XX:+UsePerfData -XX:+UseConcMarkSweepGC TestGenerationPerfCounter
+ */
+public class TestGenerationPerfCounter {
+    public static void main(String[] args) throws Exception {
+        long numGenerations =
+            PerfCounters.findByName("sun.gc.policy.generations").longValue();
+        assertEQ(numGenerations, 2L);
+    }
+}
diff --git a/test/hotspot/jtreg/gc/TestMemoryMXBeansAndPoolsPresence.java b/test/hotspot/jtreg/gc/TestMemoryMXBeansAndPoolsPresence.java
new file mode 100644
index 00000000000..e923dd4ec1c
--- /dev/null
+++ b/test/hotspot/jtreg/gc/TestMemoryMXBeansAndPoolsPresence.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.util.List;
+import java.util.ArrayList;
+import java.lang.management.*;
+import static jdk.test.lib.Asserts.*;
+import java.util.stream.*;
+
+/* @test TestMemoryMXBeansAndPoolsPresence
+ * @bug 8191564
+ * @summary Tests that GarbageCollectorMXBeans and GC MemoryPools are created.
+ * @library /test/lib
+ * @modules java.base/jdk.internal.misc
+ *          java.management
+ * @requires vm.gc == null
+ * @run main/othervm -XX:+UseG1GC TestMemoryMXBeansAndPoolsPresence G1
+ * @run main/othervm -XX:+UseConcMarkSweepGC TestMemoryMXBeansAndPoolsPresence CMS
+ * @run main/othervm -XX:+UseParallelGC TestMemoryMXBeansAndPoolsPresence Parallel
+ * @run main/othervm -XX:+UseSerialGC TestMemoryMXBeansAndPoolsPresence Serial
+ */
+
+class GCBeanDescription {
+    public String name;
+    public String[] poolNames;
+
+    public GCBeanDescription(String name, String[] poolNames) {
+        this.name = name;
+        this.poolNames = poolNames;
+    }
+}
+
+public class TestMemoryMXBeansAndPoolsPresence {
+    public static void test(GCBeanDescription... expectedBeans) {
+        List<MemoryPoolMXBean> memoryPools = ManagementFactory.getMemoryPoolMXBeans();
+
+        List<GarbageCollectorMXBean> gcBeans = ManagementFactory.getGarbageCollectorMXBeans();
+        assertEQ(expectedBeans.length, gcBeans.size());
+
+        for (GCBeanDescription desc : expectedBeans) {
+            List<GarbageCollectorMXBean> beans = gcBeans.stream()
+                                                        .filter(b -> b.getName().equals(desc.name))
+                                                        .collect(Collectors.toList());
+            assertEQ(beans.size(), 1);
+
+            GarbageCollectorMXBean bean = beans.get(0);
+            assertEQ(desc.name, bean.getName());
+
+            String[] pools = bean.getMemoryPoolNames();
+            assertEQ(desc.poolNames.length, pools.length);
+            for (int i = 0; i < desc.poolNames.length; i++) {
+                assertEQ(desc.poolNames[i], pools[i]);
+            }
+        }
+    }
+
+    public static void main(String[] args) {
+        switch (args[0]) {
+            case "G1":
+                test(new GCBeanDescription("G1 Young Generation", new String[] {"G1 Eden Space", "G1 Survivor Space"}),
+                     new GCBeanDescription("G1 Old Generation",   new String[] {"G1 Eden Space", "G1 Survivor Space", "G1 Old Gen"}));
+                break;
+            case "CMS":
+                test(new GCBeanDescription("ParNew",              new String[] {"Par Eden Space", "Par Survivor Space"}),
+                     new GCBeanDescription("ConcurrentMarkSweep", new String[] {"Par Eden Space", "Par Survivor Space", "CMS Old Gen"}));
+                break;
+            case "Parallel":
+                test(new GCBeanDescription("PS Scavenge",         new String[] {"PS Eden Space", "PS Survivor Space"}),
+                     new GCBeanDescription("PS MarkSweep",        new String[] {"PS Eden Space", "PS Survivor Space", "PS Old Gen"}));
+                break;
+            case "Serial":
+                test(new GCBeanDescription("Copy",              new String[] {"Eden Space", "Survivor Space"}),
+                     new GCBeanDescription("MarkSweepCompact",  new String[] {"Eden Space", "Survivor Space", "Tenured Gen"}));
+                break;
+            default:
+                assertTrue(false);
+                break;
+
+        }
+    }
+}
diff --git a/test/hotspot/jtreg/gc/g1/TestInvalidateArrayCopy.java b/test/hotspot/jtreg/gc/g1/TestInvalidateArrayCopy.java
new file mode 100644
index 00000000000..38e1e0bddd6
--- /dev/null
+++ b/test/hotspot/jtreg/gc/g1/TestInvalidateArrayCopy.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test TestInvalidateArrayCopy
+ * @bug 8182050
+ * @summary Check that benign (0-sized) out of heap bounds card table invalidations do not assert.
+ * @requires vm.gc.G1
+ * @requires vm.debug
+ * @key gc
+ * @run main/othervm -XX:NewSize=1M -Xlog:gc -XX:MaxNewSize=1m -XX:-UseTLAB -XX:OldSize=63M -XX:MaxHeapSize=64M TestInvalidateArrayCopy
+ */
+
+// The test allocates zero-sized arrays of j.l.O and tries to arraycopy random data into it so
+// that the asserting post barrier calls are executed. It assumes that G1 allocates eden regions
+// at the top of the heap for this problem to occur.
+public class TestInvalidateArrayCopy {
+
+    static final int NumIterations = 1000000;
+
+    // "Random" source data to "copy" into the target.
+    static Object[] sourceArray = new Object[10];
+
+    public static void main(String[] args) {
+        for (int i = 0; i < NumIterations; i++) {
+            Object[] x = new Object[0];
+            // Make sure that the compiler can't optimize out the above allocations.
+            if (i % (NumIterations / 10) == 0) {
+                System.out.println(x);
+            }
+            System.arraycopy(sourceArray, 0, x, 0, Math.min(x.length, sourceArray.length));
+        }
+    }
+}
diff --git a/test/hotspot/jtreg/gc/g1/TestVerifyGCType.java b/test/hotspot/jtreg/gc/g1/TestVerifyGCType.java
new file mode 100644
index 00000000000..033aed25492
--- /dev/null
+++ b/test/hotspot/jtreg/gc/g1/TestVerifyGCType.java
@@ -0,0 +1,284 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test TestVerifyGCType
+ * @summary Test the VerifyGCType flag to ensure basic functionality.
+ * @key gc
+ * @requires vm.gc.G1
+ * @library /test/lib
+ * @build sun.hotspot.WhiteBox
+ * @run main ClassFileInstaller sun.hotspot.WhiteBox
+ * @run driver TestVerifyGCType
+ */
+
+import java.util.ArrayList;
+import java.util.Collections;
+
+import jdk.test.lib.Asserts;
+import jdk.test.lib.Utils;
+import jdk.test.lib.process.OutputAnalyzer;
+import jdk.test.lib.process.ProcessTools;
+import sun.hotspot.WhiteBox;
+
+public class TestVerifyGCType {
+    public static final String VERIFY_TAG    = "[gc,verify]";
+    public static final String VERIFY_BEFORE = "Verifying Before GC";
+    public static final String VERIFY_DURING = "Verifying During GC";
+    public static final String VERIFY_AFTER  = "Verifying After GC";
+
+    public static void main(String args[]) throws Exception {
+        testAllVerificationEnabled();
+        testAllExplicitlyEnabled();
+        testFullAndRemark();
+        testConcurrentMark();
+        testBadVerificationType();
+        testUnsupportedCollector();
+    }
+
+    private static void testAllVerificationEnabled() throws Exception {
+        // Test with all verification enabled
+        OutputAnalyzer output = testWithVerificationType(new String[0]);
+        output.shouldHaveExitValue(0);
+
+        verifyCollection("Pause Young", true, false, true, output.getStdout());
+        verifyCollection("Pause Initial Mark", true, false, true, output.getStdout());
+        verifyCollection("Pause Mixed", true, false, true, output.getStdout());
+        verifyCollection("Pause Remark", false, true, false, output.getStdout());
+        verifyCollection("Pause Cleanup", false, true, false, output.getStdout());
+        verifyCollection("Pause Full", true, true, true, output.getStdout());
+    }
+
+    private static void testAllExplicitlyEnabled() throws Exception {
+        OutputAnalyzer output;
+        // Test with all explicitly enabled
+        output = testWithVerificationType(new String[] {
+                "young-only", "initial-mark", "mixed", "remark", "cleanup", "full"});
+        output.shouldHaveExitValue(0);
+
+        verifyCollection("Pause Young", true, false, true, output.getStdout());
+        verifyCollection("Pause Initial Mark", true, false, true, output.getStdout());
+        verifyCollection("Pause Mixed", true, false, true, output.getStdout());
+        verifyCollection("Pause Remark", false, true, false, output.getStdout());
+        verifyCollection("Pause Cleanup", false, true, false, output.getStdout());
+        verifyCollection("Pause Full", true, true, true, output.getStdout());
+    }
+
+    private static void testFullAndRemark() throws Exception {
+        OutputAnalyzer output;
+        // Test with full and remark
+        output = testWithVerificationType(new String[] {"remark", "full"});
+        output.shouldHaveExitValue(0);
+
+        verifyCollection("Pause Young", false, false, false, output.getStdout());
+        verifyCollection("Pause Initial Mark", false, false, false, output.getStdout());
+        verifyCollection("Pause Mixed", false, false, false, output.getStdout());
+        verifyCollection("Pause Remark", false, true, false, output.getStdout());
+        verifyCollection("Pause Cleanup", false, false, false, output.getStdout());
+        verifyCollection("Pause Full", true, true, true, output.getStdout());
+    }
+
+    private static void testConcurrentMark() throws Exception {
+        OutputAnalyzer output;
+        // Test with full and remark
+        output = testWithVerificationType(new String[] {"initial-mark", "cleanup", "remark"});
+        output.shouldHaveExitValue(0);
+
+        verifyCollection("Pause Young", false, false, false, output.getStdout());
+        verifyCollection("Pause Initial Mark", true, false, true, output.getStdout());
+        verifyCollection("Pause Mixed", false, false, false, output.getStdout());
+        verifyCollection("Pause Remark", false, true, false, output.getStdout());
+        verifyCollection("Pause Cleanup", false, true, false, output.getStdout());
+        verifyCollection("Pause Full", false, false, false, output.getStdout());
+    }
+
+    private static void testBadVerificationType() throws Exception {
+        OutputAnalyzer output;
+        // Test bad type
+        output = testWithVerificationType(new String[] {"old"});
+        output.shouldHaveExitValue(0);
+
+        output.shouldMatch("VerifyGCType: '.*' is unknown. Available types are: young-only, initial-mark, mixed, remark, cleanup and full");
+        verifyCollection("Pause Young", true, false, true, output.getStdout());
+        verifyCollection("Pause Initial Mark", true, false, true, output.getStdout());
+        verifyCollection("Pause Mixed", true, false, true, output.getStdout());
+        verifyCollection("Pause Remark", false, true, false, output.getStdout());
+        verifyCollection("Pause Cleanup", false, true, false, output.getStdout());
+        verifyCollection("Pause Full", true, true, true, output.getStdout());
+    }
+
+    private static void testUnsupportedCollector() throws Exception {
+        OutputAnalyzer output;
+        // Test bad gc
+        output = testWithBadGC();
+        output.shouldHaveExitValue(0);
+        output.shouldMatch("VerifyGCType is not supported by this collector.");
+    }
+
+    private static OutputAnalyzer testWithVerificationType(String[] types) throws Exception {
+        ArrayList<String> basicOpts = new ArrayList<>();
+        Collections.addAll(basicOpts, new String[] {
+                                       "-Xbootclasspath/a:.",
+                                       "-XX:+UnlockDiagnosticVMOptions",
+                                       "-XX:+UseG1GC",
+                                       "-XX:+WhiteBoxAPI",
+                                       "-Xlog:gc,gc+start,gc+verify=info",
+                                       "-Xms16m",
+                                       "-Xmx16m",
+                                       "-XX:+VerifyBeforeGC",
+                                       "-XX:+VerifyAfterGC",
+                                       "-XX:+VerifyDuringGC"});
+
+        for(String verifyType : types) {
+            basicOpts.add("-XX:VerifyGCType="+verifyType);
+        }
+
+        basicOpts.add(TriggerGCs.class.getName());
+
+        ProcessBuilder procBuilder =  ProcessTools.createJavaProcessBuilder(basicOpts.toArray(
+                                                                            new String[basicOpts.size()]));
+        OutputAnalyzer analyzer = new OutputAnalyzer(procBuilder.start());
+        return analyzer;
+    }
+
+    private static OutputAnalyzer testWithBadGC() throws Exception {
+        ProcessBuilder procBuilder =  ProcessTools.createJavaProcessBuilder(new String[] {
+                "-XX:+UseParallelGC",
+                "-XX:+UnlockDiagnosticVMOptions",
+                "-XX:VerifyGCType=full",
+                "-version"});
+
+        OutputAnalyzer analyzer = new OutputAnalyzer(procBuilder.start());
+        return analyzer;
+    }
+
+    private static void verifyCollection(String name, boolean expectBefore, boolean expectDuring, boolean expectAfter, String data) {
+        CollectionInfo ci = CollectionInfo.parseFirst(name, data);
+        Asserts.assertTrue(ci != null, "Expected GC not found: " + name + "\n" + data);
+
+        // Verify Before
+        verifyType(ci, expectBefore, VERIFY_BEFORE);
+        // Verify During
+        verifyType(ci, expectDuring, VERIFY_DURING);
+        // Verify After
+        verifyType(ci, expectAfter, VERIFY_AFTER);
+    }
+
+    private static void verifyType(CollectionInfo ci, boolean shouldExist, String pattern) {
+        if (shouldExist) {
+            Asserts.assertTrue(ci.containsVerification(pattern), "Missing expected verification for: " + ci.getName());
+        } else {
+            Asserts.assertFalse(ci.containsVerification(pattern), "Found unexpected verification for: " + ci.getName());
+        }
+    }
+
+    public static class CollectionInfo {
+        String name;
+        ArrayList<String> verification;
+        public CollectionInfo(String name) {
+            this.name = name;
+            this.verification = new ArrayList<>();
+            System.out.println("Created CollectionInfo: " + name);
+        }
+
+        public String getName() {
+            return name;
+        }
+
+        public void addVerification(String verify) {
+            System.out.println("Adding: " + verify);
+            verification.add(verify);
+        }
+
+        public boolean containsVerification(String contains) {
+            for (String entry : verification) {
+                if (entry.contains(contains)) {
+                    return true;
+                }
+            }
+            return false;
+        }
+
+        static CollectionInfo parseFirst(String name, String data) {
+            CollectionInfo result = null;
+            int firstIndex = data.indexOf(name);
+            if (firstIndex == -1) {
+                return result;
+            }
+            int nextIndex = data.indexOf(name, firstIndex + 1);
+            if (nextIndex == -1) {
+                return result;
+            }
+            // Found an entry for this name
+            result = new CollectionInfo(name);
+            String collectionData = data.substring(firstIndex, nextIndex + name.length());
+            for (String line : collectionData.split(System.getProperty("line.separator"))) {
+                if (line.contains(VERIFY_TAG)) {
+                    result.addVerification(line);
+                }
+            }
+            return result;
+        }
+    }
+
+    public static class TriggerGCs {
+        public static void main(String args[]) throws Exception {
+            WhiteBox wb = WhiteBox.getWhiteBox();
+            // Allocate some memory that can be turned into garbage.
+            Object[] used = alloc1M();
+
+            // Trigger the different GCs using the WhiteBox API.
+            wb.fullGC();  // full
+
+            // Memory have been promoted to old by full GC. Free
+            // some memory to be reclaimed by concurrent cycle.
+            partialFree(used);
+            wb.g1StartConcMarkCycle(); // initial-mark, remark and cleanup
+
+            // Sleep to make sure concurrent cycle is done
+            while (wb.g1InConcurrentMark()) {
+                Thread.sleep(1000);
+            }
+
+            // Trigger two young GCs, first will be young-only, second will be mixed.
+            wb.youngGC(); // young-only
+            wb.youngGC(); // mixed
+        }
+
+        private static Object[] alloc1M() {
+            Object[] ret = new Object[1024];
+            // Alloc 1024 1k byte arrays (~1M)
+            for (int i = 0; i < ret.length; i++) {
+                ret[i] = new byte[1024];
+            }
+            return ret;
+        }
+
+        private static void partialFree(Object[] array) {
+            // Free every other element
+            for (int i = 0; i < array.length; i+=2) {
+                array[i] = null;
+            }
+        }
+    }
+}
diff --git a/test/hotspot/jtreg/gc/metaspace/TestMetaspacePerfCounters.java b/test/hotspot/jtreg/gc/metaspace/TestMetaspacePerfCounters.java
index f648be9f484..24c3bb0e8c8 100644
--- a/test/hotspot/jtreg/gc/metaspace/TestMetaspacePerfCounters.java
+++ b/test/hotspot/jtreg/gc/metaspace/TestMetaspacePerfCounters.java
@@ -32,11 +32,13 @@ import jdk.test.lib.Platform;
 import sun.management.ManagementFactoryHelper;
 
 import static jdk.test.lib.Asserts.*;
+import gc.testlibrary.PerfCounter;
+import gc.testlibrary.PerfCounters;
 
 /* @test TestMetaspacePerfCounters
  * @bug 8014659
  * @requires vm.gc=="null"
- * @library /test/lib
+ * @library /test/lib /
  * @summary Tests that performance counters for metaspace and compressed class
  *          space exists and works.
  * @modules java.base/jdk.internal.misc
diff --git a/test/hotspot/jtreg/gc/metaspace/TestPerfCountersAndMemoryPools.java b/test/hotspot/jtreg/gc/metaspace/TestPerfCountersAndMemoryPools.java
index a59e33f7f6a..56d1c002696 100644
--- a/test/hotspot/jtreg/gc/metaspace/TestPerfCountersAndMemoryPools.java
+++ b/test/hotspot/jtreg/gc/metaspace/TestPerfCountersAndMemoryPools.java
@@ -26,10 +26,12 @@ import java.lang.management.*;
 
 import jdk.test.lib.Platform;
 import static jdk.test.lib.Asserts.*;
+import gc.testlibrary.PerfCounter;
+import gc.testlibrary.PerfCounters;
 
 /* @test TestPerfCountersAndMemoryPools
  * @bug 8023476
- * @library /test/lib
+ * @library /test/lib /
  * @requires vm.gc.Serial
  * @summary Tests that a MemoryPoolMXBeans and PerfCounters for metaspace
  *          report the same data.
diff --git a/test/hotspot/jtreg/gc/stress/gcbasher/TestGCBasherWithAllocateHeapAt.java b/test/hotspot/jtreg/gc/stress/gcbasher/TestGCBasherWithAllocateHeapAt.java
new file mode 100644
index 00000000000..8cd540eb402
--- /dev/null
+++ b/test/hotspot/jtreg/gc/stress/gcbasher/TestGCBasherWithAllocateHeapAt.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+import java.io.IOException;
+
+/*
+ * @test TestGCBasherWithAllocateHeapAt
+ * @key gc
+ * @key stress
+ * @requires vm.gc.G1
+ * @requires vm.flavor == "server" & !vm.emulatedClient
+ * @summary Stress Java heap allocation with AllocateHeapAt flag using GC basher.
+ * @run main/othervm/timeout=500 -Xlog:gc*=info -Xmx256m -server -XX:+UseG1GC -XX:AllocateHeapAt=. TestGCBasherWithAllocateHeapAt 120000
+ */
+public class TestGCBasherWithAllocateHeapAt {
+    public static void main(String[] args) throws IOException {
+        TestGCBasher.main(args);
+    }
+}
diff --git a/test/hotspot/jtreg/gc/metaspace/PerfCounter.java b/test/hotspot/jtreg/gc/testlibrary/PerfCounter.java
similarity index 98%
rename from test/hotspot/jtreg/gc/metaspace/PerfCounter.java
rename to test/hotspot/jtreg/gc/testlibrary/PerfCounter.java
index d39277c910e..2cafcdfb9fc 100644
--- a/test/hotspot/jtreg/gc/metaspace/PerfCounter.java
+++ b/test/hotspot/jtreg/gc/testlibrary/PerfCounter.java
@@ -21,6 +21,8 @@
  * questions.
  */
 
+package gc.testlibrary;
+
 import sun.jvmstat.monitor.Monitor;
 
 /**
diff --git a/test/hotspot/jtreg/gc/metaspace/PerfCounters.java b/test/hotspot/jtreg/gc/testlibrary/PerfCounters.java
similarity index 99%
rename from test/hotspot/jtreg/gc/metaspace/PerfCounters.java
rename to test/hotspot/jtreg/gc/testlibrary/PerfCounters.java
index c1242b1bee5..b6e6a1f1da1 100644
--- a/test/hotspot/jtreg/gc/metaspace/PerfCounters.java
+++ b/test/hotspot/jtreg/gc/testlibrary/PerfCounters.java
@@ -21,6 +21,8 @@
  * questions.
  */
 
+package gc.testlibrary;
+
 import sun.jvmstat.monitor.Monitor;
 import sun.jvmstat.monitor.MonitorException;
 import sun.jvmstat.monitor.MonitoredHost;
diff --git a/test/hotspot/jtreg/runtime/CommandLine/VMDeprecatedOptions.java b/test/hotspot/jtreg/runtime/CommandLine/VMDeprecatedOptions.java
index 78820042fea..bedcad2e4fe 100644
--- a/test/hotspot/jtreg/runtime/CommandLine/VMDeprecatedOptions.java
+++ b/test/hotspot/jtreg/runtime/CommandLine/VMDeprecatedOptions.java
@@ -46,6 +46,11 @@ public class VMDeprecatedOptions {
         {"MinRAMFraction",            "2"},
         {"InitialRAMFraction",        "64"},
         {"AssumeMP",                  "false"},
+        {"UseMembar",                 "true"},
+        {"FastTLABRefill",            "false"},
+        {"DeferPollingPageLoopCount", "-1"},
+        {"SafepointSpinBeforeYield",  "2000"},
+        {"DeferThrSuspendLoopCount",  "4000"},
 
         // deprecated alias flags (see also aliased_jvm_flags):
         {"DefaultMaxRAMFraction", "4"},
diff --git a/test/hotspot/jtreg/runtime/ErrorHandling/ErrorHandler.java b/test/hotspot/jtreg/runtime/ErrorHandling/ErrorHandler.java
index 26882f59d61..d820809a791 100644
--- a/test/hotspot/jtreg/runtime/ErrorHandling/ErrorHandler.java
+++ b/test/hotspot/jtreg/runtime/ErrorHandling/ErrorHandler.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2017, 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
@@ -23,6 +23,7 @@
 
 /*
  * @test
+ * @requires (vm.debug == true)
  * @bug 6888954
  * @bug 8015884
  * @summary Exercise HotSpot error handling code by invoking java with
@@ -39,6 +40,7 @@ import jdk.test.lib.process.OutputAnalyzer;
 public class ErrorHandler {
 
     public static OutputAnalyzer runTest(int testcase) throws Exception {
+        // The -XX:ErrorHandlerTest=N option requires debug bits.
         return new OutputAnalyzer(
             ProcessTools.createJavaProcessBuilder(
             "-XX:-TransmitErrorReport", "-XX:-CreateCoredumpOnCrash", "-XX:ErrorHandlerTest=" + testcase)
@@ -46,10 +48,6 @@ public class ErrorHandler {
     }
 
     public static void main(String[] args) throws Exception {
-        // Test is only applicable for debug builds
-        if (!Platform.isDebugBuild()) {
-            return;
-        }
         // Keep this in sync with hotspot/src/share/vm/utilities/debug.cpp
         int i = 1;
         String[] strings = {
@@ -69,6 +67,10 @@ public class ErrorHandler {
         String[] patterns = {
             "(SIGILL|SIGSEGV|EXCEPTION_ACCESS_VIOLATION).* at pc=",
             "(SIGBUS|SIGSEGV|SIGILL|EXCEPTION_ACCESS_VIOLATION).* at pc="
+            // -XX:ErrorHandlerTest=14 is tested by SafeFetchInErrorHandlingTest.java
+            // -XX:ErrorHandlerTest=15 is tested by SecondaryErrorTest.java
+            // -XX:ErrorHandlerTest=16 is tested by ThreadsListHandleInErrorHandlingTest.java
+            // -XX:ErrorHandlerTest=17 is tested by NestedThreadsListHandleInErrorHandlingTest.java
         };
 
         for (String s : strings) {
diff --git a/test/hotspot/jtreg/runtime/ErrorHandling/NestedThreadsListHandleInErrorHandlingTest.java b/test/hotspot/jtreg/runtime/ErrorHandling/NestedThreadsListHandleInErrorHandlingTest.java
new file mode 100644
index 00000000000..c0f2e0b7b2b
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/ErrorHandling/NestedThreadsListHandleInErrorHandlingTest.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.InputStreamReader;
+import java.util.regex.Pattern;
+
+import jdk.test.lib.process.OutputAnalyzer;
+import jdk.test.lib.Platform;
+import jdk.test.lib.process.ProcessTools;
+
+/*
+ * @test
+ * @requires (vm.debug == true)
+ * @bug 8167108
+ * @summary Nested ThreadsListHandle info should be in error handling output.
+ * @modules java.base/jdk.internal.misc
+ * @library /test/lib
+ * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+EnableThreadSMRStatistics NestedThreadsListHandleInErrorHandlingTest
+ */
+
+/*
+ * This test was created using SafeFetchInErrorHandlingTest.java
+ * as a guide.
+ */
+public class NestedThreadsListHandleInErrorHandlingTest {
+  public static void main(String[] args) throws Exception {
+
+    // The -XX:ErrorHandlerTest=N option requires debug bits.
+    ProcessBuilder pb = ProcessTools.createJavaProcessBuilder(
+        "-XX:+UnlockDiagnosticVMOptions",
+        "-Xmx100M",
+        "-XX:ErrorHandlerTest=17",
+        "-XX:-CreateCoredumpOnCrash",
+        "-version");
+
+    OutputAnalyzer output_detail = new OutputAnalyzer(pb.start());
+
+    // We should have crashed with a specific fatal error:
+    output_detail.shouldMatch("# A fatal error has been detected by the Java Runtime Environment:.*");
+    System.out.println("Found fatal error header.");
+    output_detail.shouldMatch("# +fatal error: Force crash with a nested ThreadsListHandle.");
+    System.out.println("Found specific fatal error.");
+
+    // Extract hs_err_pid file.
+    String hs_err_file = output_detail.firstMatch("# *(\\S*hs_err_pid\\d+\\.log)", 1);
+    if (hs_err_file == null) {
+        throw new RuntimeException("Did not find hs_err_pid file in output.\n");
+    }
+
+    File f = new File(hs_err_file);
+    if (!f.exists()) {
+        throw new RuntimeException("hs_err_pid file missing at "
+                                   + f.getAbsolutePath() + ".\n");
+    }
+
+    System.out.println("Found hs_err_pid file. Scanning...");
+
+    FileInputStream fis = new FileInputStream(f);
+    BufferedReader br = new BufferedReader(new InputStreamReader(fis));
+    String line = null;
+
+    Pattern [] pattern = new Pattern[] {
+        // The "Current thread" line should show a hazard ptr and
+        // a nested hazard ptr:
+        Pattern.compile("Current thread .* _threads_hazard_ptr=0x[0-9A-Fa-f][0-9A-Fa-f]*, _nested_threads_hazard_ptr_cnt=1, _nested_threads_hazard_ptrs=0x.*"),
+        // We should have a section of Threads class SMR info:
+        Pattern.compile("Threads class SMR info:"),
+        // We should have one nested ThreadsListHandle:
+        Pattern.compile(".*, _smr_nested_thread_list_max=1"),
+        // The current thread (marked with '=>') in the threads list
+        // should show a hazard ptr:
+        Pattern.compile("=>.* JavaThread \"main\" .*_threads_hazard_ptr=0x[0-9A-Fa-f][0-9A-Fa-f]*, _nested_threads_hazard_ptr_cnt=1, _nested_threads_hazard_ptrs=0x.*"),
+    };
+    int currentPattern = 0;
+
+    String lastLine = null;
+    while ((line = br.readLine()) != null) {
+        if (currentPattern < pattern.length) {
+            if (pattern[currentPattern].matcher(line).matches()) {
+                System.out.println("Found: " + line + ".");
+                currentPattern++;
+            }
+        }
+        lastLine = line;
+    }
+    br.close();
+
+    if (currentPattern < pattern.length) {
+        throw new RuntimeException("hs_err_pid file incomplete (first missing pattern: " +  currentPattern + ")");
+    }
+
+    if (!lastLine.equals("END.")) {
+        throw new RuntimeException("hs-err file incomplete (missing END marker.)");
+    } else {
+        System.out.println("End marker found.");
+    }
+
+    System.out.println("Done scanning hs_err_pid_file.");
+    System.out.println("PASSED.");
+  }
+}
diff --git a/test/hotspot/jtreg/runtime/ErrorHandling/ThreadsListHandleInErrorHandlingTest.java b/test/hotspot/jtreg/runtime/ErrorHandling/ThreadsListHandleInErrorHandlingTest.java
new file mode 100644
index 00000000000..52d9851a5bb
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/ErrorHandling/ThreadsListHandleInErrorHandlingTest.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.InputStreamReader;
+import java.util.regex.Pattern;
+
+import jdk.test.lib.process.OutputAnalyzer;
+import jdk.test.lib.Platform;
+import jdk.test.lib.process.ProcessTools;
+
+/*
+ * @test
+ * @requires (vm.debug == true)
+ * @bug 8167108
+ * @summary ThreadsListHandle info should be in error handling output.
+ * @modules java.base/jdk.internal.misc
+ * @library /test/lib
+ * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+EnableThreadSMRStatistics ThreadsListHandleInErrorHandlingTest
+ */
+
+/*
+ * This test was created using SafeFetchInErrorHandlingTest.java
+ * as a guide.
+ */
+public class ThreadsListHandleInErrorHandlingTest {
+  public static void main(String[] args) throws Exception {
+
+    // The -XX:ErrorHandlerTest=N option requires debug bits.
+    ProcessBuilder pb = ProcessTools.createJavaProcessBuilder(
+        "-XX:+UnlockDiagnosticVMOptions",
+        "-Xmx100M",
+        "-XX:ErrorHandlerTest=16",
+        "-XX:-CreateCoredumpOnCrash",
+        "-version");
+
+    OutputAnalyzer output_detail = new OutputAnalyzer(pb.start());
+
+    // We should have crashed with a specific fatal error:
+    output_detail.shouldMatch("# A fatal error has been detected by the Java Runtime Environment:.*");
+    System.out.println("Found fatal error header.");
+    output_detail.shouldMatch("# +fatal error: Force crash with an active ThreadsListHandle.");
+    System.out.println("Found specific fatal error.");
+
+    // Extract hs_err_pid file.
+    String hs_err_file = output_detail.firstMatch("# *(\\S*hs_err_pid\\d+\\.log)", 1);
+    if (hs_err_file == null) {
+        throw new RuntimeException("Did not find hs_err_pid file in output.\n");
+    }
+
+    File f = new File(hs_err_file);
+    if (!f.exists()) {
+        throw new RuntimeException("hs_err_pid file missing at "
+                                   + f.getAbsolutePath() + ".\n");
+    }
+
+    System.out.println("Found hs_err_pid file. Scanning...");
+
+    FileInputStream fis = new FileInputStream(f);
+    BufferedReader br = new BufferedReader(new InputStreamReader(fis));
+    String line = null;
+
+    Pattern [] pattern = new Pattern[] {
+        // The "Current thread" line should show a hazard ptr:
+        Pattern.compile("Current thread .* _threads_hazard_ptr=0x.*"),
+        // We should have a section of Threads class SMR info:
+        Pattern.compile("Threads class SMR info:"),
+        // The current thread (marked with '=>') in the threads list
+        // should show a hazard ptr:
+        Pattern.compile("=>.* JavaThread \"main\" .*_threads_hazard_ptr=0x.*"),
+    };
+    int currentPattern = 0;
+
+    String lastLine = null;
+    while ((line = br.readLine()) != null) {
+        if (currentPattern < pattern.length) {
+            if (pattern[currentPattern].matcher(line).matches()) {
+                System.out.println("Found: " + line + ".");
+                currentPattern++;
+            }
+        }
+        lastLine = line;
+    }
+    br.close();
+
+    if (currentPattern < pattern.length) {
+        throw new RuntimeException("hs_err_pid file incomplete (first missing pattern: " +  currentPattern + ")");
+    }
+
+    if (!lastLine.equals("END.")) {
+        throw new RuntimeException("hs-err file incomplete (missing END marker.)");
+    } else {
+        System.out.println("End marker found.");
+    }
+
+    System.out.println("Done scanning hs_err_pid_file.");
+    System.out.println("PASSED.");
+  }
+}
diff --git a/test/hotspot/jtreg/runtime/SharedArchiveFile/BootAppendTests.java b/test/hotspot/jtreg/runtime/SharedArchiveFile/BootAppendTests.java
index c6614fa4d31..9cf2534d485 100644
--- a/test/hotspot/jtreg/runtime/SharedArchiveFile/BootAppendTests.java
+++ b/test/hotspot/jtreg/runtime/SharedArchiveFile/BootAppendTests.java
@@ -86,6 +86,9 @@ public class BootAppendTests {
 
         logTestCase("5");
         testBootAppendClass();
+
+        logTestCase("6");
+        testBootAppendExtraDir();
     }
 
     private static void logTestCase(String msg) {
@@ -241,4 +244,28 @@ public class BootAppendTests {
             }
         }
     }
+
+    // Test #6: This is similar to Test #5. During runtime, an extra dir
+    //          is appended to the bootclasspath. It should not invalidate
+    //          the shared archive.
+    public static void testBootAppendExtraDir() throws Exception {
+        for (String mode : modes) {
+            CDSOptions opts = (new CDSOptions())
+                .setXShareMode(mode).setUseVersion(false)
+                .addPrefix("-Xbootclasspath/a:" + bootAppendJar + File.pathSeparator + appJar,
+                           "-showversion", "--limit-modules=java.base", "-cp", appJar)
+                .addSuffix("-Xlog:class+load=info",
+                           APP_CLASS, BOOT_APPEND_CLASS_NAME);
+
+            OutputAnalyzer out = CDSTestUtils.runWithArchive(opts);
+            CDSTestUtils.checkExec(out, opts, "[class,load] nonjdk.myPackage.MyClass");
+
+            // If CDS is enabled, the nonjdk.myPackage.MyClass should be loaded
+            // from the shared archive.
+            if (mode.equals("on")) {
+                CDSTestUtils.checkExec(out, opts,
+                    "[class,load] nonjdk.myPackage.MyClass source: shared objects file");
+            }
+        }
+    }
 }
diff --git a/test/hotspot/jtreg/runtime/Thread/CountStackFramesAtExit.java b/test/hotspot/jtreg/runtime/Thread/CountStackFramesAtExit.java
new file mode 100644
index 00000000000..af9f6453836
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/Thread/CountStackFramesAtExit.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/**
+ * @test
+ * @bug 8167108
+ * @summary Stress test java.lang.Thread.countStackFrames() at thread exit.
+ * @run main/othervm -Xlog:thread+smr=debug CountStackFramesAtExit
+ */
+
+import java.util.concurrent.CountDownLatch;
+
+public class CountStackFramesAtExit extends Thread {
+    final static int N_THREADS = 32;
+    final static int N_LATE_CALLS = 1000;
+
+    public CountDownLatch exitSyncObj = new CountDownLatch(1);
+    public CountDownLatch startSyncObj = new CountDownLatch(1);
+
+    @Override
+    public void run() {
+        // Tell main thread we have started.
+        startSyncObj.countDown();
+        try {
+            // Wait for main thread to interrupt us so we
+            // can race to exit.
+            exitSyncObj.await();
+        } catch (InterruptedException e) {
+            // ignore because we expect one
+        }
+    }
+
+    public static void main(String[] args) {
+        CountStackFramesAtExit threads[] = new CountStackFramesAtExit[N_THREADS];
+
+        for (int i = 0; i < N_THREADS; i++ ) {
+            threads[i] = new CountStackFramesAtExit();
+            int late_count = 1;
+            threads[i].start();
+            try {
+                // Wait for the worker thread to get going.
+                threads[i].startSyncObj.await();
+
+                // This interrupt() call will break the worker out of
+                // the exitSyncObj.await() call and the countStackFrames()
+                // calls will come in during thread exit.
+                threads[i].interrupt();
+                for (; late_count <= N_LATE_CALLS; late_count++) {
+                    try {
+                        threads[i].countStackFrames();
+                    } catch (IllegalThreadStateException itse) {
+                        // ignore because we expect it
+                    }
+
+                    if (!threads[i].isAlive()) {
+                        // Done with Thread.countStackFrames() calls since
+                        // thread is not alive.
+                        break;
+                    }
+                }
+            } catch (InterruptedException e) {
+                throw new Error("Unexpected: " + e);
+            }
+
+            System.out.println("INFO: thread #" + i + ": made " + late_count +
+                               " late calls to java.lang.Thread.countStackFrames()");
+            System.out.println("INFO: thread #" + i + ": N_LATE_CALLS==" +
+                               N_LATE_CALLS + " value is " +
+                               ((late_count >= N_LATE_CALLS) ? "NOT " : "") +
+                               "large enough to cause a Thread.countStackFrames() " +
+                               "call after thread exit.");
+
+            try {
+                threads[i].join();
+            } catch (InterruptedException e) {
+                throw new Error("Unexpected: " + e);
+            }
+            threads[i].countStackFrames();
+            if (threads[i].isAlive()) {
+                throw new Error("Expected !Thread.isAlive() after thread #" +
+                                i + " has been join()'ed");
+            }
+        }
+
+        String cmd = System.getProperty("sun.java.command");
+        if (cmd != null && !cmd.startsWith("com.sun.javatest.regtest.agent.MainWrapper")) {
+            // Exit with success in a non-JavaTest environment:
+            System.exit(0);
+        }
+    }
+}
diff --git a/test/hotspot/jtreg/runtime/Thread/InterruptAtExit.java b/test/hotspot/jtreg/runtime/Thread/InterruptAtExit.java
new file mode 100644
index 00000000000..e230bf4ef61
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/Thread/InterruptAtExit.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/**
+ * @test
+ * @bug 8167108
+ * @summary Stress test java.lang.Thread.interrupt() at thread exit.
+ * @run main/othervm -Xlog:thread+smr=debug InterruptAtExit
+ */
+
+import java.util.concurrent.CountDownLatch;
+
+public class InterruptAtExit extends Thread {
+    final static int N_THREADS = 32;
+    final static int N_LATE_CALLS = 1000;
+
+    public CountDownLatch exitSyncObj = new CountDownLatch(1);
+    public CountDownLatch startSyncObj = new CountDownLatch(1);
+
+    @Override
+    public void run() {
+        // Tell main thread we have started.
+        startSyncObj.countDown();
+        try {
+            // Wait for main thread to interrupt us so we
+            // can race to exit.
+            exitSyncObj.await();
+        } catch (InterruptedException e) {
+            // ignore because we expect one
+        }
+    }
+
+    public static void main(String[] args) {
+        InterruptAtExit threads[] = new InterruptAtExit[N_THREADS];
+
+        for (int i = 0; i < N_THREADS; i++ ) {
+            threads[i] = new InterruptAtExit();
+            int late_count = 1;
+            threads[i].start();
+            try {
+                // Wait for the worker thread to get going.
+                threads[i].startSyncObj.await();
+
+                // The first interrupt() call will break the
+                // worker out of the exitSyncObj.await() call
+                // and the rest will come in during thread exit.
+                for (; late_count <= N_LATE_CALLS; late_count++) {
+                    threads[i].interrupt();
+
+                    if (!threads[i].isAlive()) {
+                        // Done with Thread.interrupt() calls since
+                        // thread is not alive.
+                        break;
+                    }
+                }
+            } catch (InterruptedException e) {
+                throw new Error("Unexpected: " + e);
+            }
+
+            System.out.println("INFO: thread #" + i + ": made " + late_count +
+                               " late calls to java.lang.Thread.interrupt()");
+            System.out.println("INFO: thread #" + i + ": N_LATE_CALLS==" +
+                               N_LATE_CALLS + " value is " +
+                               ((late_count >= N_LATE_CALLS) ? "NOT " : "") +
+                               "large enough to cause a Thread.interrupt() " +
+                               "call after thread exit.");
+
+            try {
+                threads[i].join();
+            } catch (InterruptedException e) {
+                throw new Error("Unexpected: " + e);
+            }
+            threads[i].interrupt();
+            if (threads[i].isAlive()) {
+                throw new Error("Expected !Thread.isAlive() after thread #" +
+                                i + " has been join()'ed");
+            }
+        }
+
+        String cmd = System.getProperty("sun.java.command");
+        if (cmd != null && !cmd.startsWith("com.sun.javatest.regtest.agent.MainWrapper")) {
+            // Exit with success in a non-JavaTest environment:
+            System.exit(0);
+        }
+    }
+}
diff --git a/test/hotspot/jtreg/runtime/Thread/IsInterruptedAtExit.java b/test/hotspot/jtreg/runtime/Thread/IsInterruptedAtExit.java
new file mode 100644
index 00000000000..b775636e0a9
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/Thread/IsInterruptedAtExit.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/**
+ * @test
+ * @bug 8167108
+ * @summary Stress test java.lang.Thread.isInterrupted() at thread exit.
+ * @run main/othervm -Xlog:thread+smr=debug IsInterruptedAtExit
+ */
+
+import java.util.concurrent.CountDownLatch;
+
+public class IsInterruptedAtExit extends Thread {
+    final static int N_THREADS = 32;
+    final static int N_LATE_CALLS = 2000;
+
+    public CountDownLatch exitSyncObj = new CountDownLatch(1);
+    public CountDownLatch startSyncObj = new CountDownLatch(1);
+
+    @Override
+    public void run() {
+        // Tell main thread we have started.
+        startSyncObj.countDown();
+        try {
+            // Wait for main thread to interrupt us so we
+            // can race to exit.
+            exitSyncObj.await();
+        } catch (InterruptedException e) {
+            // ignore because we expect one
+        }
+    }
+
+    public static void main(String[] args) {
+        IsInterruptedAtExit threads[] = new IsInterruptedAtExit[N_THREADS];
+
+        for (int i = 0; i < N_THREADS; i++ ) {
+            threads[i] = new IsInterruptedAtExit();
+            int late_count = 1;
+            threads[i].start();
+            try {
+                // Wait for the worker thread to get going.
+                threads[i].startSyncObj.await();
+
+                // This interrupt() call will break the worker out of
+                // the exitSyncObj.await() call and the isInterrupted()
+                // calls will come in during thread exit.
+                threads[i].interrupt();
+                for (; late_count <= N_LATE_CALLS; late_count++) {
+                    threads[i].isInterrupted();
+
+                    if (!threads[i].isAlive()) {
+                        // Done with Thread.isInterrupted() calls since
+                        // thread is not alive.
+                        break;
+                    }
+                }
+            } catch (InterruptedException e) {
+                throw new Error("Unexpected: " + e);
+            }
+
+            System.out.println("INFO: thread #" + i + ": made " + late_count +
+                               " late calls to java.lang.Thread.isInterrupted()");
+            System.out.println("INFO: thread #" + i + ": N_LATE_CALLS==" +
+                               N_LATE_CALLS + " value is " +
+                               ((late_count >= N_LATE_CALLS) ? "NOT " : "") +
+                               "large enough to cause a Thread.isInterrupted() " +
+                               "call after thread exit.");
+
+            try {
+                threads[i].join();
+            } catch (InterruptedException e) {
+                throw new Error("Unexpected: " + e);
+            }
+            threads[i].isInterrupted();
+            if (threads[i].isAlive()) {
+                throw new Error("Expected !Thread.isAlive() after thread #" +
+                                i + " has been join()'ed");
+            }
+        }
+
+        String cmd = System.getProperty("sun.java.command");
+        if (cmd != null && !cmd.startsWith("com.sun.javatest.regtest.agent.MainWrapper")) {
+            // Exit with success in a non-JavaTest environment:
+            System.exit(0);
+        }
+    }
+}
diff --git a/test/hotspot/jtreg/runtime/Thread/ResumeAtExit.java b/test/hotspot/jtreg/runtime/Thread/ResumeAtExit.java
new file mode 100644
index 00000000000..d8a0dbc9f9b
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/Thread/ResumeAtExit.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/**
+ * @test
+ * @bug 8167108
+ * @summary Stress test java.lang.Thread.resume() at thread exit.
+ * @run main/othervm -Xlog:thread+smr=debug ResumeAtExit
+ */
+
+import java.util.concurrent.CountDownLatch;
+
+public class ResumeAtExit extends Thread {
+    final static int N_THREADS = 32;
+    final static int N_LATE_CALLS = 2000;
+
+    public CountDownLatch exitSyncObj = new CountDownLatch(1);
+    public CountDownLatch startSyncObj = new CountDownLatch(1);
+
+    @Override
+    public void run() {
+        // Tell main thread we have started.
+        startSyncObj.countDown();
+        try {
+            // Wait for main thread to interrupt us so we
+            // can race to exit.
+            exitSyncObj.await();
+        } catch (InterruptedException e) {
+            // ignore because we expect one
+        }
+    }
+
+    public static void main(String[] args) {
+        ResumeAtExit threads[] = new ResumeAtExit[N_THREADS];
+
+        for (int i = 0; i < N_THREADS; i++ ) {
+            threads[i] = new ResumeAtExit();
+            int late_count = 1;
+            threads[i].start();
+            try {
+                // Wait for the worker thread to get going.
+                threads[i].startSyncObj.await();
+
+                // This interrupt() call will break the worker out
+                // of the exitSyncObj.await() call and the resume()
+                // calls will come in during thread exit.
+                threads[i].interrupt();
+                for (; late_count <= N_LATE_CALLS; late_count++) {
+                    threads[i].resume();
+
+                    if (!threads[i].isAlive()) {
+                        // Done with Thread.resume() calls since
+                        // thread is not alive.
+                        break;
+                    }
+                }
+            } catch (InterruptedException e) {
+                throw new Error("Unexpected: " + e);
+            }
+
+            System.out.println("INFO: thread #" + i + ": made " + late_count +
+                               " late calls to java.lang.Thread.resume()");
+            System.out.println("INFO: thread #" + i + ": N_LATE_CALLS==" +
+                               N_LATE_CALLS + " value is " +
+                               ((late_count >= N_LATE_CALLS) ? "NOT " : "") +
+                               "large enough to cause a Thread.resume() " +
+                               "call after thread exit.");
+
+            try {
+                threads[i].join();
+            } catch (InterruptedException e) {
+                throw new Error("Unexpected: " + e);
+            }
+            threads[i].resume();
+            if (threads[i].isAlive()) {
+                throw new Error("Expected !Thread.isAlive() after thread #" +
+                                i + " has been join()'ed");
+            }
+        }
+
+        String cmd = System.getProperty("sun.java.command");
+        if (cmd != null && !cmd.startsWith("com.sun.javatest.regtest.agent.MainWrapper")) {
+            // Exit with success in a non-JavaTest environment:
+            System.exit(0);
+        }
+    }
+}
diff --git a/test/hotspot/jtreg/runtime/Thread/SetNameAtExit.java b/test/hotspot/jtreg/runtime/Thread/SetNameAtExit.java
new file mode 100644
index 00000000000..f6dd8c72cc9
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/Thread/SetNameAtExit.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/**
+ * @test
+ * @bug 8167108
+ * @summary Stress test java.lang.Thread.setName() at thread exit.
+ * @run main/othervm -Xlog:thread+smr=debug SetNameAtExit
+ */
+
+import java.util.concurrent.CountDownLatch;
+
+public class SetNameAtExit extends Thread {
+    final static int N_THREADS = 32;
+    final static int N_LATE_CALLS = 1000;
+
+    public CountDownLatch exitSyncObj = new CountDownLatch(1);
+    public CountDownLatch startSyncObj = new CountDownLatch(1);
+
+    @Override
+    public void run() {
+        // Tell main thread we have started.
+        startSyncObj.countDown();
+        try {
+            // Wait for main thread to interrupt us so we
+            // can race to exit.
+            exitSyncObj.await();
+        } catch (InterruptedException e) {
+            // ignore because we expect one
+        }
+    }
+
+    public static void main(String[] args) {
+        SetNameAtExit threads[] = new SetNameAtExit[N_THREADS];
+
+        for (int i = 0; i < N_THREADS; i++ ) {
+            threads[i] = new SetNameAtExit();
+            int late_count = 1;
+            threads[i].start();
+            try {
+                // Wait for the worker thread to get going.
+                threads[i].startSyncObj.await();
+
+                // This interrupt() call will break the worker out
+                // of the exitSyncObj.await() call and the setName()
+                // calls will come in during thread exit.
+                threads[i].interrupt();
+                for (; late_count <= N_LATE_CALLS; late_count++) {
+                    threads[i].setName("T" + i + "-" + late_count);
+
+                    if (!threads[i].isAlive()) {
+                        // Done with Thread.setName() calls since
+                        // thread is not alive.
+                        break;
+                    }
+                }
+            } catch (InterruptedException e) {
+                throw new Error("Unexpected: " + e);
+            }
+
+            System.out.println("INFO: thread #" + i + ": made " + late_count +
+                               " late calls to java.lang.Thread.setName()");
+            System.out.println("INFO: thread #" + i + ": N_LATE_CALLS==" +
+                               N_LATE_CALLS + " value is " +
+                               ((late_count >= N_LATE_CALLS) ? "NOT " : "") +
+                               "large enough to cause a Thread.setName() " +
+                               "call after thread exit.");
+
+            try {
+                threads[i].join();
+            } catch (InterruptedException e) {
+                throw new Error("Unexpected: " + e);
+            }
+            threads[i].setName("T" + i + "-done");
+            if (threads[i].isAlive()) {
+                throw new Error("Expected !Thread.isAlive() after thread #" +
+                                i + " has been join()'ed");
+            }
+        }
+
+        String cmd = System.getProperty("sun.java.command");
+        if (cmd != null && !cmd.startsWith("com.sun.javatest.regtest.agent.MainWrapper")) {
+            // Exit with success in a non-JavaTest environment:
+            System.exit(0);
+        }
+    }
+}
diff --git a/test/hotspot/jtreg/runtime/Thread/SetPriorityAtExit.java b/test/hotspot/jtreg/runtime/Thread/SetPriorityAtExit.java
new file mode 100644
index 00000000000..68589056417
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/Thread/SetPriorityAtExit.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/**
+ * @test
+ * @bug 8167108
+ * @summary Stress test java.lang.Thread.setPriority() at thread exit.
+ * @run main/othervm -Xlog:thread+smr=debug SetPriorityAtExit
+ */
+
+import java.util.concurrent.CountDownLatch;
+
+public class SetPriorityAtExit extends Thread {
+    final static int N_THREADS = 32;
+    final static int N_LATE_CALLS = 2000;
+
+    final static int MIN = java.lang.Thread.MIN_PRIORITY;
+    final static int NORM = java.lang.Thread.NORM_PRIORITY;
+
+    public CountDownLatch exitSyncObj = new CountDownLatch(1);
+    public CountDownLatch startSyncObj = new CountDownLatch(1);
+
+    @Override
+    public void run() {
+        // Tell main thread we have started.
+        startSyncObj.countDown();
+        try {
+            // Wait for main thread to interrupt us so we
+            // can race to exit.
+            exitSyncObj.await();
+        } catch (InterruptedException e) {
+            // ignore because we expect one
+        }
+    }
+
+    public static void main(String[] args) {
+        SetPriorityAtExit threads[] = new SetPriorityAtExit[N_THREADS];
+
+        int prio = MIN;
+        for (int i = 0; i < N_THREADS; i++ ) {
+            threads[i] = new SetPriorityAtExit();
+            int late_count = 1;
+            threads[i].start();
+            try {
+                // Wait for the worker thread to get going.
+                threads[i].startSyncObj.await();
+
+                // This interrupt() call will break the worker out of
+                // the exitSyncObj.await() call and the setPriority()
+                // calls will come in during thread exit.
+                threads[i].interrupt();
+                for (; late_count <= N_LATE_CALLS; late_count++) {
+                    threads[i].setPriority(prio);
+                    if (prio == MIN) {
+                        prio = NORM;
+                    } else {
+                        prio = MIN;
+                    }
+
+                    if (!threads[i].isAlive()) {
+                        // Done with Thread.setPriority() calls since
+                        // thread is not alive.
+                        break;
+                    }
+                }
+            } catch (InterruptedException e) {
+                throw new Error("Unexpected: " + e);
+            }
+
+            System.out.println("INFO: thread #" + i + ": made " + late_count +
+                               " late calls to java.lang.Thread.setPriority()");
+            System.out.println("INFO: thread #" + i + ": N_LATE_CALLS==" +
+                               N_LATE_CALLS + " value is " +
+                               ((late_count >= N_LATE_CALLS) ? "NOT " : "") +
+                               "large enough to cause a Thread.setPriority() " +
+                               "call after thread exit.");
+
+            try {
+                threads[i].join();
+            } catch (InterruptedException e) {
+                throw new Error("Unexpected: " + e);
+            }
+            threads[i].setPriority(prio);
+            if (threads[i].isAlive()) {
+                throw new Error("Expected !Thread.isAlive() after thread #" +
+                                i + " has been join()'ed");
+            }
+        }
+
+        String cmd = System.getProperty("sun.java.command");
+        if (cmd != null && !cmd.startsWith("com.sun.javatest.regtest.agent.MainWrapper")) {
+            // Exit with success in a non-JavaTest environment:
+            System.exit(0);
+        }
+    }
+}
diff --git a/test/hotspot/jtreg/runtime/Thread/StopAtExit.java b/test/hotspot/jtreg/runtime/Thread/StopAtExit.java
new file mode 100644
index 00000000000..3d4306a2da3
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/Thread/StopAtExit.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/**
+ * @test
+ * @bug 8167108
+ * @summary Stress test java.lang.Thread.stop() at thread exit.
+ * @run main/othervm -Xlog:thread+smr=debug StopAtExit
+ */
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+public class StopAtExit extends Thread {
+    final static int N_THREADS = 32;
+    final static int N_LATE_CALLS = 1000;
+
+    public CountDownLatch exitSyncObj = new CountDownLatch(1);
+    public CountDownLatch startSyncObj = new CountDownLatch(1);
+
+    @Override
+    public void run() {
+        try {
+            // Tell main thread we have started.
+            startSyncObj.countDown();
+            try {
+                // Wait for main thread to interrupt us so we
+                // can race to exit.
+                exitSyncObj.await();
+            } catch (InterruptedException e) {
+                // ignore because we expect one
+            }
+        } catch (ThreadDeath td) {
+            // ignore because we're testing Thread.stop() which throws it
+        } catch (NoClassDefFoundError ncdfe) {
+            // ignore because we're testing Thread.stop() which can cause it
+        }
+    }
+
+    public static void main(String[] args) {
+        StopAtExit threads[] = new StopAtExit[N_THREADS];
+
+        for (int i = 0; i < N_THREADS; i++ ) {
+            threads[i] = new StopAtExit();
+            int late_count = 1;
+            threads[i].start();
+            try {
+                // Wait for the worker thread to get going.
+                threads[i].startSyncObj.await();
+
+                // This interrupt() call will break the worker out
+                // of the exitSyncObj.await() call and the stop()
+                // calls will come in during thread exit.
+                threads[i].interrupt();
+                for (; late_count <= N_LATE_CALLS; late_count++) {
+                    threads[i].stop();
+
+                    if (!threads[i].isAlive()) {
+                        // Done with Thread.stop() calls since
+                        // thread is not alive.
+                        break;
+                    }
+                }
+            } catch (InterruptedException e) {
+                throw new Error("Unexpected: " + e);
+            } catch (NoClassDefFoundError ncdfe) {
+                // Ignore because we're testing Thread.stop() which can
+                // cause it. Yes, a NoClassDefFoundError that happens
+                // in a worker thread can subsequently be seen in the
+                // main thread.
+            }
+
+            System.out.println("INFO: thread #" + i + ": made " + late_count +
+                               " late calls to java.lang.Thread.stop()");
+            System.out.println("INFO: thread #" + i + ": N_LATE_CALLS==" +
+                               N_LATE_CALLS + " value is " +
+                               ((late_count >= N_LATE_CALLS) ? "NOT " : "") +
+                               "large enough to cause a Thread.stop() " +
+                               "call after thread exit.");
+
+            try {
+                threads[i].join();
+            } catch (InterruptedException e) {
+                throw new Error("Unexpected: " + e);
+            }
+            threads[i].stop();
+            if (threads[i].isAlive()) {
+                throw new Error("Expected !Thread.isAlive() after thread #" +
+                                i + " has been join()'ed");
+            }
+        }
+
+        String cmd = System.getProperty("sun.java.command");
+        if (cmd != null && !cmd.startsWith("com.sun.javatest.regtest.agent.MainWrapper")) {
+            // Exit with success in a non-JavaTest environment:
+            System.exit(0);
+        }
+    }
+}
diff --git a/test/hotspot/jtreg/runtime/Thread/SuspendAtExit.java b/test/hotspot/jtreg/runtime/Thread/SuspendAtExit.java
new file mode 100644
index 00000000000..05c1fb4dd2a
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/Thread/SuspendAtExit.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/**
+ * @test
+ * @bug 8167108
+ * @summary Stress test java.lang.Thread.suspend() at thread exit.
+ * @run main/othervm -Xlog:thread+smr=debug SuspendAtExit
+ */
+
+import java.util.concurrent.CountDownLatch;
+
+public class SuspendAtExit extends Thread {
+    final static int N_THREADS = 32;
+    final static int N_LATE_CALLS = 10000;
+
+    public CountDownLatch exitSyncObj = new CountDownLatch(1);
+    public CountDownLatch startSyncObj = new CountDownLatch(1);
+
+    @Override
+    public void run() {
+        // Tell main thread we have started.
+        startSyncObj.countDown();
+        try {
+            // Wait for main thread to interrupt us so we
+            // can race to exit.
+            exitSyncObj.await();
+        } catch (InterruptedException e) {
+            // ignore because we expect one
+        }
+    }
+
+    public static void main(String[] args) {
+        SuspendAtExit threads[] = new SuspendAtExit[N_THREADS];
+
+        for (int i = 0; i < N_THREADS; i++ ) {
+            threads[i] = new SuspendAtExit();
+            int late_count = 1;
+            threads[i].start();
+            try {
+                // Wait for the worker thread to get going.
+                threads[i].startSyncObj.await();
+
+                // This interrupt() call will break the worker out
+                // of the exitSyncObj.await() call and the suspend()
+                // calls will come in during thread exit.
+                threads[i].interrupt();
+                for (; late_count <= N_LATE_CALLS; late_count++) {
+                    threads[i].suspend();
+
+                    if (!threads[i].isAlive()) {
+                        // Done with Thread.suspend() calls since
+                        // thread is not alive.
+                        break;
+                    }
+                    threads[i].resume();
+                }
+            } catch (InterruptedException e) {
+                throw new Error("Unexpected: " + e);
+            }
+
+            System.out.println("INFO: thread #" + i + ": made " + late_count +
+                               " late calls to java.lang.Thread.suspend()");
+            System.out.println("INFO: thread #" + i + ": N_LATE_CALLS==" +
+                               N_LATE_CALLS + " value is " +
+                               ((late_count >= N_LATE_CALLS) ? "NOT " : "") +
+                               "large enough to cause a Thread.suspend() " +
+                               "call after thread exit.");
+
+            try {
+                threads[i].join();
+            } catch (InterruptedException e) {
+                throw new Error("Unexpected: " + e);
+            }
+            threads[i].suspend();
+            threads[i].resume();
+            if (threads[i].isAlive()) {
+                throw new Error("Expected !Thread.isAlive() after thread #" +
+                                i + " has been join()'ed");
+            }
+        }
+
+        String cmd = System.getProperty("sun.java.command");
+        if (cmd != null && !cmd.startsWith("com.sun.javatest.regtest.agent.MainWrapper")) {
+            // Exit with success in a non-JavaTest environment:
+            System.exit(0);
+        }
+    }
+}
diff --git a/test/hotspot/jtreg/runtime/Thread/TestThreadDumpSMRInfo.java b/test/hotspot/jtreg/runtime/Thread/TestThreadDumpSMRInfo.java
new file mode 100644
index 00000000000..02b673bc299
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/Thread/TestThreadDumpSMRInfo.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug     8167108
+ * @summary Checks whether jstack reports a "Threads class SMR info" section.
+ *
+ * @library /test/lib
+ * @modules java.base/jdk.internal.misc
+ *          java.management
+ * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+EnableThreadSMRStatistics TestThreadDumpSMRInfo
+ */
+
+import jdk.test.lib.process.OutputAnalyzer;
+import jdk.test.lib.JDKToolFinder;
+
+public class TestThreadDumpSMRInfo {
+    // jstack tends to be closely bound to the VM that we are running
+    // so use getTestJDKTool() instead of getCompileJDKTool() or even
+    // getJDKTool() which can fall back to "compile.jdk".
+    final static String JSTACK = JDKToolFinder.getTestJDKTool("jstack");
+    final static String PID = "" + ProcessHandle.current().pid();
+
+    // Here's a sample "Threads class SMR info" section:
+    //
+    // Threads class SMR info:
+    // _smr_java_thread_list=0x0000000000ce8da0, length=23, elements={
+    // 0x000000000043a800, 0x0000000000aee800, 0x0000000000b06800, 0x0000000000b26000,
+    // 0x0000000000b28800, 0x0000000000b2b000, 0x0000000000b2e000, 0x0000000000b30000,
+    // 0x0000000000b32800, 0x0000000000b35000, 0x0000000000b3f000, 0x0000000000b41800,
+    // 0x0000000000b44000, 0x0000000000b46800, 0x0000000000b48800, 0x0000000000b53000,
+    // 0x0000000000b55800, 0x0000000000b57800, 0x0000000000b5a000, 0x0000000000b5c800,
+    // 0x0000000000cc8800, 0x0000000000fd9800, 0x0000000000ef4800
+    // }
+    // _smr_java_thread_list_alloc_cnt=24, _smr_java_thread_list_free_cnt=23, _smr_java_thread_list_max=23, _smr_nested_thread_list_max=0
+    // _smr_delete_lock_wait_cnt=0, _smr_delete_lock_wait_max=0
+    // _smr_to_delete_list_cnt=0, _smr_to_delete_list_max=1
+
+    final static String HEADER_STR = "Threads class SMR info:";
+
+    static boolean verbose = false;
+
+    public static void main(String[] args) throws Exception {
+        if (args.length != 0) {
+            int arg_i = 0;
+            if (args[arg_i].equals("-v")) {
+                verbose = true;
+                arg_i++;
+            }
+        }
+
+        ProcessBuilder pb = new ProcessBuilder(JSTACK, PID);
+        OutputAnalyzer output = new OutputAnalyzer(pb.start());
+
+        if (verbose) {
+            System.out.println("stdout: " + output.getStdout());
+        }
+
+        output.shouldHaveExitValue(0);
+        System.out.println("INFO: jstack ran successfully.");
+
+        output.shouldContain(HEADER_STR);
+        System.out.println("INFO: Found: '" + HEADER_STR + "' in jstack output.");
+
+        System.out.println("Test PASSED.");
+    }
+
+    static void usage() {
+        System.err.println("Usage: java TestThreadDumpSMRInfo [-v]");
+        System.exit(1);
+    }
+}
diff --git a/test/hotspot/jtreg/runtime/appcds/AppCDSOptions.java b/test/hotspot/jtreg/runtime/appcds/AppCDSOptions.java
new file mode 100644
index 00000000000..6db82a771ab
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/AppCDSOptions.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+import jdk.test.lib.cds.CDSOptions;
+
+// This class represents options used for
+// during creation of the archive and/or running JVM with archive
+
+public class AppCDSOptions extends CDSOptions {
+    public String appJar;
+
+    // Application classes to be archived
+    public String[] appClasses;
+
+    public AppCDSOptions setAppJar(String appJar) {
+        this.appJar = appJar;
+        return this;
+    }
+
+    public AppCDSOptions setAppClasses(String[] appClasses) {
+        this.appClasses = appClasses;
+        return this;
+    }
+
+}
diff --git a/test/hotspot/jtreg/runtime/appcds/AppendClasspath.java b/test/hotspot/jtreg/runtime/appcds/AppendClasspath.java
new file mode 100644
index 00000000000..a59d1f3753b
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/AppendClasspath.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+/*
+ * @test
+ * @summary At run time, it is OK to append new elements to the classpath that was used at dump time.
+ * AppCDS does not support uncompressed oops
+ * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true)
+ * @library /test/lib
+ * @modules java.base/jdk.internal.misc
+ *          java.management
+ *          jdk.jartool/sun.tools.jar
+ * @compile test-classes/Hello.java
+ * @compile test-classes/HelloMore.java
+ * @run main AppendClasspath
+ */
+
+import java.io.File;
+import jdk.test.lib.process.OutputAnalyzer;
+
+public class AppendClasspath {
+
+  public static void main(String[] args) throws Exception {
+    String appJar = JarBuilder.getOrCreateHelloJar();
+    String appJar2 = JarBuilder.build("AppendClasspath_HelloMore", "HelloMore");
+
+    // Dump an archive with a specified JAR file in -classpath
+    TestCommon.testDump(appJar, TestCommon.list("Hello"));
+
+    // PASS: 1) runtime with classpath containing the one used in dump time
+    OutputAnalyzer output = TestCommon.execCommon(
+        "-cp", appJar + File.pathSeparator + appJar2,
+        "HelloMore");
+    TestCommon.checkExec(output);
+
+    final String errorMessage1 = "Unable to use shared archive";
+    final String errorMessage2 = "shared class paths mismatch";
+    // FAIL: 2) runtime with classpath different from the one used in dump time
+    // (runtime has an extra jar file prepended to the class path)
+    output = TestCommon.execCommon(
+        "-cp", appJar2 + File.pathSeparator + appJar,
+        "HelloMore");
+    output.shouldContain(errorMessage1);
+    output.shouldContain(errorMessage2);
+    output.shouldHaveExitValue(1);
+
+    // FAIL: 3) runtime with classpath part of the one used in dump time
+    TestCommon.testDump(appJar + File.pathSeparator + appJar2,
+                                      TestCommon.list("Hello"));
+    output = TestCommon.execCommon(
+        "-cp", appJar2,
+        "Hello");
+    output.shouldContain(errorMessage1);
+    output.shouldContain(errorMessage2);
+    output.shouldHaveExitValue(1);
+
+    // FAIL: 4) runtime with same set of jar files in the classpath but
+    // with different order
+    output = TestCommon.execCommon(
+        "-cp", appJar2 + File.pathSeparator + appJar,
+        "HelloMore");
+    output.shouldContain(errorMessage1);
+    output.shouldContain(errorMessage2);
+    output.shouldHaveExitValue(1);
+  }
+}
diff --git a/test/hotspot/jtreg/runtime/appcds/BootClassPathMismatch.java b/test/hotspot/jtreg/runtime/appcds/BootClassPathMismatch.java
new file mode 100644
index 00000000000..4e2bbdbce49
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/BootClassPathMismatch.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+/*
+ * @test
+ * @summary bootclasspath mismatch test.
+ * AppCDS does not support uncompressed oops
+ * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true)
+ * @library /test/lib
+ * @modules java.base/jdk.internal.misc
+ *          java.management
+ *          jdk.jartool/sun.tools.jar
+ * @compile test-classes/Hello.java
+ * @run main BootClassPathMismatch
+ */
+
+import jdk.test.lib.process.OutputAnalyzer;
+import java.io.File;
+import java.nio.file.Files;
+import java.nio.file.FileAlreadyExistsException;
+import java.nio.file.StandardCopyOption;
+import java.nio.file.Paths;
+
+
+public class BootClassPathMismatch {
+    private static final String mismatchMessage = "shared class paths mismatch";
+
+    public static void main(String[] args) throws Exception {
+        JarBuilder.getOrCreateHelloJar();
+        copyHelloToNewDir();
+
+        BootClassPathMismatch test = new BootClassPathMismatch();
+        test.testBootClassPathMismatch();
+        test.testBootClassPathMismatch2();
+        test.testBootClassPathMatch();
+    }
+
+    /* Error should be detected if:
+     * dump time: -Xbootclasspath/a:${testdir}/hello.jar
+     * run-time : -Xbootclasspath/a:${testdir}/newdir/hello.jar
+     */
+    public void testBootClassPathMismatch() throws Exception {
+        String appJar = JarBuilder.getOrCreateHelloJar();
+        String appClasses[] = {"Hello"};
+        OutputAnalyzer dumpOutput = TestCommon.dump(
+            appJar, appClasses, "-Xbootclasspath/a:" + appJar);
+        String testDir = TestCommon.getTestDir("newdir");
+        String otherJar = testDir + File.separator + "hello.jar";
+        OutputAnalyzer execOutput = TestCommon.exec(
+            appJar, "-verbose:class", "-Xbootclasspath/a:" + otherJar, "Hello");
+        try {
+            TestCommon.checkExec(execOutput, mismatchMessage);
+        } catch (java.lang.RuntimeException re) {
+          String cause = re.getMessage();
+          if (!mismatchMessage.equals(cause)) {
+              throw re;
+          }
+        }
+    }
+
+    /* Error should be detected if:
+     * dump time: <no bootclasspath specified>
+     * run-time : -Xbootclasspath/a:${testdir}/hello.jar
+     */
+    public void testBootClassPathMismatch2() throws Exception {
+        String appJar = JarBuilder.getOrCreateHelloJar();
+        String appClasses[] = {"Hello"};
+        OutputAnalyzer dumpOutput = TestCommon.dump(appJar, appClasses);
+        OutputAnalyzer execOutput = TestCommon.exec(
+            appJar, "-verbose:class", "-Xbootclasspath/a:" + appJar, "Hello");
+        try {
+            TestCommon.checkExec(execOutput, mismatchMessage);
+        } catch (java.lang.RuntimeException re) {
+          String cause = re.getMessage();
+          if (!mismatchMessage.equals(cause)) {
+              throw re;
+          }
+        }
+    }
+
+    /* No error if:
+     * dump time: -Xbootclasspath/a:${testdir}/hello.jar
+     * run-time : -Xbootclasspath/a:${testdir}/hello.jar
+     */
+    public void testBootClassPathMatch() throws Exception {
+        String appJar = TestCommon.getTestJar("hello.jar");
+        String appClasses[] = {"Hello"};
+        OutputAnalyzer dumpOutput = TestCommon.dump(
+            appJar, appClasses, "-Xbootclasspath/a:" + appJar);
+        OutputAnalyzer execOutput = TestCommon.exec(
+            appJar, "-verbose:class",
+            "-Xbootclasspath/a:" + appJar, "Hello");
+        TestCommon.checkExec(execOutput,
+                "[class,load] Hello source: shared objects file");
+    }
+
+    private static void copyHelloToNewDir() throws Exception {
+        String classDir = System.getProperty("test.classes");
+        String dstDir = classDir + File.separator + "newdir";
+        try {
+            Files.createDirectory(Paths.get(dstDir));
+        } catch (FileAlreadyExistsException e) { }
+
+        Files.copy(Paths.get(classDir, "hello.jar"),
+            Paths.get(dstDir, "hello.jar"),
+            StandardCopyOption.REPLACE_EXISTING);
+    }
+}
diff --git a/test/hotspot/jtreg/runtime/appcds/CaseSensitiveClassPath.java b/test/hotspot/jtreg/runtime/appcds/CaseSensitiveClassPath.java
new file mode 100644
index 00000000000..56315c16d64
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/CaseSensitiveClassPath.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+
+/*
+ * @test
+ * @summary Test case sensitive aspect of comparing class paths
+ *     between dump time and archive use time
+ * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true)
+ * @library /test/lib
+ * @modules java.base/jdk.internal.misc
+ *          java.management
+ *          jdk.jartool/sun.tools.jar
+ * @requires os.family != "mac"
+ * @compile test-classes/Hello.java
+ * @run main CaseSensitiveClassPath
+ */
+
+import java.nio.file.FileAlreadyExistsException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.nio.file.StandardCopyOption;
+import jdk.test.lib.Platform;
+import jdk.test.lib.process.OutputAnalyzer;
+
+
+// Excluded from running on MAC: a more comprehensive case sensitivity detection
+// and fix mechanism is needed, which is planned to be implemented in the future.
+public class CaseSensitiveClassPath {
+    public static void main(String[] args) throws Exception {
+        String appJar = JarBuilder.getOrCreateHelloJar();
+        String appJarUpper = appJar.replace("hello", "Hello");
+
+        OutputAnalyzer out = TestCommon.dump(appJar, TestCommon.list("Hello"));
+        TestCommon.checkDump(out);
+
+        Path jarPath = Paths.get(appJar);
+        Path jarPathUpper = null;
+
+        boolean fileExists = false;
+        try {
+            jarPathUpper = Files.createFile(Paths.get(appJarUpper));
+        } catch (FileAlreadyExistsException faee) {
+            fileExists = true;
+        }
+
+        if (!fileExists) {
+            try {
+                Files.copy(jarPath, jarPathUpper, StandardCopyOption.REPLACE_EXISTING);
+            } catch (Exception e) {
+                throw new java.lang.RuntimeException(
+                    "Failed copying file from " + appJar + " to " + appJarUpper + ".", e);
+            }
+        } else {
+            jarPathUpper = Paths.get(appJarUpper);
+        }
+
+        out = TestCommon.exec(appJarUpper, "Hello", "-Xlog:class+path=info",
+                              "-Xlog:cds");
+        if (TestCommon.isUnableToMap(out))
+            return;
+
+        if (Files.isSameFile(jarPath, jarPathUpper)) {
+            TestCommon.checkExec(out, "Hello World");
+        } else {
+            out.shouldContain("shared class paths mismatch")
+                .shouldHaveExitValue(1);
+        }
+   }
+}
diff --git a/test/hotspot/jtreg/runtime/appcds/ClassLoaderTest.java b/test/hotspot/jtreg/runtime/appcds/ClassLoaderTest.java
new file mode 100644
index 00000000000..8012a1de39a
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/ClassLoaderTest.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+/*
+ * @test
+ * @summary Initiating and defining classloader test.
+ * AppCDS does not support uncompressed oops
+ * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true)
+ * @library /test/lib
+ * @modules java.base/jdk.internal.misc
+ *          java.management
+ *          jdk.jartool/sun.tools.jar
+ * @compile test-classes/Hello.java
+ * @compile test-classes/HelloWB.java
+ * @compile test-classes/ForNameTest.java
+ * @compile test-classes/BootClassPathAppendHelper.java
+ * @build sun.hotspot.WhiteBox
+ * @run main ClassFileInstaller sun.hotspot.WhiteBox
+ * @run main ClassLoaderTest
+ */
+
+import java.io.File;
+import jdk.test.lib.process.OutputAnalyzer;
+
+public class ClassLoaderTest {
+    public static void main(String[] args) throws Exception {
+        JarBuilder.build(true, "ClassLoaderTest-WhiteBox", "sun/hotspot/WhiteBox");
+        JarBuilder.getOrCreateHelloJar();
+        JarBuilder.build("ClassLoaderTest-HelloWB", "HelloWB");
+        JarBuilder.build("ClassLoaderTest-ForName", "ForNameTest");
+        ClassLoaderTest test = new ClassLoaderTest();
+        test.testBootLoader();
+        test.testDefiningLoader();
+    }
+
+    public void testBootLoader() throws Exception {
+        String appJar = TestCommon.getTestJar("ClassLoaderTest-HelloWB.jar");
+        String appClasses[] = {"HelloWB"};
+        String whiteBoxJar = TestCommon.getTestJar("ClassLoaderTest-WhiteBox.jar");
+        String bootClassPath = "-Xbootclasspath/a:" + appJar +
+            File.pathSeparator + whiteBoxJar;
+
+        TestCommon.dump(appJar, appClasses, bootClassPath);
+
+        OutputAnalyzer runtimeOutput = TestCommon.execCommon(
+            "-XX:+UnlockDiagnosticVMOptions", "-XX:+WhiteBoxAPI",
+            "-cp", appJar, bootClassPath, "-Xlog:class+load", "HelloWB");
+
+        if (!TestCommon.isUnableToMap(runtimeOutput)) {
+            runtimeOutput.shouldNotContain(
+                "[class,load] HelloWB source: shared objects file by jdk/internal/misc/ClassLoaders$AppClassLoader");
+            runtimeOutput.shouldContain("[class,load] HelloWB source: shared objects file");
+        }
+    }
+
+    public void testDefiningLoader() throws Exception {
+        // The boot loader should be used to load the class when it's
+        // on the bootclasspath, regardless who is the initiating classloader.
+        // In this test case, the AppClassLoader is the initiating classloader.
+        String helloJar = TestCommon.getTestJar("hello.jar");
+        String appJar = helloJar + System.getProperty("path.separator") +
+                        TestCommon.getTestJar("ClassLoaderTest-ForName.jar");
+        String whiteBoxJar = TestCommon.getTestJar("ClassLoaderTest-WhiteBox.jar");
+        String bootClassPath = "-Xbootclasspath/a:" + helloJar +
+            File.pathSeparator + whiteBoxJar;
+
+        TestCommon.dump(helloJar, TestCommon.list("Hello"), bootClassPath);
+
+        TestCommon.execCommon("-XX:+UnlockDiagnosticVMOptions", "-XX:+WhiteBoxAPI",
+            "-cp", appJar, bootClassPath, "-XX:+TraceClassPaths", "ForNameTest");
+    }
+}
diff --git a/test/hotspot/jtreg/runtime/appcds/ClassPathAttr.java b/test/hotspot/jtreg/runtime/appcds/ClassPathAttr.java
new file mode 100644
index 00000000000..d32f7b8f2be
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/ClassPathAttr.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+/*
+ * @test
+ * @summary Class-Path: attribute in MANIFEST file
+ * AppCDS does not support uncompressed oops
+ * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true)
+ * @library /test/lib
+ * @modules java.base/jdk.internal.misc
+ *          java.management
+ *          jdk.jartool/sun.tools.jar
+ * @run main ClassPathAttr
+ */
+
+import jdk.test.lib.process.OutputAnalyzer;
+import java.io.File;
+import java.nio.file.Files;
+import java.nio.file.FileAlreadyExistsException;
+import java.nio.file.StandardCopyOption;
+import java.nio.file.Paths;
+
+
+public class ClassPathAttr {
+
+  public static void main(String[] args) throws Exception {
+    buildCpAttr("cpattr1", "cpattr1.mf", "CpAttr1", "CpAttr1");
+    buildCpAttr("cpattr1_long", "cpattr1_long.mf", "CpAttr1", "CpAttr1");
+    buildCpAttr("cpattr2", "cpattr2.mf", "CpAttr2", "CpAttr2");
+    buildCpAttr("cpattr3", "cpattr3.mf", "CpAttr3", "CpAttr2", "CpAttr3");
+    buildCpAttr("cpattr4", "cpattr4.mf", "CpAttr4",
+        "CpAttr2", "CpAttr3", "CpAttr4", "CpAttr5");
+    buildCpAttr("cpattr5_123456789_223456789_323456789_423456789_523456789_623456789", "cpattr5_extra_long.mf", "CpAttr5", "CpAttr5");
+
+    for (int i=1; i<=2; i++) {
+      String jar1 = TestCommon.getTestJar("cpattr1.jar");
+      String jar4 = TestCommon.getTestJar("cpattr4.jar");
+      if (i == 2) {
+        // Test case #2 -- same as #1, except we use cpattr1_long.jar, which has a super-long
+        // Class-Path: attribute.
+        jar1 = TestCommon.getTestJar("cpattr1_long.jar");
+      }
+      String cp = jar1 + File.pathSeparator + jar4;
+
+      TestCommon.testDump(cp, TestCommon.list("CpAttr1",
+                                                          "CpAttr2",
+                                                          "CpAttr3",
+                                                          "CpAttr4",
+                                                          "CpAttr5"));
+
+      OutputAnalyzer output = TestCommon.execCommon(
+          "-cp", cp,
+          "CpAttr1");
+      TestCommon.checkExec(output);
+
+      // Logging test for class+path.
+      output = TestCommon.execCommon(
+          "-Xlog:class+path",
+          "-cp", cp,
+          "CpAttr1");
+      if (!TestCommon.isUnableToMap(output)){
+        output.shouldMatch("checking shared classpath entry: .*cpattr2.jar");
+        output.shouldMatch("checking shared classpath entry: .*cpattr3.jar");
+      }
+      //  Make sure aliased TraceClassPaths still works
+      output = TestCommon.execCommon(
+          "-XX:+TraceClassPaths",
+          "-cp", cp,
+          "CpAttr1");
+      if (!TestCommon.isUnableToMap(output)){
+        output.shouldMatch("checking shared classpath entry: .*cpattr2.jar");
+        output.shouldMatch("checking shared classpath entry: .*cpattr3.jar");
+      }
+    }
+  }
+
+  private static void buildCpAttr(String jarName, String manifest, String enclosingClassName, String ...testClassNames) throws Exception {
+    String jarClassesDir = System.getProperty("test.classes") + File.separator + jarName + "_classes";
+    try { Files.createDirectory(Paths.get(jarClassesDir)); } catch (FileAlreadyExistsException e) { }
+
+    JarBuilder.compile(jarClassesDir, System.getProperty("test.src") + File.separator +
+        "test-classes" + File.separator + enclosingClassName + ".java");
+    JarBuilder.buildWithManifest(jarName, manifest, jarClassesDir, testClassNames);
+  }
+}
diff --git a/test/hotspot/jtreg/runtime/appcds/CommandLineFlagCombo.java b/test/hotspot/jtreg/runtime/appcds/CommandLineFlagCombo.java
new file mode 100644
index 00000000000..8f1fc68b81b
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/CommandLineFlagCombo.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+/*
+ * @test CommandLineFlagCombo
+ * AppCDS does not support uncompressed oops
+ * @requires (vm.gc=="null") & ((vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true))
+ * @summary Test command line flag combinations that
+ *          could likely affect the behaviour of AppCDS
+ * @library /test/lib
+ * @modules java.base/jdk.internal.misc
+ *          java.management
+ *          jdk.jartool/sun.tools.jar
+ * @compile test-classes/Hello.java
+ * @run main/timeout=240 CommandLineFlagCombo
+ */
+
+import jdk.test.lib.BuildHelper;
+import jdk.test.lib.Platform;
+import jdk.test.lib.process.OutputAnalyzer;
+
+public class CommandLineFlagCombo {
+
+    // shared base address test table
+    private static final String[] testTable = {
+        "-XX:+UseG1GC", "-XX:+UseSerialGC", "-XX:+UseParallelGC", "-XX:+UseConcMarkSweepGC",
+        "-XX:+FlightRecorder",
+        "-XX:+UseLargePages", // may only take effect on machines with large-pages
+        "-XX:+UseCompressedClassPointers",
+        "-XX:+UseCompressedOops",
+        "-XX:ObjectAlignmentInBytes=16",
+        "-XX:ObjectAlignmentInBytes=32",
+        "-XX:ObjectAlignmentInBytes=64"
+    };
+
+    public static void main(String[] args) throws Exception {
+        String appJar = JarBuilder.getOrCreateHelloJar();
+        String classList[] = {"Hello"};
+
+        for (String testEntry : testTable) {
+            System.out.println("CommandLineFlagCombo = " + testEntry);
+
+            if (skipTestCase(testEntry))
+                continue;
+
+            OutputAnalyzer dumpOutput;
+
+            if (testEntry.equals("-XX:+FlightRecorder")) {
+                dumpOutput = TestCommon.dump(appJar, classList, "-XX:+UnlockCommercialFeatures", testEntry);
+            } else {
+                dumpOutput = TestCommon.dump(appJar, classList, testEntry);
+            }
+
+            TestCommon.checkDump(dumpOutput, "Loading classes to share");
+
+            OutputAnalyzer execOutput;
+            if (testEntry.equals("-XX:+FlightRecorder")) {
+                execOutput = TestCommon.exec(appJar, "-XX:+UnlockCommercialFeatures", testEntry, "Hello");
+            } else {
+                execOutput = TestCommon.exec(appJar, testEntry, "Hello");
+            }
+            TestCommon.checkExec(execOutput, "Hello World");
+        }
+
+        for (int i=0; i<2; i++) {
+            String g1Flag, serialFlag;
+
+            // Interned strings are supported only with G1GC. However, we should not crash if:
+            // 0: archive has shared strings, but run time doesn't support shared strings
+            // 1: archive has no shared strings, but run time supports shared strings
+
+            String dump_g1Flag     = "-XX:" + (i == 0 ? "+" : "-") + "UseG1GC";
+            String run_g1Flag      = "-XX:" + (i != 0 ? "+" : "-") + "UseG1GC";
+            String dump_serialFlag = "-XX:" + (i != 0 ? "+" : "-") + "UseSerialGC";
+            String run_serialFlag  = "-XX:" + (i == 0 ? "+" : "-") + "UseSerialGC";
+
+            OutputAnalyzer dumpOutput = TestCommon.dump(
+               appJar, classList, dump_g1Flag, dump_serialFlag);
+
+            TestCommon.checkDump(dumpOutput, "Loading classes to share");
+
+            OutputAnalyzer execOutput = TestCommon.exec(appJar, run_g1Flag, run_serialFlag, "Hello");
+            TestCommon.checkExec(execOutput, "Hello World");
+        }
+    }
+
+    private static boolean skipTestCase(String testEntry) throws Exception {
+        if (Platform.is32bit())
+        {
+            if (testEntry.equals("-XX:+UseCompressedOops") ||
+                testEntry.equals("-XX:+UseCompressedClassPointers") ||
+                testEntry.contains("ObjectAlignmentInBytes") )
+            {
+                System.out.println("Test case not applicable on 32-bit platforms");
+                return true;
+            }
+        }
+
+        if (!BuildHelper.isCommercialBuild() && testEntry.equals("-XX:+FlightRecorder"))
+        {
+            System.out.println("Test case not applicable on non-commercial builds");
+            return true;
+        }
+
+        return false;
+    }
+}
diff --git a/test/hotspot/jtreg/runtime/appcds/CommandLineFlagComboNegative.java b/test/hotspot/jtreg/runtime/appcds/CommandLineFlagComboNegative.java
new file mode 100644
index 00000000000..75effb9926c
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/CommandLineFlagComboNegative.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+/*
+ * @test CommandLineFlagComboNegative
+ * @summary Test command line flag combinations that differ between
+ *          the dump and execute steps, in such way that they cause errors
+ *          E.g. use compressed oops for creating and archive, but then
+ *               execute w/o compressed oops
+ * AppCDS does not support uncompressed oops
+ * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true)
+ * @library /test/lib
+ * @modules java.base/jdk.internal.misc
+ *          java.management
+ *          jdk.jartool/sun.tools.jar
+ * @compile test-classes/Hello.java
+ * @run main CommandLineFlagComboNegative
+ */
+
+import java.util.ArrayList;
+import jdk.test.lib.Platform;
+import jdk.test.lib.process.OutputAnalyzer;
+
+public class CommandLineFlagComboNegative {
+
+    private class TestVector {
+        public String testOptionForDumpStep;
+        public String testOptionForExecuteStep;
+        public String expectedErrorMsg;
+        public int expectedErrorCode;
+
+        public TestVector(String testOptionForDumpStep, String testOptionForExecuteStep,
+                          String expectedErrorMsg, int expectedErrorCode) {
+            this.testOptionForDumpStep=testOptionForDumpStep;
+            this.testOptionForExecuteStep=testOptionForExecuteStep;
+            this.expectedErrorMsg=expectedErrorMsg;
+            this.expectedErrorCode=expectedErrorCode;
+        }
+    }
+
+    private ArrayList<TestVector> testTable = new ArrayList<TestVector>();
+
+    private void initTestTable() {
+        // These options are not applicable on 32-bit platforms
+        if (Platform.is64bit()) {
+            testTable.add( new TestVector("-XX:ObjectAlignmentInBytes=8", "-XX:ObjectAlignmentInBytes=16",
+                "An error has occurred while processing the shared archive file", 1) );
+            testTable.add( new TestVector("-XX:ObjectAlignmentInBytes=64", "-XX:ObjectAlignmentInBytes=32",
+                "An error has occurred while processing the shared archive file", 1) );
+            testTable.add( new TestVector("-XX:+UseCompressedOops", "-XX:-UseCompressedOops",
+                "Class data sharing is inconsistent with other specified options", 1) );
+            testTable.add( new TestVector("-XX:+UseCompressedClassPointers", "-XX:-UseCompressedClassPointers",
+                "Class data sharing is inconsistent with other specified options", 1) );
+        }
+    }
+
+    private void runTests() throws Exception
+    {
+        for (TestVector testEntry : testTable) {
+            System.out.println("CommandLineFlagComboNegative: dump = " + testEntry.testOptionForDumpStep);
+            System.out.println("CommandLineFlagComboNegative: execute = " + testEntry.testOptionForExecuteStep);
+
+            String appJar = JarBuilder.getOrCreateHelloJar();
+            OutputAnalyzer dumpOutput = TestCommon.dump(
+               appJar, new String[] {"Hello"}, testEntry.testOptionForDumpStep);
+
+            TestCommon.checkDump(dumpOutput, "Loading classes to share");
+
+            OutputAnalyzer execOutput = TestCommon.exec(appJar, testEntry.testOptionForExecuteStep, "Hello");
+            execOutput.shouldContain(testEntry.expectedErrorMsg);
+            execOutput.shouldHaveExitValue(testEntry.expectedErrorCode);
+        }
+    }
+
+    public static void main(String[] args) throws Exception {
+        CommandLineFlagComboNegative thisClass = new CommandLineFlagComboNegative();
+        thisClass.initTestTable();
+        thisClass.runTests();
+    }
+}
diff --git a/test/hotspot/jtreg/runtime/appcds/CompilerUtils.java b/test/hotspot/jtreg/runtime/appcds/CompilerUtils.java
new file mode 100644
index 00000000000..cfb5d20ba5c
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/CompilerUtils.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+import javax.tools.JavaCompiler;
+import javax.tools.StandardJavaFileManager;
+import javax.tools.StandardLocation;
+import javax.tools.ToolProvider;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.Arrays;
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * This class consists exclusively of static utility methods for invoking the
+ * java compiler.
+ *
+ * This class will eventually move to jdk.testlibrary.
+ */
+
+public final class CompilerUtils {
+    private CompilerUtils() { }
+
+    /**
+     * Compile all the java sources in {@code <source>/**} to
+     * {@code <destination>/**}. The destination directory will be created if
+     * it doesn't exist.
+     *
+     * All warnings/errors emitted by the compiler are output to System.out/err.
+     *
+     * @return true if the compilation is successful
+     *
+     * @throws IOException if there is an I/O error scanning the source tree or
+     *                     creating the destination directory
+     */
+    public static boolean compile(Path source, Path destination, String ... options)
+        throws IOException
+    {
+        JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
+        StandardJavaFileManager jfm = compiler.getStandardFileManager(null, null, null);
+
+        List<Path> sources
+            = Files.find(source, Integer.MAX_VALUE,
+                (file, attrs) -> (file.toString().endsWith(".java")))
+                .collect(Collectors.toList());
+
+        Files.createDirectories(destination);
+        jfm.setLocationFromPaths(StandardLocation.CLASS_OUTPUT,
+                                 Arrays.asList(destination));
+
+        List<String> opts = Arrays.asList(options);
+        JavaCompiler.CompilationTask task
+            = compiler.getTask(null, jfm, null, opts, null,
+                jfm.getJavaFileObjectsFromPaths(sources));
+
+        return task.call();
+    }
+}
diff --git a/test/hotspot/jtreg/runtime/appcds/DirClasspathTest.java b/test/hotspot/jtreg/runtime/appcds/DirClasspathTest.java
new file mode 100644
index 00000000000..047f2d00080
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/DirClasspathTest.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+/*
+ * @test
+ * @summary AppCDS handling of directories in -cp
+ * AppCDS does not support uncompressed oops
+ * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true)
+ * @library /test/lib
+ * @run main DirClasspathTest
+ */
+
+import jdk.test.lib.Platform;
+import jdk.test.lib.process.OutputAnalyzer;
+import java.io.File;
+
+public class DirClasspathTest {
+    public static void main(String[] args) throws Exception {
+        File dir = new File(System.getProperty("user.dir"));
+        File emptydir = new File(dir, "emptydir");
+        emptydir.mkdir();
+
+        // Empty dir in -cp: should be OK
+        OutputAnalyzer output;
+        if (!Platform.isWindows()) {
+            // This block fails on Windows because of JDK-8192927
+            output = TestCommon.dump(emptydir.getPath(), TestCommon.list("DoesntMatter"), "-Xlog:class+path=info");
+            TestCommon.checkDump(output);
+        }
+
+        // Non-empty dir in -cp: should fail
+        // <dir> is not empty because it has at least one subdirectory, i.e., <emptydir>
+        output = TestCommon.dump(dir.getPath(), TestCommon.list("DoesntMatter"), "-Xlog:class+path=info");
+        output.shouldNotHaveExitValue(0);
+        output.shouldContain("CDS allows only empty directories in archived classpaths");
+    }
+}
diff --git a/test/hotspot/jtreg/runtime/appcds/DumpClassList.java b/test/hotspot/jtreg/runtime/appcds/DumpClassList.java
new file mode 100644
index 00000000000..4526770c2da
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/DumpClassList.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+/*
+ * @test
+ * @summary DumpLoadedClassList should exclude generated classes, classes in bootclasspath/a and
+ *          --patch-module.
+ * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true)
+ * @library /test/lib
+ * @modules java.base/jdk.internal.misc
+ *          jdk.jartool/sun.tools.jar
+ * @compile test-classes/ArrayListTest.java
+ * @run main DumpClassList
+ */
+
+import jdk.test.lib.compiler.InMemoryJavaCompiler;
+import jdk.test.lib.process.OutputAnalyzer;
+import jdk.test.lib.process.ProcessTools;
+
+public class DumpClassList {
+    public static void main(String[] args) throws Exception {
+        // build The app
+        String[] appClass = new String[] {"ArrayListTest"};
+        String classList = "app.list";
+
+        JarBuilder.build("app", appClass[0]);
+        String appJar = TestCommon.getTestJar("app.jar");
+
+        // build patch-module
+        String source = "package java.lang; "                       +
+                        "public class NewClass { "                  +
+                        "    static { "                             +
+                        "        System.out.println(\"NewClass\"); "+
+                        "    } "                                    +
+                        "}";
+
+        ClassFileInstaller.writeClassToDisk("java/lang/NewClass",
+             InMemoryJavaCompiler.compile("java.lang.NewClass", source, "--patch-module=java.base"),
+             System.getProperty("test.classes"));
+
+        String patchJar = JarBuilder.build("javabase", "java/lang/NewClass");
+
+        // build bootclasspath/a
+        String source2 = "package boot.append; "                 +
+                        "public class Foo { "                    +
+                        "    static { "                          +
+                        "        System.out.println(\"Foo\"); "  +
+                        "    } "                                 +
+                        "}";
+
+        ClassFileInstaller.writeClassToDisk("boot/append/Foo",
+             InMemoryJavaCompiler.compile("boot.append.Foo", source2),
+             System.getProperty("test.classes"));
+
+        String appendJar = JarBuilder.build("bootappend", "boot/append/Foo");
+
+        // dump class list
+        ProcessBuilder pb = ProcessTools.createJavaProcessBuilder(
+            true,
+            "-XX:DumpLoadedClassList=" + classList,
+            "--patch-module=java.base=" + patchJar,
+            "-Xbootclasspath/a:" + appendJar,
+            "-cp",
+            appJar,
+            appClass[0]);
+        OutputAnalyzer output = TestCommon.executeAndLog(pb, "dumpClassList");
+        TestCommon.checkExecReturn(output, 0, true,
+                                   "hello world",
+                                   "skip writing class java/lang/NewClass") // skip classes outside of jrt image
+            .shouldNotContain("skip writing class boot/append/Foo");        // but classes on -Xbootclasspath/a should not be skipped
+
+        output = TestCommon.createArchive(appJar, appClass,
+                                          "-Xbootclasspath/a:" + appendJar,
+                                          "-XX:+UnlockDiagnosticVMOptions",
+                                          "-Xlog:class+load",
+                                          "-XX:SharedClassListFile=" + classList);
+        TestCommon.checkDump(output)
+            .shouldNotContain("Preload Warning: Cannot find java/lang/invoke/LambdaForm")
+            .shouldNotContain("Preload Warning: Cannot find boot/append/Foo")
+            .shouldContain("[info][class,load] boot.append.Foo");
+    }
+}
diff --git a/test/hotspot/jtreg/runtime/appcds/ExtraSymbols.invalid_1.txt b/test/hotspot/jtreg/runtime/appcds/ExtraSymbols.invalid_1.txt
new file mode 100644
index 00000000000..1f4be1af531
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/ExtraSymbols.invalid_1.txt
@@ -0,0 +1,11 @@
+VERSION: 1.0
+@SECTION: Symbol
+0 -1:
+41 -1: (Ljava/util/Set<TE;>;Ljava/lang/Object;)V
+11 -1 linkMethod
+18 -1: type can't be null
+20 -1: isAlphaNumericString
+43 -1: (Ljava/lang/Class<*>;Ljava/lang/Class<*>;)Z
+1 -1: \t
+15 -1: IntCumulateTask
+1 -1: \n
diff --git a/test/hotspot/jtreg/runtime/appcds/ExtraSymbols.invalid_2.txt b/test/hotspot/jtreg/runtime/appcds/ExtraSymbols.invalid_2.txt
new file mode 100644
index 00000000000..f45450da674
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/ExtraSymbols.invalid_2.txt
@@ -0,0 +1,5 @@
+@SECTION: Symbol
+20 -1: isAlphaNumericString
+43 -1: (Ljava/lang/Class<*>;Ljava/lang/Class<*>;)Z
+15 -1: IntCumulateTask
+1 -1: \n
diff --git a/test/hotspot/jtreg/runtime/appcds/ExtraSymbols.invalid_3.txt b/test/hotspot/jtreg/runtime/appcds/ExtraSymbols.invalid_3.txt
new file mode 100644
index 00000000000..360e94b24fd
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/ExtraSymbols.invalid_3.txt
@@ -0,0 +1,13 @@
+VERSION: 1.0
+@SECTION: Symbol
+11 -1: linkMethod
+18 -1: isAlphaNumericString
+33 -1: java/util/Locale$LocaleNameGetter
+23 -1: sun/invoke/util/Wrapper
+12 -1: reduceToLong
+11 -1: setReadOnly
+8 -1: endsWith
+55 -1: <T:Ljava/lang/Object;>(Ljava/lang/ClassValue<TT;>;TT;)V
+20 -1: createAnnotationData
+6 -1: OfLong
+17 -1: getClassSignature
diff --git a/test/hotspot/jtreg/runtime/appcds/ExtraSymbols.java b/test/hotspot/jtreg/runtime/appcds/ExtraSymbols.java
new file mode 100644
index 00000000000..4fc3bb8757e
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/ExtraSymbols.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+/*
+ * @test
+ * @summary Adding extra symbols into CDS archive using -XX:SharedArchiveConfigFile
+ * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true)
+ * @library /test/lib
+ * @modules java.base/jdk.internal.misc
+ *          java.management
+ *          jdk.jartool/sun.tools.jar
+ * @compile test-classes/Hello.java
+ * @run main ExtraSymbols
+ */
+
+import java.io.*;
+import jdk.test.lib.process.OutputAnalyzer;
+
+public class ExtraSymbols {
+    public static void main(String[] args) throws Exception {
+        String appJar = JarBuilder.getOrCreateHelloJar();
+
+        // 1. Dump without extra symbols.
+        OutputAnalyzer output = TestCommon.dump(appJar, TestCommon.list("Hello"));
+        checkOutput(output);
+        int numEntries1 = numOfEntries(output);
+
+        // 2. Dump an archive with extra symbols. All symbols in
+        // ExtraSymbols.symbols.txt are valid. Dumping should succeed.
+        output = TestCommon.dump(appJar, TestCommon.list("Hello"),
+            "-XX:SharedArchiveConfigFile=" + TestCommon.getSourceFile("ExtraSymbols.symbols.txt"));
+        checkOutput(output);
+        int numEntries2 = numOfEntries(output);
+        if (numEntries2 <= numEntries1) {
+            throw new RuntimeException("No extra symbols added to archive");
+        }
+        output = TestCommon.exec(appJar, "Hello");
+        TestCommon.checkExec(output);
+
+        // 3. Dump with invalid symbol files. Dumping should fail.
+        String invalid_symbol_files[] = {"ExtraSymbols.invalid_1.txt",
+                                         "ExtraSymbols.invalid_2.txt",
+                                         "ExtraSymbols.invalid_3.txt"};
+        String err_msgs[] = {"Corrupted at line",
+                             "wrong version of hashtable dump file",
+                             "Corrupted at line"};
+        for (int i = 0; i < invalid_symbol_files.length; i++) {
+            output = TestCommon.dump(appJar, TestCommon.list("Hello"),
+                                     "-XX:SharedArchiveConfigFile=" +
+                                     TestCommon.getSourceFile(invalid_symbol_files[i]));
+            output.shouldContain("Error occurred during initialization of VM");
+            output.shouldContain(err_msgs[i]);
+        }
+    }
+
+    static int numOfEntries(OutputAnalyzer output) {
+        String s = output.firstMatch("Number of entries       : .*");
+        String subs[] = s.split("[:]");
+        int numEntries = Integer.parseInt(subs[1].trim());
+        return numEntries;
+    }
+
+    static void checkOutput(OutputAnalyzer output) throws Exception {
+        output.shouldContain("Loading classes to share");
+        output.shouldContain("Shared symbol table stats -------- base:");
+        output.shouldHaveExitValue(0);
+    }
+}
diff --git a/test/hotspot/jtreg/runtime/appcds/ExtraSymbols.symbols.txt b/test/hotspot/jtreg/runtime/appcds/ExtraSymbols.symbols.txt
new file mode 100644
index 00000000000..9a75ededb39
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/ExtraSymbols.symbols.txt
@@ -0,0 +1,10826 @@
+VERSION: 1.0
+@SECTION: Symbol
+69 -1: ------------------------------------------------------------123456789
+68 -1: # The values in this file are only used for testing the operation of
+63 -1: # adding extra symbols into the CDS archive. None of the values
+70 -1: # are interpreted in any way. So even if they contain names of classes
+70 -1: # that have been renamed or removed, or string literals that have been
+66 -1: # changed or remove from Java source code, it would not affect the
+26 -1: # correctness of the test.
+0 -1: 
+41 -1: (Ljava/util/Set<TE;>;Ljava/lang/Object;)V
+11 -1: linkMethod 
+18 -1: type can't be null
+20 -1: isAlphaNumericString
+43 -1: (Ljava/lang/Class<*>;Ljava/lang/Class<*>;)Z
+72 -1: (Ljava/lang/String;[Ljava/lang/String;Ljava/io/File;)Ljava/lang/Process;
+1 -1: \t
+15 -1: IntCumulateTask
+1 -1: \n
+33 -1: java/util/Locale$LocaleNameGetter
+23 -1: sun/invoke/util/Wrapper
+57 -1: (Ljava/io/InputStream;Ljava/nio/charset/CharsetDecoder;)V
+12 -1: reduceToLong
+11 -1: setReadOnly
+34 -1: (Ljava/lang/reflect/Executable;)[B
+54 -1: ([Ljava/net/URL;Ljava/security/AccessControlContext;)V
+15 -1: LegacyMergeSort
+8 -1: endsWith
+55 -1: <T:Ljava/lang/Object;>(Ljava/lang/ClassValue<TT;>;TT;)V
+20 -1: createAnnotationData
+6 -1: OfLong
+90 -1: <K:Ljava/lang/Object;V:Ljava/lang/Object;>(Ljava/util/Map<TK;TV;>;)Ljava/util/Map<TK;TV;>;
+1 -1:  
+17 -1: getClassSignature
+1 -1: "
+1 -1: #
+1 -1: (
+21 -1: MethodHandleImpl.java
+10 -1: getUTF8At0
+1 -1: )
+1 -1: *
+1 -1: +
+1 -1: ,
+1 -1: -
+1 -1: .
+18 -1: unsignedEntryNames
+1 -1: /
+1 -1: 0
+19 -1: java/io/InputStream
+38 -1: java/util/concurrent/ThreadLocalRandom
+1 -1: :
+1 -1: ;
+1 -1: <
+13 -1: getAndAddLong
+1 -1: =
+1 -1: >
+1 -1: ?
+20 -1: getMethodAtIfLoaded0
+1 -1: @
+1 -1: A
+7 -1: isAlive
+1 -1: B
+10 -1: checkIndex
+1 -1: C
+1 -1: D
+1 -1: E
+1 -1: F
+1 -1: I
+30 -1: sun/misc/JavaUtilZipFileAccess
+11 -1: classloader
+1 -1: J
+1 -1: L
+14 -1: packageEnabled
+8 -1: ([BIII)V
+24 -1: Ljava/io/BufferedWriter;
+1 -1: S
+32 -1: (Ljava/util/function/Consumer;)V
+11 -1: refKindName
+1 -1: U
+1 -1: V
+3 1: yyy
+18 -1: JavaNetAccess.java
+1 -1: Z
+7 -1: members
+1 -1: [
+1 -1: ]
+13 -1: ShortLanguage
+1 -1: _
+9 -1: invoke__L
+28 -1: (D)Ljava/lang/StringBuilder;
+15 -1: isInvokeSpecial
+1 -1: c
+17 -1: subListRangeCheck
+1 -1: e
+29 -1: Ljava/security/AllPermission;
+27 -1: (C)Ljava/lang/StringBuffer;
+28 -1: ([Ljava/lang/Comparable;II)V
+50 -1: (Ljava/util/zip/ZipFile;Ljava/util/zip/Inflater;)V
+9 -1: invoke__V
+1 -1: m
+101 -1: (Ljava/io/OutputStream;Ljava/lang/Object;Ljava/nio/charset/CharsetEncoder;)Lsun/nio/cs/StreamEncoder;
+13 -1: MAX_SURROGATE
+18 -1: Ljava/lang/String;
+21 -1: ensureProtectedAccess
+18 -1: getIfModifiedSince
+1 -1: r
+9 -1: setExtra0
+1 -1: s
+47 -1: Ljava/lang/Enum<Lsun/launcher/LauncherHelper;>;
+1 -1: x
+1 -1: {
+7 -1: getLast
+1 -1: |
+1 -1: }
+1 -1: ~
+71 -1: (Ljava/lang/Object;)Ljava/util/concurrent/ConcurrentHashMap$KeySetView;
+34 -1: (Ljava/nio/charset/Charset;[BII)[C
+10 -1: DST_NSHIFT
+25 -1: ForEachTransformedKeyTask
+26 -1: Ljava/nio/charset/Charset;
+56 -1: (Ljava/lang/reflect/Method;)Lsun/reflect/MethodAccessor;
+22 -1: StackTraceElement.java
+24 -1: sun.zip.zipFile.openTime
+27 -1: JNI_COPY_TO_ARRAY_THRESHOLD
+26 -1: java/lang/ClassValue$Entry
+19 -1: [Ljava/lang/Thread;
+56 -1: (Ljava/lang/ClassLoader$NativeLibrary;)Ljava/lang/Class;
+7 -1: message
+18 -1: parameterToArgSlot
+20 -1: [[Ljava/lang/String;
+11 -1: bumpVersion
+26 -1: Ljava/lang/reflect/Method;
+9 -1: getMethod
+6 -1: (I)TE;
+49 -1: (Ljava/lang/String;)Ljava/lang/invoke/MemberName;
+33 -1: sun/misc/URLClassPath$JarLoader$1
+57 -1: (BLjava/lang/Class;Ljava/lang/String;Ljava/lang/Object;)V
+33 -1: sun/misc/URLClassPath$JarLoader$2
+33 -1: sun/misc/URLClassPath$JarLoader$3
+87 -1: (ILjava/lang/Object;Ljava/lang/Object;Ljava/util/HashMap$Node;)Ljava/util/HashMap$Node;
+19 -1: FileDescriptor.java
+12 -1: forEachValue
+36 -1: (Ljava/util/List;)[Ljava/lang/Class;
+53 -1: (Ljava/lang/CharSequence;II)Ljava/lang/StringBuilder;
+8 -1: hasArray
+4 -1: ROWS
+10 -1: linkMethod
+9 -1: remaining
+23 -1: ARRAY_FLOAT_BASE_OFFSET
+35 -1: java/lang/reflect/ReflectPermission
+24 -1: ()Ljava/net/InetAddress;
+7 -1: ngroups
+81 -1: Ljava/lang/Object;Ljava/security/PrivilegedExceptionAction<Ljava/lang/Class<*>;>;
+10 -1: putTreeVal
+4 -1: list
+5 -1: trace
+7 -1: blocker
+21 -1: reset() not supported
+8 -1: JAPANESE
+11 -1: PRIVATE_USE
+53 -1: (Ljava/lang/Class<*>;)Ljava/lang/invoke/MethodHandle;
+32 -1: Invalid JavaFX launch parameters
+15 -1: SECONDS_PER_DAY
+11 -1: UTF_16.java
+24 -1: sun/nio/cs/UTF_8$Encoder
+102 -1: (Ljava/security/AccessControlContext;Ljava/security/AccessControlContext;[Ljava/security/Permission;)V
+20 -1: (Lsun/misc/Signal;)V
+22 -1: MagicAccessorImpl.java
+84 -1: (Ljava/lang/String;Ljava/nio/ByteBuffer;Ljava/security/CodeSource;)Ljava/lang/Class;
+14 -1: altMetafactory
+13 -1: queryOverflow
+30 -1:  exists, but is not accessible
+3 -1: edt
+14 -1: MAX_ARRAY_SIZE
+20 -1: aliases_UTF_16LE_BOM
+34 -1: Ljava/lang/reflect/Constructor<*>;
+20 -1: (S)Ljava/lang/Short;
+6 -1: STRICT
+19 -1: internalCallerClass
+27 -1: java/nio/DirectLongBufferRU
+13 -1: TIMED_WAITING
+15 -1: toGenericString
+6 -1: client
+10 -1: attachImpl
+22 -1: ReflectionFactory.java
+8 -1: jsse.jar
+37 -1: (IZ)Ljava/lang/AbstractStringBuilder;
+41 -1: java/util/LinkedHashMap$LinkedKeyIterator
+15 -1: computeIfAbsent
+10 -1: GET_TARGET
+53 -1: <E:Ljava/lang/Enum<TE;>;>(Ljava/lang/Class<TE;>;)[TE;
+35 -1: java/util/Collections$SingletonList
+7 -1: addYear
+35 -1: Ljava/lang/Class<Ljava/lang/Byte;>;
+65 -1: (Ljava/util/LinkedHashMap$Entry;Ljava/util/LinkedHashMap$Entry;)V
+14 -1: image/x-bitmap
+10 -1: (IIII[JI)V
+50 -1: (Lsun/misc/URLClassPath$JarLoader;Ljava/net/URL;)V
+13 -1: getLineNumber
+20 -1: toUpperCaseCharArray
+62 -1: (Ljava/util/concurrent/locks/Condition;)Ljava/util/Collection;
+11 -1: rotateRight
+10 -1: checkPtype
+85 -1: (JLjava/util/function/ToLongFunction<-TK;>;JLjava/util/function/LongBinaryOperator;)J
+81 -1: (Ljava/lang/Class;Ljava/lang/reflect/Constructor;)Ljava/lang/reflect/Constructor;
+15 -1: Illegal style: 
+28 -1: (Ljava/lang/StringBuilder;)V
+41 -1: 1.8.0-internal-iklam_2013_11_27_21_25-b00
+25 -1: Invalid authority field: 
+55 -1: (Ljava/lang/CharSequence;)Ljava/util/function/Supplier;
+12 -1: staticOffset
+32 -1: java/util/HashMap$KeySpliterator
+13 -1: javaNioAccess
+24 -1: (Ljava/util/SortedSet;)V
+17 -1: thenComparingLong
+2 -1: \n\n
+22 -1: registerFieldsToFilter
+34 -1: java/lang/invoke/LambdaMetafactory
+225 -1: (Ljava/util/concurrent/ConcurrentHashMap$BulkTask;III[Ljava/util/concurrent/ConcurrentHashMap$Node;Ljava/util/concurrent/ConcurrentHashMap$MapReduceMappingsTask;Ljava/util/function/BiFunction;Ljava/util/function/BiFunction;)V
+26 -1: [[Ljava/lang/CharSequence;
+32 -1: java/util/Collections$CheckedMap
+147 -1: <E:Ljava/lang/Object;>Ljava/util/AbstractSequentialList<TE;>;Ljava/util/List<TE;>;Ljava/util/Deque<TE;>;Ljava/lang/Cloneable;Ljava/io/Serializable;
+204 -1: (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
+27 -1: sun/nio/cs/UTF_16BE$Decoder
+12 -1: getZoneInfo0
+77 -1: (Ljava/lang/Class;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/MethodType;
+9 -1: Traverser
+35 -1: Ljava/lang/ref/ReferenceQueue<TT;>;
+27 -1: lambda$comparing$ea9a8b3a$1
+7 -1: ([CI)[C
+6 -1: getenv
+9 -1: newMethod
+52 -1: <T:Ljava/lang/Object;>Ljava/lang/reflect/Executable;
+164 -1: (Ljava/security/ProtectionDomain;Ljava/security/DomainCombiner;Ljava/security/AccessControlContext;Ljava/security/AccessControlContext;[Ljava/security/Permission;)V
+40 -1: (Ljava/lang/String;)Ljava/util/TimeZone;
+11 -1: countTokens
+202 -1: <K:Ljava/lang/Object;V:Ljava/lang/Object;>Ljava/util/concurrent/ConcurrentHashMap$CollectionView<TK;TV;Ljava/util/Map$Entry<TK;TV;>;>;Ljava/util/Set<Ljava/util/Map$Entry<TK;TV;>;>;Ljava/io/Serializable;
+78 -1: <T:Ljava/lang/Object;>(Ljava/util/Collection<TT;>;)Ljava/util/Collection<TT;>;
+34 -1: (Ljava/lang/reflect/Constructor;)I
+15 -1: comparingDouble
+24 -1: ()Ljava/util/Collection;
+14 -1: invokeFinalize
+14 -1: encodeISOArray
+77 -1: (Ljava/lang/ref/Reference;Ljava/lang/ref/Reference;)Ljava/lang/ref/Reference;
+11 -1: bad index: 
+34 -1: (Ljava/lang/reflect/Constructor;)V
+68 -1: (Ljava/util/jar/JarEntry;Lsun/security/util/ManifestEntryVerifier;)V
+53 -1: ([Ljava/util/concurrent/ConcurrentHashMap$Node;IIIJ)V
+27 -1: ([CII)Ljava/nio/CharBuffer;
+6 -1: setOut
+41 -1: (ILjava/lang/Object;Ljava/lang/Object;I)V
+12 -1: MIN_EXPONENT
+30 -1: PrivilegedExceptionAction.java
+18 -1: key cannot be null
+6 -1: CENHDR
+73 -1: (ITK;TV;Ljava/util/HashMap$Node<TK;TV;>;)Ljava/util/HashMap$Node<TK;TV;>;
+23 -1: java/lang/reflect/Array
+8 -1: AF_LIMIT
+2 -1: \r\n
+11 -1: getFileName
+10 -1: parseShort
+22 -1: java/lang/LinkageError
+15 -1: FT_LAST_WRAPPER
+32 -1: java/util/ArrayDeque$DeqIterator
+24 -1: pc-multilingual-850+euro
+3 -1: zfc
+14 -1: incrementExact
+38 -1: (IIII)Lsun/util/calendar/CalendarDate;
+8 -1: (II[BI)V
+8 -1: isLocked
+13 -1: ZoneInfo.java
+36 -1: (Lsun/util/calendar/CalendarDate;J)V
+35 -1: java/lang/invoke/MethodHandleImpl$1
+31 -1: (Ljava/util/Comparator<-TE;>;)V
+19 -1: CharsetEncoder.java
+52 -1: <T:Ljava/lang/Object;>()Ljava/util/Enumeration<TT;>;
+44 -1: (Ljava/io/InputStream;)Ljava/io/InputStream;
+7 -1:  field 
+5 -1: abort
+25 -1: java/lang/SecurityManager
+66 -1: java/util/concurrent/ConcurrentHashMap$MapReduceValuesToDoubleTask
+1316 -1: \xe4\xa0\x80\xe1\x80\x8f\xe4\xa0\x80\xe1\x80\x8f\xe4\xa0\x80\xe1\x80\x8f\xe4\xa0\x80\xe1\x80\x8f\xe4\xa0\x80\xe1\x80\x8f\xe4\xa0\x80\xe1\x80\x8f\xe4\xa0\x80\xe1\x80\x8f\xe4\xa0\x80\xe1\x80\x8f\xe4\xa0\x80\xe1\x80\x8f\xe5\xa0\x80\xe4\x80\x8f\xe5\x80\x80\xe4\x80\x8f\xe5\xa0\x80\xe4\x80\x8f\xe6\x80\x80\xe4\x80\x8f\xe5\x80\x80\xe4\x80\x8f\xe4\xa0\x80\xe1\x80\x8f\xe4\xa0\x80\xe1\x80\x8f\xe4\xa0\x80\xe1\x80\x8f\xe4\xa0\x80\xe1\x80\x8f\xe4\xa0\x80\xe1\x80\x8f\xe4\xa0\x80\xe1\x80\x8f\xe4\xa0\x80\xe1\x80\x8f\xe4\xa0\x80\xe1\x80\x8f\xe4\xa0\x80\xe1\x80\x8f\xe4\xa0\x80\xe1\x80\x8f\xe4\xa0\x80\xe1\x80\x8f\xe4\xa0\x80\xe1\x80\x8f\xe4\xa0\x80\xe1\x80\x8f\xe4\xa0\x80\xe1\x80\x8f\xe5\x80\x80\xe4\x80\x8f\xe5\x80\x80\xe4\x80\x8f\xe5\x80\x80\xe4\x80\x8f\xe5\xa0\x80\xe4\x80\x8f\xe6\x80\x80\xe4\x80\x8c\xe6\xa0\x80\x18\xe6\xa0\x80\x18\xe2\xa0\x80\x18\xe2\xa0\x80\xe6\x80\x9a\xe2\xa0\x80\x18\xe6\xa0\x80\x18\xe6\xa0\x80\x18\xee\xa0\x80\x15\xee\xa0\x80\x16\xe6\xa0\x80\x18\xe2\x80\x80\x19\xe3\xa0\x80\x18\xe2\x80\x80\x14\xe3\xa0\x80\x18\xe3\xa0\x80\x18\xe1\xa0\x80\xe3\x98\x89\xe1\xa0\x80\xe3\x98\x89\xe1\xa0\x80\xe3\x98\x89\xe1\xa0\x80\xe3\x98\x89\xe1\xa0\x80\xe3\x98\x89\xe1\xa0\x80\xe3\x98\x89\xe1\xa0\x80\xe3\x98\x89\xe1\xa0\x80\xe3\x98\x89\xe1\xa0\x80\xe3\x98\x89\xe1\xa0\x80\xe3\x98\x89\xe3\xa0\x80\x18\xe6\xa0\x80\x18\xee\xa0\x80\x19\xe6\xa0\x80\x19\xee\xa0\x80\x19\xe6\xa0\x80\x18\xe6\xa0\x80\x18\xc2\x82\xe7\xbf\xa1\xc2\x82\xe7\xbf\xa1\xc2\x82\xe7\xbf\xa1\xc2\x82\xe7\xbf\xa1\xc2\x82\xe7\xbf\xa1\xc2\x82\xe7\xbf\xa1\xc2\x82\xe7\xbf\xa1\xc2\x82\xe7\xbf\xa1\xc2\x82\xe7\xbf\xa1\xc2\x82\xe7\xbf\xa1\xc2\x82\xe7\xbf\xa1\xc2\x82\xe7\xbf\xa1\xc2\x82\xe7\xbf\xa1\xc2\x82\xe7\xbf\xa1\xc2\x82\xe7\xbf\xa1\xc2\x82\xe7\xbf\xa1\xc2\x82\xe7\xbf\xa1\xc2\x82\xe7\xbf\xa1\xc2\x82\xe7\xbf\xa1\xc2\x82\xe7\xbf\xa1\xc2\x82\xe7\xbf\xa1\xc2\x82\xe7\xbf\xa1\xc2\x82\xe7\xbf\xa1\xc2\x82\xe7\xbf\xa1\xc2\x82\xe7\xbf\xa1\xc2\x82\xe7\xbf\xa1\xee\xa0\x80\x15\xe6\xa0\x80\x18\xee\xa0\x80\x16\xe6\xa0\x80\x1b\xe6\xa0\x80\xe5\x80\x97\xe6\xa0\x80\x1b\xc2\x81\xe7\xbf\xa2\xc2\x81\xe7\xbf\xa2\xc2\x81\xe7\xbf\xa2\xc2\x81\xe7\xbf\xa2\xc2\x81\xe7\xbf\xa2\xc2\x81\xe7\xbf\xa2\xc2\x81\xe7\xbf\xa2\xc2\x81\xe7\xbf\xa2\xc2\x81\xe7\xbf\xa2\xc2\x81\xe7\xbf\xa2\xc2\x81\xe7\xbf\xa2\xc2\x81\xe7\xbf\xa2\xc2\x81\xe7\xbf\xa2\xc2\x81\xe7\xbf\xa2\xc2\x81\xe7\xbf\xa2\xc2\x81\xe7\xbf\xa2\xc2\x81\xe7\xbf\xa2\xc2\x81\xe7\xbf\xa2\xc2\x81\xe7\xbf\xa2\xc2\x81\xe7\xbf\xa2\xc2\x81\xe7\xbf\xa2\xc2\x81\xe7\xbf\xa2\xc2\x81\xe7\xbf\xa2\xc2\x81\xe7\xbf\xa2\xc2\x81\xe7\xbf\xa2\xc2\x81\xe7\xbf\xa2\xee\xa0\x80\x15\xe6\xa0\x80\x19\xee\xa0\x80\x16\xe6\xa0\x80\x19\xe4\xa0\x80\xe1\x80\x8f\xe4\xa0\x80\xe1\x80\x8f\xe4\xa0\x80\xe1\x80\x8f\xe4\xa0\x80\xe1\x80\x8f\xe4\xa0\x80\xe1\x80\x8f\xe4\xa0\x80\xe1\x80\x8f\xe5\x80\x80\xe1\x80\x8f\xe4\xa0\x80\xe1\x80\x8f\xe4\xa0\x80\xe1\x80\x8f\xe4\xa0\x80\xe1\x80\x8f\xe4\xa0\x80\xe1\x80\x8f\xe4\xa0\x80\xe1\x80\x8f\xe4\xa0\x80\xe1\x80\x8f\xe4\xa0\x80\xe1\x80\x8f\xe4\xa0\x80\xe1\x80\x8f\xe4\xa0\x80\xe1\x80\x8f\xe4\xa0\x80\xe1\x80\x8f\xe4\xa0\x80\xe1\x80\x8f\xe4\xa0\x80\xe1\x80\x8f\xe4\xa0\x80\xe1\x80\x8f\xe4\xa0\x80\xe1\x80\x8f\xe4\xa0\x80\xe1\x80\x8f\xe4\xa0\x80\xe1\x80\x8f\xe4\xa0\x80\xe1\x80\x8f\xe4\xa0\x80\xe1\x80\x8f\xe4\xa0\x80\xe1\x80\x8f\xe4\xa0\x80\xe1\x80\x8f\xe4\xa0\x80\xe1\x80\x8f\xe4\xa0\x80\xe1\x80\x8f\xe4\xa0\x80\xe1\x80\x8f\xe4\xa0\x80\xe1\x80\x8f\xe4\xa0\x80\xe1\x80\x8f\xe4\xa0\x80\xe1\x80\x8f\xe3\xa0\x80\x0c\xe6\xa0\x80\x18\xe2\xa0\x80\xe6\x80\x9a\xe2\xa0\x80\xe6\x80\x9a\xe2\xa0\x80\xe6\x80\x9a\xe2\xa0\x80\xe6\x80\x9a\xe6\xa0\x80\x1c\xe6\xa0\x80\x18\xe6\xa0\x80\x1b\xe6\xa0\x80\x1c\xc0\x80\xe7\x80\x85\xee\xa0\x80\x1d\xe6\xa0\x80\x19\xe4\xa0\x80\xe1\x80\x90\xe6\xa0\x80\x1c\xe6\xa0\x80\x1b\xe2\xa0\x80\x1c\xe2\xa0\x80\x19\xe1\xa0\x80\xd8\x8b\xe1\xa0\x80\xd8\x8b\xe6\xa0\x80\x1b\xdf\xbd\xe7\x80\x82\xe6\xa0\x80\x18\xe6\xa0\x80\x18\xe6\xa0\x80\x1b\xe1\xa0\x80\xd4\x8b\xc0\x80\xe7\x80\x85\xee\xa0\x80\x1e\xe6\xa0\x80\xe0\xa0\x8b\xe6\xa0\x80\xe0\xa0\x8b\xe6\xa0\x80\xe0\xa0\x8b\xe6\xa0\x80\x18\xc2\x82\xe7\x80\x81\xc2\x82\xe7\x80\x81\xc2\x82\xe7\x80\x81\xc2\x82\xe7\x80\x81\xc2\x82\xe7\x80\x81\xc2\x82\xe7\x80\x81\xc2\x82\xe7\x80\x81\xc2\x82\xe7\x80\x81\xc2\x82\xe7\x80\x81\xc2\x82\xe7\x80\x81\xc2\x82\xe7\x80\x81\xc2\x82\xe7\x80\x81\xc2\x82\xe7\x80\x81\xc2\x82\xe7\x80\x81\xc2\x82\xe7\x80\x81\xc2\x82\xe7\x80\x81\xc2\x82\xe7\x80\x81\xc2\x82\xe7\x80\x81\xc2\x82\xe7\x80\x81\xc2\x82\xe7\x80\x81\xc2\x82\xe7\x80\x81\xc2\x82\xe7\x80\x81\xc2\x82\xe7\x80\x81\xe6\xa0\x80\x19\xc2\x82\xe7\x80\x81\xc2\x82\xe7\x80\x81\xc2\x82\xe7\x80\x81\xc2\x82\xe7\x80\x81\xc2\x82\xe7\x80\x81\xc2\x82\xe7\x80\x81\xc2\x82\xe7\x80\x81\xdf\xbd\xe7\x80\x82\xc2\x81\xe7\x80\x82\xc2\x81\xe7\x80\x82\xc2\x81\xe7\x80\x82\xc2\x81\xe7\x80\x82\xc2\x81\xe7\x80\x82\xc2\x81\xe7\x80\x82\xc2\x81\xe7\x80\x82\xc2\x81\xe7\x80\x82\xc2\x81\xe7\x80\x82\xc2\x81\xe7\x80\x82\xc2\x81\xe7\x80\x82\xc2\x81\xe7\x80\x82\xc2\x81\xe7\x80\x82\xc2\x81\xe7\x80\x82\xc2\x81\xe7\x80\x82\xc2\x81\xe7\x80\x82\xc2\x81\xe7\x80\x82\xc2\x81\xe7\x80\x82\xc2\x81\xe7\x80\x82\xc2\x81\xe7\x80\x82\xc2\x81\xe7\x80\x82\xc2\x81\xe7\x80\x82\xc2\x81\xe7\x80\x82\xe6\xa0\x80\x19\xc2\x81\xe7\x80\x82\xc2\x81\xe7\x80\x82\xc2\x81\xe7\x80\x82\xc2\x81\xe7\x80\x82\xc2\x81\xe7\x80\x82\xc2\x81\xe7\x80\x82\xc2\x81\xe7\x80\x82\xd8\x9d\xe7\x80\x82
+6 -1: (J[I)I
+162 -1: (Ljava/util/List<Ljava/util/Locale$LanguageRange;>;Ljava/util/Collection<Ljava/util/Locale;>;Ljava/util/Locale$FilteringMode;)Ljava/util/List<Ljava/util/Locale;>;
+21 -1: getQualifiedFieldName
+46 -1: Ljava/util/Set<Ljava/util/Map$Entry<TK;TV;>;>;
+47 -1: (Ljava/util/Collection;Ljava/util/Collection;)Z
+10 -1: getRuntime
+30 -1: threadLocalRandomSecondarySeed
+18 -1: (Ljava/io/File;I)J
+10 -1: methodName
+34 -1: sun/reflect/generics/tree/TypeTree
+35 -1: (Ljava/io/File;)[Ljava/lang/String;
+31 -1: java/util/Collections$EmptyList
+15 -1: LF_INVINTERFACE
+9 -1: notifyAll
+18 -1: (Ljava/io/File;I)V
+94 -1: (Ljava/lang/String;[BIILjava/security/ProtectionDomain;Ljava/lang/String;)Ljava/lang/Class<*>;
+45 -1: (Ljava/lang/String;)Ljava/net/ContentHandler;
+3 -1: enc
+3 -1: end
+18 -1: (Ljava/io/File;I)Z
+47 -1: (Ljava/lang/Object;Ljava/lang/reflect/Method;)V
+76 -1: (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Z)V
+19 -1: getURLStreamHandler
+46 -1: (Ljava/lang/ClassLoader;Ljava/lang/Class<*>;)V
+17 -1: COMPILE_THRESHOLD
+15 -1: charset is null
+7 -1: ibm-912
+10 -1: basicTypes
+7 -1: ibm-914
+78 -1: (Ljava/lang/Class;Ljava/lang/ref/SoftReference;Ljava/lang/ref/SoftReference;)Z
+7 -1: ibm-915
+12 -1: JarFileEntry
+12 -1: setThreshold
+22 -1: (ILjava/lang/Object;)V
+55 -1: <T::Lsun/reflect/generics/tree/Tree;>Ljava/lang/Object;
+16 -1: Unknown signal: 
+3 -1: zip
+13 -1: CR_UNMAPPABLE
+19 -1: getClassAtIfLoaded0
+21 -1: WindowsClientCounters
+29 -1: Ljava/lang/invoke/MethodType;
+91 -1: <E:Ljava/lang/Object;>Ljava/util/Collections$UnmodifiableList<TE;>;Ljava/util/RandomAccess;
+23 -1: StackOverflowError.java
+13 -1: Launcher.java
+9 -1: Signature
+7 -1: ibm-920
+153 -1: <K:Ljava/lang/Object;V:Ljava/lang/Object;>([Ljava/util/concurrent/ConcurrentHashMap$Node<TK;TV;>;ILjava/util/concurrent/ConcurrentHashMap$Node<TK;TV;>;)V
+13 -1: setExtensions
+26 -1: [Ljava/lang/ref/Reference;
+7 -1: ibm-923
+12 -1: BMH.reinvoke
+34 -1: java/lang/IllegalArgumentException
+53 -1: (Ljava/lang/String;)Ljava/lang/NumberFormatException;
+5 -1: .dirs
+13 -1: finishToArray
+22 -1: (ZI)Ljava/lang/String;
+84 -1: (Ljava/lang/String;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/RuntimeException;
+15 -1: charsetProvider
+24 -1: ()Ljava/lang/Class<TT;>;
+10 -1: wordsInUse
+26 -1: (Ljava/io/ExpiringCache;)I
+53 -1: ()Ljava/util/Iterator<Ljava/util/Map$Entry<TK;TV;>;>;
+25 -1: com/sun/management/GcInfo
+26 -1: getCompatibilityExtensions
+69 -1: (Ljava/lang/ref/ReferenceQueue;Ljava/util/concurrent/ConcurrentMap;)V
+15 -1: getConstantPool
+24 -1: [[Ljava/lang/Comparable;
+26 -1: (Ljava/io/ExpiringCache;)V
+8 -1: getTable
+53 -1: sun/reflect/generics/repository/ConstructorRepository
+5 -1: range
+36 -1: (Ljava/lang/String;)Ljava/lang/Byte;
+72 -1: (Ljava/lang/Class;Ljava/lang/Class;Ljava/lang/String;Ljava/lang/Class;)V
+60 -1: <T:Ljava/lang/Object;>([TT;TT;Ljava/util/Comparator<-TT;>;)I
+20 -1: (Ljava/nio/Bits$1;)V
+30 -1: ()Ljava/util/Spliterator<TK;>;
+6 -1: ([BB)I
+53 -1: (Ljava/lang/ref/Finalizer;Lsun/misc/JavaLangAccess;)V
+11 -1: memberTypes
+45 -1: (ILjava/lang/String;)Ljava/lang/StringBuffer;
+12 -1: OTHER_SYMBOL
+65 -1: (Ljava/lang/Class;Ljava/lang/Class;)Ljava/lang/invoke/MethodType;
+43 -1: (Ljava/util/Set;)[Ljava/lang/reflect/Field;
+30 -1: (Ljava/lang/ref/Reference$1;)V
+18 -1: GREGORIAN_INSTANCE
+31 -1: Ljava/lang/FunctionalInterface;
+57 -1: (Ljava/lang/Error;Ljava/lang/Exception;)Ljava/lang/Error;
+54 -1: ([Ljava/lang/reflect/Field;)[Ljava/lang/reflect/Field;
+14 -1: not an array: 
+6 -1: ([BB)V
+9 -1: ISO8859_1
+8 -1: addTrans
+27 -1: getFunctionalInterfaceClass
+29 -1: lambda$comparingInt$7b0bb60$1
+8 -1: TreeNode
+138 -1: <K:Ljava/lang/Object;V:Ljava/lang/Object;>Ljava/util/Dictionary<TK;TV;>;Ljava/util/Map<TK;TV;>;Ljava/lang/Cloneable;Ljava/io/Serializable;
+3 -1: era
+22 -1: fakeMethodHandleInvoke
+9 -1: addToList
+39 -1: (Ljava/lang/Class;[Ljava/lang/String;)V
+17 -1: launchApplication
+21 -1: randomNumberGenerator
+51 -1: Ljava/lang/ThreadLocal<Ljava/lang/ThreadLocal<*>;>;
+35 -1: java/io/ObjectOutputStream$PutField
+42 -1: (ILjava/util/function/IntBinaryOperator;)I
+3 -1: err
+13 -1: cachedDecoder
+32 -1: sun/util/calendar/ZoneInfoFile$1
+23 -1: doIntersectionPrivilege
+19 -1: cspc850multilingual
+56 -1: Ljava/util/Map<Ljava/lang/Class<*>;[Ljava/lang/String;>;
+11 -1: loader_data
+27 -1: (Ljava/util/jar/Manifest;)V
+5 -1: files
+90 -1: Ljava/util/concurrent/ConcurrentMap<Ljava/lang/String;Lsun/util/calendar/CalendarSystem;>;
+36 -1: [[Ljava/lang/invoke/LambdaForm$Name;
+5 -1: lines
+55 -1: (Lsun/misc/URLClassPath$JarLoader;)Lsun/misc/MetaIndex;
+9 -1: ansi-1251
+15 -1: refKindIsMethod
+29 -1: java/lang/reflect/Constructor
+3 -1: est
+19 -1: Lsun/misc/Launcher;
+109 -1: <T:Ljava/lang/Object;>(Ljava/security/PrivilegedExceptionAction<TT;>;Ljava/security/AccessControlContext;)TT;
+10 -1: getOffsets
+9 -1: removeAll
+23 -1: java/util/regex/Matcher
+8 -1: sumCount
+7 -1: implies
+10 -1: MAIN_CLASS
+75 -1: (Ljava/util/List<Lsun/launcher/LauncherHelper$StdArg;>;)[Ljava/lang/String;
+11 -1: getISO3Code
+4 -1: high
+53 -1: (TK;Ljava/util/function/BiFunction<-TK;-TV;+TV;>;)TV;
+17 -1: setNormalizedDate
+23 -1: AbstractRepository.java
+28 -1: java/util/LinkedList$ListItr
+8 -1: isFrozen
+38 -1: (Ljava/lang/String;Z)Ljava/lang/Class;
+16 -1: ReflectUtil.java
+30 -1: ()Ljava/util/stream/IntStream;
+57 -1: (Ljava/lang/Object;JLjava/lang/Object;)Ljava/lang/Object;
+11 -1: getResource
+16 -1: ThreadDeath.java
+24 -1: unmodifiableNavigableSet
+59 -1: (Ljava/lang/String;)Ljava/util/Enumeration<Ljava/net/URL;>;
+24 -1: java.security.auth.debug
+58 -1: (Ljava/io/FileInputStream;)Ljava/nio/channels/FileChannel;
+25 -1: ()Ljava/util/Enumeration;
+11 -1: getInstance
+6 -1: MONDAY
+15 -1: jdkMinorVersion
+16 -1: newThreadWithAcc
+6 -1: CENHOW
+32 -1:     Max. Heap Size (Estimated): 
+61 -1: (Ljava/lang/invoke/MethodType;Z)Ljava/lang/invoke/LambdaForm;
+11 -1: windows-932
+7 -1: Index: 
+11 -1: composeList
+6 -1: utf-16
+6 -1: ibm437
+10 -1: getJarFile
+8 -1: , rem = 
+13 -1: multiNewArray
+14 -1: getDefaultPort
+39 -1: Ljava/security/cert/CertificateFactory;
+10 -1: L_RESERVED
+19 -1: getMethodAtIfLoaded
+8 -1: needCast
+8 -1: IS_FIELD
+15 -1: ClassValue.java
+31 -1: ()Ljava/util/function/Supplier;
+125 -1: (Ljava/lang/Class<*>;)Ljava/util/Map<Ljava/lang/Class<+Ljava/lang/annotation/Annotation;>;Ljava/lang/annotation/Annotation;>;
+34 -1: lambda$comparingByValue$827a17d5$1
+4 -1: NONE
+21 -1: java/nio/DoubleBuffer
+33 -1: ()Lsun/reflect/LangReflectAccess;
+26 -1: invalid compression method
+6 -1: (TK;)Z
+16 -1: FT_UNCHECKED_REF
+14 -1: getGenericType
+17 -1: pathSeparatorChar
+8 -1: writeUTF
+8 -1: NO_PROXY
+188 -1: (Ljava/lang/String;Ljava/util/Map<Ljava/lang/String;Ljava/lang/String;>;Ljava/util/Map<Ljava/lang/String;Ljava/lang/String;>;Ljava/util/Map<Ljava/lang/String;Ljava/nio/charset/Charset;>;)V
+13 -1: finalRefCount
+12 -1: NF_checkCast
+6 -1: utf-32
+26 -1: (Ljava/util/ArrayDeque;I)Z
+19 -1: prefetchWriteStatic
+14 -1: computeInvoker
+5 -1:  cap=
+19 -1: generateCertificate
+15 -1: methodModifiers
+3 -1: exc
+27 -1: ()Lsun/misc/JavaLangAccess;
+5 -1: State
+14 -1: NullComparator
+10 -1: getClassAt
+15 -1: printProperties
+110 -1: (Ljava/lang/Class;[Ljava/lang/Class;[Ljava/lang/Class;IILjava/lang/String;[B[B)Ljava/lang/reflect/Constructor;
+40 -1: (Ljava/util/List<*>;Ljava/util/Random;)V
+63 -1: ()[Ljava/lang/reflect/TypeVariable<Ljava/lang/reflect/Method;>;
+28 -1: (I)Ljava/lang/StringBuilder;
+48 -1: ([DIILjava/util/function/DoubleBinaryOperator;)V
+3 -1: exp
+11 -1: interpret_L
+17 -1: Serializable.java
+8 -1: FJDouble
+12 -1: HashMap.java
+9 -1: sys_paths
+17 -1: getMainAttributes
+14 -1: asDoubleBuffer
+10 -1: buildNames
+26 -1: TOPLEVEL_WINDOW_PERMISSION
+4 -1: Type
+31 -1: (Ljava/util/Collection<+TE;>;)V
+64 -1: (Ljava/lang/String;ZLjava/lang/ClassLoader;)Ljava/lang/Class<*>;
+100 -1: <E:Ljava/lang/Object;>(Ljava/util/Collection<TE;>;Ljava/lang/Class<TE;>;)Ljava/util/Collection<TE;>;
+10 -1: checkError
+31 -1: (Ljava/util/Collection<+TE;>;)Z
+31 -1: java/lang/NoSuchMethodException
+6 -1: attach
+87 -1: (BLjava/lang/Class;Ljava/lang/String;Ljava/lang/Object;)Ljava/lang/invoke/MethodHandle;
+9 -1: writeChar
+44 -1: java/util/ArraysParallelSortHelpers$FJObject
+34 -1: (Ljava/lang/Class;Ljava/io/File;)Z
+21 -1: java/util/zip/ZipFile
+5 -1: dirty
+6 -1: (JIZ)V
+12 -1: leftoverChar
+39 -1:  is being loaded in another classloader
+10 -1: writeBytes
+6 -1: unlink
+41 -1: (TT;Ljava/lang/ref/ReferenceQueue<TT;>;)V
+21 -1: getBootstrapResources
+95 -1: <K:Ljava/lang/Object;V:Ljava/lang/Object;>Ljava/util/AbstractMap<TK;TV;>;Ljava/io/Serializable;
+10 -1: PathStatus
+25 -1: java/io/InputStreamReader
+15 -1: ISO_8859-9:1989
+37 -1: java/lang/ExceptionInInitializerError
+14 -1: exceptionTypes
+19 -1: BufferedReader.java
+34 -1: Could not create SecurityManager: 
+13 -1: definePackage
+12 -1: getAndAddInt
+50 -1: (Ljava/lang/Class;)Ljava/lang/invoke/MethodHandle;
+30 -1: (Ljava/lang/SecurityManager;)V
+12 -1: fxLaunchName
+22 -1: BINARYSEARCH_THRESHOLD
+29 -1: JVMTI_THREAD_STATE_TERMINATED
+9 -1: fullFence
+60 -1: (Ljava/lang/String;Z)Ljava/util/Enumeration<Ljava/net/URL;>;
+29 -1: java/lang/Thread$WeakClassKey
+47 -1: (Ljava/nio/charset/Charset;Ljava/lang/String;)V
+8 -1: (TV;)TV;
+60 -1: (Ljava/lang/Class<*>;Ljava/lang/String;Ljava/lang/Object;I)V
+47 -1: java/security/cert/CertificateEncodingException
+12 -1: BA_DIRECTORY
+53 -1: (Ljava/lang/Class;ZLjava/lang/Class;)Ljava/util/List;
+9 -1: checkRead
+6 -1: <init>
+4 -1: args
+17 -1: genericMethodType
+10 -1: writeFloat
+26 -1: Can't handle static method
+49 -1: (Ljava/lang/String;)Ljava/lang/invoke/MethodType;
+22 -1: MapReduceKeysToIntTask
+17 -1: jvm_micro_version
+29 -1: (Ljava/util/Map<+TK;+TV;>;Z)V
+40 -1: ([Ljava/lang/Object;Ljava/lang/Object;)I
+7 -1: vmindex
+22 -1: maybeCompileToBytecode
+89 -1: Ljava/util/concurrent/atomic/AtomicReferenceFieldUpdater$AtomicReferenceFieldUpdaterImpl;
+11 -1: getLauncher
+17 -1: jvm_major_version
+36 -1: ([IIII)Ljava/util/Spliterator$OfInt;
+40 -1: ([Ljava/lang/Object;Ljava/lang/Object;)V
+33 -1: IllegalMonitorStateException.java
+73 -1: ([ILjava/util/function/IntUnaryOperator;)Ljava/util/function/IntConsumer;
+39 -1: (Ljava/lang/String;)Ljava/lang/Package;
+29 -1: java/lang/CharacterDataLatin1
+52 -1: (Ljava/lang/annotation/Annotation;)Ljava/lang/Class;
+49 -1: (Ljava/lang/CharSequence;II)Ljava/nio/CharBuffer;
+53 -1: (Ljava/lang/Object;)Ljava/nio/charset/CharsetDecoder;
+22 -1: Ljava/net/FileNameMap;
+16 -1: isAnonymousClass
+4 -1: item
+7 -1: compute
+12 -1: user.country
+22 -1: malformed context url:
+16 -1: jvm_build_number
+69 -1: (Ljava/lang/ThreadLocal;)Ljava/lang/ThreadLocal$ThreadLocalMap$Entry;
+17 -1: getDirectionality
+4 -1: save
+8 -1: UNMARKED
+58 -1: (Ljava/lang/String;Ljava/lang/Object;[Ljava/lang/Object;)V
+12 -1: searchFields
+9 -1: frequency
+23 -1: getLocalizedInputStream
+2 -1:   
+126 -1: (Ljava/lang/Class<*>;Ljava/lang/String;Ljava/lang/Object;ILjava/lang/Class<*>;)Ljava/util/List<Ljava/lang/invoke/MemberName;>;
+39 -1: (Ljava/lang/String;Z)Ljava/lang/String;
+7 -1: setZone
+2 -1:  "
+9 -1: checkRef(
+11 -1: loadFromXML
+49 -1: (Ljava/util/jar/JarFile;Ljava/util/Enumeration;)V
+68 -1: (IILsun/util/calendar/CalendarDate;)Lsun/util/calendar/CalendarDate;
+2 -1:  (
+54 -1: (Ljava/util/TimeZone;)Lsun/util/calendar/CalendarDate;
+6 -1: rewind
+13 -1: getAndSetLong
+30 -1: java/lang/invoke/MethodHandles
+11 -1: ListPattern
+97 -1: <E:Ljava/lang/Object;>Ljava/util/AbstractList<TE;>;Ljava/util/RandomAccess;Ljava/io/Serializable;
+25 -1: (Ljava/io/InputStream;I)V
+9 -1: setMethod
+10 -1: H_REG_NAME
+39 -1: ([Ljava/lang/Object;)Ljava/lang/Object;
+16 -1: AbstractMap.java
+68 -1: Ljava/util/Hashtable<Ljava/lang/String;Ljava/net/URLStreamHandler;>;
+58 -1: (Ljava/lang/String;[Ljava/lang/String;)Ljava/lang/Process;
+23 -1: getFileSystemAttributes
+12 -1: toSurrogates
+2 -1: !/
+5 -1: empty
+24 -1: isUnicodeIdentifierStart
+35 -1: sun/nio/cs/StandardCharsets$Classes
+27 -1: [Ljava/security/Permission;
+13 -1: getDefinition
+11 -1: permission=
+42 -1: (ILjava/lang/String;)Ljava/nio/ByteBuffer;
+69 -1: (Ljava/util/List<Ljava/lang/Class<*>;>;)Ljava/lang/invoke/MethodType;
+3 1: zzz
+17 -1: hasLongPrimitives
+2 -1: !=
+13 -1: getInterfaces
+2 -1: " 
+11 -1: noInflation
+14 -1: aliases_UTF_16
+2 -1: ")
+17 -1: ()Ljava/util/Map;
+55 -1: ()Ljava/util/Map<Ljava/lang/String;Ljava/lang/String;>;
+6 -1: charAt
+12 -1: getStringAt0
+10 -1: superClone
+28 -1: Ljava/util/AbstractSet<TK;>;
+40 -1: java/lang/ref/Reference$ReferenceHandler
+22 -1: NaturalOrderComparator
+9 -1: markValue
+9 -1: getRegion
+26 -1: null permissions parameter
+17 -1: Ljava/util/Stack;
+14 -1: codebase=<URL>
+36 -1: (Ljava/util/List;)Ljava/lang/Object;
+17 -1: setJavaLangAccess
+16 -1: hasQueuedThreads
+5 -1: (CC)I
+8 -1: toString
+5 -1: (CC)J
+11 -1: permissions
+10 -1: getHeaders
+27 -1: java/io/BufferedInputStream
+21 -1: unicodelittleunmarked
+51 -1: (Ljava/net/URLClassLoader;Ljava/util/Enumeration;)V
+14 -1: generateMethod
+11 -1: skipForward
+55 -1: java/util/concurrent/ConcurrentHashMap$ForEachEntryTask
+5 -1: (CC)Z
+15 -1: getURLClassPath
+84 -1: Ljava/lang/invoke/MethodType$ConcurrentWeakInternSet<Ljava/lang/invoke/MethodType;>;
+23 -1: primitiveParameterCount
+8 -1: security
+14 -1: aliases_UTF_32
+22 -1: ()Ljava/util/Set<TK;>;
+9 -1: listFiles
+15 -1: insertElementAt
+42 -1: Ljava/util/Comparator<Ljava/lang/String;>;
+11 -1: getUserInfo
+46 -1: ([JIILjava/util/function/LongBinaryOperator;)V
+23 -1: (Ljava/util/Iterator;)V
+46 -1: ([Ljava/lang/Object;II)Ljava/util/Spliterator;
+67 -1: (Ljava/lang/invoke/MemberName;Ljava/lang/Object;)Ljava/lang/Object;
+14 -1: normalizeMonth
+13 -1: getStackTrace
+51 -1: java/lang/invoke/MethodType$ConcurrentWeakInternSet
+8 -1: makeChar
+2 -1: %%
+9 -1: getTarget
+21 -1: packageDefinitionLock
+52 -1: java/util/concurrent/ConcurrentHashMap$ValueIterator
+12 -1: OTHER_NUMBER
+22 -1: java/util/jar/JarEntry
+11 -1: access$1000
+16 -1: NON_SPACING_MARK
+13 -1: last-modified
+68 -1: Ljava/lang/Object;Ljava/security/PrivilegedAction<Ljava/lang/Void;>;
+16 -1: Australia/Darwin
+55 -1: (Ljava/lang/management/ThreadInfo;[Ljava/lang/Object;)V
+29 -1: Ljava/lang/ref/WeakReference;
+19 -1: expungeStaleEntries
+41 -1: Ljava/security/PrivilegedActionException;
+6 -1: update
+23 -1: (Ljava/lang/Object;JB)V
+10 -1: newUpdater
+39 -1: (Ljava/net/URL;)Ljava/util/jar/JarFile;
+25 -1: Ljava/net/ContentHandler;
+21 -1: ARRAY_INT_INDEX_SCALE
+13 -1: hasSurrogates
+27 -1: (Ljava/lang/ThreadGroup;Z)Z
+20 -1: createGCNotification
+21 -1: negative day of week 
+11 -1: getInIfOpen
+22 -1: java/util/RandomAccess
+24 -1:     available locales = 
+21 -1: AccessController.java
+44 -1:  can not access a protected member of class 
+4 -1: LONG
+15 -1: objectOnlyTypes
+75 -1: (Ljava/io/InputStream;Ljava/lang/Object;Ljava/nio/charset/CharsetDecoder;)V
+14 -1: getFindClasses
+10 -1: storeFence
+16 -1: asNormalOriginal
+45 -1: (Ljava/lang/String;)Ljava/lang/StringBuilder;
+6 -1: millis
+16 -1: America/St_Johns
+38 -1: ()Ljava/lang/IllegalArgumentException;
+37 -1: DIRECTIONALITY_POP_DIRECTIONAL_FORMAT
+15 -1: implReplaceWith
+29 -1: ([C)Ljava/lang/StringBuilder;
+15 -1: Appendable.java
+41 -1: (Ljava/lang/String;)Ljava/io/InputStream;
+26 -1: Illegal Initial Capacity: 
+9 -1: checkBase
+7 -1: setYear
+15 -1: DISPLAY_VARIANT
+7 -1: getType
+31 -1: Ljava/lang/ref/Reference<+TT;>;
+15 -1: isFieldOrMethod
+35 -1: appendToClassPathForInstrumentation
+16 -1: LocaleNameGetter
+7 -1: compact
+55 -1: ()Ljava/util/Map<Ljava/lang/String;Ljava/lang/Object;>;
+10 -1: dummyQueue
+3 -1: ROC
+2 -1: ("
+15 -1: checkPermission
+38 -1: java/util/zip/ZipFile$ZipEntryIterator
+8 -1: hexDigit
+8 -1:  pairs: 
+2 -1: ()
+2 -1: )\n
+43 -1: handler for url different from this handler
+15 -1: isAutoDetecting
+37 -1: (Ljava/util/LinkedList$Node<TE;>;)TE;
+11 -1: Unsafe.java
+12 -1: windows-1250
+39 -1: java/util/Collections$CheckedCollection
+12 -1: windows-1251
+15 -1: codePointAtImpl
+12 -1: windows-1252
+12 -1: windows-1253
+12 -1: windows-1254
+58 -1: (Ljava/lang/String;Ljava/lang/Integer;)Ljava/lang/Integer;
+5 -1: deref
+12 -1: windows-1255
+12 -1: windows-1256
+12 -1: windows-1257
+12 -1: windows-1258
+14 -1: FT_CHECKED_REF
+47 -1: (Ljava/util/Hashtable;Ljava/util/Hashtable$1;)V
+37 -1: (J)Lsun/util/calendar/Gregorian$Date;
+19 -1: checkPropertyAccess
+4 -1: file
+17 -1: emptyListIterator
+26 -1: sun/util/calendar/ZoneInfo
+14 -1: file.separator
+4 -1: fill
+62 -1: (Ljava/util/Spliterator$OfLong;Z)Ljava/util/stream/LongStream;
+18 -1: java/util/Iterator
+20 -1: reduceValuesToDouble
+12 -1: LF_CS_LINKER
+26 -1: java/util/Arrays$ArrayList
+45 -1: Ljava/util/concurrent/ConcurrentHashMap$Node;
+6 -1: skipLF
+2 -1: )=
+39 -1: (I[Ljava/lang/invoke/LambdaForm$Name;)I
+90 -1: (Ljava/lang/invoke/MethodType;Ljava/lang/invoke/LambdaForm;Ljava/lang/invoke/MemberName;)V
+18 -1: parameterModifiers
+31 -1: (Ljava/util/Collection<+TK;>;)Z
+21 -1: proxy can not be null
+22 -1: java/io/FileDescriptor
+6 -1: Loader
+21 -1: numberOfTrailingZeros
+10 -1: addMapping
+39 -1: (I[Ljava/lang/invoke/LambdaForm$Name;)Z
+30 -1: java/util/Locale$LanguageRange
+20 -1: getReflectionFactory
+16 -1: shouldMeterInput
+56 -1: ([Ljava/lang/reflect/Method;)[Ljava/lang/reflect/Method;
+23 -1: sun/net/ProgressMonitor
+510 -1: \xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\x01\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\x01\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80\xc0\x80
+28 -1: java/util/Spliterator$OfLong
+24 -1: SynchronizedNavigableMap
+4 -1: find
+6 -1: unsafe
+31 -1: java/nio/ByteBufferAsIntBufferB
+2 -1: ,\n
+6 -1: [ call
+14 -1: registerFilter
+10 -1: ValuesView
+9 -1: untreeify
+59 -1: ([Ljava/lang/Object;IILjava/util/function/BinaryOperator;)V
+13 -1: getSimpleName
+41 -1: (Ljava/util/Vector;Ljava/util/Vector$1;)V
+31 -1: java/nio/ByteBufferAsIntBufferL
+45 -1: (Ljava/lang/reflect/Field;)Ljava/lang/Object;
+13 -1: getDefaultRef
+18 -1: mapAlternativeName
+30 -1: setDefaultAllowUserInteraction
+13 -1: cannotCastMsg
+4 -1: )=>{
+7 -1: println
+2 -1: , 
+70 -1: (Ljava/nio/Buffer;IILjava/nio/Buffer;II)Ljava/nio/charset/CoderResult;
+32 -1: (ILjava/util/Collection<+TE;>;)Z
+9 -1: interpret
+104 -1: <E:Ljava/lang/Object;>(Ljava/util/NavigableSet<TE;>;Ljava/lang/Class<TE;>;)Ljava/util/NavigableSet<TE;>;
+7 -1: advance
+86 -1: Ljava/lang/Object;Ljava/security/PrivilegedExceptionAction<Ljava/lang/reflect/Field;>;
+11 -1: Stack trace
+6 -1: raise0
+68 -1: ()Ljava/util/Collections$UnmodifiableNavigableMap$EmptyNavigableMap;
+32 -1: sun/util/calendar/Gregorian$Date
+33 -1: java/lang/ref/ReferenceQueue$Lock
+19 -1: constructorAccessor
+6 -1: IBM367
+18 -1: CharacterData.java
+8 -1: parseURL
+32 -1: java/io/FilePermissionCollection
+7 -1: ([JI)[J
+9 -1: JIS_X0201
+2 -1: -1
+8 -1: encoding
+63 -1: ([Ljava/util/WeakHashMap$Entry;[Ljava/util/WeakHashMap$Entry;)V
+9 -1: localhost
+66 -1: (Ljava/lang/ThreadLocal$ThreadLocalMap;Ljava/lang/ThreadLocal$1;)V
+54 -1: (II[Ljava/lang/Class<*>;)Ljava/lang/invoke/MethodType;
+67 -1: (Ljava/lang/Class;Ljava/lang/Class;)Ljava/lang/invoke/MethodHandle;
+14 -1: containsAllPDs
+23 -1: setAllowUserInteraction
+30 -1: Ljava/security/DomainCombiner;
+26 -1: Ljava/security/Permission;
+30 -1: serializePropertiesToByteArray
+112 -1: (Ljava/util/Iterator<Ljava/nio/charset/Charset;>;Ljava/util/Map<Ljava/lang/String;Ljava/nio/charset/Charset;>;)V
+2 -1: ..
+6 -1: LOCEXT
+2 -1: ./
+26 -1: (Ljava/nio/ByteBuffer;II)V
+18 -1: (Ljava/io/File;J)Z
+45 -1: (Ljava/lang/StringBuffer;Ljava/lang/String;)V
+17 -1: asCollectorChecks
+7 -1: actions
+5 -1: ([I)I
+20 -1: asChange_otherthread
+20 -1: forInputStreamReader
+69 -1: java/util/concurrent/locks/AbstractQueuedSynchronizer$ConditionObject
+5 -1: FINAL
+17 -1: staticPermissions
+44 -1: (Ljava/lang/Object;)Ljava/lang/StringBuffer;
+5 -1: ([I)V
+4 -1: swap
+10 -1: readOffset
+2 -1: /*
+112 -1: <U:Ljava/lang/Object;>(JLjava/util/function/Function<-TK;+TU;>;Ljava/util/function/BiFunction<-TU;-TU;+TU;>;)TU;
+12 -1: isAsciiDigit
+2 -1: /-
+2 -1: /.
+2 -1: //
+5 -1: zones
+37 -1: (ILjava/lang/Object;)Ljava/util/List;
+17 -1: SHUFFLE_THRESHOLD
+32 -1: java/lang/CharacterDataUndefined
+23 -1: sun.reflect.noInflation
+11 -1: Can not set
+36 -1: (Ljava/util/Properties$LineReader;)V
+9 -1: debugName
+78 -1: (Ljava/util/HashMap$Node;Ljava/util/HashMap$Node;)Ljava/util/HashMap$TreeNode;
+6 -1: (III)J
+58 -1: (Ljava/util/List;Ljava/util/Collection;)Ljava/lang/String;
+19 -1: BufferedWriter.java
+148 -1: <T:Ljava/lang/Object;>(Ljava/lang/Class<TT;>;[Ljava/lang/Class<*>;[Ljava/lang/Class<*>;IILjava/lang/String;[B[B)Ljava/lang/reflect/Constructor<TT;>;
+6 -1: printf
+7 -1: signers
+2 -1: 0.
+4 -1: Date
+15 -1: findReplacement
+6 -1: (III)V
+32 -1: java/nio/ReadOnlyBufferException
+92 -1: ([Ljava/lang/String;)Ljava/util/Map<Ljava/lang/String;Ljava/util/List<Ljava/lang/String;>;>;
+6 -1: (III)Z
+43 -1: (Ljava/lang/ClassValue;Ljava/lang/Object;)V
+32 -1: java/nio/ByteBufferAsLongBufferB
+16 -1: Constructor.java
+10 -1: removeLast
+9 -1: ([CII[B)I
+40 -1: ()Ljava/util/List<Ljava/lang/Class<*>;>;
+2 -1: 1.
+7 -1: VARARGS
+32 -1: java/nio/ByteBufferAsLongBufferL
+18 -1: java/lang/Shutdown
+40 -1:  not supported, using ISO-8859-1 instead
+40 -1: Ljava/util/Collections$EmptyEnumeration;
+146 -1: <K:Ljava/lang/Object;V:Ljava/lang/Object;>(Ljava/util/SortedMap<TK;TV;>;Ljava/lang/Class<TK;>;Ljava/lang/Class<TV;>;)Ljava/util/SortedMap<TK;TV;>;
+20 -1: aliases_UTF_32LE_BOM
+23 -1: getEnclosingConstructor
+2 -1: 0X
+11 -1: NO_TIMEZONE
+37 -1: ()Lsun/misc/JavaNioAccess$BufferPool;
+18 -1: printXUsageMessage
+9 -1: isPrivate
+63 -1: (Ljava/lang/invoke/MemberName;Ljava/lang/invoke/MethodHandle;)V
+17 -1: maxSkipBufferSize
+76 -1: (Ljava/io/OutputStream;Ljava/lang/Object;Ljava/nio/charset/CharsetEncoder;)V
+40 -1: (Ljava/lang/String;II)Ljava/lang/String;
+17 -1: selectAlternative
+53 -1: [Ljava/util/concurrent/ConcurrentHashMap$CounterCell;
+87 -1: <S:Ljava/lang/Object;>(Ljava/util/function/Supplier<+TS;>;)Ljava/lang/ThreadLocal<TS;>;
+40 -1: Ljava/util/concurrent/ConcurrentHashMap;
+41 -1: (Ljava/lang/Character;)Ljava/lang/String;
+19 -1: getMemberRefInfoAt0
+14 -1: reduceToDouble
+18 -1: SUPPRESSED_CAPTION
+6 -1: CANADA
+64 -1: (IZ[Ljava/lang/Class<*>;[Ljava/lang/Class<*>;)Ljava/lang/String;
+12 -1: UnicodeBlock
+2 -1: 0x
+44 -1: (Ljava/lang/CharSequence;II)Ljava/io/Writer;
+23 -1: getPermissionCollection
+19 -1: threadLocalHashCode
+9 -1: createMap
+25 -1: checkForSpecialAttributes
+12 -1: Mark invalid
+36 -1: java/lang/CharSequence$1CharIterator
+11 -1:  local time
+16 -1: Enumeration.java
+27 -1: (Ljava/util/zip/ZipEntry;)V
+11 -1: MATH_SYMBOL
+8 -1: filename
+24 -1: (Ljava/util/List<*>;II)V
+9 -1: bindCache
+18 -1: java/io/FileFilter
+12 -1: checkInvoker
+18 -1: OSEnvironment.java
+8 -1: EmptyMap
+11 -1: getIterator
+35 -1: java/util/function/IntUnaryOperator
+35 -1: java/util/WeakHashMap$EntryIterator
+90 -1: <E:Ljava/lang/Object;>(Ljava/util/Queue<TE;>;Ljava/lang/Class<TE;>;)Ljava/util/Queue<TE;>;
+8 -1: batchFor
+16 -1: isValidCodePoint
+27 -1: ([Lsun/util/calendar/Era;)V
+16 -1: ThreadGroup.java
+33 2: sun/net/www/protocol/file/Handler
+7 -1: isField
+22 -1: sun/misc/OSEnvironment
+38 -1: Ljava/lang/Class<Ljava/lang/Boolean;>;
+12 -1: ADDRESS_SIZE
+8 -1: forDigit
+49 -1: (Ljava/lang/Object;)Ljava/util/WeakHashMap$Entry;
+13 -1: getCodeSource
+41 -1: ([Ljava/lang/Class<*>;)Ljava/lang/String;
+39 -1: (Ljava/util/function/Predicate<-TE;>;)Z
+13 -1: asFloatBuffer
+34 -1: ()Lsun/reflect/generics/tree/Tree;
+3 -1: ftp
+18 -1: maybeReBoxElements
+82 -1: ([BLsun/reflect/ConstantPool;Ljava/lang/Class;)[[Ljava/lang/annotation/Annotation;
+48 -1: ()Ljava/util/Set<Ljava/util/Map$Entry<TK;TV;>;>;
+27 -1: (Ljava/util/NavigableMap;)V
+18 -1: parameterSlotCount
+20 -1: NF_getCallSiteTarget
+16 -1: aliases_US_ASCII
+13 -1: NF_staticBase
+31 -1: sun/reflect/ConstructorAccessor
+26 -1: guessContentTypeFromStream
+10 -1: Deprecated
+35 -1: System initialization has completed
+11 -1: initialized
+7 -1: compare
+15 -1: maxDirectMemory
+19 -1: setLastModifiedTime
+7 -1: (J[BZ)J
+12 -1: readEpochSec
+66 -1: (Ljava/lang/String;Ljava/lang/String;)Lsun/util/locale/BaseLocale;
+69 -1: ()Lsun/misc/JavaSecurityProtectionDomainAccess$ProtectionDomainCache;
+9 -1: Constants
+11 -1: valueOffset
+62 -1: (Ljava/util/Hashtable<Ljava/lang/String;Ljava/lang/Object;>;)V
+33 -1: java/lang/CharacterDataPrivateUse
+21 -1: Exception in thread "
+40 -1: ()Ljava/util/Set<Ljava/lang/Character;>;
+12 -1: Asia/Yerevan
+40 -1: (Ljava/lang/Throwable;)Ljava/lang/Error;
+25 -1: (IS)Ljava/nio/ByteBuffer;
+7 -1: (I[CI)I
+45 -1: java/nio/charset/UnmappableCharacterException
+23 -1: java/util/WeakHashMap$1
+21 -1: setFXLaunchParameters
+1250 -1: ADANDAEAREAFAFGAGATGAIAIAALALBAMARMANANTAOAGOAQATAARARGASASMATAUTAUAUSAWABWAXALAAZAZEBABIHBBBRBBDBGDBEBELBFBFABGBGRBHBHRBIBDIBJBENBLBLMBMBMUBNBRNBOBOLBQBESBRBRABSBHSBTBTNBVBVTBWBWABYBLRBZBLZCACANCCCCKCDCODCFCAFCGCOGCHCHECICIVCKCOKCLCHLCMCMRCNCHNCOCOLCRCRICUCUBCVCPVCWCUWCXCXRCYCYPCZCZEDEDEUDJDJIDKDNKDMDMADODOMDZDZAECECUEEESTEGEGYEHESHERERIESESPETETHFIFINFJFJIFKFLKFMFSMFOFROFRFRAGAGABGBGBRGDGRDGEGEOGFGUFGGGGYGHGHAGIGIBGLGRLGMGMBGNGINGPGLPGQGNQGRGRCGSSGSGTGTMGUGUMGWGNBGYGUYHKHKGHMHMDHNHNDHRHRVHTHTIHUHUNIDIDNIEIRLILISRIMIMNININDIOIOTIQIRQIRIRNISISLITITAJEJEYJMJAMJOJORJPJPNKEKENKGKGZKHKHMKIKIRKMCOMKNKNAKPPRKKRKORKWKWTKYCYMKZKAZLALAOLBLBNLCLCALILIELKLKALRLBRLSLSOLTLTULULUXLVLVALYLBYMAMARMCMCOMDMDAMEMNEMFMAFMGMDGMHMHLMKMKDMLMLIMMMMRMNMNGMOMACMPMNPMQMTQMRMRTMSMSRMTMLTMUMUSMVMDVMWMWIMXMEXMYMYSMZMOZNANAMNCNCLNENERNFNFKNGNGANINICNLNLDNONORNPNPLNRNRUNUNIUNZNZLOMOMNPAPANPEPERPFPYFPGPNGPHPHLPKPAKPLPOLPMSPMPNPCNPRPRIPSPSEPTPRTPWPLWPYPRYQAQATREREUROROURSSRBRURUSRWRWASASAUSBSLBSCSYCSDSDNSESWESGSGPSHSHNSISVNSJSJMSKSVKSLSLESMSMRSNSENSOSOMSRSURSSSSDSTSTPSVSLVSXSXMSYSYRSZSWZTCTCATDTCDTFATFTGTGOTHTHATJTJKTKTKLTLTLSTMTKMTNTUNTOTONTRTURTTTTOTVTUVTWTWNTZTZAUAUKRUGUGAUMUMIUSUSAUYURYUZUZBVAVATVCVCTVEVENVGVGBVIVIRVNVNMVUVUTWFWLFWSWSMYEYEMYTMYTZAZAFZMZMBZWZWE
+60 -1: Ljava/util/Set<Ljava/lang/Class<+Ljava/lang/ClassLoader;>;>;
+24 -1: ()Ljava/security/Policy;
+7 -1: initted
+44 -1: java/util/Collections$UnmodifiableCollection
+12 -1: Pacific/Apia
+23 -1: checkProxyPackageAccess
+7 -1: (I[CI)V
+64 -1: ([Ljava/lang/Object;IILjava/lang/Object;Ljava/util/Comparator;)I
+16 -1: getJavaNioAccess
+7 -1: reverse
+7 -1: nocerts
+16 -1: activeGroupCount
+34 -1: java/util/jar/JarFile$JarFileEntry
+7 -1: loaders
+9 -1: toRadians
+24 -1: java/util/HashMap$KeySet
+37 -1: (Ljava/lang/Class;)Ljava/lang/Object;
+6 -1: getRef
+6 -1: H_DASH
+17 -1: LinkageError.java
+66 -1: (Ljava/lang/invoke/MethodTypeForm;)Ljava/lang/invoke/MethodHandle;
+41 -1: (Ljava/nio/ByteBuffer;)Ljava/util/BitSet;
+10 -1: addMinutes
+58 -1: <T:Ljava/lang/Object;>([TT;II)Ljava/util/Spliterator<TT;>;
+9 -1: parseJars
+13 -1: getUnsignedCS
+28 -1: (Ljava/util/AbstractList;I)V
+22 -1: threadLocalRandomProbe
+19 -1: newDirectByteBuffer
+27 -1: Filter already registered: 
+8 -1: unescape
+31 -1: sun/misc/URLClassPath$JarLoader
+6 -1: TAIWAN
+53 -1: <T:Ljava/lang/Object;>()Ljava/util/ListIterator<TT;>;
+17 -1: REVERSE_THRESHOLD
+31 -1: Java(TM) SE Runtime Environment
+7 -1: SECONDS
+70 -1: (Ljava/util/function/ToLongFunction<-TT;>;)Ljava/util/Comparator<TT;>;
+7 -1: BLOCKED
+6 -1: Caches
+63 -1: (Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;)V
+2 -1: : 
+210 -1: (Ljava/util/Map<Ljava/lang/Class<+Ljava/lang/annotation/Annotation;>;Ljava/lang/annotation/Annotation;>;Ljava/util/Map<Ljava/lang/Class<+Ljava/lang/annotation/Annotation;>;Ljava/lang/annotation/Annotation;>;I)V
+153 -1: (JLjava/util/function/BiFunction<Ljava/util/Map$Entry<TK;TV;>;Ljava/util/Map$Entry<TK;TV;>;+Ljava/util/Map$Entry<TK;TV;>;>;)Ljava/util/Map$Entry<TK;TV;>;
+22 -1: ()Ljava/lang/Class<*>;
+28 -1: Ljava/lang/OutOfMemoryError;
+19 -1: writeFileDescriptor
+39 -1: Ljava/util/LinkedHashMap$Entry<TK;TV;>;
+26 -1: (ILjava/util/Collection;)Z
+18 -1: getEncodedInternal
+16 -1: ForEachValueTask
+23 -1: (Ljava/util/List<*>;I)V
+19 -1: SharedArchiveLoader
+20 -1: probeBackupLocations
+24 -1: java/lang/StringCoding$1
+28 -1: lookupContentHandlerClassFor
+36 -1: ()Lsun/misc/Launcher$ExtClassLoader;
+27 -1: reflectionFactoryAccessPerm
+14 -1: ACCESSOR_FORMS
+35 -1: ([JII)Ljava/util/stream/LongStream;
+34 -1: ISO-8859-1 charset not available: 
+8 -1: cscesu-8
+2 -1: ;/
+17 -1: typeToPackageName
+34 -1: (Ljava/net/URL;)Ljava/lang/String;
+5 -1: (IZ)V
+25 -1: Prohibited package name: 
+51 -1: (Ljava/lang/Object;Ljava/lang/ref/ReferenceQueue;)V
+108 -1: (JLjava/util/function/ToIntFunction<Ljava/util/Map$Entry<TK;TV;>;>;ILjava/util/function/IntBinaryOperator;)I
+12 -1: soleInstance
+27 -1: (Ljava/io/BufferedReader;)V
+71 -1: (Ljava/nio/charset/CodingErrorAction;)Ljava/nio/charset/CharsetDecoder;
+17 -1: Ljava/lang/Class;
+38 -1: (Ljava/lang/String;)Ljava/lang/Double;
+10 -1: viewAsType
+22 -1: (Ljava/io/DataInput;)I
+22 -1: (Ljava/io/DataInput;)J
+105 -1: (Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/WrongMethodTypeException;
+16 -1: setJavaNetAccess
+73 -1: (ILjava/lang/Object;Ljava/lang/Object;ZZ)Ljava/util/HashMap$Node<TK;TV;>;
+22 -1: (Ljava/io/DataInput;)V
+14 -1: getHostAddress
+37 -1: sun/reflect/annotation/TypeAnnotation
+14 -1: ENCLOSING_MARK
+5 -1: FALSE
+14 -1: preDefineClass
+9 -1: newKeySet
+18 -1: getWaitQueueLength
+32 -1: ()Lsun/misc/URLClassPath$Loader;
+54 -1: (Ljava/nio/CharBuffer;I)Ljava/nio/charset/CoderResult;
+3 -1: SEP
+45 -1: (ITK;TV;Ljava/util/Hashtable$Entry<TK;TV;>;)V
+17 -1: getDeclaredFields
+7 -1: getDate
+10 -1: getClasses
+240 -1: (Ljava/util/concurrent/ConcurrentHashMap$BulkTask;III[Ljava/util/concurrent/ConcurrentHashMap$Node;Ljava/util/concurrent/ConcurrentHashMap$MapReduceEntriesToIntTask;Ljava/util/function/ToIntFunction;ILjava/util/function/IntBinaryOperator;)V
+12 -1: WeakClassKey
+14 -1: LF_GEN_INVOKER
+25 -1: Ljava/lang/StringBuilder;
+2 -1: > 
+9 -1: getJarMap
+4 -1: asin
+37 -1: (Ljava/net/URLStreamHandlerFactory;)V
+30 -1: not a constructor type or name
+4 -1: main
+22 -1: java/io/FilenameFilter
+22 -1: sun.java.launcher.diag
+30 -1: ()Ljava/util/Spliterator<TE;>;
+57 -1: <T::Ljava/lang/Comparable<-TT;>;>(Ljava/util/List<TT;>;)V
+86 -1: <T:Ljava/lang/Object;>(Ljava/lang/ThreadLocal<Ljava/lang/ref/SoftReference<TT;>;>;)TT;
+18 -1: canBeCalledVirtual
+9 -1: Shift_JIS
+24 -1: ()Ljava/util/ArrayDeque;
+32 -1: (Ljava/lang/Class$MethodArray;)V
+22 -1: java/lang/StringCoding
+33 -1: sun/util/locale/LocaleObjectCache
+20 -1: Sorry, deque too big
+38 -1: java/lang/Throwable$WrappedPrintWriter
+25 -1: (Ljava/io/InputStream;J)J
+44 -1: (Ljava/security/Permission;)Ljava/util/List;
+5 -1: thunk
+5 -1: props
+15 -1: getLastModified
+120 -1: (Ljava/lang/String;Ljava/lang/String;Ljava/util/HashMap<Ljava/lang/String;Ljava/util/LinkedList<Ljava/lang/String;>;>;)V
+146 -1: (Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MemberName;Ljava/lang/Class;Ljava/lang/invoke/MethodHandleImpl$1;)V
+27 -1: parseExtensionsDependencies
+10 -1: getRawType
+39 -1: (Ljava/nio/charset/CodingErrorAction;)V
+22 -1: ObjectStreamField.java
+6 -1: handle
+11 -1: hasPrevious
+18 -1: instanceof Float: 
+52 -1: (ZLjava/io/OutputStream;Ljava/nio/charset/Charset;)V
+4 -1: make
+13 -1: isIdeographic
+26 -1: java/util/HashMap$EntrySet
+51 -1: (Ljava/util/Hashtable;)[Ljava/util/Hashtable$Entry;
+91 -1: (Ljava/lang/CharSequence;Ljava/lang/Iterable<+Ljava/lang/CharSequence;>;)Ljava/lang/String;
+13 -1: makeAllocator
+10 -1: , headless
+20 -1: expungeStaleElements
+58 -1: sun/reflect/annotation/TypeAnnotation$TypeAnnotationTarget
+41 -1: ([Ljava/lang/Object;Ljava/lang/Class$1;)V
+50 -1: <T:Ljava/lang/Object;>(I)Ljava/util/Iterator<TT;>;
+7 -1: TreeBin
+21 -1: Ljava/io/IOException;
+56 -1: (I[Ljava/lang/Class;)[Ljava/lang/invoke/LambdaForm$Name;
+6 -1: LOCFLG
+6 -1: DIRECT
+3 -1: SIG
+37 -1: java/security/NoSuchProviderException
+39 -1: " with illegal data type conversion to 
+16 -1: getCodeSourceURL
+51 -1: java/util/concurrent/ConcurrentHashMap$EntrySetView
+47 -1: (Ljava/util/ArrayList;Ljava/util/ArrayList$1;)V
+6 -1: delete
+38 -1: sun/reflect/UnsafeFieldAccessorFactory
+11 -1: isDestroyed
+140 -1: ([Ljava/util/concurrent/ConcurrentHashMap$Node;ILjava/util/concurrent/ConcurrentHashMap$Node;Ljava/util/concurrent/ConcurrentHashMap$Node;)Z
+9 -1: unaligned
+36 -1: ()Lsun/misc/Launcher$AppClassLoader;
+57 -1: (Ljava/lang/String;Ljava/util/Locale;)[Ljava/lang/String;
+37 -1: sun/reflect/annotation/AnnotationType
+49 -1: <T:Ljava/lang/Object;>([TT;)Ljava/util/List<TT;>;
+24 -1: (S)Ljava/nio/ByteBuffer;
+6 -1: ([DD)I
+9 -1: setTarget
+29 -1: (IF)Ljava/lang/StringBuilder;
+12 -1: forBasicType
+10 -1: (IIII[BI)V
+8 -1: ([DIID)I
+9 -1: BASE_YEAR
+19 -1: ()Ljava/lang/Error;
+42 -1: (Ljava/util/Map<TE;Ljava/lang/Boolean;>;)V
+6 -1: ([DD)V
+14 -1: Illegal size: 
+8 -1: ([DIID)V
+7 -1: (II[I)I
+17 -1: java_profile_name
+28 -1: java/util/AbstractCollection
+43 -1: (Ljava/net/URL;)[Ljava/security/CodeSource;
+62 -1: (Lsun/misc/URLClassPath$JarLoader;)Ljava/net/URLStreamHandler;
+33 -1: [Ljava/util/HashMap$Node<TK;TV;>;
+15 -1: (Native Method)
+11 -1: fileNameMap
+26 -1: ()Ljava/util/ListIterator;
+25 -1: java/util/LinkedList$Node
+18 -1: SELECT_ALTERNATIVE
+35 -1: (Ljava/lang/Object;)Ljava/util/Set;
+19 -1: java/io/IOException
+16 -1: : already loaded
+9 -1: image/gif
+6 -1: (TE;)I
+25 -1: (Ljava/util/Properties;)V
+40 -1: (Ljava/lang/String;)Ljava/nio/file/Path;
+19 -1: checkedNavigableMap
+9 -1: checkInt(
+17 -1: getContentTypeFor
+26 -1: ()Ljava/io/FileDescriptor;
+69 -1: (Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/ref/ReferenceQueue;)V
+6 -1: (TE;)V
+25 -1: WARNING: Default charset 
+14 -1: ZipFile closed
+2 -1: CA
+6 -1: (TE;)Z
+64 -1: java/util/concurrent/ConcurrentHashMap$MapReduceValuesToLongTask
+15 -1: FileSystem.java
+75 -1: (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+10 -1: FileLoader
+44 -1: (Lsun/util/PreHashedMap;)[Ljava/lang/Object;
+19 -1: availableProcessors
+2 -1: CN
+36 -1: ([JII)Ljava/util/Spliterator$OfLong;
+11 -1: access$1100
+18 -1: getFieldAtIfLoaded
+30 -1: PrivilegedActionException.java
+6 -1: EUC-JP
+20 -1: (F)Ljava/lang/Float;
+22 -1: unable to instantiate 
+31 -1: java/lang/reflect/ReflectAccess
+23 -1: (Ljava/lang/Object;JC)V
+3 -1: get
+59 -1: <T:Ljava/lang/Object;>([TT;IILjava/util/Comparator<-TT;>;)V
+2 -1: DE
+13 -1: GMT_ID_LENGTH
+7 -1: execute
+12 -1: MethodHandle
+18 -1: AllPermission.java
+54 -1: (Ljava/util/Locale;)Lsun/util/locale/LocaleExtensions;
+23 -1: MapReduceKeysToLongTask
+12 -1: varargsArray
+23 -1: java/util/jar/JarFile$1
+23 -1: java/util/jar/JarFile$2
+23 -1: java/util/jar/JarFile$3
+12 -1: getAndUpdate
+13 -1: reserveMemory
+17 -1: expungeStaleEntry
+6 -1: EUC-KR
+120 -1: <K:Ljava/lang/Object;V:Ljava/lang/Object;>Ljava/lang/ref/WeakReference<Ljava/lang/Object;>;Ljava/util/Map$Entry<TK;TV;>;
+69 -1: (Ljava/lang/StringBuffer;Ljava/lang/String;)Ljava/util/regex/Matcher;
+26 -1: ()Ljava/util/NavigableMap;
+7 -1: L_PCHAR
+23 -1: (Ljava/lang/String$1;)V
+4 -1: KEYS
+37 -1: (Ljava/util/List;Ljava/lang/Object;)I
+31 -1: Ljava/lang/ArithmeticException;
+15 -1: [Ljava/net/URL;
+37 -1: (Ljava/util/List;Ljava/lang/Object;)V
+18 -1: MAX_HIGH_SURROGATE
+6 -1: (JCZ)V
+4 -1: mark
+17 -1: setMethodAccessor
+21 -1: java/io/ExpiringCache
+21 -1: PrivilegedAction.java
+21 -1: MappedByteBuffer.java
+2 -1: FR
+10 -1: copyMemory
+8 -1: L_SERVER
+13 -1: assertionLock
+12 -1: searchValues
+46 -1: (Ljava/util/Collection<*>;Ljava/lang/Object;)I
+21 -1: ProtectionDomainCache
+32 -1: USE_PREDEFINED_INTERPRET_METHODS
+2 -1: GB
+16 -1: getFinalRefCount
+23 -1: Ljava/lang/ClassLoader;
+4 -1: mask
+94 -1: <T:Ljava/lang/Object;>(Ljava/util/function/ToDoubleFunction<-TT;>;)Ljava/util/Comparator<TT;>;
+4 -1: bind
+41 -1: (Ljava/lang/Class<*>;I)Ljava/lang/Object;
+9 -1: COUNT_GWT
+16 -1: DASH_PUNCTUATION
+24 -1: UNICODE_LOCALE_EXTENSION
+15 -1: checkInvariants
+10 -1: stringSize
+12 -1: deepHashCode
+30 -1: java/security/cert/Certificate
+19 -1: America/Los_Angeles
+19 -1: unmappableForLength
+6 -1: UTF-16
+10 -1: methodType
+21 -1: sun/misc/URLClassPath
+19 -1: META-INF/INDEX.LIST
+10 -1: jniVersion
+6 -1: IBM437
+29 -1: sun/reflect/FieldAccessorImpl
+21 -1: ()Ljava/lang/Package;
+32 -1: java/security/SecurityPermission
+57 -1: (Lsun/util/calendar/Era;)Lsun/util/calendar/CalendarDate;
+34 -1: [Ljava/util/concurrent/locks/Lock;
+11 -1: replacement
+20 -1: ()Ljava/lang/String;
+6 -1: resize
+12 -1: UTF_32BE_BOM
+26 -1: (Ljava/util/jar/JarFile;)V
+24 -1: DEFAULT_INITIAL_CAPACITY
+87 -1: (ILjava/lang/Object;Ljava/lang/Class;)Ljava/util/concurrent/ConcurrentHashMap$TreeNode;
+74 -1: (Ljava/util/jar/JarFile;)Ljava/util/Enumeration<Ljava/util/jar/JarEntry;>;
+18 -1: parameterSlotDepth
+26 -1: (Ljava/util/jar/JarFile;)Z
+18 -1: makePlatformString
+24 -1: doPrivilegedWithCombiner
+48 -1: (Ljava/lang/String;)Lsun/util/calendar/ZoneInfo;
+27 -1: ()Ljava/lang/ref/Reference;
+40 -1: java/util/ArrayList$ArrayListSpliterator
+67 -1: ([Ljava/lang/Object;IILjava/util/Comparator;[Ljava/lang/Object;II)V
+2 -1: ID
+19 -1: stringPropertyNames
+28 -1: (Ljava/util/Collections$1;)V
+12 -1: STATE_YELLOW
+12 -1: isNormalized
+10 -1: fromIndex(
+16 -1: getFloatVolatile
+37 -1: Lsun/util/calendar/BaseCalendar$Date;
+10 -1: properties
+17 -1: peakFinalRefCount
+102 -1: (Ljava/util/HashMap$Node<TK;TV;>;Ljava/util/HashMap$Node<TK;TV;>;)Ljava/util/HashMap$TreeNode<TK;TV;>;
+68 -1: (Ljava/lang/invoke/MemberName;)Ljava/lang/invoke/DirectMethodHandle;
+2 -1: IT
+9 -1: ([BI[BI)V
+51 -1: scl           permissions SecureClassLoader assigns
+36 -1: (Ljava/util/Set;Ljava/lang/Object;)V
+11 -1: ([SII[SII)V
+21 -1: sun.io.useCanonCaches
+18 -1: Illegal capacity: 
+22 -1: (Ljava/lang/Integer;)I
+83 -1: ([Ljava/lang/Object;Ljava/lang/StringBuilder;Ljava/util/Set<[Ljava/lang/Object;>;)V
+65 -1: (Ljava/util/HashMap<TK;TV;>;[Ljava/util/HashMap$Node<TK;TV;>;II)V
+17 -1: java/util/Objects
+48 -1: (ILjava/util/List;)Ljava/lang/invoke/LambdaForm;
+31 -1: java/util/Properties$XmlSupport
+10 -1: L_LOWALPHA
+13 -1: long overflow
+25 -1: NullPointerException.java
+32 -1: (I)Ljava/lang/StackTraceElement;
+34 -1: ()[Ljava/lang/reflect/Constructor;
+2 -1: JP
+3 -1: SST
+12 -1: ShortCountry
+48 -1: (Ljava/util/stream/Collector;)Ljava/lang/Object;
+29 -1: Lsun/reflect/CallerSensitive;
+10 -1: addElement
+12 -1: lastReturned
+6 -1: putInt
+34 -1: sun.misc.JarIndex.metaInfFilenames
+13 -1: getBaseLocale
+20 -1: StringTokenizer.java
+8 -1: entrySet
+11 -1: getTypeName
+17 -1: America/Sao_Paulo
+5 -1: \t... 
+35 -1: (Lsun/util/calendar/CalendarDate;)I
+28 -1: java/lang/StackOverflowError
+35 -1: (Lsun/util/calendar/CalendarDate;)J
+10 -1: logicalAnd
+18 -1: csISOLatinCyrillic
+43 -1: (Ljava/lang/String;II)Ljava/nio/CharBuffer;
+73 -1: (Ljava/lang/Class<*>;[Ljava/lang/Class<*>;Z)Ljava/lang/invoke/MethodType;
+14 -1: aliases_MS1250
+14 -1: aliases_MS1251
+17 -1: getImplMethodKind
+17 -1: getLastAccessTime
+14 -1: aliases_MS1252
+14 -1: aliases_MS1253
+35 -1: (Lsun/util/calendar/CalendarDate;)V
+2 -1: KR
+14 -1: aliases_MS1254
+14 -1: getGenericInfo
+8 -1: utf_32be
+14 -1: aliases_MS1257
+35 -1: (Lsun/util/calendar/CalendarDate;)Z
+13 -1: StringEncoder
+7 -1: LDT2037
+7 -1: generic
+2 -1: L9
+45 -1: ([DLjava/util/function/IntToDoubleFunction;)V
+17 -1: isOtherAlphabetic
+9 -1: implWrite
+24 -1: PC-Multilingual-850+euro
+12 -1: valueMatches
+78 -1: (Ljava/lang/String;Lsun/util/locale/ParseStatus;)Lsun/util/locale/LanguageTag;
+3 -1: gmt
+19 -1: (Ljava/io/Reader;)V
+25 -1: (JJ)Ljava/nio/ByteBuffer;
+7 -1: val$url
+26 -1: (Ljava/nio/ByteBuffer;IJ)V
+10 -1: isImplicit
+19 -1: getDeclaredClasses0
+4 -1: (I)B
+4 -1: (I)C
+4 -1: (I)D
+9 -1: byteValue
+4 -1: (I)F
+5 -1: ([J)I
+6 -1: isLive
+5 -1: sleep
+4 -1: (I)I
+4 -1: (I)J
+6 -1: outBuf
+77 -1: <E:Ljava/lang/Object;>Ljava/util/AbstractCollection<TE;>;Ljava/util/Set<TE;>;
+4 -1: (I)S
+5 -1: ([J)V
+4 -1: (I)V
+30 -1: (I[C)Ljava/lang/StringBuilder;
+14 -1: intBitsToFloat
+4 -1: (I)Z
+15 -1: MethodType.java
+14 -1: resolveSibling
+9 -1: Enum.java
+111 -1: ([Ljava/util/concurrent/ConcurrentHashMap$Node<TK;TV;>;[Ljava/util/concurrent/ConcurrentHashMap$Node<TK;TV;>;)V
+14 -1: IS_CONSTRUCTOR
+4 -1: bits
+34 -1: java/security/PermissionCollection
+9 -1: autoFlush
+21 -1: java/util/Collections
+12 -1: bindReceiver
+20 -1: DMH.invokeStaticInit
+11 -1: charsetName
+14 -1: x-utf-32be-bom
+5 -1: cause
+7 -1: handle0
+43 -1: ([I[C[Ljava/lang/invoke/LambdaForm$Name;I)Z
+30 -1: getDefaultAllowUserInteraction
+18 -1: ConcurrentMap.java
+35 -1: (Ljava/lang/String;Z)Ljava/net/URL;
+33 -1: Ljava/nio/charset/CharsetDecoder;
+36 -1: java/lang/invoke/MethodHandleStatics
+34 -1: java/util/concurrent/ConcurrentMap
+16 -1: collectArguments
+22 -1: packageAssertionStatus
+79 -1: (JLjava/util/function/ToLongFunction;JLjava/util/function/LongBinaryOperator;)J
+39 -1: Cannot reflectively create enum objects
+62 -1: attempt to add a Permission to a readonly PermissionCollection
+22 -1: FieldAccessorImpl.java
+9 -1: ByteCache
+4 -1: TRUE
+85 -1: Ljava/lang/Object;Ljava/io/Serializable;Ljava/lang/Comparable<Ljava/lang/Character;>;
+17 -1: java.library.path
+7 -1: encoder
+21 -1:     default locale = 
+11 -1: secondOfDay
+37 -1: (Lsun/util/calendar/ZoneInfoFile$1;)V
+35 -1: sun/reflect/UnsafeFieldAccessorImpl
+24 -1: (Ljava/lang/Object;JJJ)V
+16 -1: countStackFrames
+24 -1: (Ljava/lang/Object;JJJ)Z
+17 -1: nonfairTryAcquire
+20 -1: ArrayListSpliterator
+5 -1: /DMH=
+68 -1: (Ljava/lang/CharSequence;Ljava/lang/CharSequence;)Ljava/lang/String;
+2 -1: PI
+8 -1: cspcp852
+112 -1: (Ljava/util/List<Ljava/util/Locale$LanguageRange;>;Ljava/util/Collection<Ljava/util/Locale;>;)Ljava/util/Locale;
+8 -1: cspcp855
+58 -1: (Ljava/lang/Object;JLjava/lang/Object;Ljava/lang/Object;)Z
+33 -1: sun/misc/PerfCounter$CoreCounters
+6 -1: CESU_8
+7 -1: vmslots
+4 -1: Init
+7 -1: handler
+11 -1: getProperty
+10 -1: isVolatile
+8 -1: ([J[IJ)I
+25 -1: ()Ljava/lang/ClassLoader;
+8 -1: asChange
+81 -1: (Ljava/net/URLClassLoader;Ljava/lang/String;Lsun/misc/Resource;)Ljava/lang/Class;
+12 -1: naturalOrder
+8 -1: getState
+40 -1: (Ljava/lang/Object;I)[Ljava/lang/String;
+36 -1: java/security/AccessControlException
+10 -1: linkBefore
+48 -1: (Ljava/util/HashMap;[Ljava/util/HashMap$Node;Z)V
+26 -1: (Ljava/util/WeakHashMap;)V
+23 -1: bad method type alias: 
+7 -1: putChar
+18 -1: basicTypeSignature
+33 -1: sun/misc/InvalidJarIndexException
+13 -1: getPrivateuse
+14 -1: isConstantZero
+24 -1: java/io/FilePermission$1
+9 -1: Long.java
+19 -1: getLocaleExtensions
+10 -1: discovered
+17 -1: Invalid file path
+12 -1: MAX_MH_ARITY
+20 -1: observesDaylightTime
+31 -1: Ljava/lang/invoke/MethodHandle;
+6 -1: , end 
+24 -1: java/io/FileDescriptor$1
+12 -1: loadLibrary.
+11 -1: Method.java
+12 -1: loadLibrary0
+23 -1: java/util/stream/Stream
+37 -1: sun.lang.ClassLoader.allowArraySyntax
+8 -1: appClass
+15 -1: FileReader.java
+5 -1: (IF)I
+29 -1: [Ljava/lang/ClassValue$Entry;
+12 -1: (principals 
+30 -1: jar           jar verification
+22 -1: ARRAY_CHAR_BASE_OFFSET
+18 -1: newDirectoryStream
+4 -1: atan
+5 -1: (IF)V
+24 -1: (Ljava/lang/Character;)I
+2 -1: TH
+10 -1: startsWith
+9 -1: baseCount
+13 -1: canonicalize0
+47 -1: (Ljava/lang/ClassLoader;[Ljava/lang/Class<*>;)V
+22 -1: Ljava/io/OutputStream;
+2 -1: TW
+10 -1: H_RESERVED
+19 -1: URLClassLoader.java
+16 -1: isAccessibleFrom
+59 -1: Ljava/util/Hashtable<Ljava/lang/Object;Ljava/lang/Object;>;
+33 -1: java/lang/invoke/ConstantCallSite
+14 -1: Ljava/net/URL;
+12 -1: deleteOnExit
+20 -1: MAX_SKIP_BUFFER_SIZE
+30 -1: [Ljava/lang/ref/WeakReference;
+15 -1: contentPathProp
+9 -1: initCause
+53 -1: (Ljava/util/Queue;Ljava/lang/Class;)Ljava/util/Queue;
+20 -1: java/util/TimeZone$1
+2 -1: UK
+86 -1: (BLjava/lang/Class;Ljava/lang/invoke/MemberName;)Ljava/lang/invoke/DirectMethodHandle;
+51 -1: (II[Ljava/lang/Class;)Ljava/lang/invoke/MethodType;
+18 -1: Lsun/misc/Cleaner;
+31 -1: RuntimeInvisibleTypeAnnotations
+2 -1: US
+7 -1: makeInt
+40 -1: sun/reflect/annotation/AnnotationSupport
+28 -1: sun/reflect/misc/ReflectUtil
+8 -1: utf_32le
+40 -1: (Ljava/lang/Object;JLjava/lang/Object;)V
+24 -1: java/nio/file/WatchEvent
+66 -1: (Ljava/lang/Class;Ljava/lang/Object;)Ljava/lang/invoke/MethodType;
+91 -1: (Ljava/lang/String;[BIILjava/security/ProtectionDomain;Ljava/lang/String;)Ljava/lang/Class;
+114 -1: (Ljava/lang/ThreadLocal;ILjava/lang/ThreadLocal$ThreadLocalMap$Entry;)Ljava/lang/ThreadLocal$ThreadLocalMap$Entry;
+20 -1: internalWriteEntries
+79 -1: <T:Ljava/lang/Object;>(TT;Ljava/util/function/Supplier<Ljava/lang/String;>;)TT;
+14 -1: bitIndex < 0: 
+88 -1: (Ljava/security/PrivilegedAction;Ljava/security/AccessControlContext;)Ljava/lang/Object;
+6 -1: , len 
+28 -1: java/io/BufferedOutputStream
+11 -1: System.java
+11 -1: csISOLatin1
+11 -1: csISOLatin2
+28 -1: Ljava/io/OutputStreamWriter;
+27 -1: IllegalAccessException.java
+11 -1: csISOLatin4
+14 -1: isDaylightTime
+11 -1: csISOLatin5
+21 -1: getYearLengthInMonths
+13 -1: canonicalizes
+93 -1: "'s signer information does not match signer information of other classes in the same package
+32 -1: ()Lsun/management/GcInfoBuilder;
+37 -1: (IILjava/nio/charset/CoderResult$1;)V
+10 -1: stateNames
+46 -1: (Ljava/lang/String;)Ljava/nio/charset/Charset;
+21 -1: acquireMethodAccessor
+2 -1: X-
+32 -1: java/security/ProtectionDomain$1
+5 -1: atan2
+32 -1: java/security/ProtectionDomain$2
+32 -1: java/security/ProtectionDomain$3
+41 -1: malformed input: partial character at end
+18 -1: dropParameterTypes
+44 -1: (Ljava/lang/String;)Ljava/lang/StringBuffer;
+18 -1: CalendarUtils.java
+5 -1: month
+84 -1: Ljava/lang/Object;Ljava/security/PrivilegedExceptionAction<Ljava/lang/ClassLoader;>;
+33 -1: sun/misc/JavaNioAccess$BufferPool
+18 -1: SearchMappingsTask
+38 -1: DelegatingConstructorAccessorImpl.java
+80 -1: (Ljava/lang/invoke/MemberName;Ljava/lang/Class<*>;)Ljava/lang/invoke/MemberName;
+8 -1: instance
+54 -1: Parameter annotations don't match number of parameters
+14 -1: createConstant
+35 -1: java/lang/invoke/MemberName$Factory
+4 -1: scrt
+34 -1: Ljava/lang/InstantiationException;
+20 -1: allowUserInteraction
+15 -1: java/util/Deque
+16 -1: forEachRemaining
+35 -1: (J)Lsun/util/calendar/CalendarDate;
+13 -1: no such field
+25 -1: java/nio/file/FileSystems
+16 -1: expectedModCount
+44 -1: (Ljava/io/File;ILjava/nio/charset/Charset;)V
+12 -1: createString
+15 -1: useDaylightTime
+31 -1: java/util/Properties$LineReader
+111 -1: (Ljava/lang/Class<*>;Ljava/lang/String;Ljava/lang/String;ILjava/lang/Class<*>;I[Ljava/lang/invoke/MemberName;)I
+16 -1: java/util/Locale
+26 -1: URLClassPath.getResource("
+58 -1: (Ljava/util/SortedMap;Ljava/lang/Class;Ljava/lang/Class;)V
+10 -1: appendTail
+7 -1: cleaner
+78 -1: (Lsun/nio/cs/FastCharsetProvider;Ljava/lang/String;)Ljava/nio/charset/Charset;
+18 -1: convertOldISOCodes
+4 -1: year
+38 -1: java/lang/ReflectiveOperationException
+28 -1: (Ljava/lang/StringBuffer;B)V
+27 -1: (D)Ljava/lang/StringBuffer;
+17 -1: emptyNavigableSet
+7 -1: indices
+10 -1:  is sealed
+21 -1: SPECIFICATION_VERSION
+3 -1: TE;
+8 -1: addMonth
+24 -1: java/util/HashMap$Values
+17 -1: SEARCH_ALL_SUPERS
+18 -1: uncaught exception
+14 -1: declaredFields
+2 -1: [B
+2 -1: [C
+8 -1: ABSTRACT
+2 -1: [D
+2 -1: [F
+2 -1: [I
+2 -1: [J
+5 -1: false
+36 -1: (Ljava/net/URL;Ljava/lang/String;J)V
+12 -1: equalContext
+11 -1: VOID_RESULT
+2 -1: [S
+24 -1: (JLjava/lang/Object;JJ)V
+36 -1: Ljava/security/PermissionCollection;
+2 -1: [Z
+17 -1: floatToRawIntBits
+2 -1: []
+21 -1: ()[Ljava/lang/Thread;
+20 -1: [Ljava/lang/Integer;
+42 -1: (Ljava/lang/Class<*>;Ljava/lang/String;Z)V
+19 -1: checkPrintJobAccess
+40 -1: (Ljava/lang/Object;)Ljava/lang/Class<*>;
+31 -1: (Lsun/reflect/FieldAccessor;Z)V
+10 -1: reallyPoll
+52 -1: (Ljava/lang/ref/Reference;)Ljava/lang/ref/Reference;
+100 -1: <U:Ljava/lang/Object;>(JLjava/util/function/Function<-TV;+TU;>;Ljava/util/function/Consumer<-TU;>;)V
+19 -1: CheckedNavigableMap
+48 -1: (Ljava/lang/String;)Ljava/lang/RuntimeException;
+35 -1: java/util/Hashtable$ValueCollection
+89 -1: (JLjava/util/function/ToDoubleFunction<-TK;>;DLjava/util/function/DoubleBinaryOperator;)D
+10 -1: Stack.java
+57 -1: (Ljava/lang/Class;Ljava/lang/String;Ljava/lang/Object;I)V
+12 -1: getTimeOfDay
+12 -1: reduceValues
+22 -1: warnUnsupportedCharset
+34 -1: (Ljava/lang/ref/Reference<+TS;>;)Z
+31 -1: EEE, dd MMM yyyy HH:mm:ss 'GMT'
+19 -1: setCachedLambdaForm
+21 -1: (I)Ljava/lang/Object;
+85 -1: (Lsun/util/locale/BaseLocale;Lsun/util/locale/LocaleExtensions;Ljava/util/Locale$1;)V
+36 -1: java/security/AccessControlContext$1
+27 -1: Lsun/net/www/MessageHeader;
+23 -1: ()[Ljava/lang/Class<*>;
+14 -1: refKindIsValid
+41 -1: sun/reflect/NativeConstructorAccessorImpl
+6 -1: binary
+23 -1: ()Ljava/nio/CharBuffer;
+8 -1: getSpace
+45 -1: bootstrap method failed to produce a CallSite
+15 -1: getISO3Language
+17 -1: TRANSITION_NSHIFT
+163 -1: ([Ljava/lang/String;Ljava/util/Map<Ljava/lang/String;Ljava/util/List<Ljava/lang/String;>;>;)Ljava/util/Map<Ljava/lang/String;Ljava/util/List<Ljava/lang/String;>;>;
+7 -1: os.name
+38 -1: java/util/function/IntToDoubleFunction
+8 -1: checkInt
+21 -1: java/lang/VerifyError
+42 -1: sunpkcs11     SunPKCS11 provider debugging
+19 -1: URI is not absolute
+23 -1: java/io/DataInputStream
+53 -1: (Ljava/lang/Object;)Ljava/nio/charset/CharsetEncoder;
+2 -1: _#
+41 -1: (Ljava/io/FilenameFilter;)[Ljava/io/File;
+26 -1: Ljava/util/jar/Attributes;
+7 -1: collect
+17 -1: Lsun/misc/Unsafe;
+97 -1: (Ljava/lang/Class;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/MethodHandle;
+78 -1: <T:Ljava/lang/Object;>(Ljava/util/Enumeration<TT;>;)Ljava/util/ArrayList<TT;>;
+28 -1: (II)Ljava/lang/StringBuffer;
+54 -1: (Ljava/lang/reflect/Constructor<*>;)Ljava/lang/String;
+28 -1: ()Ljava/util/ResourceBundle;
+48 -1: java/util/ArraysParallelSortHelpers$FJInt$Sorter
+9 -1: no access
+57 -1: <S:Ljava/lang/Object;>Ljava/lang/ref/ReferenceQueue<TS;>;
+7 -1: getPerf
+11 -1: getClassAt0
+11 -1: rtypeOffset
+20 -1: thread can't be null
+12 -1: addArguments
+12 -1: utf_32le_bom
+21 -1: allowThreadSuspension
+25 -1: defaultExpectedLineLength
+11 -1: removeFirst
+13 -1: reduceEntries
+20 -1: Read-ahead limit < 0
+57 -1: <T:Ljava/lang/Object;>(Ljava/util/Collection<-TT;>;[TT;)Z
+39 -1: ()Ljava/lang/invoke/MemberName$Factory;
+13 -1: ZipCoder.java
+16 -1: java/lang/Thread
+27 -1: java/lang/NoSuchMethodError
+66 -1: (Ljava/util/jar/JarFile;Ljava/net/URL;)[Ljava/security/CodeSource;
+22 -1: java/lang/StringBuffer
+3 -1: TK;
+34 -1: (I)Ljava/lang/invoke/MethodHandle;
+30 -1: (Ljava/lang/reflect/Method;Z)V
+13 -1: file.encoding
+14 -1: removeTreeNode
+27 -1: sun/misc/JavaSecurityAccess
+58 -1: ([Ljava/lang/Object;ILjava/lang/Class;)[Ljava/lang/Object;
+19 -1: equalLimitedContext
+22 -1: appendVmSynonymMessage
+10 -1: applyAsInt
+23 -1: privateGetPublicMethods
+11 -1: dumpThreads
+25 -1: RuntimeVisibleAnnotations
+37 -1: (IF)Ljava/lang/AbstractStringBuilder;
+18 -1: getBooleanVolatile
+27 -1: (Ljava/util/zip/ZipFile;J)V
+25 -1: INITIAL_QUOTE_PUNCTUATION
+51 -1: (Ljava/util/Map;)[Ljava/lang/annotation/Annotation;
+54 -1: (ILjava/lang/Object;)Ljava/lang/AbstractStringBuilder;
+20 -1: reflectionDataOffset
+2 1: aa
+51 -1: (Ljava/util/jar/Attributes$Name;)Ljava/lang/String;
+13 -1: transferLinks
+18 -1: java/util/Vector$1
+10 -1: ensureOpen
+22 -1: getDeclaredConstructor
+2 -1: am
+19 -1: NF_allocateInstance
+66 -1: (Ljava/util/Spliterator$OfDouble;Z)Ljava/util/stream/DoubleStream;
+6 -1: ([SS)I
+17 -1: newReflectionData
+19 -1: subclassAuditsQueue
+11 -1: access$1200
+16 -1: ValueSpliterator
+18 -1: preparedLambdaForm
+38 -1: (Ljava/net/URL;)Ljava/net/InetAddress;
+14 -1: getMaxPriority
+32 -1: ()[Ljava/lang/StackTraceElement;
+67 -1: java/util/concurrent/ConcurrentHashMap$MapReduceEntriesToDoubleTask
+62 -1: (Ljava/util/Hashtable<Ljava/lang/String;Ljava/lang/String;>;)V
+6 -1: EXTHDR
+14 -1: java/util/Date
+2 -1: az
+6 -1: ([SS)V
+15 -1: arrayBaseOffset
+9 -1: isTrusted
+23 -1: (Ljava/lang/Object;JD)V
+2 -1: bb
+10 -1: ([BII[BI)V
+7 -1: toLower
+14 -1: invoke_LLLLL_L
+7 -1: jarfile
+42 -1: (Ljava/lang/String;)Lsun/misc/PerfCounter;
+28 -1: java/lang/ref/Reference$Lock
+16 -1: java/util/BitSet
+22 -1: jarfile parsing error!
+18 -1: jdk_update_version
+14 -1: invoke_LLLLL_V
+20 -1: java/util/LinkedList
+22 -1: erasedInvokerWithDrops
+25 -1: timeout value is negative
+21 -1: getCalendarProperties
+25 -1: not a method descriptor: 
+2 -1: cb
+31 -1: (Lsun/misc/JavaUtilJarAccess;)V
+2 -1: cd
+43 -1: Lsun/util/PreHashedMap<Ljava/lang/String;>;
+12 -1: getEntrySize
+2 -1: ce
+24 -1: guessContentTypeFromName
+2 -1: ch
+14 -1: JulianCalendar
+22 -1: [Ljava/lang/Cloneable;
+122 -1: <E:Ljava/lang/Object;>Ljava/util/AbstractCollection<TE;>;Ljava/util/Deque<TE;>;Ljava/lang/Cloneable;Ljava/io/Serializable;
+34 -1: Lsun/util/locale/BaseLocale$Cache;
+14 -1: LinkedEntrySet
+72 -1: <E:Ljava/lang/Object;>Ljava/util/AbstractSet<TE;>;Ljava/io/Serializable;
+39 -1: ()Ljava/io/ObjectOutputStream$PutField;
+2 -1: cs
+8 -1: indexFor
+25 -1: (Ljava/util/List<+TE;>;)V
+7 -1: subpath
+11 -1: invokeExact
+14 -1: setFileNameMap
+22 -1: LangReflectAccess.java
+8 -1: referent
+34 -1: java/util/MissingResourceException
+77 -1: (Ljava/lang/String;Ljava/util/jar/Manifest;Ljava/net/URL;)Ljava/lang/Package;
+11 -1: ([FII[FII)V
+17 -1: getParameterCount
+18 -1: isMemberAccessible
+10 -1: getExtDirs
+69 -1: <T:Ljava/lang/Object;>(Ljava/util/List<-TT;>;Ljava/util/List<+TT;>;)V
+9 -1: getNextPC
+13 -1: setProperties
+2 -1: de
+16 -1: generateCertPath
+6 -1: decode
+16 -1: getDefaultParent
+50 -1: (Ljava/net/URL;[Ljava/security/cert/Certificate;)V
+24 -1: Certificate factory for 
+35 -1: ()Ljava/lang/reflect/AnnotatedType;
+2 -1: ee
+12 -1: asLongBuffer
+7 -1: PRIVATE
+16 -1: aliases_UTF_32BE
+2 -1: en
+2 -1: eq
+7 -1: indexOf
+136 -1: (Ljava/lang/invoke/MethodHandle;Ljava/lang/Class<+Ljava/lang/Throwable;>;Ljava/lang/invoke/MethodHandle;)Ljava/lang/invoke/MethodHandle;
+24 -1: (Ljava/lang/Class<*>;I)V
+72 -1: (Ljava/lang/String;Ljava/lang/Object;)Ljava/lang/IllegalAccessException;
+35 -1: java/util/jar/JavaUtilJarAccessImpl
+24 -1: (Ljava/lang/Class<*>;I)Z
+2 -1: ex
+24 -1: getProtectionDomainCache
+101 -1: (Ljava/lang/ThreadGroup;Ljava/lang/Runnable;Ljava/lang/String;JLjava/security/AccessControlContext;)V
+3 -1: hit
+8 -1: UTF_16BE
+2 -1: fd
+16 -1: EmptyEnumeration
+4 -1: attr
+16 -1: fromIndex < -1: 
+7 -1: getIntB
+22 -1: java/io/FilePermission
+2 -1: fr
+2 -1: fs
+7 -1: lazySet
+7 -1: getIntL
+37 -1: configfile    JAAS ConfigFile loading
+24 -1: sun/nio/cs/StreamEncoder
+40 -1: (Ljava/time/ZoneId;)Ljava/util/TimeZone;
+132 -1: (Ljava/util/concurrent/ConcurrentHashMap$BulkTask;III[Ljava/util/concurrent/ConcurrentHashMap$Node;Ljava/util/function/BiConsumer;)V
+2 -1: gc
+18 -1: Ljava/lang/Thread;
+10 -1: cacheArray
+48 -1: (Ljava/util/jar/JarFile;)Ljava/util/jar/JarFile;
+61 -1: (Ljava/util/NavigableMap;Ljava/lang/Class;Ljava/lang/Class;)V
+8 -1: readByte
+7 -1: ([S[S)Z
+24 -1: findBootstrapClassOrNull
+2 -1: hb
+7 -1: REPLACE
+45 -1: ()Ljava/util/Enumeration<Ljava/lang/String;>;
+10 -1: isOverflow
+2 -1: he
+13 -1: getCachedJan1
+12 -1: getClassPath
+8 -1: leapYear
+31 -1: java/lang/invoke/MethodTypeForm
+10 -1: ANNOTATION
+20 -1: getGregorianCalendar
+11 -1: ISO_8859_13
+11 -1: ISO_8859_15
+6 -1: invoke
+2 -1: ht
+7 -1: ListItr
+15 -1: synchronizedMap
+7 -1: cleanup
+94 -1: (Ljava/lang/Class<+Ljava/lang/annotation/Annotation;>;)Lsun/reflect/annotation/AnnotationType;
+3 -1: TT;
+69 -1: (JLsun/util/calendar/CalendarDate;)Lsun/util/calendar/Gregorian$Date;
+81 -1: <K:Ljava/lang/Object;V:Ljava/lang/Object;>(Ljava/util/HashMap$TreeNode<TK;TV;>;)Z
+20 -1:     Min. Heap Size: 
+33 -1: (IZ)Ljava/lang/invoke/MethodType;
+2 -1: id
+7 -1: ([DI)[D
+37 -1: (Ljava/lang/reflect/Constructor<*>;)I
+42 -1: <T::Ljava/lang/Comparable<-TT;>;>([TT;II)V
+13 -1: addSuppressed
+28 -1: internalMemberNameEnsureInit
+2 -1: in
+17 -1: getCompressedSize
+34 -1: sun/misc/Launcher$AppClassLoader$1
+88 -1: (Ljava/security/DomainCombiner;Ljava/lang/Class<*>;)Ljava/security/AccessControlContext;
+37 -1: (Ljava/lang/reflect/Constructor<*>;)V
+2 -1: is
+2 -1: it
+6 -1: ENDOFF
+4 -1: void
+2 -1: iw
+14 -1: emptySortedSet
+2 -1: ix
+17 -1: unicode-1-1-utf-8
+64 -1: (Ljava/lang/Class;Ljava/util/List;)Ljava/lang/invoke/MethodType;
+46 -1: (Lsun/misc/URLClassPath$Loader;)Ljava/net/URL;
+2 -1: ja
+13 -1: synchronized 
+76 -1: ([DLjava/util/function/IntToDoubleFunction;)Ljava/util/function/IntConsumer;
+11 -1: wrapperType
+51 -1: <T:Ljava/lang/Object;>()Ljava/util/Comparator<TT;>;
+106 -1: (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/Object;
+17 -1: removeAllElements
+2 -1: ji
+24 -1: java/util/Vector$ListItr
+14 -1: getNestedTypes
+6 -1: L_MARK
+27 -1: Source does not fit in dest
+2 -1: l1
+2 -1: jp
+2 -1: l2
+4 -1: (J)B
+57 -1: (Ljava/lang/String;Ljava/util/Locale;I)Ljava/lang/String;
+4 -1: (J)C
+27 -1: ForEachTransformedValueTask
+2 -1: l4
+26 -1: java/util/Hashtable$KeySet
+4 -1: (J)D
+2 -1: l5
+39 -1: java/security/BasicPermissionCollection
+4 -1: (J)F
+2 -1: jv
+29 -1: MapReduceMappingsToDoubleTask
+18 -1: copyFromShortArray
+2 -1: l9
+3 -1: TV;
+4 -1: (J)I
+4 -1: (J)J
+23 -1: Lsun/util/calendar/Era;
+8 -1: x-EUC-TW
+15 -1: checkSetFactory
+7 -1: Special
+4 -1: (J)S
+4 -1: (J)V
+7 -1: invoke_
+25 -1: (IC)Ljava/nio/ByteBuffer;
+68 -1: (JLjava/util/concurrent/TimeUnit;)Ljava/nio/file/attribute/FileTime;
+36 -1: ()Ljava/net/URLStreamHandlerFactory;
+4 -1: (J)Z
+181 -1: <T:Ljava/lang/Object;>(Ljava/lang/Class<*>;Ljava/lang/ref/SoftReference<Ljava/lang/Class$ReflectionData<TT;>;>;Ljava/lang/ref/SoftReference<Ljava/lang/Class$ReflectionData<TT;>;>;)Z
+9 -1: getOffset
+46 -1: (ILjava/lang/String;)Ljava/lang/StringBuilder;
+7 -1: element
+15 -1: createByteArray
+17 -1: uncaughtException
+2 -1: ko
+29 -1: java/nio/file/DirectoryStream
+15 -1: getISOCountries
+246 -1: <NoSuchMemberException:Ljava/lang/ReflectiveOperationException;>(BLjava/lang/invoke/MemberName;Ljava/lang/Class<*>;Ljava/lang/Class<TNoSuchMemberException;>;)Ljava/lang/invoke/MemberName;^Ljava/lang/IllegalAccessException;^TNoSuchMemberException;
+7 -1: invoker
+17 -1: langReflectAccess
+10 -1: bindSingle
+23 -1: java/lang/reflect/Proxy
+2 -1: lb
+2 -1: lc
+29 -1: CREATE_CLASSLOADER_PERMISSION
+5 -1: isSet
+18 -1: ([Ljava/io/File;)V
+15 -1: urlNoFragString
+21 -1: DirectByteBuffer.java
+15 -1: java.class.path
+15 -1: createDirectory
+4 -1:  GMT
+37 -1: sun/reflect/annotation/ExceptionProxy
+28 -1: (Ljava/util/Map<+TK;+TV;>;)V
+17 -1: getContentHandler
+26 -1: GenericDeclRepository.java
+56 -1: (Ljava/lang/String;)Ljava/lang/IllegalArgumentException;
+15 -1: getJavaIOAccess
+39 -1: java/util/Collections$EmptyListIterator
+34 -1: java/lang/ConditionalSpecialCasing
+82 -1: (Ljava/lang/String;Ljava/lang/String;)Ljava/lang/management/GarbageCollectorMBean;
+58 -1: (Ljava/util/function/ToIntFunction;)Ljava/util/Comparator;
+21 -1: ()Lsun/misc/Launcher;
+2 -1: lt
+92 -1: ([Ljava/util/concurrent/ConcurrentHashMap$Node;IIILjava/util/concurrent/ConcurrentHashMap;)V
+8 -1: disjoint
+62 -1: (Ljava/net/URL;Ljava/lang/String;Ljava/net/URLStreamHandler;)V
+24 -1: ([Ljava/lang/Object;)TT;
+4 -1: lmap
+8 -1: SATURDAY
+12 -1: toStringUTF8
+91 -1: (Ljava/lang/Object;Ljava/lang/Object;Ljava/util/function/BinaryOperator;)Ljava/lang/Object;
+46 -1: pkcs11        PKCS11 session manager debugging
+27 -1: (Z)Ljava/lang/StringBuffer;
+7 -1: forEach
+17 -1: (Ljava/io/File;)I
+17 -1: (Ljava/io/File;)J
+5 -1: RESET
+6 -1: isFile
+14 -1: Exception.java
+8 -1: isPublic
+27 -1: computeInitialPreparedForms
+50 -1: (BZLjava/lang/Class;)Ljava/lang/invoke/LambdaForm;
+19 -1: getAvailableLocales
+17 -1: (Ljava/io/File;)V
+28 -1: ()Ljava/nio/charset/Charset;
+10 -1: appendNull
+17 -1: (Ljava/io/File;)Z
+23 -1: java/lang/InternalError
+2 -1: ne
+6 -1: radix 
+8 -1: checksum
+66 -1: (Lsun/net/www/MessageHeader;Ljava/lang/String;Ljava/lang/Object;)V
+45 -1: (Ljava/io/FilenameFilter;)[Ljava/lang/String;
+8 -1: checkJar
+28 -1:     default format locale = 
+13 -1: normalizeTime
+26 -1: java/util/AbstractList$Itr
+30 -1: java/util/function/IntFunction
+7 -1: TIS-620
+12 -1: reverseBytes
+2 -1: of
+26 -1: java/lang/ClassFormatError
+109 -1: (Ljava/lang/invoke/MethodType;Ljava/lang/invoke/LambdaForm;Ljava/lang/Object;)Ljava/lang/invoke/MethodHandle;
+10 -1: delimiters
+20 -1: indexOfSupplementary
+16 -1: aliases_UTF_32LE
+134 -1: <K:Ljava/lang/Object;V:Ljava/lang/Object;>(Ljava/util/Map<TK;TV;>;Ljava/lang/Class<TK;>;Ljava/lang/Class<TV;>;)Ljava/util/Map<TK;TV;>;
+17 -1: [Ljava/lang/Long;
+2 -1: or
+12 -1: getClassName
+31 -1: (Ljava/nio/charset/Charset;FF)V
+6 -1: ([FF)I
+39 -1: (Ljava/util/Locale;)[Ljava/lang/String;
+33 -1: java/util/WeakHashMap$KeyIterator
+8 -1: UTF_16LE
+12 -1: isISOControl
+75 -1: (Ljava/nio/CharBuffer;Ljava/nio/ByteBuffer;Z)Ljava/nio/charset/CoderResult;
+7 -1: ([I[I)Z
+28 -1: Self-causation not permitted
+6 -1: ([FF)V
+14 -1: defaultCharset
+12 -1: isJavaLetter
+2 -1: pm
+39 -1: cannot reflectively invoke MethodHandle
+20 -1: getSystemClassLoader
+42 -1: ([Ljava/lang/Object;IILjava/lang/Object;)I
+56 -1: (Ljava/lang/Object;Ljava/lang/String;)Ljava/lang/Object;
+42 -1: ([Ljava/lang/Object;IILjava/lang/Object;)V
+12 -1: ZipFile.java
+43 -1: (Ljava/util/zip/ZipFile;)Ljava/lang/String;
+22 -1: Ljava/net/InetAddress;
+15 -1: getCharVolatile
+32 -1: ()[Ljava/lang/reflect/Parameter;
+13 -1: delimsChanged
+13 -1: getFileSystem
+13 -1: METHOD_RETURN
+20 -1: sun/misc/PerfCounter
+61 -1: (Ljava/util/HashMap<TK;TV;>;)Ljava/util/HashMap$Node<TK;TV;>;
+8 -1: newIndex
+18 -1: getDisplayLanguage
+36 -1: (C)Ljava/lang/AbstractStringBuilder;
+36 -1: java/lang/StringCoding$StringEncoder
+12 -1: forEachEntry
+23 -1: [Ljava/io/Serializable;
+13 -1: totalCapacity
+26 -1: java/io/FileOutputStream$1
+14 -1: signatureArity
+90 -1: (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;[Ljava/security/cert/Certificate;)V
+17 -1: reflectionFactory
+6 -1: ibm737
+17 -1: fillInStackTrace0
+16 -1: allocateInstance
+52 -1: (Lsun/util/locale/BaseLocale$Key;)Ljava/lang/String;
+9 -1: addExtURL
+16 -1: copyFromIntArray
+65 -1: (Ljava/security/Permission;Z)Ljava/security/PermissionCollection;
+57 -1: java/util/concurrent/ConcurrentHashMap$SearchMappingsTask
+10 -1: toEpochDay
+4 -1: gate
+24 -1: sun/nio/cs/UTF_8$Decoder
+8 -1: entries2
+18 -1: isCharsetSupported
+10 -1: toCustomID
+2 -1: rw
+33 -1: java/nio/ByteBufferAsFloatBufferB
+25 -1: (ID)Ljava/nio/ByteBuffer;
+12 -1: addTimeOfDay
+61 -1: (Ljava/security/ProtectionDomain;Ljava/security/Permission;)Z
+2 -1: sd
+25 -1: (Ljava/net/InetAddress;)V
+33 -1: java/nio/ByteBufferAsFloatBufferL
+2 -1: se
+15 -1: hasQueuedThread
+24 -1: assertMemberIsConsistent
+37 -1: java/util/Collections$UnmodifiableMap
+2 -1: sp
+20 -1: setJavaUtilJarAccess
+96 -1: (Ljava/lang/String;[BIILjava/lang/ClassLoader;Ljava/security/ProtectionDomain;)Ljava/lang/Class;
+8 -1: language
+76 -1: <T:Ljava/lang/Object;>(Ljava/util/SortedSet<TT;>;)Ljava/util/SortedSet<TT;>;
+11 -1: findLibrary
+61 -1: (Ljava/lang/Class<*>;)Lsun/reflect/annotation/AnnotationType;
+6 -1: ([BZ)V
+24 -1: DEFAULT_BYTE_BUFFER_SIZE
+25 -1: (Ljava/util/ArrayList;I)V
+2 -1: th
+47 -1: (Ljava/util/Collection;)Ljava/util/Enumeration;
+49 -1: (Lsun/misc/URLClassPath$JarLoader;)Ljava/net/URL;
+7 -1: ([D[D)Z
+2 -1: to
+22 -1: java/util/Locale$Cache
+8 -1: iterator
+30 -1: (Ljava/lang/StringBuilder;IZ)V
+17 -1: ()Ljava/util/Set;
+2 -1: tr
+27 -1: (Ljava/nio/ByteBuffer;ISZ)V
+6 -1: method
+13 -1: allPermission
+9 -1: ruleArray
+8 -1: UTC_TIME
+10 -1: LF_COUNTER
+26 -1: Lsun/nio/cs/StreamDecoder;
+25 -1: ()Lsun/util/calendar/Era;
+6 -1: LOCHDR
+21 -1: sun/net/www/MimeTable
+12 -1: Cannot cast 
+2 -1: us
+2 -1: ut
+52 -1: Ljava/lang/invoke/MethodHandle$PolymorphicSignature;
+6 -1: encode
+15 -1: CharBuffer.java
+24 -1: (C)Ljava/nio/ByteBuffer;
+56 -1: (Ljava/lang/Class<+Ljava/lang/annotation/Annotation;>;)V
+18 -1: getEnclosingMethod
+56 -1: (Ljava/lang/Class<+Ljava/lang/annotation/Annotation;>;)Z
+6 -1: ibm775
+9 -1: (IIIIII)I
+44 -1: ([JLjava/util/function/IntToLongFunction;I)V
+9 -1: (IIIIII)J
+64 -1: (Ljava/util/Locale$LocaleKey;)Lsun/util/locale/LocaleExtensions;
+2 -1: x-
+2 -1: vm
+5 -1: clock
+9 -1: (IIIIII)V
+10 -1: XmlSupport
+19 -1: sun/nio/cs/US_ASCII
+10 -1: toRealPath
+5 -1: cp367
+6 -1: ST_END
+58 -1: [Lsun/util/calendar/ZoneInfoFile$ZoneOffsetTransitionRule;
+12 -1: hasSameRules
+108 -1: (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Lsun/util/locale/LocaleExtensions;
+22 -1: java/lang/Class$Atomic
+4 -1: sync
+6 -1: listen
+12 -1: firstElement
+142 -1: (Ljava/lang/Class;Ljava/lang/String;[Ljava/lang/Class;Ljava/lang/Class;[Ljava/lang/Class;IILjava/lang/String;[B[B[B)Ljava/lang/reflect/Method;
+18 -1: internalProperties
+28 -1: (Ljava/lang/StringBuffer;C)V
+7 -1: factory
+18 -1: ()Ljava/util/List;
+50 -1: (Ljava/util/concurrent/CountedCompleter;[D[DIIII)V
+44 -1: (Ljava/lang/Class;)Lsun/invoke/util/Wrapper;
+83 -1: (JLjava/util/function/ToDoubleFunction;DLjava/util/function/DoubleBinaryOperator;)D
+12 -1: erasedType: 
+53 -1: (Ljava/util/AbstractList;Ljava/util/AbstractList$1;)V
+20 -1: Cannot find package 
+27 -1: java/util/ArrayList$ListItr
+10 -1: copyMethod
+23 -1: java/lang/ThreadLocal$1
+16 -1: iso_646.irv:1983
+42 -1: (Ljava/lang/Thread;Ljava/lang/Throwable;)V
+19 -1: DEFAULT_LOAD_FACTOR
+40 -1: ([Ljava/lang/Object;)[Ljava/lang/Object;
+10 -1: (JJJ[BII)I
+12 -1: singletonMap
+8 -1: RESERVED
+9 -1: zipAccess
+21 -1: SynchronizedSortedMap
+4 -1: flag
+15 -1: UnmodifiableSet
+18 -1: WrappedPrintWriter
+7 -1: resume0
+2 -1: yi
+10 -1: erasedType
+31 -1: CHECK_AWT_EVENTQUEUE_PERMISSION
+8 -1: <clinit>
+59 -1: (Ljava/lang/String;)Ljava/security/cert/CertificateFactory;
+40 -1: java/lang/management/MemoryManagerMXBean
+33 -1: newGetIntIllegalArgumentException
+16 -1: iso_646.irv:1991
+58 -1: (Ljava/lang/ClassValue$Entry;)Ljava/lang/ClassValue$Entry;
+2 -1: zc
+26 -1: (Ljava/util/AbstractMap;)V
+6 -1: THROWS
+11 -1: toCharArray
+64 -1: (Ljava/lang/reflect/Constructor;)Ljava/lang/reflect/Constructor;
+2 -1: zh
+68 -1: Ljava/lang/ref/SoftReference<Ljava/lang/Class$ReflectionData<TT;>;>;
+25 -1: (Ljava/util/Collection;)V
+20 -1: getJdkSpecialVersion
+17 -1: getTypeParameters
+32 -1: [Ljava/lang/ClassValue$Entry<*>;
+25 -1: (Ljava/util/Collection;)Z
+26 -1: Lsun/nio/ch/Interruptible;
+5 -1: 0.0p0
+5 -1: CACHE
+7 -1: namesOK
+21 -1: Ljava/lang/Exception;
+51 -1: (Ljava/net/URL;Lsun/net/www/protocol/jar/Handler;)V
+19 -1: jdk_special_version
+66 -1: <T:Ljava/lang/Object;>(Ljava/util/List<TT;>;)Ljava/util/List<TT;>;
+75 -1: (Ljava/util/Comparator;Ljava/util/function/Function;)Ljava/util/Comparator;
+11 -1: Arrays.java
+19 -1: (Ljava/lang/Byte;)I
+17 -1: java/lang/Class$1
+17 -1: java/lang/Class$2
+17 -1: java/lang/Class$3
+47 -1: java/lang/invoke/MethodHandleImpl$WrappedMember
+17 -1: java/lang/Class$4
+44 -1: (Ljava/lang/Throwable;)Ljava/lang/Throwable;
+9 -1: charCount
+24 -1: ()Ljava/net/FileNameMap;
+44 -1: sun/util/locale/provider/TimeZoneNameUtility
+17 -1: not an array type
+2 -1: {}
+24 -1: (Lsun/misc/Launcher$1;)V
+12 -1: directMemory
+10 -1: parameters
+5 -1: java.
+14 -1: allocateDirect
+51 -1: (Ljava/lang/StringBuffer;)Ljava/lang/StringBuilder;
+23 -1: java/nio/file/Watchable
+37 -1: createDiagnosticFrameworkNotification
+54 -1: (Ljava/lang/Class<*>;Z)Ljava/lang/invoke/MethodHandle;
+35 -1: sun/nio/cs/StandardCharsets$Aliases
+9 -1: retDelims
+11 -1: MAX_ENTRIES
+12 -1: CumulateTask
+64 -1: java/util/concurrent/ConcurrentHashMap$ForEachTransformedKeyTask
+3 -1: iae
+12 -1: AF_PUTSTATIC
+21 -1: java/lang/Throwable$1
+45 -1: (Ljava/util/HashMap;)Ljava/util/HashMap$Node;
+77 -1: (JLjava/util/function/ToIntFunction;ILjava/util/function/IntBinaryOperator;)I
+5 -1: after
+29 -1: (Ljava/security/CodeSource;)Z
+248 -1: (Ljava/util/concurrent/ConcurrentHashMap$BulkTask;III[Ljava/util/concurrent/ConcurrentHashMap$Node;Ljava/util/concurrent/ConcurrentHashMap$MapReduceValuesToDoubleTask;Ljava/util/function/ToDoubleFunction;DLjava/util/function/DoubleBinaryOperator;)V
+6 -1: H_URIC
+7 -1: L_DIGIT
+7 -1: toNanos
+24 -1: (D)Ljava/nio/ByteBuffer;
+25 -1: getDiagnosticCommandMBean
+6 2: [LFoo;
+14 -1: path.separator
+16 -1: toUnsignedString
+40 -1: DIRECTIONALITY_EUROPEAN_NUMBER_SEPARATOR
+87 -1: (Ljava/security/Permission;[Ljava/security/cert/Certificate;)Ljava/security/Permission;
+16 -1: inheritedChannel
+11 -1: audio/basic
+27 -1: sun.classloader.findClasses
+10 -1: queryCount
+20 -1: NF_ensureInitialized
+12 -1: getBufIfOpen
+23 -1: sun/nio/cs/UTF_16LE_BOM
+134 -1: (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;
+17 -1: getImplMethodName
+14 -1: linkMethod => 
+25 -1: Ljava/lang/invoke/Stable;
+32 -1: Ljava/lang/annotation/Retention;
+7 -1: doInput
+9 -1: -_.!~*'()
+62 -1: (Ljava/lang/Class<*>;Ljava/lang/String;Ljava/lang/Class<*>;B)V
+18 -1: separateWithCommas
+43 -1: com/sun/crypto/provider/CipherBlockChaining
+14 -1: createTempFile
+9 -1: implFlush
+20 -1: getOffsetsByStandard
+21 -1: OutOfMemoryError.java
+7 -1: jce.jar
+46 -1: java/util/Collections$UnmodifiableNavigableMap
+30 -1: (Ljava/io/File;)Ljava/io/File;
+15 -1: LinkedList.java
+15 -1: iso_8859-9:1989
+50 -1: sun/reflect/generics/factory/CoreReflectionFactory
+10 -1: : JVM has 
+19 -1: HeapByteBuffer.java
+6 -1: getURL
+37 -1: java/security/cert/CertificateFactory
+23 -1: getAllowUserInteraction
+12 -1: otherParents
+25 -1: ARRAY_BOOLEAN_BASE_OFFSET
+9 -1: L_UPALPHA
+41 -1: ([Ljava/util/Hashtable$Entry<**>;TK;TV;)V
+6 -1: LOCHOW
+11 -1: access$1300
+30 -1: sun/reflect/MethodAccessorImpl
+130 -1: (Ljava/util/List<Ljava/util/Locale$LanguageRange;>;Ljava/util/Collection<Ljava/util/Locale;>;)Ljava/util/List<Ljava/util/Locale;>;
+9 -1: MALFORMED
+38 -1: (Ljava/lang/String;I)Ljava/lang/Short;
+26 -1: File format not recognised
+12 -1: setTimeOfDay
+19 -1: java/lang/Exception
+15 -1: getOutputStream
+74 -1: (Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
+41 -1: (Ljava/lang/String;Z)Ljava/util/TimeZone;
+49 -1: Lsun/reflect/generics/repository/ClassRepository;
+8 -1: getCerts
+90 -1: (Ljava/lang/Class<*>;ZLjava/lang/Class<*>;)Ljava/util/List<Ljava/lang/invoke/MemberName;>;
+5 -1: clone
+32 -1: ()Ljava/lang/ref/Reference$Lock;
+15 -1: caseIgnoreMatch
+23 -1: (Ljava/util/Set<TE;>;)V
+25 -1: enumerateStringProperties
+9 -1: inherited
+4 -1: flip
+8 -1: setMonth
+38 -1: (Ljava/util/function/Consumer<-TV;>;)V
+55 -1: java/util/concurrent/ConcurrentHashMap$ReduceValuesTask
+37 -1: getJavaSecurityProtectionDomainAccess
+67 -1: (Ljava/util/NavigableSet;Ljava/lang/Class;)Ljava/util/NavigableSet;
+10 -1: reduceKeys
+14 -1: MAX_CODE_POINT
+24 -1: getGenericExceptionTypes
+8 -1: fraction
+30 -1: java/lang/InterruptedException
+46 -1: (Ljava/lang/String;II[BI)Ljava/nio/ByteBuffer;
+114 -1: <K:Ljava/lang/Object;V:Ljava/lang/Object;>([Ljava/util/HashMap$Node<TK;TV;>;Ljava/util/HashMap$TreeNode<TK;TV;>;)V
+66 -1: (Ljava/lang/String;Ljava/lang/Throwable;)Ljava/lang/InternalError;
+45 -1: (Ljava/lang/Object;)Ljava/lang/StringBuilder;
+8 -1: putLongB
+101 -1: (Ljava/nio/channels/ReadableByteChannel;Ljava/nio/charset/CharsetDecoder;I)Lsun/nio/cs/StreamDecoder;
+16 -1: standardProvider
+14 -1: parameterCount
+59 -1: (Ljava/lang/String;Ljava/lang/ClassLoader;)Ljava/util/List;
+8 -1: putLongL
+12 -1: (TT;TV;TV;)Z
+37 -1: Lsun/reflect/ConstructorAccessorImpl;
+11 -1: ([CII[CII)V
+14 -1: parameterArray
+15 -1: | interpretName
+62 -1: (JLjava/util/function/Function;Ljava/util/function/Consumer;)V
+30 -1: (Z)Lsun/reflect/FieldAccessor;
+19 -1: jvm_special_version
+22 -1: java/util/ArrayDeque$1
+12 -1: initVersions
+22 -1: java/lang/CharSequence
+21 -1: NF_internalMemberName
+5 -1: ()TE;
+97 -1: (Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodHandle;IZ)Ljava/lang/invoke/MethodHandle;
+25 -1: java/nio/MappedByteBuffer
+6 -1: addURL
+17 -1: isConvertibleFrom
+14 -1: extendWithType
+9 -1: interrupt
+11 -1: floorDivide
+16 -1: x-ISO-2022-CN-GB
+12 -1: CheckedQueue
+7 -1: setLong
+64 -1: (Ljava/lang/ThreadGroup;Ljava/lang/Runnable;Ljava/lang/String;)V
+108 -1: <K:Ljava/lang/Object;V:Ljava/lang/Object;>(Ljava/util/NavigableMap<TK;TV;>;)Ljava/util/NavigableMap<TK;TV;>;
+38 -1: java/nio/channels/spi/SelectorProvider
+17 -1: makeGuardWithTest
+20 -1: (Ljava/util/List;Z)V
+8 -1: val$path
+12 -1: Runtime.java
+7 -1: channel
+71 -1: <T:Ljava/lang/Object;>([TT;IILjava/util/function/BinaryOperator<TT;>;)V
+18 -1: initializedHeaders
+32 -1: ()[Lsun/launcher/LauncherHelper;
+13 -1: jvInitialized
+10 -1: getSeconds
+7 -1: Decoder
+20 -1: getYearFromFixedDate
+6 -1: PREFIX
+21 -1: sun.boot.library.path
+11 -1: FIXED_DATES
+33 -1: (JLjava/util/function/Consumer;)V
+27 -1: initializeJavaAssertionMaps
+13 -1: toOctalString
+9 -1: fixResult
+10 -1: typeParams
+94 -1: ([Ljava/util/concurrent/ConcurrentHashMap$Node;I)Ljava/util/concurrent/ConcurrentHashMap$Node;
+4 -1: Help
+11 -1: setIfNotSet
+39 -1: java/lang/UnsupportedOperationException
+15 -1: zip file closed
+8 -1: floorDiv
+10 -1: canExecute
+10 -1: encodeLoop
+18 -1: addRequestProperty
+56 -1: (Ljava/lang/Class;Ljava/lang/Class;Ljava/lang/Object;I)V
+10 -1: superclass
+5 -1: close
+56 -1: (Ljava/lang/Class;Ljava/lang/Class;Ljava/lang/Object;I)Z
+6 -1: ignore
+32 -1: ()Ljava/lang/ref/ReferenceQueue;
+19 -1: java/util/Formatter
+27 -1: java/lang/ClassLoaderHelper
+23 -1: (Ljava/lang/Object;JZ)V
+29 -1: java/nio/file/WatchEvent$Kind
+6 -1: CENLEN
+4 -1: SIZE
+68 -1: <T:Ljava/lang/Object;>(Ljava/util/Deque<TT;>;)Ljava/util/Queue<TT;>;
+9 -1: isEscaped
+12 -1: LF_INTERPRET
+22 -1: (I)Ljava/lang/Integer;
+60 -1: (Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/MemberName;
+49 -1: ([Ljava/lang/Class<*>;Ljava/lang/StringBuilder;)V
+11 -1: getZipEntry
+16 -1: metaInfFilenames
+27 -1: (Ljava/lang/StringBuffer;)V
+57 -1: (Ljava/lang/Object;)Ljava/util/WeakHashMap$Entry<TK;TV;>;
+76 -1: (Ljava/lang/String;[BIILjava/security/ProtectionDomain;)Ljava/lang/Class<*>;
+8 -1: ([CII)[B
+27 -1: (Ljava/lang/StringBuffer;)Z
+24 -1: getManifestFromReference
+8 -1: ([CII)[C
+10 -1: H_LOWALPHA
+18 -1: FileURLMapper.java
+12 -1: fxLaunchMode
+24 -1: isMethodHandleInvokeName
+17 -1: winTimeToFileTime
+19 -1: checkTopLevelWindow
+26 -1: MapReduceMappingsToIntTask
+13 -1: NORM_PRIORITY
+18 -1: lookupViaProviders
+30 -1: (I)Ljava/util/LinkedList$Node;
+3 -1: UTC
+10 -1: UNMAPPABLE
+68 -1: (Ljava/lang/Class;Ljava/lang/String;Ljava/lang/invoke/MethodType;B)V
+9 -1: META-INF/
+13 -1: Iterable.java
+71 -1: ([Ljava/lang/reflect/Field;Ljava/lang/String;)Ljava/lang/reflect/Field;
+9 -1: setOffset
+8 -1: FJObject
+50 -1: (Ljava/lang/CharSequence;)Ljava/util/StringJoiner;
+23 -1: java/nio/HeapByteBuffer
+23 -1: sun/util/PreHashedMap$1
+23 -1: sun/util/PreHashedMap$2
+34 -1: newGetCharIllegalArgumentException
+40 -1: jca           JCA engine class debugging
+39 -1: (Ljava/lang/Object;I)Ljava/lang/Object;
+42 -1: (Ljava/lang/String;)Ljava/net/InetAddress;
+25 -1: (Ljava/net/FileNameMap;)V
+44 -1: (Ljava/util/SortedMap;)Ljava/util/SortedMap;
+52 -1: (Ljava/lang/String;Ljava/lang/Long;)Ljava/lang/Long;
+27 -1: ()[Ljava/util/HashMap$Node;
+29 -1: java/util/EmptyStackException
+16 -1: not a field type
+14 -1: Ljava/io/File;
+28 -1: ()[Ljava/security/Principal;
+69 -1: (Ljava/io/OutputStream;Ljava/lang/Object;Ljava/nio/charset/Charset;)V
+5 -1: ()TK;
+10 -1: ISO8859-13
+40 -1: java/lang/invoke/DirectMethodHandle$Lazy
+30 -1: The object is not initialized.
+10 -1: ISO8859-15
+88 -1: <E:Ljava/lang/Object;>(Ljava/util/List<TE;>;Ljava/lang/Class<TE;>;)Ljava/util/List<TE;>;
+25 -1: (ZILjava/lang/String;II)Z
+9 -1: stackSize
+61 -1: (Ljava/util/Comparator;Ljava/lang/Object;Ljava/lang/Object;)I
+16 -1: synchronizedList
+90 -1: (Ljava/util/Comparator;Ljava/util/function/Function;Ljava/lang/Object;Ljava/lang/Object;)I
+9 -1: nullCheck
+174 -1: Ljava/util/concurrent/ConcurrentMap<Ljava/lang/invoke/MethodType$ConcurrentWeakInternSet$WeakEntry<TT;>;Ljava/lang/invoke/MethodType$ConcurrentWeakInternSet$WeakEntry<TT;>;>;
+14 -1: java/lang/Enum
+3 -1: int
+13 -1: detailMessage
+56 -1: java/util/concurrent/ConcurrentHashMap$SearchEntriesTask
+10 -1: ISO-8859-1
+10 -1: ISO-8859-2
+10 -1: ISO-8859-3
+10 -1: ISO-8859-4
+10 -1: ISO-8859-5
+10 -1: ISO-8859-6
+24 -1: ([Ljava/lang/Object;II)V
+10 -1: ISO-8859-7
+10 -1: ISO-8859-8
+139 -1: ([Ljava/util/concurrent/ConcurrentHashMap$Node;Ljava/util/concurrent/ConcurrentHashMap$Node;)[Ljava/util/concurrent/ConcurrentHashMap$Node;
+10 -1: ISO-8859-9
+5 -1: round
+25 -1: DIRECTIONALITY_WHITESPACE
+13 -1: NamedFunction
+10 -1: startAgent
+3 -1: ioe
+7 -1: closing
+24 -1: appendSchemeSpecificPart
+56 -1: sun/reflect/ReflectionFactory$GetReflectionFactoryAction
+83 -1: Ljava/lang/Object;Ljava/security/PrivilegedAction<Lsun/reflect/ReflectionFactory;>;
+17 -1: isCharsetDetected
+11 -1: getJarFiles
+22 -1: getEnclosingMethodInfo
+11 -1: setReadable
+61 -1: (Ljava/lang/invoke/MethodType;Ljava/lang/invoke/LambdaForm;)V
+10 -1: attachment
+34 -1: (Ljava/io/File;)Ljava/lang/String;
+18 -1: ZipFileInputStream
+15 -1: CodeSource.java
+61 -1: (Ljava/util/jar/JarFile;)Ljava/util/List<Ljava/lang/Object;>;
+7 -1: cp00858
+108 -1: ([Ljava/lang/invoke/LambdaForm$Name;[Ljava/lang/invoke/LambdaForm$Name;II)Ljava/lang/invoke/LambdaForm$Name;
+38 -1: ([DII)Ljava/util/Spliterator$OfDouble;
+8 -1: val$name
+11 -1: LF_REINVOKE
+12 -1: validateTime
+17 -1: copyFromCharArray
+32 -1: throwSetIllegalArgumentException
+14 -1: fieldModifiers
+52 -1: (Ljava/lang/ClassValue;)Ljava/lang/ClassValue$Entry;
+58 -1: (Ljava/io/OutputStream;Ljava/nio/charset/CharsetEncoder;)V
+14 -1: generalInvoker
+11 -1: interrupted
+246 -1: (Ljava/util/concurrent/ConcurrentHashMap$BulkTask;III[Ljava/util/concurrent/ConcurrentHashMap$Node;Ljava/util/concurrent/ConcurrentHashMap$MapReduceMappingsToLongTask;Ljava/util/function/ToLongBiFunction;JLjava/util/function/LongBinaryOperator;)V
+20 -1: java/util/Dictionary
+17 -1: getDoubleVolatile
+41 -1: (Ljava/lang/Class<*>;Ljava/lang/Object;)Z
+8 -1: intValue
+24 -1: (F)Ljava/nio/ByteBuffer;
+5 -1: reset
+54 -1: (ILjava/util/List;)[Ljava/lang/invoke/LambdaForm$Name;
+19 -1: [Ljava/util/Locale;
+43 -1: (Ljava/lang/String;II)Ljava/nio/ByteBuffer;
+38 -1: ()Ljava/io/ObjectInputStream$GetField;
+18 -1: [Locked by thread 
+10 -1: checkedMap
+16 -1: checkedSortedMap
+14 -1: illegal symbol
+16 -1: TITLECASE_LETTER
+4 -1: root
+22 -1: (ZLjava/lang/String;)V
+27 -1: ([JII)Ljava/nio/LongBuffer;
+15 -1: printStackTrace
+30 -1: newConstructorForSerialization
+14 -1: getPermissions
+13 -1: toStringCache
+15 -1: equalParamTypes
+37 -1: throwFinalFieldIllegalAccessException
+35 -1: (Ljava/lang/String;Ljava/io/File;)V
+34 -1: java/nio/charset/CoderResult$Cache
+3 -1: .\n\n
+33 -1: Ljava/nio/charset/CharsetEncoder;
+11 -1: lambdaForms
+65 -1: <T::Ljava/lang/annotation/Annotation;>(Ljava/lang/Class<TT;>;)TT;
+39 -1: (CLjava/lang/Class;Ljava/lang/Object;)Z
+3 -1: ise
+14 -1: forLanguageTag
+45 -1: ([Ljava/lang/Class<*>;[Ljava/lang/Class<*>;)Z
+36 -1: RuntimeInvisibleParameterAnnotations
+12 -1: binarySearch
+24 -1: getAssociatedAnnotations
+10 -1: decoderFor
+6 -1: ibm813
+10 -1: logicalXor
+10 -1: setVarargs
+71 -1: (Ljava/lang/Class;Ljava/lang/String;Ljava/lang/Class;Ljava/lang/Void;)V
+4 -1: cast
+6 -1: ibm819
+23 -1: getParentDelegationTime
+8 -1: ([FII)[F
+4 -1: .tmp
+6 -1: (JII)J
+27 -1: ()Ljava/lang/ref/Finalizer;
+9 -1: sunec.jar
+22 -1: java/net/URLConnection
+41 -1: (Ljava/lang/Runnable;Ljava/lang/String;)V
+20 -1: SecurityManager.java
+18 -1: getZipFileOpenTime
+7 -1: country
+13 -1: inflaterCache
+4 -1:     
+17 -1: java/lang/Runtime
+125 -1: (Lsun/management/GcInfoBuilder;JJJ[Ljava/lang/management/MemoryUsage;[Ljava/lang/management/MemoryUsage;[Ljava/lang/Object;)V
+24 -1: java/util/ResourceBundle
+64 -1: Ljava/util/Hashtable<Lsun/misc/Signal;Lsun/misc/SignalHandler;>;
+79 -1: ([Ljava/util/WeakHashMap$Entry<TK;TV;>;[Ljava/util/WeakHashMap$Entry<TK;TV;>;)V
+8 -1: x-ibm737
+39 -1: (Ljava/security/AccessControlContext;)Z
+21 -1: (Ljava/lang/Class;I)V
+4 -1:    -
+15 -1: calculateFields
+22 -1: Ljava/util/Properties;
+8 -1: getArray
+21 -1: (Ljava/lang/Class;I)Z
+41 -1: (Ljava/lang/ThreadGroup;)Ljava/lang/Void;
+17 -1: Ljava/io/Console;
+115 -1: (Ljava/security/PrivilegedAction;Ljava/security/AccessControlContext;[Ljava/security/Permission;)Ljava/lang/Object;
+12 -1: nextThreadID
+81 -1: (Ljava/net/URLClassLoader;Ljava/lang/SecurityManager;Ljava/security/Permission;)V
+23 -1: ARRAY_SHORT_BASE_OFFSET
+10 -1: interpret_
+144 -1: (Ljava/net/URL;Ljava/lang/String;Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+20 -1: isIPv6LiteralAddress
+13 -1: launcher_name
+36 -1: java/util/function/IntToLongFunction
+38 -1: java/util/WeakHashMap$EntrySpliterator
+8 -1: copyInto
+3 -1: ACT
+11 -1: metafactory
+31 -1: ([BLjava/nio/charset/Charset;)V
+30 -1: java/lang/annotation/Retention
+13 -1: getYearLength
+42 -1: java/util/AbstractMap$SimpleImmutableEntry
+31 -1: ()Ljava/lang/invoke/MemberName;
+21 -1: sun/misc/JavaIOAccess
+16 -1: jdk_build_number
+8 -1: ST_RESET
+96 -1: (BLjava/lang/Class;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/MemberName;
+13 -1: getTypeString
+15 -1: maxCharsPerByte
+8 -1: checkKey
+49 -1: (Ljava/nio/charset/Charset;Lsun/nio/cs/UTF_8$1;)V
+80 -1: (Ljava/lang/ClassValue;Ljava/lang/ClassValue$Entry;)Ljava/lang/ClassValue$Entry;
+32 -1: Ljava/security/ProtectionDomain;
+5 -1: ()TT;
+6 -1: insert
+10 -1: intersects
+38 -1: ([Ljava/lang/Class;Ljava/lang/Class;)V
+15 -1: java/lang/Class
+12 -1: getPublicKey
+37 -1: (ID)Ljava/lang/AbstractStringBuilder;
+6 -1: ibm850
+6 -1: locsig
+6 -1: ibm852
+19 -1: changeReferenceKind
+3 -1: AET
+42 -1: (Ljava/util/Collection;Ljava/lang/Class;)V
+6 -1: ibm855
+16 -1: getPolicyNoCheck
+39 -1: ([B)[[Ljava/lang/annotation/Annotation;
+6 -1: ibm857
+10 -1: Error.java
+38 -1: ()Ljava/util/List<Ljava/lang/Object;>;
+14 -1: createInstance
+5 -1: cp437
+28 -1: Lsun/util/locale/BaseLocale;
+17 -1: getStandardOffset
+29 -1: sun/nio/cs/StandardCharsets$1
+36 -1: Ljava/security/ProtectionDomain$Key;
+14 -1: ArrayList.java
+78 -1: (Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/MemberName;
+38 -1: java/lang/invoke/MethodHandleImpl$Lazy
+42 -1: (Ljava/util/LinkedHashMap$Entry<TK;TV;>;)V
+8 -1: getLongB
+7 -1: vmentry
+21 -1: lookupExtendedCharset
+32 -1: <T:Ljava/lang/Object;>([TT;)[TT;
+53 -1: Ljava/util/concurrent/ConcurrentHashMap$EntrySetView;
+6 -1: ibm862
+8 -1: getLongL
+32 -1: (Ljava/lang/invoke/MemberName;)J
+67 -1: Ljava/lang/Object;Ljava/security/PrivilegedAction<Lsun/misc/Perf;>;
+6 -1: region
+6 -1: ibm866
+89 -1: (Ljava/lang/Class;[Ljava/lang/Class;[Ljava/lang/Class;I)Lsun/reflect/ConstructorAccessor;
+22 -1: java/util/ListIterator
+9 -1: wednesday
+16 -1: unsuspendThreads
+20 -1: Not a Proxy instance
+45 -1: Ljava/lang/reflect/InvocationTargetException;
+61 -1: (Ljava/lang/CharSequence;II)Ljava/lang/AbstractStringBuilder;
+5 -1: ()TV;
+32 -1: (Ljava/lang/invoke/MemberName;)V
+82 -1: (Ljava/util/jar/JarFile;Ljava/util/jar/JarEntry;)[Ljava/security/cert/Certificate;
+34 -1: java/lang/ClassValue$ClassValueMap
+32 -1: (Ljava/lang/invoke/MemberName;)Z
+15 -1: SPACE_SEPARATOR
+17 -1: caseIgnoreCompare
+58 -1: (Ljava/lang/Class;)Ljava/lang/invoke/MethodHandles$Lookup;
+4 -1: Code
+3 -1: AGT
+54 -1: (Ljava/lang/StringBuilder;II)Ljava/lang/StringBuilder;
+6 -1: ibm874
+15 -1: newMemberBuffer
+42 -1: (Ljava/nio/file/Path;)Ljava/nio/file/Path;
+33 -1: Ljava/lang/NumberFormatException;
+10 -1: Field.java
+67 -1: <U:Ljava/lang/Object;>(JLjava/util/function/Function<-TK;+TU;>;)TU;
+4 -1: rows
+12 -1: MIN_PRIORITY
+25 -1: URI has a query component
+41 -1: provider      security provider debugging
+29 -1: sun/nio/cs/ISO_8859_1$Encoder
+26 -1: (Ljava/util/zip/ZipFile;)I
+26 -1: (Ljava/util/zip/ZipFile;)J
+20 -1: Bad digit at end of 
+29 -1: Ljava/lang/RuntimePermission;
+12 -1: initResolved
+9 -1: loadFence
+13 -1: fieldAccessor
+26 -1: (Ljava/util/zip/ZipFile;)V
+36 -1: java/lang/CloneNotSupportedException
+19 -1: getBasicConstraints
+16 -1: putOrderedObject
+26 -1: (Ljava/util/zip/ZipFile;)Z
+6 -1: target
+50 -1: (Ljava/util/concurrent/CountedCompleter;[I[IIIII)V
+16 -1: changeReturnType
+13 -1: CAUSE_CAPTION
+14 -1: checkExactType
+50 -1: sun/util/locale/provider/LocaleServiceProviderPool
+47 -1: java/lang/invoke/DirectMethodHandle$Constructor
+20 -1: CallerSensitive.java
+30 -1: java/security/ProtectionDomain
+26 -1: java.launcher.opt.vmselect
+4 -1: \tat 
+42 -1: java/util/ArraysParallelSortHelpers$FJLong
+39 -1: (Ljava/lang/String;)[Ljava/lang/String;
+19 -1: sun.net.www.content
+34 -1: ([III)Ljava/util/stream/IntStream;
+20 -1: (Ljava/util/Deque;)V
+35 -1: (Ljava/lang/reflect/Constructor;)[B
+16 -1: WeakHashMap.java
+8 -1: ([III)[I
+46 -1: (Ljava/util/Properties;Ljava/io/InputStream;)V
+54 -1: (I)Ljava/util/concurrent/ConcurrentHashMap$KeySetView;
+65 -1: (ILjava/lang/Object;Ljava/lang/Object;ZZ)Ljava/util/HashMap$Node;
+27 -1: URI path component is empty
+3 -1: -1-
+32 -1: Ljava/io/InterruptedIOException;
+9 -1: setLocale
+7 -1: [^, ;]*
+6 -1: format
+61 -1: (Ljava/util/function/Supplier;IZ)Ljava/util/stream/IntStream;
+20 -1: getRequestProperties
+16 -1: reallocateMemory
+28 -1: java/lang/IllegalAccessError
+5 -1: query
+83 -1: (Ljava/lang/invoke/MethodHandle;Ljava/lang/Class;II)Ljava/lang/invoke/MethodHandle;
+7 -1: threadQ
+6 -1: STATIC
+7 -1: enqueue
+21 -1: uninitializedCallSite
+3 -1: -2-
+26 -1: ()Ljava/util/NavigableSet;
+27 -1: getUncaughtExceptionHandler
+42 -1: ([Ljava/net/URL;)Ljava/net/URLClassLoader;
+30 -1: java/lang/UnsatisfiedLinkError
+39 -1: java/util/Collections$ReverseComparator
+7 -1: resolve
+4 -1: poll
+7 -1: (TE;I)V
+21 -1: : Unknown launch mode
+53 -1: (Ljava/lang/Class;)[Ljava/lang/annotation/Annotation;
+36 -1: sun.classloader.parentDelegationTime
+25 -1: java/net/URLStreamHandler
+39 -1: (Ljava/lang/Object;J)Ljava/lang/Object;
+53 -1: Ljava/util/Map<Ljava/lang/String;Ljava/lang/String;>;
+51 -1: java/util/ArraysParallelSortHelpers$FJDouble$Sorter
+26 -1: getAnnotatedParameterTypes
+18 -1: codePointCountImpl
+7 -1: threads
+12 -1: offsetBefore
+29 -1: ()Ljava/util/Collection<TV;>;
+58 -1: (Lsun/invoke/util/Wrapper;)Ljava/lang/invoke/MethodHandle;
+17 -1: DMH.invokeSpecial
+3 -1: -3-
+16 -1: decodeBufferLoop
+43 -1: java/util/concurrent/atomic/AtomicReference
+16 -1: reduceKeysToLong
+27 -1: newIllegalArgumentException
+3 -1: ALL
+5 -1: cache
+5 -1: queue
+4 -1: 8bit
+3 -1: -4-
+35 -1: Ljava/util/Set<Ljava/lang/String;>;
+9 -1: MIN_RADIX
+26 -1: ZipFileInflaterInputStream
+13 -1: MANIFEST_NAME
+18 -1: java/util/TimeZone
+175 -1: (Ljava/lang/invoke/MethodType;Ljava/lang/invoke/LambdaForm;Ljava/lang/invoke/MemberName;Ljava/lang/invoke/MemberName;Ljava/lang/Class;Ljava/lang/invoke/DirectMethodHandle$1;)V
+16 -1: getContentLength
+11 -1: setDoOutput
+22 -1: (Ljava/io/Closeable;)V
+13 -1: TimeZone.java
+28 -1: sun/misc/ExtensionDependency
+6 -1: bindTo
+3 -1: -5-
+40 -1: ()[Ljava/util/WeakHashMap$Entry<TK;TV;>;
+20 -1: ()Lsun/misc/Cleaner;
+9 -1: compareTo
+68 -1: java/util/concurrent/ConcurrentHashMap$MapReduceMappingsToDoubleTask
+11 -1: checkedList
+14 -1: (ITK;TV;ZZ)TV;
+5 -1: greek
+3 -1: jar
+21 -1: (Ljava/lang/String;)B
+21 -1: (Ljava/lang/String;)C
+21 -1: (Ljava/lang/String;)D
+5 -1: (JS)V
+21 -1: (Ljava/lang/String;)F
+13 -1: LETTER_NUMBER
+14 -1: isAlphaNumeric
+21 -1: (Ljava/lang/String;)I
+73 -1: (Ljava/nio/charset/Charset;Ljava/lang/String;Ljava/lang/StringCoding$1;)V
+21 -1: (Ljava/lang/String;)J
+25 -1: makeExactOrGeneralInvoker
+3 -1: -6-
+25 -1: java/nio/StringCharBuffer
+21 -1: Ljava/util/Hashtable;
+8 -1: ENQUEUED
+8 -1: finalize
+22 -1: DirectLongBufferU.java
+21 -1: (Ljava/lang/String;)S
+10 -1: localhost:
+33 -1: isKnownNotToHaveSpecialAttributes
+21 -1: (Ljava/lang/String;)V
+45 -1: (Ljava/lang/Class<*>;[Ljava/lang/Class<*>;Z)V
+21 -1: (Ljava/lang/String;)Z
+22 -1: (Ljava/util/Vector;I)V
+44 -1: java/nio/charset/UnsupportedCharsetException
+23 -1: java/lang/CharacterName
+7 -1: checkIO
+33 -1: (I)Lsun/misc/URLClassPath$Loader;
+90 -1: (Ljava/lang/Class<*>;Ljava/lang/reflect/Constructor<*>;)Ljava/lang/reflect/Constructor<*>;
+18 -1: getHeaderFieldDate
+9 -1: MIN_VALUE
+95 -1: (Ljava/util/concurrent/locks/AbstractQueuedSynchronizer$ConditionObject;)Ljava/util/Collection;
+44 -1: (Ljava/lang/String;Z)Ljava/util/Enumeration;
+8 -1: NOVEMBER
+4 -1: gcal
+17 -1: getConnectTimeout
+124 -1: (Ljava/lang/invoke/MethodHandle;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;
+24 -1: INDEXOFSUBLIST_THRESHOLD
+16 -1: isJulianLeapYear
+21 -1: reduceEntriesToDouble
+15 -1: getPrefixLength
+17 -1:  is not param at 
+14 -1: inDaylightTime
+39 -1: (Ljava/lang/Class;[I)Ljava/lang/Object;
+5 -1: force
+98 -1: (Ljava/lang/Class;Lsun/reflect/annotation/AnnotationType;Lsun/reflect/annotation/AnnotationType;)Z
+40 -1: (Lsun/reflect/ConstructorAccessorImpl;)V
+27 -1: RuntimeInvisibleAnnotations
+17 -1: checkTargetChange
+9 -1: skipBytes
+4 -1: port
+25 -1: sun/nio/cs/UTF_16$Encoder
+28 -1: MIN_SUPPLEMENTARY_CODE_POINT
+4 -1: node
+11 -1: not param: 
+9 -1: debugInit
+6 -1: setURL
+14 -1: getMonthLength
+23 -1: ()Ljava/nio/ByteBuffer;
+17 -1: CALENDAR_JAPANESE
+11 -1: access$1400
+16 -1: ()Ljava/net/URI;
+29 -1: (IZ)Ljava/lang/StringBuilder;
+15 -1: Native Library 
+30 -1: Invalid lambda deserialization
+14 -1: throwException
+18 -1: nothing to verify!
+23 -1: (Ljava/lang/Object;JF)V
+3 -1: ART
+16 -1: isExtClassLoader
+18 -1: Illegal Capacity: 
+26 -1: java/util/zip/ZipException
+4 -1: /../
+34 -1: ([II)Ljava/util/Spliterator$OfInt;
+26 -1: (I)Ljava/util/Enumeration;
+60 -1: (Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/MethodType;
+43 -1: not a field or nested class, no simple type
+12 -1: nextPutIndex
+15 -1: getConstructor0
+3 -1: AST
+11 -1: fromURIPath
+49 -1: (Ljava/util/Collections$UnmodifiableCollection;)V
+8 -1: makeImpl
+51 -1: (Ljava/util/WeakHashMap;Ljava/util/WeakHashMap$1;)V
+32 -1: (Ljava/util/function/Supplier;)V
+20 -1: namedFunctionInvoker
+37 -1: newGetBooleanIllegalArgumentException
+19 -1: (Ljava/io/File;ZI)V
+8 -1: NULL_KEY
+36 -1: Ljava/lang/reflect/Constructor<TT;>;
+21 -1: (Ljava/lang/Object;)B
+21 -1: (Ljava/lang/Object;)C
+21 -1: (Ljava/lang/Object;)D
+21 -1: (Ljava/lang/Object;)F
+30 -1: ()Lsun/util/locale/BaseLocale;
+21 -1: (Ljava/lang/Object;)I
+21 -1: (Ljava/lang/Object;)J
+10 -1: lineNumber
+95 -1: (JLjava/util/function/ToDoubleBiFunction<-TK;-TV;>;DLjava/util/function/DoubleBinaryOperator;)D
+16 -1: CoderResult.java
+44 -1: (Ljava/util/NavigableSet;Ljava/lang/Class;)V
+21 -1: (Ljava/lang/Object;)S
+13 -1: getNameString
+129 -1: (Ljava/lang/invoke/MethodType;Ljava/lang/invoke/LambdaForm;Ljava/lang/invoke/MemberName;Ljava/lang/invoke/DirectMethodHandle$1;)V
+21 -1: (Ljava/lang/Object;)V
+21 -1: (Ljava/lang/Object;)Z
+81 -1: Ljava/util/HashMap<Ljava/lang/String;Ljava/util/LinkedList<Ljava/lang/String;>;>;
+49 -1: java/util/concurrent/locks/ReentrantLock$FairSync
+11 -1: csisolatin0
+11 -1: csisolatin1
+7 -1: isSpace
+10 -1: getDefault
+11 -1: csisolatin2
+16 -1: ()Ljava/net/URL;
+11 -1: csisolatin4
+14 -1: invokeExact_MT
+11 -1: csisolatin5
+28 -1: (Ljava/io/FileInputStream;)V
+11 -1: csisolatin9
+8 -1: isLetter
+15 -1: getConstructors
+21 -1: mainAppContextDefault
+29 -1: ()[Ljava/lang/reflect/Method;
+23 -1: Ljava/util/WeakHashMap;
+12 -1: LF_INVSTATIC
+17 -1: DirectBuffer.java
+10 -1: newEncoder
+10 -1: getVersion
+32 -1: java/lang/IllegalAccessException
+20 -1: java/util/Collection
+61 -1: (Ljava/util/concurrent/ConcurrentHashMap;Ljava/lang/Object;)V
+19 -1: $deserializeLambda$
+8 -1: removeIf
+25 -1: sun/reflect/FieldAccessor
+129 -1: <U::Ljava/lang/Comparable<-TU;>;>(Ljava/util/function/Function<-TT;+TU;>;Ljava/util/Comparator<-TU;>;)Ljava/util/Comparator<TT;>;
+17 -1: parseAbsoluteSpec
+37 -1: ([Ljava/util/HashMap$Node<TK;TV;>;I)V
+17 -1: java/util/HashSet
+13 -1: spreadInvoker
+20 -1: suppressAccessChecks
+32 -1: Ljava/lang/InterruptedException;
+11 -1: oldMappings
+9 -1: lookupTag
+16 -1: java/lang/System
+5 -1: LFI: 
+6 -1: IBM737
+9 -1: SHORT_IDS
+45 -1: ([IIILjava/util/function/IntBinaryOperator;)V
+20 -1: getMetaInfEntryNames
+10 -1: isReadOnly
+50 -1: <E:Ljava/lang/Object;>()Ljava/util/SortedSet<TE;>;
+12 -1: java.vm.name
+30 -1: java/lang/Class$AnnotationData
+61 -1: (ILjava/lang/invoke/LambdaForm;)Ljava/lang/invoke/LambdaForm;
+7 -1: address
+44 -1: (Ljava/util/function/BiConsumer<-TK;-TV;>;)V
+58 -1: ([Ljava/util/concurrent/ConcurrentHashMap$Node<TK;TV;>;I)V
+112 -1: (Ljava/lang/Object;TV;Ljava/lang/ref/ReferenceQueue<Ljava/lang/Object;>;ILjava/util/WeakHashMap$Entry<TK;TV;>;)V
+14 -1: aliases_KOI8_R
+3 -1: AWT
+14 -1: aliases_KOI8_U
+24 -1: ARRAY_DOUBLE_BASE_OFFSET
+23 -1: Ljava/util/jar/JarFile;
+91 -1: (Ljava/lang/Class<TT;>;[Ljava/lang/Class<*>;[Ljava/lang/Class<*>;IILjava/lang/String;[B[B)V
+14 -1: mappingAddress
+19 -1: [Ljava/lang/Object;
+17 -1: sun/misc/JarIndex
+9 -1: image/jpg
+49 -1: (Ljava/lang/String;I)Lsun/util/calendar/ZoneInfo;
+37 -1: java/lang/invoke/DirectMethodHandle$1
+34 -1: java/util/Collections$SingletonMap
+89 -1: (JLjava/util/function/ToIntBiFunction<-TK;-TV;>;ILjava/util/function/IntBinaryOperator;)I
+46 -1: (Ljava/util/Comparator;)Ljava/util/Comparator;
+11 -1: isTitleCase
+38 -1: java/lang/IllegalMonitorStateException
+33 -1: java/nio/BufferUnderflowException
+28 -1: java/lang/ClassValue$Version
+11 -1: printLocale
+13 -1: STORE_BARRIER
+42 -1: ([ILjava/util/function/IntUnaryOperator;)V
+26 -1: sun/util/locale/BaseLocale
+29 -1: java/io/ObjectStreamException
+41 -1: sun/reflect/UnsafeStaticFieldAccessorImpl
+60 -1: ([Ljava/lang/Object;Ljava/util/Iterator;)[Ljava/lang/Object;
+30 -1: (Ljava/nio/charset/Charset;)[B
+73 -1: ([Ljava/lang/String;[Ljava/lang/String;Ljava/io/File;)Ljava/lang/Process;
+3 -1: VST
+80 -1: Java(TM) SE Runtime Environment (build 1.8.0-internal-iklam_2013_11_27_21_25-b00
+85 -1: (Ljava/lang/String;Ljava/lang/String;ILjava/lang/String;Ljava/net/URLStreamHandler;)V
+20 -1: getStackTraceElement
+36 -1: java/util/LinkedHashMap$LinkedKeySet
+17 -1: getNormalizedYear
+15 -1: maxBytesPerChar
+16 -1: java/util/Random
+34 -1: (I[C)Ljava/lang/invoke/LambdaForm;
+11 -1: nbits < 0: 
+7 -1: H_PCHAR
+29 -1: (Ljava/nio/charset/Charset;)I
+36 -1: (I[I[C)Ljava/lang/invoke/LambdaForm;
+23 -1: java/lang/ClassLoader$1
+23 -1: java/lang/ClassLoader$2
+23 -1: java/lang/ClassLoader$3
+9 -1: sizeTable
+36 -1: (Z)Ljava/lang/AbstractStringBuilder;
+29 -1: (Ljava/nio/charset/Charset;)V
+25 -1: ARRAY_BOOLEAN_INDEX_SCALE
+29 -1: (Ljava/nio/charset/Charset;)Z
+6 -1: keySet
+20 -1: declaredConstructors
+25 -1: oracle/jrockit/jfr/Timing
+12 -1: sizeIsSticky
+6 -1: IBM775
+17 -1: currentTimeMillis
+28 -1: java/nio/DirectDoubleBufferS
+71 -1: (Ljava/lang/Class<*>;Ljava/lang/String;Ljava/lang/invoke/MethodType;B)V
+28 -1: java/nio/DirectDoubleBufferU
+19 -1: cachedFixedDateJan1
+15 -1: getClassContext
+65 -1: (Ljava/security/CodeSource;Ljava/security/PermissionCollection;)V
+16 -1: unmodifiableList
+10 -1: getDoubleB
+82 -1: (Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodHandle;)Ljava/lang/String;
+11 -1: getEntryCrc
+10 -1: getDoubleL
+20 -1: (C)Ljava/lang/Class;
+11 -1: Null action
+22 -1: java/io/BufferedWriter
+67 -1: (Ljava/lang/String;[Ljava/lang/Class<*>;)Ljava/lang/reflect/Method;
+22 -1: parseSelectAnnotations
+6 -1: rename
+20 -1: acquireFieldAccessor
+43 -1: (Ljava/util/Vector;[Ljava/lang/Object;III)V
+32 -1: Ljava/lang/NullPointerException;
+29 -1: (Ljava/lang/ThreadLocal<*>;)V
+18 -1: descendingIterator
+37 -1: java/util/Collections$SynchronizedSet
+24 -1: mark/reset not supported
+12 -1: ) > toIndex(
+13 -1: <<ALL FILES>>
+10 -1: fastRemove
+4 -1: load
+31 -1: sun/reflect/ReflectionFactory$1
+39 -1: (Ljava/util/List<*>;)Ljava/lang/Object;
+39 -1: Ljava/util/Map<TE;Ljava/lang/Boolean;>;
+9 -1: offerLast
+31 -1: ()Ljava/lang/invoke/MethodType;
+40 -1: <E:Ljava/lang/Object;>Ljava/lang/Object;
+17 -1: getObjectVolatile
+14 -1: suspendThreads
+55 -1: (Ljava/lang/String;ZLjava/util/Set;)Lsun/misc/Resource;
+75 -1: <T:Ljava/lang/Object;>(Ljava/util/List<+Ljava/lang/Comparable<-TT;>;>;TT;)I
+6 -1: Lookup
+20 -1: java/io/OutputStream
+41 -1: Could not create application class loader
+28 -1: Lsun/misc/JavaUtilJarAccess;
+46 -1: java/util/Collections$SynchronizedNavigableSet
+8 -1: override
+21 -1: threadLocalRandomSeed
+10 -1: TEXT_PLAIN
+22 -1: ([B)Ljava/lang/String;
+29 -1: java/nio/InvalidMarkException
+14 -1: Throwable.java
+27 -1: newWrongMethodTypeException
+6 -1: ptypes
+8 -1: bugLevel
+62 -1: (ILjava/lang/CharSequence;II)Ljava/lang/AbstractStringBuilder;
+37 -1: ()Ljava/util/Locale$LocaleNameGetter;
+14 -1: getEntryMethod
+7 -1: getByte
+12 -1: UTF-32BE-BOM
+4 -1: lock
+34 -1: java/security/AccessControlContext
+79 -1: (Ljava/lang/String;Ljava/lang/invoke/MethodType;I)Ljava/lang/invoke/MemberName;
+5 -1: DEBUG
+5 -1: unbox
+17 -1: CLASSPATH_OPTOSFT
+4 -1: cbrt
+21 -1: LocalizedObjectGetter
+21 -1: ProtectionDomain.java
+22 -1: ([J)Ljava/util/BitSet;
+17 -1: getUnresolvedName
+34 -1: (Ljava/util/Map;Ljava/util/Map;I)V
+7 -1: cskoi8r
+14 -1: getInterfaces0
+48 -1: (Lsun/net/www/MessageHeader;)[Ljava/lang/String;
+14 -1: getFileNameMap
+16 -1: preserveCombiner
+19 -1: getDefaultUseCaches
+57 -1: (Ljava/lang/Class;[B[Ljava/lang/Object;)Ljava/lang/Class;
+21 -1: (D)Ljava/lang/Double;
+15 -1: iso8859_15_fdis
+23 -1: java/util/regex/Pattern
+6 -1: ibm912
+14 -1: findBuiltinLib
+42 -1: java/lang/annotation/AnnotationFormatError
+6 -1: ibm914
+6 -1: ibm915
+44 -1: java/nio/charset/IllegalCharsetNameException
+22 -1: COMBINING_SPACING_MARK
+20 -1: ()Ljava/lang/Thread;
+8 -1: readLine
+12 -1: (unresolved 
+65 -1: (Ljava/lang/String;Z)Ljava/util/Enumeration<Lsun/misc/Resource;>;
+41 -1: ([Ljava/lang/String;[Ljava/lang/String;)V
+30 -1: java/lang/Class$ReflectionData
+8 -1: requests
+52 -1: (Ljava/nio/charset/Charset;Lsun/nio/cs/US_ASCII$1;)V
+12 -1: ACCESS_WRITE
+6 -1: ibm920
+12 -1: CR_ERROR_MIN
+6 -1: jarMap
+6 -1: ibm923
+15 -1: java/lang/Error
+11 -1: VM_SETTINGS
+18 -1: name can't be null
+7 -1: PRESENT
+19 -1: setSecurityManager0
+101 -1: (Ljava/nio/channels/WritableByteChannel;Ljava/nio/charset/CharsetEncoder;I)Lsun/nio/cs/StreamEncoder;
+25 -1: ()Ljava/util/jar/JarFile;
+26 -1: java/io/ObjectOutputStream
+13 -1: no !/ in spec
+5 -1: (II)C
+12 -1: setPriority0
+30 -1: (Z)[Ljava/lang/reflect/Method;
+28 -1: sun/nio/cs/ThreadLocalCoders
+5 -1: (II)I
+22 -1: java/io/BufferedReader
+26 -1: ()Lsun/misc/JavaNetAccess;
+10 -1: Asia/Dhaka
+14 -1: parallelStream
+5 -1: (II)V
+5 -1: (II)Z
+31 -1: Ljava/lang/reflect/Constructor;
+21 -1: ()Ljava/time/Instant;
+31 -1: (Ljava/lang/String;III[J[I[IZ)V
+8 -1: Volatile
+23 -1: isUnicodeIdentifierPart
+27 -1: longPrimitiveParameterCount
+16 -1: Map is non-empty
+24 -1: getLocalizedOutputStream
+14 -1: java/lang/Byte
+10 -1: staticBase
+11 -1: lastElement
+17 -1: replaceStaleEntry
+17 -1: MAX_LOW_SURROGATE
+28 -1: java.launcher.X.macosx.usage
+20 -1: registerShutdownHook
+16 -1: SECOND_IN_MILLIS
+8 -1: Embedded
+16 -1: BootstrapMethods
+14 -1: numInvocations
+79 -1: <T:Ljava/lang/Object;>(Ljava/util/Collection<TT;>;)Ljava/util/Enumeration<TT;>;
+10 -1: rotateLeft
+46 -1: ([Ljava/lang/Object;)Ljava/util/stream/Stream;
+6 -1: verify
+17 -1: OTHER_PUNCTUATION
+26 -1: acquireConstructorAccessor
+38 -1: (Ljava/lang/String;I)Ljava/lang/Class;
+30 -1: java/net/UnknownContentHandler
+20 -1: PREFIX_LENGTH_OFFSET
+12 -1: nextGetIndex
+14 -1: standardOffset
+10 -1: entryNames
+15 -1: application/xml
+3 -1: BET
+39 -1: ([DIII)Ljava/util/Spliterator$OfDouble;
+83 -1: (JLjava/util/function/BiFunction;Ljava/util/function/BiFunction;)Ljava/lang/Object;
+10 -1: initMethod
+47 -1: (Ljava/util/LinkedList$Node;)Ljava/lang/Object;
+8 -1: isSealed
+12 -1: isAccessible
+11 -1: audio/x-wav
+46 -1: (Ljava/lang/String;)Ljava/util/jar/Attributes;
+24 -1: ()Ljava/io/OutputStream;
+15 -1: FIELD_MODIFIERS
+30 -1: sun/misc/URLClassPath$Loader$1
+20 -1: recursive invocation
+34 -1: (Ljava/lang/String;)Ljava/net/URL;
+12 -1: linkNodeLast
+34 -1: call site initialization exception
+17 -1: casAnnotationType
+8 -1: x-ibm874
+7 -1: isUpper
+58 -1: java/util/concurrent/ConcurrentHashMap$MapReduceValuesTask
+31 -1: Ill-formed Unicode locale key: 
+12 -1: defineClass0
+12 -1: defineClass1
+12 -1: defineClass2
+59 -1: Can not call newInstance() on the Class for java.lang.Class
+10 -1: codePoints
+3 -1: ...
+14 -1: readAheadLimit
+14 -1: parallelSetAll
+41 -1: ([Ljava/lang/Object;I)[Ljava/lang/Object;
+148 -1: (Ljava/lang/Throwable$PrintStreamOrWriter;[Ljava/lang/StackTraceElement;Ljava/lang/String;Ljava/lang/String;Ljava/util/Set<Ljava/lang/Throwable;>;)V
+10 -1: Deque.java
+21 -1: Must be volatile type
+7 -1: setForm
+58 -1: Ljava/lang/Number;Ljava/lang/Comparable<Ljava/lang/Byte;>;
+47 -1: (Ljava/lang/String;Ljava/security/CodeSource;)V
+30 -1: java/io/InterruptedIOException
+44 -1: java/util/Collections$SynchronizedCollection
+16 -1: Invalid Jar file
+32 -1: sun/util/calendar/CalendarSystem
+67 -1: (JLsun/util/calendar/CalendarDate;)Lsun/util/calendar/CalendarDate;
+19 -1: DEFAULT_BUFFER_SIZE
+16 -1: readObjectNoData
+16 -1: setJavaNioAccess
+73 -1: (Ljava/lang/invoke/LambdaForm$Name;[Ljava/lang/Object;)Ljava/lang/Object;
+14 -1: copyToIntArray
+10 -1: hasWaiters
+20 -1: (I)Ljava/lang/Class;
+35 -1: all           turn on all debugging
+14 -1: Invalid host: 
+26 -1: Lsun/nio/cs/StreamEncoder;
+43 -1: sun/misc/JavaSecurityProtectionDomainAccess
+11 -1: getNamedCon
+8 -1: H_SERVER
+27 -1: java/util/function/Consumer
+12 -1: isLocalClass
+81 -1: (Ljava/util/LinkedHashMap$Entry<TK;TV;>;Ljava/util/LinkedHashMap$Entry<TK;TV;>;)V
+83 -1: Ljava/lang/Object;Ljava/io/Serializable;Ljava/lang/Comparable<Ljava/lang/Boolean;>;
+4 -1: exec
+43 -1: java/lang/reflect/InvocationTargetException
+35 -1: (Ljava/io/File;Ljava/lang/String;)V
+9 -1: modifiers
+35 -1: (Ljava/io/File;Ljava/lang/String;)Z
+9 -1: Byte.java
+12 -1: unknown mode
+18 -1: initializeVerifier
+24 -1: (Ljava/nio/ByteBuffer;)I
+22 -1: ([Ljava/lang/Object;)I
+11 -1: correctType
+6 -1: escape
+52 -1: (Ljava/security/ProtectionDomain;)Ljava/lang/String;
+11 -1: annotations
+121 -1: (Ljava/lang/Class;ILjava/lang/Class;Ljava/lang/String;Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/invoke/MemberName;
+22 -1:  using an instance of 
+14 -1: getSpeciesData
+28 -1: ()Ljava/security/CodeSource;
+12 -1: JZENTRY_NAME
+24 -1: (Ljava/nio/ByteBuffer;)V
+4 -1: ZBSC
+22 -1: ([Ljava/lang/Object;)V
+7 -1: isParam
+165 -1: (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
+70 -1: (ILjava/util/List<Ljava/lang/Class<*>;>;)Ljava/lang/invoke/LambdaForm;
+24 -1: Ljava/io/FilePermission;
+22 -1: ([Ljava/lang/Object;)Z
+11 -1: mergeHeader
+11 -1: applyAsLong
+28 -1: (IJ)Ljava/lang/StringBuffer;
+10 -1: arityCheck
+52 -1: (ILjava/lang/Class<*>;)Ljava/lang/invoke/MethodType;
+13 -1: toUpperCaseEx
+9 -1: nextIndex
+11 -1: start > end
+4 -1: long
+6 -1: Static
+27 -1: ()Ljava/lang/reflect/Field;
+10 -1: bufUpdater
+43 -1: averageBytesPerChar exceeds maxBytesPerChar
+105 -1: (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lsun/util/locale/BaseLocale$1;)V
+23 -1: ARRAY_SHORT_INDEX_SCALE
+15 -1: getHeaderFields
+36 -1: java/util/HashMap$HashMapSpliterator
+10 -1: Guard.java
+29 -1: java/util/RandomAccessSubList
+6 -1: addAll
+7 -1: getTime
+16 -1: invokeHandleForm
+32 -1: sun/util/locale/LocaleExtensions
+16 -1: checkAndLoadMain
+19 -1: INTERFACE_MODIFIERS
+11 -1: resolveName
+20 -1: getContentLengthLong
+53 -1: (ICLjava/lang/Object;)Ljava/lang/invoke/MethodHandle;
+19 -1: name cannot be null
+23 -1: hasReceiverTypeDispatch
+12 -1: getExtension
+49 -1: Ljava/util/Set<Ljava/security/ProtectionDomain;>;
+114 -1: (Ljava/lang/String;[J[I[J[I[Lsun/util/calendar/ZoneInfoFile$ZoneOffsetTransitionRule;)Lsun/util/calendar/ZoneInfo;
+24 -1: NativeSignalHandler.java
+58 -1: (Ljava/lang/Thread;)Ljava/lang/ThreadLocal$ThreadLocalMap;
+10 -1: interface 
+68 -1: (Ljava/lang/String;)Ljava/lang/invoke/BoundMethodHandle$SpeciesData;
+15 -1: addShutdownHook
+32 -1: java/security/AccessController$1
+10 -1: filterTags
+19 -1: [Ljava/lang/Number;
+92 -1: <T:Ljava/lang/Object;>(Ljava/util/function/ToLongFunction<-TT;>;)Ljava/util/Comparator<TT;>;
+43 -1: java/util/LinkedHashMap$LinkedEntryIterator
+5 -1: utf-8
+11 -1: iso-8859-13
+11 -1: iso-8859-15
+58 -1: (Lsun/misc/URLClassPath$JarLoader;)Ljava/util/jar/JarFile;
+41 -1:               CertPathValidator debugging
+22 -1: ARRAY_LONG_BASE_OFFSET
+57 -1: (Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;
+13 -1: queuePrintJob
+14 -1: Watchable.java
+15 -1: jdkMajorVersion
+62 -1: (Ljava/lang/String;ZJJ)Ljava/lang/management/MemoryPoolMXBean;
+23 -1: twoToTheDoubleScaleDown
+22 -1: registerVMNotification
+30 -1: ()Lsun/misc/JavaUtilJarAccess;
+17 -1: getTargetVolatile
+4 -1: exit
+13 -1: StringDecoder
+12 -1: hasRemaining
+9 -1: bigEndian
+14 -1: checkMulticast
+13 -1: clearProperty
+18 -1: ForEachMappingTask
+15 -1: Collection.java
+55 -1: (Ljava/io/InputStream;)Ljava/security/cert/Certificate;
+5 -1: UTF-8
+11 -1: transitions
+7 -1: wrapAlt
+27 -1: ClassNotFoundException.java
+20 -1: UnresolvedPermission
+14 -1: charsetForName
+9 -1: getParent
+18 -1: [Ljava/lang/Short;
+24 -1: UnmodifiableNavigableMap
+5 -1: xflow
+10 -1: interfaces
+19 -1: doubleToRawLongBits
+73 -1: (Ljava/lang/reflect/Constructor<*>;[Ljava/lang/Object;)Ljava/lang/Object;
+10 -1: getInCheck
+21 -1: (Ljava/lang/Thread;)V
+15 -1: fromIndex < 0: 
+63 -1: (ITK;TV;Ljava/util/concurrent/ConcurrentHashMap$Node<TK;TV;>;)V
+9 -1: pollFirst
+21 -1: (Ljava/lang/Thread;)Z
+16 -1: checkProxyMethod
+39 -1: generateLambdaFormInterpreterEntryPoint
+15 -1: findLoadedClass
+6 -1: system
+5 -1: ITALY
+45 -1: combiner      SubjectDomainCombiner debugging
+34 -1: NativeConstructorAccessorImpl.java
+22 -1: ([C)Ljava/lang/String;
+39 -1: (Ljava/lang/String;Ljava/lang/Class;Z)V
+18 -1: java/lang/Thread$1
+20 -1: window can't be null
+10 -1: Debug.java
+17 -1: singletonIterator
+53 -1: java/util/concurrent/ConcurrentHashMap$ForEachKeyTask
+20 -1: java/security/Policy
+13 -1: getDescriptor
+32 -1: (I)Ljava/lang/invoke/MethodType;
+15 -1: nativeLibraries
+27 -1: sun/util/locale/LanguageTag
+8 -1: priority
+12 -1: IntegerCache
+14 -1: connectTimeout
+9 -1: namePairs
+17 -1: vmAllowSuspension
+16 -1: METHOD_MODIFIERS
+51 -1: (Ljava/lang/Class<*>;)Ljava/lang/invoke/MethodType;
+20 -1: MIN_TREEIFY_CAPACITY
+13 -1: getEntryBytes
+33 -1: ()Lsun/reflect/ReflectionFactory;
+17 -1: getDisplayCountry
+13 -1: isWrapperType
+5 -1: utf16
+12 -1: parallelSort
+27 -1: (Ljava/nio/ByteBuffer;IIZ)V
+56 -1: (Ljava/lang/Class;Ljava/lang/Class;ILjava/lang/Class;I)Z
+9 -1: isDefined
+20 -1: sun/misc/FloatConsts
+10 -1: putDoubleB
+30 -1: java/lang/NoSuchFieldException
+27 -1: Value out of range. Value:"
+36 -1: sun/reflect/NativeMethodAccessorImpl
+7 -1: decoder
+38 -1: ([Ljava/lang/invoke/MutableCallSite;)V
+10 -1: putDoubleL
+68 -1: (Ljava/lang/reflect/Method;)Lsun/reflect/generics/scope/MethodScope;
+37 -1: java/lang/invoke/MethodHandles$Lookup
+9 -1: Void.java
+28 -1: sun/util/locale/BaseLocale$1
+10 -1: stackTrace
+7 -1: toClass
+11 -1: access$1500
+41 -1: (Ljava/lang/Object;I)Ljava/lang/Class<*>;
+148 -1: (Ljava/lang/invoke/MethodType;Ljava/lang/invoke/LambdaForm;Ljava/lang/invoke/MemberName;Ljava/lang/Object;JLjava/lang/invoke/DirectMethodHandle$1;)V
+22 -1: ARRAY_BYTE_BASE_OFFSET
+13 -1: ZipEntry.java
+56 -1: (Ljava/util/List;Ljava/util/Collection;)Ljava/util/List;
+5 -1: utf32
+16 -1: ISO_646.irv:1991
+5 -1: p-126
+20 -1: sun.net.www.protocol
+3 -1: key
+20 -1: IMPLEMENTATION_TITLE
+93 -1: (Lsun/util/locale/BaseLocale;Lsun/util/locale/LocaleExtensions;)Lsun/util/locale/LanguageTag;
+66 -1: ([Ljava/lang/Object;[Ljava/lang/Object;IIILjava/util/Comparator;)V
+27 -1: java/nio/DirectFloatBufferS
+27 -1: java/nio/DirectFloatBufferU
+15 -1: JZENTRY_COMMENT
+8 -1: casTabAt
+10 -1: getVariant
+24 -1: Ljava/lang/Thread$State;
+35 -1: ()Ljava/lang/AbstractStringBuilder;
+114 -1: (Ljava/security/CodeSource;Ljava/security/PermissionCollection;Ljava/lang/ClassLoader;[Ljava/security/Principal;)V
+16 -1: getShortVolatile
+18 -1: SoftReference.java
+3 -1: BST
+12 -1: isCastableTo
+28 -1: sun.zip.disableMemoryMapping
+11 -1: copyOfRange
+17 -1: ()Lsun/misc/Perf;
+59 -1: (Ljava/lang/String;[Ljava/io/File;Ljava/lang/ClassLoader;)V
+27 -1: (Lsun/misc/JavaAWTAccess;)V
+8 -1: DECLARED
+18 -1: loadedLibraryNames
+6 -1: CENNAM
+7 -1: encprop
+5 -1: ABASE
+27 -1: java/util/WeakHashMap$Entry
+13 -1: wrapWithPrims
+5 -1: UTF32
+29 -1: Ljava/net/URISyntaxException;
+6 -1: groups
+65 -1: <T:Ljava/lang/Object;>(Ljava/util/Set<+TT;>;)Ljava/util/Set<TT;>;
+32 -1: lambda$comparingByKey$6d558cbf$1
+15 -1: removeElementAt
+49 -1: [Ljava/util/concurrent/ConcurrentHashMap$Segment;
+32 -1: Sign character in wrong position
+6 -1: IBM819
+39 -1: java/security/cert/CertificateException
+4 -1: join
+30 -1: Ljava/lang/invoke/ForceInline;
+14 -1: expandCapacity
+19 -1: Ljava/lang/Integer;
+11 -1: NUMBER_THAI
+10 -1: getExtURLs
+9 -1: retainAll
+21 -1: (S)Ljava/lang/String;
+8 -1: truncate
+51 -1: java/util/ArraysParallelSortHelpers$FJObject$Sorter
+28 -1: newIndexOutOfBoundsException
+26 -1: JavaUtilJarAccessImpl.java
+22 -1: (II)Ljava/util/BitSet;
+10 -1: getLongAt0
+65 -1: <A::Ljava/lang/annotation/Annotation;>(Ljava/lang/Class<TA;>;)TA;
+26 -1: (Ljava/lang/ThreadLocal;)I
+5 -1: (J)[B
+27 -1: Ljava/lang/CharacterData00;
+18 -1: sun/misc/Cleaner$1
+59 -1: (Ljava/util/List;Ljava/lang/Object;Ljava/util/Comparator;)I
+114 -1: (JLjava/util/function/ToDoubleFunction<Ljava/util/Map$Entry<TK;TV;>;>;DLjava/util/function/DoubleBinaryOperator;)D
+28 -1: java/lang/ClassCastException
+26 -1: (Ljava/lang/ThreadLocal;)V
+27 -1: ()[Ljava/lang/reflect/Type;
+13 -1: not invoker: 
+56 -1: (Ljava/net/URL;Ljava/net/Proxy;)Ljava/net/URLConnection;
+6 -1: before
+37 -1: ([DII)Ljava/util/stream/DoubleStream;
+9 -1: logicalOr
+9 -1: IS_METHOD
+12 -1: SPACE_USABLE
+12 -1: lastModified
+10 -1: setSigners
+8 -1: Invokers
+7 -1: nCopies
+12 -1: utf-32le-bom
+7 -1: (IIII)J
+17 -1: jdkSpecialVersion
+26 -1: ()Ljava/lang/StringBuffer;
+17 -1: SearchEntriesTask
+14 -1: java/net/Parts
+20 -1: Ljava/lang/Runnable;
+35 -1: java/util/WeakHashMap$ValueIterator
+19 -1: FinalReference.java
+7 -1: (IIII)V
+23 -1: Ljava/lang/ThreadGroup;
+10 -1: nullsFirst
+8 -1: setCache
+55 -1: (Ljava/util/List;Ljava/lang/Object;Ljava/lang/Object;)Z
+24 -1: java/util/SimpleTimeZone
+6 -1: IBM850
+6 -1: IBM852
+25 -1: sun/net/www/MeteredStream
+4 -1: exts
+6 -1: IBM855
+16 -1: allocateElements
+6 -1: IBM857
+19 -1: setDefaultUseCaches
+6 -1: IBM858
+5 -1: slice
+9 -1: marklimit
+77 -1: Ljava/lang/Object;Ljava/security/PrivilegedExceptionAction<Ljava/lang/Void;>;
+32 -1: java/util/Collections$CheckedSet
+12 -1: getModifiers
+8 -1: protocol
+10 -1: getInteger
+33 -1: ([J)Ljava/util/stream/LongStream;
+6 -1: IBM862
+8 -1: Map.java
+35 -1: java/lang/Class$EnclosingMethodInfo
+25 -1: (J)Ljava/math/BigInteger;
+31 -1: (Ljava/net/URL;Ljava/io/File;)V
+6 -1: IBM866
+6 -1: unload
+28 -1: sun/invoke/util/VerifyAccess
+105 -1: ()Ljava/util/Map<Ljava/lang/Class<+Ljava/lang/annotation/Annotation;>;Ljava/lang/annotation/Annotation;>;
+25 -1: Resetting to invalid mark
+20 -1: java/util/Vector$Itr
+5 -1: SHIFT
+11 -1: NonfairSync
+18 -1: getSecurityManager
+34 -1: ()[Ljava/lang/ClassValue$Entry<*>;
+28 -1: (J)Ljava/lang/StringBuilder;
+28 -1: (Ljava/security/PublicKey;)V
+12 -1: getResources
+6 -1: IBM874
+27 -1:  which Java does not define
+36 -1: (Ljava/lang/invoke/MethodTypeForm;)V
+48 -1: array length is not legal for long[] or double[]
+18 -1: IS_FIELD_OR_METHOD
+7 -1: Aliases
+17 -1: checkedExceptions
+13 -1: getDayOfMonth
+51 -1: (Ljava/util/Spliterator;Z)Ljava/util/stream/Stream;
+20 -1: java/io/EOFException
+26 -1: Enclosing method not found
+17 -1: flushLeftoverChar
+122 -1: (Ljava/lang/Class<*>;[Ljava/lang/Class<*>;[Ljava/lang/Class<*>;IILjava/lang/String;[B[B)Ljava/lang/reflect/Constructor<*>;
+18 -1: buildAnnotatedType
+21 -1: setContextClassLoader
+22 -1: java/io/UnixFileSystem
+20 -1: nonSyncContentEquals
+43 -1: java/util/Collections$SynchronizedSortedMap
+15 -1: Properties.java
+35 -1: com.oracle.usagetracker.config.file
+13 -1: java/util/Map
+18 -1: setEagerValidation
+13 -1: getSetMessage
+6 -1: unlock
+14 -1: refKindIsField
+22 -1: bad field type alias: 
+17 -1: casAnnotationData
+6 -1: AUGUST
+106 -1: (Ljava/util/concurrent/CountedCompleter;[Ljava/lang/Object;[Ljava/lang/Object;IIIILjava/util/Comparator;)V
+11 -1: monitorExit
+17 -1: linkMethodTracing
+69 -1: (Ljava/lang/String;Ljava/lang/String;Lsun/util/locale/BaseLocale$1;)V
+21 -1: java/lang/ClassLoader
+39 -1:               PKCS11 KeyStore debugging
+10 -1: checkRtype
+25 -1: getLocalGregorianCalendar
+23 -1: GenericDeclaration.java
+12 -1: isViewableAs
+22 -1: static_oop_field_count
+72 -1: (Ljava/util/function/ToDoubleFunction<-TT;>;)Ljava/util/Comparator<TT;>;
+11 -1: languageKey
+6 -1: Class 
+34 -1: java/util/HashMap$ValueSpliterator
+37 -1: (IJ)Ljava/lang/AbstractStringBuilder;
+17 -1: privilegedContext
+36 -1: java/util/LinkedHashMap$LinkedValues
+11 -1: getHostName
+10 -1: beginEntry
+7 -1: isAlpha
+61 -1: (Ljava/lang/invoke/MethodType;I)Ljava/lang/invoke/LambdaForm;
+10 -1: expandArgs
+14 -1: Finalizer.java
+14 -1: timeDefinition
+28 -1: ()Ljava/util/jar/Attributes;
+14 -1: ansi_x3.4-1968
+11 -1: setPriority
+23 -1: (C)Ljava/lang/Class<*>;
+26 -1: (Ljava/lang/Object;TV;)TV;
+70 -1: (Ljava/util/function/BiFunction;Ljava/lang/Object;Ljava/lang/Object;)V
+48 -1: ()Lsun/reflect/generics/factory/GenericsFactory;
+25 -1: java/lang/invoke/CallSite
+8 -1: tzdb.dat
+17 -1: containsAllLimits
+17 -1: fileNameMapLoaded
+6 -1: values
+17 -1: setLastAccessTime
+12 -1: expandFromVM
+50 -1: java/lang/invoke/MethodHandle$PolymorphicSignature
+3 -1: .EC
+14 -1: access denied 
+22 -1: java/util/AbstractList
+47 -1: (IILjava/lang/String;)Ljava/lang/StringBuilder;
+52 -1: ()Lsun/reflect/generics/repository/MethodRepository;
+22 -1: (Ljava/lang/String;)[B
+57 -1: (Ljava/lang/Object;)Ljava/lang/invoke/DirectMethodHandle;
+18 -1: compareAndSwapLong
+4 -1:  != 
+6 -1: StdArg
+29 -1: (Ljava/security/Permission;)V
+22 -1: ([D)Ljava/lang/String;
+28 -1: Lsun/reflect/MethodAccessor;
+14 -1: ansi_x3.4-1986
+20 -1: getPeakFinalRefCount
+29 -1: (Ljava/security/Permission;)Z
+5 -1: debug
+38 -1: (Ljava/lang/reflect/Constructor<*>;)[B
+27 -1: java/util/GregorianCalendar
+16 -1: Null replacement
+26 -1: ()Ljava/lang/reflect/Type;
+28 -1: DIRECTIONALITY_LEFT_TO_RIGHT
+102 -1: (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Lsun/util/locale/BaseLocale;
+15 -1: isConvertibleTo
+24 -1: ARRAY_DOUBLE_INDEX_SCALE
+16 -1: getComponentType
+29 -1: sun/util/locale/LocaleMatcher
+11 -1: LOCALECACHE
+6 -1: UNWRAP
+16 -1: AbstractSet.java
+3 -1: CAT
+36 -1: java/lang/annotation/RetentionPolicy
+14 -1: getParameters0
+8 -1: .Handler
+33 -1: Ljava/lang/IllegalStateException;
+10 -1: RAW_RETURN
+20 -1: java/lang/ClassValue
+16 -1: getDisplayString
+152 -1: <K:Ljava/lang/Object;V:Ljava/lang/Object;>([Ljava/util/concurrent/ConcurrentHashMap$Node<TK;TV;>;I)Ljava/util/concurrent/ConcurrentHashMap$Node<TK;TV;>;
+67 -1: <K:Ljava/lang/Object;V:Ljava/lang/Object;>()Ljava/util/Map<TK;TV;>;
+214 -1: (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
+33 -1: java/lang/invoke/SerializedLambda
+42 -1: ([Ljava/lang/Object;II)[Ljava/lang/Object;
+22 -1: java/util/zip/ZipUtils
+9 -1: setDaemon
+26 -1: java/net/HttpURLConnection
+6 -1: mkdirs
+20 -1: (Ljava/io/Reader;I)V
+28 -1: (IC)Ljava/lang/StringBuffer;
+45 -1: ([Ljava/lang/Class<*>;I)[Ljava/lang/Class<*>;
+29 -1: java/lang/invoke/MethodHandle
+28 -1: sun/misc/CompoundEnumeration
+6 -1: setVal
+23 -1: INTERNED_ARGUMENT_LIMIT
+4 -1: NULL
+49 -1: (Ljava/lang/invoke/MemberName;Ljava/lang/Class;)Z
+43 -1: java/util/Collections$UnmodifiableSortedMap
+39 -1: (Ljava/lang/Object;Ljava/lang/Object;)I
+6 -1: ([JJ)I
+19 -1: java/io/PrintWriter
+25 -1: ()Ljava/lang/ThreadGroup;
+5 -1: (IJ)J
+16 -1: onMalformedInput
+15 -1: decrementAndGet
+11 -1: -2147483648
+6 -1: reduce
+12 -1: asCharBuffer
+39 -1: (Ljava/lang/Object;Ljava/lang/Object;)V
+44 -1: (Ljava/util/SortedSet;)Ljava/util/SortedSet;
+9 -1: backtrace
+3 3: Bar
+47 -1: ()Lsun/misc/JavaSecurityProtectionDomainAccess;
+39 -1: (Ljava/lang/Object;Ljava/lang/Object;)Z
+5 -1: (IJ)V
+6 -1: ([JJ)V
+22 -1: ([Ljava/lang/Thread;)I
+5 -1: (IJ)Z
+7 -1: ([BII)I
+79 -1: <T:Ljava/lang/Object;>(Ljava/util/Comparator<-TT;>;)Ljava/util/Comparator<TT;>;
+12 -1: getUnchecked
+10 -1: getBaseURL
+36 -1: (Ljava/lang/Object;)Ljava/util/List;
+53 -1: (Ljava/util/function/Function;)Ljava/util/Comparator;
+10 -1: getComment
+7 -1: ([BII)V
+30 -1: privateGetDeclaredConstructors
+58 -1: (Ljava/lang/String;ZILjava/util/Locale;)Ljava/lang/String;
+18 -1: unknown era name: 
+13 -1: invokeSpecial
+9 -1: checkLink
+16 -1: cspc8codepage437
+6 -1: stream
+18 -1: sun/nio/cs/UTF_8$1
+18 -1: contextClassLoader
+50 -1: ([Ljava/util/concurrent/ConcurrentHashMap$Node;I)V
+30 -1: sun/util/calendar/BaseCalendar
+11 -1: enumeration
+18 -1: key can't be empty
+137 -1: <U:Ljava/lang/Object;>(JLjava/util/function/Function<Ljava/util/Map$Entry<TK;TV;>;+TU;>;Ljava/util/function/BiFunction<-TU;-TU;+TU;>;)TU;
+10 -1: getBoolean
+5 -1: eetop
+49 -1: (Ljava/lang/invoke/MethodType;)Ljava/lang/String;
+43 -1: sun/reflect/generics/scope/ConstructorScope
+13 -1: CANADA_FRENCH
+39 -1: Ljava/nio/channels/ReadableByteChannel;
+15 -1: java/lang/Float
+29 -1: DIRECTIONALITY_OTHER_NEUTRALS
+52 -1: (ZLjava/nio/charset/Charset;Ljava/io/OutputStream;)V
+8 -1: appendTo
+19 -1: PARAGRAPH_SEPARATOR
+16 -1: (Unknown Source)
+4 -1: tree
+38 -1: (I[C)Ljava/lang/AbstractStringBuilder;
+14 -1: VerifierStream
+48 -1: (Ljava/util/Collection<TE;>;Ljava/lang/Object;)V
+15 -1: releaseInflater
+20 -1: getHeaderNamesInList
+17 -1: getSystemPackages
+8 -1: teardown
+6 -1: (BZI)I
+10 -1: checkWrite
+19 -1: JavaLangAccess.java
+31 -1: Ljava/lang/ClassValue$Identity;
+50 -1: (Ljava/util/concurrent/CountedCompleter;[S[SIIII)V
+24 -1: getDeclaredConstructors0
+3 -1: /..
+3 -1: /./
+16 -1: hashCodeForCache
+18 -1: Property settings:
+26 -1: Illegal initial capacity: 
+10 -1: text/plain
+61 -1: (Ljava/util/function/ToDoubleFunction;)Ljava/util/Comparator;
+24 -1: createMemoryManagerMBean
+10 -1: ,lastRule=
+9 -1: GMT-00:00
+5 -1: mtime
+40 -1: (Ljava/lang/String;I)[Ljava/lang/String;
+11 -1: (TT;TV;)TV;
+154 -1: (Ljava/lang/Class<*>;Ljava/lang/String;[Ljava/lang/Class<*>;Ljava/lang/Class<*>;[Ljava/lang/Class<*>;IILjava/lang/String;[B[B[B)Ljava/lang/reflect/Method;
+41 -1: (Ljava/util/jar/JarFile;)Ljava/util/List;
+43 -1: (JILjava/lang/Object;)Ljava/nio/ByteBuffer;
+19 -1: MethodTypeForm.java
+21 -1: java/util/jar/JarFile
+30 -1: java/lang/Integer$IntegerCache
+22 -1: getDisplayVariantArray
+6 -1: setAll
+13 -1: ClassValueMap
+52 -1: (Ljava/security/PublicKey;Ljava/security/Provider;)V
+51 -1: java/util/concurrent/ConcurrentHashMap$BaseIterator
+59 -1: (Ljava/lang/Runnable;Ljava/security/AccessControlContext;)V
+100 -1: (Ljava/util/concurrent/ConcurrentMap;Ljava/util/function/BiFunction;)Ljava/util/function/BiConsumer;
+8 -1: default 
+13 -1: compareAndSet
+10 -1: iso8859-13
+9 -1: putShortB
+14 -1: skipDelimiters
+28 -1: URI has a fragment component
+10 -1: iso8859-15
+42 -1: (Ljava/net/Proxy;)Ljava/net/URLConnection;
+23 -1: needsPackageAccessCheck
+9 -1: putShortL
+3 -1: //[
+69 -1: (Ljava/security/AccessControlContext;Ljava/security/DomainCombiner;)V
+18 -1: too many arguments
+35 -1: ([III)Ljava/util/Spliterator$OfInt;
+10 -1: CopiesList
+10 -1: iso-8859-1
+9 -1: ([BII[C)I
+10 -1: iso-8859-2
+11 -1: returnCount
+10 -1: iso-8859-4
+10 -1: iso-8859-5
+8 -1: utf_16be
+10 -1: iso-8859-7
+9 -1: isLimited
+9 -1: parseByte
+10 -1: iso-8859-9
+13 -1: , s.length() 
+10 -1: matchCerts
+14 -1: RECURSIVE_CHAR
+11 -1: reduceToInt
+11 -1: displayName
+9 -1: calendars
+64 -1: (Ljava/lang/String;ZLjava/util/jar/JarEntry;)Lsun/misc/Resource;
+11 -1: isProtected
+78 -1: (Ljava/util/SortedMap;Ljava/lang/Class;Ljava/lang/Class;)Ljava/util/SortedMap;
+4 -1: trim
+20 -1: java/nio/FloatBuffer
+17 -1: PreHashedMap.java
+74 -1: Ljava/util/concurrent/ConcurrentMap<Ljava/lang/String;Ljava/lang/String;>;
+22 -1: ([S)Ljava/lang/String;
+19 -1: PrintStreamOrWriter
+38 -1: java/util/Collections$EmptyEnumeration
+22 -1: java/util/LinkedList$1
+13 -1: sunpkcs11.jar
+25 -1: java/nio/DirectByteBuffer
+96 -1: (ZLjava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodHandle;)Ljava/lang/invoke/MethodHandle;
+7 -1: isArray
+43 -1: (Ljava/lang/String;)Ljava/util/Enumeration;
+52 -1: java/lang/invoke/MethodHandleImpl$AsVarargsCollector
+59 -1: (Ljava/lang/String;Lsun/misc/Resource;)Ljava/lang/Class<*>;
+26 -1: setJavaNetHttpCookieAccess
+15 -1: wrongTargetType
+57 -1: java/util/concurrent/ConcurrentHashMap$ForEachMappingTask
+33 -1: [Ljava/lang/reflect/TypeVariable;
+5 -1: load0
+39 -1: (Ljava/lang/String;)Ljava/lang/Boolean;
+21 -1: isHeldByCurrentThread
+14 -1: outOfBoundsMsg
+30 -1: Ljava/lang/ref/Reference$Lock;
+11 -1: ISO-8859-13
+84 -1: <T:Ljava/lang/Object;>(Ljava/lang/ClassValue<TT;>;)Ljava/lang/ClassValue$Entry<TT;>;
+11 -1: ISO-8859-15
+40 -1: (Ljava/net/URL;)Ljava/net/URLConnection;
+84 -1: <T:Ljava/lang/Object;:Ljava/lang/Comparable<-TT;>;>(Ljava/util/Collection<+TT;>;)TT;
+38 -1: sun/reflect/generics/scope/MethodScope
+5 -1: mutex
+11 -1: loaderTypes
+8 -1: defaults
+22 -1: getActualTypeArguments
+41 -1: DIRECTIONALITY_EUROPEAN_NUMBER_TERMINATOR
+4 -1: keys
+71 -1: (Ljava/lang/Class<*>;Ljava/lang/Class<*>;)Ljava/lang/invoke/MethodType;
+94 -1: (Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;I)Ljava/lang/invoke/MethodHandle;
+113 -1: <E:Ljava/lang/Object;>Ljava/util/AbstractSet<TE;>;Ljava/util/Set<TE;>;Ljava/lang/Cloneable;Ljava/io/Serializable;
+12 -1: checkConnect
+39 -1: (Ljava/lang/String;Ljava/util/Locale;)V
+26 -1: ([CII[C)Ljava/lang/String;
+12 -1: isDoubleWord
+37 -1: configparser  JAAS ConfigFile parsing
+27 -1: sun/misc/Perf$GetPerfAction
+44 -1: (Ljava/util/Collections$UnmodifiableList;I)V
+4 -1: acos
+26 -1: java/nio/DirectLongBufferS
+7 -1: (ITE;)V
+14 -1: putIntVolatile
+24 -1: setContentHandlerFactory
+26 -1: java/nio/DirectLongBufferU
+10 -1: fieldCount
+11 -1: invokeBasic
+50 -1: (Ljava/util/zip/ZipEntry;)Ljava/util/jar/JarEntry;
+24 -1: java/util/Locale$Builder
+9 -1: setParent
+11 -1: asLifoQueue
+33 -1: lambda$comparingDouble$8dcf42ea$1
+24 -1: (Ljava/lang/Throwable;)I
+35 -1: (Lsun/misc/JavaUtilZipFileAccess;)V
+49 -1: (ILjava/lang/Object;)Ljava/util/HashMap$TreeNode;
+10 -1: CLASS_PATH
+6 -1: tclass
+11 -1: getExponent
+23 -1: getAnnotatedReturnType0
+18 -1: checkPackageAccess
+35 -1: Can not instantiate java.lang.Class
+24 -1: (Ljava/lang/Throwable;)V
+195 -1: (Ljava/lang/invoke/LambdaForm$Name;Ljava/lang/invoke/LambdaForm$Name;Ljava/lang/invoke/BoundMethodHandle$SpeciesData;Ljava/lang/invoke/BoundMethodHandle$SpeciesData;)Ljava/lang/invoke/LambdaForm;
+17 -1: Empty replacement
+3 -1: .SF
+14 -1: ByteOrder.java
+39 -1: ()Lsun/util/calendar/BaseCalendar$Date;
+35 -1: ()[Ljava/security/ProtectionDomain;
+12 -1: setElementAt
+30 -1: (Ljava/security/CodeSource;Z)Z
+45 -1: (Ljava/lang/Class<*>;)Ljava/lang/ClassLoader;
+52 -1: (Ljava/nio/charset/Charset;)Ljava/util/zip/ZipCoder;
+13 -1: foldArguments
+23 -1: java/time/LocalDateTime
+30 -1: [Lsun/launcher/LauncherHelper;
+16 -1: 0123456789abcdef
+60 -1: (Ljava/util/Spliterator$OfInt;Z)Ljava/util/stream/IntStream;
+33 -1: (ILjava/lang/String;IIIIIIIIIII)V
+20 -1: DMH.newInvokeSpecial
+28 -1: java/nio/charset/CoderResult
+33 -1: sun/nio/cs/StandardCharsets$Cache
+11 -1: saveConvert
+14 -1: ExtClassLoader
+12 -1: parentOrNull
+20 -1: insertParameterTypes
+32 -1: (II)Ljava/util/stream/IntStream;
+13 -1: setStackTrace
+20 -1:  is not an enum type
+3 -1: CNT
+4 -1: host
+85 -1: ([Ljava/lang/Object;Ljava/util/function/IntFunction;)Ljava/util/function/IntConsumer;
+11 -1: batchRemove
+8 -1: newField
+16 5: sun/nio/cs/UTF_8
+104 -1: (Ljava/lang/invoke/LambdaForm$Name;Ljava/lang/invoke/LambdaForm$Name;)Ljava/lang/invoke/LambdaForm$Name;
+8 -1: saturday
+35 -1: java/util/ArraysParallelSortHelpers
+15 -1: java/util/Queue
+40 -1: (Ljava/lang/Class<*>;)Ljava/lang/String;
+7 -1: toChars
+5 -1: first
+17 -1: ArrayDecoder.java
+30 -1: ()Lsun/reflect/MethodAccessor;
+26 -1: thread group can't be null
+13 -1: IllegalName: 
+32 -1: java/util/Collections$SetFromMap
+14 -1: line.separator
+17 -1: getDeclaredMethod
+10 -1: getMinutes
+35 -1: (Lsun/util/locale/BaseLocale$Key;)I
+40 -1: ([Ljava/lang/String;)Ljava/lang/Process;
+31 -1: Ljava/util/LinkedHashMap$Entry;
+13 -1: , str.length 
+8 -1: getProbe
+6 -1: ([DI)I
+5 -1: (CI)I
+23 -1: saveAndRemoveProperties
+6 -1: rehash
+3 -1: lcb
+31 -1: Ljava/util/Arrays$NaturalOrder;
+55 -1: (IILjava/lang/String;)Ljava/lang/AbstractStringBuilder;
+10 -1: loadFactor
+15 -1: putLongVolatile
+34 -1: sun/misc/URLClassPath$FileLoader$1
+12 -1: Europe/Paris
+8 -1: DECEMBER
+86 -1: Ljava/lang/Object;Ljava/security/PrivilegedAction<Lsun/misc/Launcher$AppClassLoader;>;
+22 -1: getImplMethodSignature
+38 -1: Malformed enclosing method information
+8 -1: maskNull
+3 -1: lct
+21 -1: CONSTRUCTOR_MODIFIERS
+36 -1: ()Lsun/misc/JavaNetHttpCookieAccess;
+12 -1: HashIterator
+84 -1: (Ljava/lang/Class;Ljava/lang/Class$AnnotationData;Ljava/lang/Class$AnnotationData;)Z
+33 -1: java/lang/Character$UnicodeScript
+5 -1: toHex
+27 -1: java/security/AllPermission
+17 -1: appendReplacement
+20 -1: SimpleImmutableEntry
+18 -1: getRequestProperty
+12 -1: compareCerts
+44 -1: java/util/ArrayPrefixHelpers$IntCumulateTask
+19 -1: makeSpreadArguments
+222 -1: (Ljava/util/concurrent/ConcurrentHashMap$BulkTask;III[Ljava/util/concurrent/ConcurrentHashMap$Node;Ljava/util/concurrent/ConcurrentHashMap$MapReduceEntriesTask;Ljava/util/function/Function;Ljava/util/function/BiFunction;)V
+8 -1: addFirst
+6 -1: nextUp
+35 -1: (Ljava/net/ContentHandlerFactory;)V
+40 -1: (Ljava/lang/String;)Ljava/lang/Class<*>;
+23 -1: java/util/LocaleISOData
+14 -1: PREPARED_FORMS
+6 -1: FJByte
+20 -1: getGenericSuperclass
+6 -1: offset
+16 -1: LocaleUtils.java
+12 -1: isUnresolved
+18 -1: aliases_ISO_8859_1
+18 -1: aliases_ISO_8859_2
+15 -1: isSurrogatePair
+18 -1: aliases_ISO_8859_4
+18 -1: aliases_ISO_8859_5
+6 -1: EXTLEN
+18 -1: aliases_ISO_8859_7
+15 -1: Comparator.java
+18 -1: aliases_ISO_8859_9
+15 -1: ISO_8859-2:1987
+22 -1: Ljava/util/List<+TE;>;
+16 -1: Unknown Category
+3 -1: CST
+51 -1: <E:Ljava/lang/Object;>Ljava/util/AbstractList<TE;>;
+6 -1: FRIDAY
+40 -1: (Ljava/lang/String;ZZ)Ljava/lang/String;
+13 -1: isInterrupted
+8 -1: utf_16le
+89 -1: (BLjava/lang/Class<*>;Ljava/lang/invoke/MemberName;)Ljava/lang/invoke/DirectMethodHandle;
+22 -1: checkInvocationCounter
+12 -1: EPOCH_OFFSET
+35 -1: (JJILjava/nio/DirectByteBuffer$1;)V
+7 -1: canRead
+9 -1: getLoader
+18 -1: publicConstructors
+23 -1: factory already defined
+33 -1: java/lang/ref/ReferenceQueue$Null
+37 -1: (Ljava/util/List;Ljava/util/Random;)V
+25 -1: setPackageAssertionStatus
+20 -1: MapReduceEntriesTask
+11 -1: OPEN_DELETE
+35 -1: (Ljava/util/Set;Ljava/lang/Class;)V
+9 -1: rootGroup
+10 -1: updateForm
+22 -1: JavaUtilJarAccess.java
+3 -1: CTT
+57 -1: (Lsun/reflect/MethodInfo;)Ljava/lang/reflect/Constructor;
+82 -1: <T:Ljava/lang/Object;>(Ljava/util/NavigableSet<TT;>;)Ljava/util/NavigableSet<TT;>;
+13 -1: getReturnType
+34 -1: java/util/HashMap$EntrySpliterator
+14 -1: TIME_UNDEFINED
+32 -1: com/sun/crypto/provider/AESCrypt
+7 -1: H_DIGIT
+20 -1: clearAssertionStatus
+44 -1: java/lang/invoke/MethodHandleImpl$BindCaller
+8 -1: scloader
+6 -1: IBM923
+5 -1: read0
+5 -1: read1
+4 -1: true
+9 -1: BA_HIDDEN
+16 -1: jvmUpdateVersion
+36 -1: java/lang/StringCoding$StringDecoder
+37 -1: (J)Ljava/nio/file/attribute/FileTime;
+3 -1: lib
+17 -1: getParameterTypes
+15 -1: FinalizerThread
+31 -1: ()Lsun/util/calendar/Gregorian;
+50 -1: (Ljava/lang/CharSequence;)Ljava/lang/StringBuffer;
+13 -1: PROP_SETTINGS
+33 -1: java/util/function/BinaryOperator
+70 -1: (ILjava/util/List<Ljava/lang/Class<*>;>;)Ljava/lang/invoke/MethodType;
+25 -1: getDefaultRequestProperty
+27 -1: (Ljava/util/jar/JarEntry;)V
+27 -1: SPLITERATOR_CHARACTERISTICS
+18 -1: FieldAccessor.java
+10 -1: setComment
+62 -1: (Ljava/lang/String;)Ljava/lang/management/MemoryManagerMXBean;
+11 -1: array_klass
+39 -1: ()Ljava/lang/Class$EnclosingMethodInfo;
+67 -1: ([Ljava/lang/ClassValue$Entry<*>;ILjava/lang/ClassValue$Entry<*>;)I
+9 -1: (II[CII)I
+50 -1: (Ljava/util/jar/JarFile;Ljava/util/zip/ZipEntry;)V
+20 -1: SPECIFICATION_VENDOR
+72 -1: (Lsun/misc/URLClassPath$JarLoader;Ljava/net/URL;)Ljava/util/jar/JarFile;
+87 -1: <T:Ljava/lang/Object;>(Ljava/lang/ThreadLocal<Ljava/lang/ref/SoftReference<TT;>;>;TT;)V
+9 -1: isVarArgs
+10 -1: setBoolean
+12 -1: (TK;TV;TV;)Z
+16 -1: findSharedClass0
+5 -1: csize
+49 -1: Ljava/security/cert/CertificateEncodingException;
+40 -1: java/util/concurrent/locks/ReentrantLock
+86 -1: (Ljava/io/OutputStream;Ljava/lang/Object;Ljava/lang/String;)Lsun/nio/cs/StreamEncoder;
+5 -1: ready
+38 -1: Ljava/security/AccessControlException;
+28 -1: UnmodifiableRandomAccessList
+69 -1: <T:Ljava/lang/Object;>([TT;Ljava/util/function/BinaryOperator<TT;>;)V
+27 -1: Ljava/lang/invoke/Invokers;
+39 -1: java/util/LinkedList$DescendingIterator
+11 -1: writeFields
+17 -1: classLoaderDepth0
+18 -1: permutedTypesMatch
+52 -1: (Ljava/lang/Class;Ljava/lang/Class;)Ljava/util/List;
+12 -1: linkToStatic
+10 -1: CheckedMap
+6 -1: CENOFF
+8 -1: lastRule
+15 -1: java/lang/Short
+39 -1: ()Ljava/lang/Class$ReflectionData<TT;>;
+8 -1: nextDown
+14 -1: image/x-pixmap
+39 -1: (Ljava/lang/Class;[Ljava/lang/Object;)V
+25 -1: defineClassSourceLocation
+23 -1: sun/misc/PostVMInitHook
+15 -1: could not load 
+16 -1: allowArraySyntax
+90 -1: Ljava/util/concurrent/atomic/AtomicReferenceFieldUpdater<Ljava/io/BufferedInputStream;[B>;
+10 -1: putBoolean
+11 -1:  has params
+14 -1: setMaxPriority
+10 -1: mayContain
+46 -1: java/lang/reflect/MalformedParametersException
+10 -1: baseLocale
+14 -1: isSubwordOrInt
+10 -1: nextDouble
+32 -1: java/lang/Character$UnicodeBlock
+85 -1: (JLjava/util/function/ToDoubleBiFunction;DLjava/util/function/DoubleBinaryOperator;)D
+20 -1: numberOfLeadingZeros
+59 -1: (I[Ljava/lang/Class<*>;)[Ljava/lang/invoke/LambdaForm$Name;
+7 -1: setSize
+29 -1: java/io/FileNotFoundException
+9 -1: getString
+24 -1: ([CII)Ljava/lang/String;
+38 -1: (Ljava/lang/String;Ljava/lang/Class;)V
+5 -1: shift
+18 -1: getConstructorSlot
+41 -1: java/lang/ThreadLocal$SuppliedThreadLocal
+16 -1: UNASSIGNED_STACK
+20 -1: Malformed class name
+12 -1: ofEpochMilli
+34 -1: sun/launcher/LauncherHelper$StdArg
+33 -1: java/nio/ByteBufferAsShortBufferB
+7 -1: convert
+21 -1: ()[Ljava/util/Locale;
+15 -1: ISO_8859-5:1988
+35 -1: av[0] not instace of MethodHandle: 
+33 -1: java/nio/ByteBufferAsShortBufferL
+5 -1: hypot
+16 -1: InputStream.java
+13 -1: reinvokerForm
+39 -1: JVMTI_THREAD_STATE_WAITING_INDEFINITELY
+16 -1: sun/misc/Version
+66 -1: <T::Ljava/lang/annotation/Annotation;>(Ljava/lang/Class<TT;>;)[TT;
+11 -1: codePointAt
+30 -1: ([Ljava/lang/reflect/Method;)V
+9 -1: duplicate
+9 -1: interface
+5 -1: X.509
+24 -1: SynchronizedNavigableSet
+8 -1: us-ascii
+17 -1: getUnresolvedType
+21 -1: PRIVATE_USE_EXTENSION
+4 -1: form
+93 -1: (Ljava/util/ArrayPrefixHelpers$LongCumulateTask;Ljava/util/function/LongBinaryOperator;[JII)V
+27 -1: sealing violation: package 
+34 -1: RuntimeVisibleParameterAnnotations
+17 -1: LF_INVSTATIC_INIT
+14 -1: Gregorian.java
+32 -1: java/util/function/UnaryOperator
+3 -1: log
+3 -1: low
+22 -1: sun/misc/JavaNetAccess
+9 -1: getLength
+21 -1: getRawTypeAnnotations
+36 -1: (Ljava/lang/String;)Ljava/lang/Long;
+9 -1: getNumber
+66 -1: (ILjava/lang/Object;)Ljava/util/concurrent/ConcurrentHashMap$Node;
+20 -1: (Ljava/lang/Class;)C
+89 -1: <K:Ljava/lang/Object;V:Ljava/lang/Object;>Ljava/lang/Object;Ljava/util/Map$Entry<TK;TV;>;
+6 -1: ENDSIG
+20 -1: (Ljava/lang/Class;)I
+20 -1: (Ljava/lang/Class;)J
+24 -1: [[Ljava/io/Serializable;
+22 -1: serialPersistentFields
+7 -1: console
+142 -1: (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
+27 -1: (Ljava/nio/ByteBuffer;ICZ)V
+20 -1: (Ljava/lang/Class;)V
+40 -1: java/lang/ArrayIndexOutOfBoundsException
+6 -1: this$0
+51 -1: (Ljava/lang/invoke/MemberName;[Ljava/lang/Object;)V
+18 -1: packageAccessValid
+33 -1: ([Ljava/lang/StackTraceElement;)V
+8 -1: constant
+113 -1: (Ljava/lang/String;Ljava/nio/ByteBuffer;IILjava/security/ProtectionDomain;Ljava/lang/String;)Ljava/lang/Class<*>;
+8 -1: isMethod
+20 -1: (Ljava/lang/Class;)Z
+6 -1: ENDSIZ
+8 -1: newEntry
+57 -1: (Ljava/lang/Object;)Ljava/lang/IndexOutOfBoundsException;
+25 -1: (Ljava/util/Comparator;)V
+8 -1: isBridge
+6 -1: ([BI)I
+6 -1: ([BI)J
+16 -1: getReferenceKind
+26 -1: [Ljava/security/Principal;
+71 -1: (Ljava/lang/Class;[Ljava/lang/reflect/Field;)[Ljava/lang/reflect/Field;
+32 -1: ()Ljava/lang/ClassValue$Version;
+16 -1: SearchValuesTask
+17 -1: setCompressedSize
+16 -1: DEFAULT_CAPACITY
+108 -1: <K:Ljava/lang/Object;V::Ljava/lang/Comparable<-TV;>;>()Ljava/util/Comparator<Ljava/util/Map$Entry<TK;TV;>;>;
+27 -1: java/util/ComparableTimSort
+41 -1: null StackTraceElement in serial stream. 
+6 -1: ([BI)V
+44 -1: (Ljava/util/jar/JarFile;)Lsun/misc/JarIndex;
+71 -1: (Ljava/util/jar/JarFile;Ljava/util/Enumeration;)Ljava/util/Enumeration;
+52 -1: (Ljava/lang/invoke/MemberName;Ljava/lang/Class<*>;)Z
+36 -1: [Ljava/lang/reflect/TypeVariable<*>;
+17 -1: OutputStream.java
+8 -1: combiner
+15 -1: decodeArrayLoop
+19 -1: (Ljava/io/Writer;)V
+41 -1: (Ljava/util/List<*>;Ljava/util/List<*>;)I
+35 -1: ()[Ljava/security/cert/Certificate;
+33 -1: ([I)Ljava/util/Spliterator$OfInt;
+9 -1: NF_asType
+17 -1: java/io/Closeable
+11 -1: updateBytes
+12 -1: charsets.jar
+18 -1: getDeclaredFields0
+47 -1: (Ljava/lang/Object;I)Ljava/lang/reflect/Member;
+60 -1: (Ljava/lang/String;ILjava/lang/String;)Ljava/nio/ByteBuffer;
+15 -1: getTotalSeconds
+57 -1: (Ljava/util/Collection<+Ljava/util/Map$Entry<TK;TV;>;>;)Z
+4 -1: JULY
+10 -1: Exceptions
+41 -1: ()Ljava/util/List<Ljava/io/IOException;>;
+14 -1: ParseUtil.java
+13 -1: getJarFileURL
+29 -1: setJavaIOFileDescriptorAccess
+24 -1: ARRAY_OBJECT_BASE_OFFSET
+21 -1: onUnmappableCharacter
+53 -1: (Ljava/lang/Object;Ljava/lang/Object;)Ljava/util/Map;
+24 -1: MethodHandleNatives.java
+40 -1: java/nio/charset/MalformedInputException
+37 -1: [Ljava/lang/reflect/AnnotatedElement;
+15 -1: CLASSPATH_CHARS
+18 -1: [Ljava/lang/Class;
+7 -1: FJFloat
+47 -1: <T:Ljava/lang/Object;>(Ljava/util/List<TT;>;I)V
+19 -1: Ljava/lang/Runtime;
+23 -1: java/lang/CharacterData
+42 -1: (Ljava/lang/Void;Ljava/lang/ClassLoader;)V
+76 -1: (Ljava/nio/channels/ReadableByteChannel;Ljava/nio/charset/CharsetDecoder;I)V
+56 -1: (Ljava/lang/Class;Ljava/lang/String;Ljava/lang/Class;B)V
+19 -1: java/util/zip/CRC32
+33 -1: <T:Ljava/lang/Object;>([TT;I)[TT;
+22 -1: ([F)Ljava/lang/String;
+5 -1: UTF_8
+20 -1: aliases_UTF_32BE_BOM
+11 -1: Buffer.java
+78 -1: <T:Ljava/lang/Object;>(Ljava/util/Comparator<TT;>;)Ljava/util/Comparator<TT;>;
+44 -1: (Ljava/lang/String;)Ljava/util/zip/ZipEntry;
+9 -1: malformed
+4 -1: JUNE
+51 -1: (Ljava/util/jar/JarFile;Ljava/util/jar/JarFile$1;)V
+6 -1: locale
+34 -1: (Ljava/util/function/BiFunction;)V
+10 -1: setMinutes
+40 -1: (Ljava/lang/reflect/AccessibleObject;Z)V
+12 -1: maybeCompile
+46 -1: (Ljava/lang/Class;Ljava/lang/reflect/Method;)V
+7 -1: getEras
+55 -1: <T:Ljava/lang/Object;>([TT;Ljava/util/Iterator<*>;)[TT;
+17 -1: toUnsignedString0
+32 -1: (Ljava/lang/invoke/MethodType;)V
+32 -1: (Ljava/lang/invoke/MethodType;)Z
+10 -1: Class.java
+27 -1: ()Ljava/util/Iterator<TK;>;
+29 -1: WINDOWS_EPOCH_IN_MICROSECONDS
+41 -1: (Ljava/io/InputStream;)Ljava/lang/String;
+4 -1: prev
+24 -1: ()Ljava/util/Properties;
+11 -1: awaitBooted
+19 -1: generateConstructor
+22 -1: sun/misc/SharedSecrets
+19 -1: getDateTimeInstance
+43 -1: (IIILsun/util/calendar/BaseCalendar$Date;)J
+5 -1: setID
+11 -1: Locale.java
+12 -1: getRootGroup
+15 -1: setLastModified
+7 -1: trouble
+28 -1: (Z)Ljava/lang/StringBuilder;
+5 -1: setIO
+17 -1: loadClassInternal
+23 -1: java/lang/ref/Finalizer
+8 -1: EmptySet
+16 -1: aliases_UTF_16BE
+50 -1: (Ljava/util/NavigableMap;)Ljava/util/NavigableMap;
+15 -1: unmodifiableMap
+48 -1: (Ljava/lang/Class<*>;)Lsun/reflect/ConstantPool;
+15 -1: arrayContentsEq
+7 -1: EXT_TAG
+31 -1: (Ljava/util/HashMap$TreeNode;)Z
+5 -1: cp737
+22 -1: java/util/zip/Checksum
+5 -1: names
+22 -1: ConcurrentHashMap.java
+7 -1: ([J[J)Z
+7 -1: WAITING
+31 -1: sun.launcher.resources.launcher
+14 -1: getThreadGroup
+8 -1: PutField
+12 -1: hugeCapacity
+9 -1: isPackage
+72 -1: (Ljava/lang/ThreadLocal<*>;)Ljava/lang/ThreadLocal$ThreadLocalMap$Entry;
+23 7: sun/nio/ch/DirectBuffer
+13 -1: Checksum.java
+25 -1: (Ljava/nio/ByteBuffer;I)C
+51 -1: (Ljava/lang/invoke/MethodHandle;)Ljava/lang/Object;
+25 -1: (Ljava/nio/ByteBuffer;I)D
+7 -1: treeify
+25 -1: (Ljava/nio/ByteBuffer;I)F
+5 -1: setIn
+25 -1: (Ljava/nio/ByteBuffer;I)I
+20 -1: TRACE_METHOD_LINKAGE
+25 -1: (Ljava/nio/ByteBuffer;I)J
+7 -1: putIntB
+22 -1: createGarbageCollector
+50 -1: <T:Ljava/lang/Object;>(Ljava/lang/Class<TT;>;)[TT;
+25 -1: (Ljava/nio/ByteBuffer;I)S
+7 -1: putIntL
+19 -1: (B)Ljava/lang/Byte;
+14 -1: Hashtable.java
+29 -1: java/lang/ArrayStoreException
+11 -1: all_allowed
+16 -1: getLastRawOffset
+7 -1: inReady
+36 -1: java/lang/ThreadLocal$ThreadLocalMap
+40 -1: (ILjava/lang/String;Ljava/lang/String;)V
+23 -1: Ljava/lang/ThreadLocal;
+16 -1: classValueOrNull
+62 -1: (Ljava/lang/invoke/MemberName;)Ljava/lang/invoke/MethodHandle;
+23 -1: preparedFieldLambdaForm
+22 -1: (Z)Ljava/lang/Boolean;
+14 -1: ThreadLocalMap
+27 -1: java/lang/StackTraceElement
+13 -1: getEntryCSize
+19 -1: java.security.debug
+53 -1: (Ljava/util/Collection<*>;Ljava/util/Collection<*>;)Z
+6 -1: LOCLEN
+40 -1: Ljava/lang/Class<Ljava/lang/Character;>;
+6 -1: (JJB)V
+66 -1: Ljava/util/Hashtable<Ljava/lang/String;Ljava/net/ContentHandler;>;
+31 -1: [[Ljava/lang/StackTraceElement;
+9 -1: putStatic
+16 -1: Asia/Ho_Chi_Minh
+15 -1: getDisplayNames
+13 -1: convertToAbbr
+23 -1: Method not implemented.
+15 -1: isCCLOverridden
+14 -1: doubleCapacity
+137 -1: (Ljava/lang/Class<*>;ZLjava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/Class<*>;)Ljava/util/List<Ljava/lang/invoke/MemberName;>;
+219 -1: (Ljava/util/concurrent/ConcurrentHashMap$BulkTask;III[Ljava/util/concurrent/ConcurrentHashMap$Node;Ljava/util/concurrent/ConcurrentHashMap$MapReduceKeysTask;Ljava/util/function/Function;Ljava/util/function/BiFunction;)V
+7 -1: native 
+29 -1: (Ljava/lang/reflect/Field;Z)V
+18 -1: Ljava/util/Locale;
+31 -1: Ljava/util/concurrent/TimeUnit;
+16 -1: threadsSuspended
+7 -1: ([III)V
+20 -1: setMaxDelimCodePoint
+18 -1: contentClassPrefix
+13 -1: mappingOffset
+10 -1: toIndex = 
+47 -1: (Ljava/lang/CharSequence;)Ljava/io/PrintStream;
+12 -1: booleanValue
+13 -1: putMapEntries
+17 -1: defaultBundleName
+50 -1: (Ljava/util/concurrent/CountedCompleter;[B[BIIII)V
+10 -1: executable
+20 -1: java/time/ZoneOffset
+28 -1: java/lang/ref/FinalReference
+11 -1: newTreeNode
+7 -1: lookup2
+10 -1: TableStack
+59 -1: Ljava/util/concurrent/ConcurrentHashMap$ValuesView<TK;TV;>;
+11 -1: getAccessor
+9 -1: available
+18 -1: java/io/FileReader
+34 -1: java/security/ProtectionDomain$3$1
+16 -1: integer overflow
+11 -1: internTable
+28 -1: Ljava/util/HashMap$TreeNode;
+19 -1: | invocationCounter
+12 -1: findResource
+9 -1: isLoaded0
+5 -1: cp775
+24 -1: DIRECTIONALITY_UNDEFINED
+9 -1: isInvalid
+7 -1: lookupN
+35 -1: (Lsun/reflect/MethodAccessorImpl;)V
+6 -1: ENDSUB
+4 -1:  to 
+59 -1: ([Ljava/lang/Object;IILjava/lang/Class;)[Ljava/lang/Object;
+10 -1: meta-index
+6 -1: INDENT
+9 -1: WEDNESDAY
+40 -1: ()Ljava/lang/annotation/RetentionPolicy;
+14 -1: getUsableSpace
+7 -1: TUESDAY
+51 -1: (Ljava/lang/Class;I)Ljava/lang/invoke/MethodHandle;
+12 -1: getSubjectDN
+21 -1: Ljava/io/InputStream;
+25 -1: (IC)Ljava/nio/CharBuffer;
+52 -1: (Ljava/nio/CharBuffer;)Ljava/util/function/Supplier;
+17 -1: ()[Ljava/net/URL;
+6 -1: search
+10 -1: Main-Class
+8 -1: ([CIIC)I
+16 -1: Certificate.java
+14 -1: spreadInvokers
+22 -1: sun/nio/cs/ISO_8859_15
+6 -1: accept
+18 -1: ReflectAccess.java
+13 -1: java/nio/Bits
+14 -1: linkToCallSite
+46 -1: Ljava/nio/charset/UnsupportedCharsetException;
+8 -1: ([CIIC)V
+9 -1: (TT;TV;)V
+26 -1: java/lang/OutOfMemoryError
+34 -1: policy        loading and granting
+76 -1: (Ljava/nio/CharBuffer;ILjava/nio/ByteBuffer;I)Ljava/nio/charset/CoderResult;
+13 -1: x-windows-949
+21 -1: Ljava/io/PrintStream;
+9 -1: initNames
+12 -1: testAnyFlags
+65 -1: (Ljava/lang/reflect/Method;)Ljava/lang/invoke/DirectMethodHandle;
+34 -1: (Ljava/util/List;)Ljava/util/List;
+10 -1: CacheEntry
+10 -1: hasAllPerm
+26 -1: java/nio/charset/Charset$1
+26 -1: java/nio/charset/Charset$2
+19 -1: ()Ljava/util/Stack;
+26 -1: java/nio/charset/Charset$3
+62 -1: (Ljava/lang/String;)Lsun/util/calendar/LocalGregorianCalendar;
+23 -1: ARRAY_FLOAT_INDEX_SCALE
+23 -1: (Ljava/lang/Object;IS)V
+13 -1: x-windows-950
+31 -1: Ljava/util/Hashtable$Entry<**>;
+87 -1: Ljava/util/WeakHashMap<Ljava/lang/ClassValue$Identity;Ljava/lang/ClassValue$Entry<*>;>;
+9 -1: permClass
+37 -1: (Ljava/security/ProtectionDomain$3;)V
+99 -1: <S::Lsun/reflect/generics/tree/Signature;>Lsun/reflect/generics/repository/AbstractRepository<TS;>;
+37 -1: ()Ljava/util/function/BinaryOperator;
+64 -1: java/util/Collections$UnmodifiableNavigableMap$EmptyNavigableMap
+91 -1: (Ljava/util/ArrayPrefixHelpers$IntCumulateTask;Ljava/util/function/IntBinaryOperator;[III)V
+6 -1: getCrc
+25 -1: ByteArrayInputStream.java
+9 -1: SYNTHETIC
+52 -1: Ljava/lang/ref/PhantomReference<Ljava/lang/Object;>;
+246 -1: (Ljava/util/concurrent/ConcurrentHashMap$BulkTask;III[Ljava/util/concurrent/ConcurrentHashMap$Node;Ljava/util/concurrent/ConcurrentHashMap$MapReduceKeysToDoubleTask;Ljava/util/function/ToDoubleFunction;DLjava/util/function/DoubleBinaryOperator;)V
+38 -1: java/lang/Throwable$WrappedPrintStream
+21 -1: Illegal load factor: 
+43 -1: Ljava/util/Deque<Ljava/util/zip/Inflater;>;
+3 -1: map
+6 -1: expand
+6 -1: access
+3 -1: max
+33 -1: impliesCreateAccessControlContext
+3 -1: may
+53 -1: java/util/concurrent/ConcurrentHashMap$ReduceKeysTask
+91 -1: Ljava/lang/Object;Ljava/security/PrivilegedExceptionAction<Lsun/misc/URLClassPath$Loader;>;
+60 -1: attempt to add a Permission to a readonly Permissions object
+21 -1: canonicalizeExtension
+11 -1: copyValueOf
+25 -1: (IJ)Ljava/nio/LongBuffer;
+112 -1: <U:Ljava/lang/Object;>(JLjava/util/function/Function<-TV;+TU;>;Ljava/util/function/BiFunction<-TU;-TU;+TU;>;)TU;
+11 -1: DeqIterator
+11 -1: SpeciesData
+8 -1: getCause
+16 -1: aliases_UTF_16LE
+51 -1: (TT;TV;Ljava/util/function/BinaryOperator<TV;>;)TV;
+25 -1: (JF)Ljava/nio/ByteBuffer;
+16 -1: sun/misc/IOUtils
+32 -1: Ljava/util/Locale$FilteringMode;
+6 -1: .class
+13 -1: getPermission
+13 -1: startsWithLOC
+8 -1: Identity
+23 -1: ([BII)Ljava/lang/Class;
+15 -1: putByteVolatile
+36 -1: (Ljava/util/Deque;)Ljava/util/Queue;
+22 -1: (Ljava/lang/Object;S)V
+47 -1: java/util/concurrent/ConcurrentHashMap$BulkTask
+4 -1: n = 
+9 -1: (ITE;)TE;
+5 -1: zeroD
+18 -1: formatUnsignedLong
+29 -1:     default display locale = 
+23 -1: java/io/File$PathStatus
+5 -1: zeroF
+20 -1: Ljava/util/Set<TK;>;
+20 -1: (Ljava/util/List;I)V
+5 -1: zeroI
+5 -1: zeroJ
+7 -1: context
+39 -1: Ljava/nio/channels/WritableByteChannel;
+5 -1: zeroL
+34 -1: Lsun/util/calendar/CalendarSystem;
+24 -1: JVMTI_THREAD_STATE_ALIVE
+18 -1: (Ljava/util/Set;)V
+18 -1: (Ljava/util/Set;)Z
+42 -1: (TT;Ljava/lang/ref/ReferenceQueue<-TT;>;)V
+7 -1: entries
+30 -1: (Ljava/util/WeakHashMap;IIII)V
+15 -1: csisolatingreek
+38 -1: ([Ljava/lang/Class;)Ljava/lang/Object;
+12 -1: isMalformed3
+12 -1: isMalformed4
+5 -1: FJInt
+23 -1: java/util/LinkedHashMap
+20 -1: malformedInputAction
+12 -1: Charset.java
+5 -1: LLL_L
+42 -1: (Ljava/util/Collection;)Ljava/lang/Object;
+22 -1: makeMethodHandleInvoke
+3 -1: mdt
+7 -1: unicode
+12 -1: newInstance0
+10 -1: checkCerts
+34 -1: java/util/WeakHashMap$HashIterator
+23 -1: (Ljava/lang/Object;JI)I
+9 -1: hexDigits
+13 -1: javaToDosTime
+24 -1: (I)Ljava/nio/LongBuffer;
+6 -1: A_DATA
+12 -1: deepToString
+23 -1: (Ljava/lang/Object;JI)V
+91 -1: (JLjava/util/function/ToLongBiFunction<-TK;-TV;>;JLjava/util/function/LongBinaryOperator;)J
+23 -1: bad spread array length
+11 -1: readTimeout
+14 -1: toAbsolutePath
+8 -1: isFinite
+19 -1: currentLoadedClass0
+3 -1: \xef\xbf\xbd
+23 -1: (Ljava/nio/file/Path;)I
+8 -1: handlers
+21 -1: (Ljava/util/List;II)V
+89 -1: (Lsun/misc/URLClassPath$Loader;Ljava/lang/String;Ljava/net/URL;Ljava/net/URLConnection;)V
+8 -1: OVERFLOW
+8 -1: newTable
+8 -1: THURSDAY
+6 -1: notify
+12 -1: initialValue
+35 -1: (I)Ljava/util/LinkedList$Node<TE;>;
+18 -1: AsVarargsCollector
+26 -1: (Lsun/misc/JavaIOAccess;)V
+18 -1: ()Ljava/lang/Void;
+23 -1: (Ljava/nio/file/Path;)Z
+16 -1: MINUTE_IN_MILLIS
+67 -1: (Ljava/lang/Class;[Ljava/lang/Class;Z)Ljava/lang/invoke/MethodType;
+18 -1: Ljava/util/Vector;
+70 -1: (Ljava/lang/reflect/Constructor;[Ljava/lang/Object;)Ljava/lang/Object;
+40 -1: (Ljava/lang/Object;ILjava/lang/Object;)V
+25 -1: UnresolvedPermission.java
+14 -1: ReduceKeysTask
+21 -1: ()[Ljava/lang/Object;
+129 -1: <K:Ljava/lang/Object;V:Ljava/lang/Object;E:Ljava/lang/Object;>Ljava/lang/Object;Ljava/util/Collection<TE;>;Ljava/io/Serializable;
+16 -1: ClassLoader.java
+46 -1: Ljava/util/Comparators$NaturalOrderComparator;
+17 -1: compareAndSwapInt
+22 -1: packageDefinitionValid
+41 -1: ([Ljava/lang/Object;[Ljava/lang/Object;)Z
+162 -1: (Ljava/util/List<Ljava/util/Locale$LanguageRange;>;Ljava/util/Collection<Ljava/lang/String;>;Ljava/util/Locale$FilteringMode;)Ljava/util/List<Ljava/lang/String;>;
+16 -1: sun.zip.zipFiles
+17 -1: java_runtime_name
+31 -1: (Ljava/lang/ClassValue$Entry;)V
+31 -1: (Ljava/lang/ClassValue$Entry;)Z
+30 -1: <T:Ljava/lang/Object;>(TT;)TT;
+39 -1: JavaSecurityProtectionDomainAccess.java
+24 -1: (I)Ljava/lang/Throwable;
+7 -1: FJShort
+9 -1: putFloatB
+19 -1: checkedNavigableSet
+25 -1: java/lang/invoke/Invokers
+18 -1: setIfModifiedSince
+14 -1: parameterTypes
+41 -1: (Ljava/lang/Object;Ljava/lang/Runnable;)V
+9 -1: putFloatL
+11 -1: getTypeCode
+5 -1: (ZZ)I
+24 -1: java/lang/ProcessBuilder
+9 -1: UNDERFLOW
+21 -1: VolatileCallSite.java
+24 -1: (C)Ljava/nio/CharBuffer;
+55 -1: java/util/concurrent/ConcurrentHashMap$ForEachValueTask
+26 -1: (Ljava/lang/String;[CII)[B
+18 -1: reduceKeysToDouble
+5 -1: (ZZ)Z
+23 -1: setCallSiteTargetNormal
+3 -1: min
+4 -1: ceil
+62 -1: (Ljava/lang/String;)Ljava/util/LinkedList<Ljava/lang/String;>;
+29 -1: (Ljava/util/AbstractList;II)V
+32 -1: Ljava/lang/Class$AnnotationData;
+21 -1: createFileExclusively
+64 -1: (Ljava/lang/ref/SoftReference;I)Ljava/lang/Class$ReflectionData;
+26 -1: java/lang/Short$ShortCache
+54 -1: (Ljava/net/URL;Ljava/io/File;)Ljava/net/URLConnection;
+29 -1: Lsun/nio/cs/Surrogate$Parser;
+58 -1: (Ljava/lang/Class;)Lsun/reflect/annotation/AnnotationType;
+8 -1: findForm
+53 -1: Ljava/lang/invoke/MethodType$ConcurrentWeakInternSet;
+39 -1: (Lsun/misc/Perf;Ljava/nio/ByteBuffer;)V
+16 -1: mergePermissions
+11 -1: totalMemory
+53 -1: java/lang/invoke/DirectMethodHandle$EnsureInitialized
+139 -1: <K:Ljava/lang/Object;V:Ljava/lang/Object;>Ljava/util/AbstractMap<TK;TV;>;Ljava/util/concurrent/ConcurrentMap<TK;TV;>;Ljava/io/Serializable;
+29 -1: java/util/HashMap$KeyIterator
+20 -1: STACK_TRACE_SENTINEL
+5 -1: order
+18 -1: java/lang/Runnable
+8 -1: GetField
+13 -1: Empty command
+7 -1: CONTROL
+9 -1: blockedOn
+12 -1: testAllFlags
+11 -1: getInflater
+16 -1: threadTerminated
+44 -1: (Ljava/lang/ThreadGroup;Ljava/lang/String;)V
+20 -1: java.runtime.version
+8 -1: peekLast
+23 -1: java/util/ArrayList$Itr
+21 -1: (Ljava/util/Locale;)V
+13 -1: isOptimizable
+8 -1: FairSync
+7 -1: CHINESE
+15 -1: initHelpMessage
+30 -1: ()Ljava/util/HashMap$TreeNode;
+29 -1: Ljava/lang/SecurityException;
+7 -1: charset
+35 -1: sun/security/util/SecurityConstants
+19 -1: sun.nio.cs.bugLevel
+8 2: Foo.java
+49 -1: ([Ljava/util/concurrent/ConcurrentHashMap$Node;)V
+12 -1: EntrySetView
+37 -1: (Lsun/misc/JavaNetHttpCookieAccess;)V
+35 -1: Ljava/util/Hashtable$Entry<TK;TV;>;
+20 -1: NF_constructorMethod
+8 -1: getMonth
+38 -1: (Ljava/util/Iterator;Ljava/util/Map;)V
+14 -1: getIntVolatile
+6 -1: [name=
+8 -1: oop_size
+20 -1: Can't load library: 
+30 -1: ()Ljava/util/Spliterator<TV;>;
+33 -1: Lsun/reflect/ConstructorAccessor;
+61 -1: Ljava/lang/Number;Ljava/lang/Comparable<Ljava/lang/Integer;>;
+15 -1: printVmSettings
+33 -1: stack         include stack trace
+45 -1: ([Ljava/lang/Object;I)Ljava/util/Spliterator;
+37 -1: sun/reflect/generics/scope/ClassScope
+36 -1: java/io/UnsupportedEncodingException
+24 -1: (J)Ljava/nio/LongBuffer;
+11 -1: addressSize
+15 -1: ByteBuffer.java
+62 -1: (Ljava/lang/String;)Lsun/reflect/generics/tree/ClassSignature;
+9 -1: (TT;TT;)I
+25 -1: java/io/DefaultFileSystem
+15 -1: BaseLocale.java
+14 -1: BitSetIterator
+17 -1: AbstractList.java
+57 -1: Ljava/lang/ref/WeakReference<Ljava/lang/ThreadLocal<*>;>;
+178 -1: (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
+9 -1: arguments
+26 -1: java/util/Locale$LocaleKey
+9 -1: setLength
+29 -1: sun/nio/cs/ISO_8859_1$Decoder
+9 -1: zipfs.jar
+24 -1: Ljava/util/zip/ZipCoder;
+14 -1: , new state = 
+93 -1: ([Ljava/util/concurrent/ConcurrentHashMap$Node;IIIJLjava/util/concurrent/ConcurrentHashMap;)V
+39 -1: java/security/PrivilegedExceptionAction
+9 -1: dnsns.jar
+20 -1: iteratorBinarySearch
+14 -1: initializePath
+22 -1: DefaultFileSystem.java
+17 -1: Ljava/util/Deque;
+8 -1: DEFLATED
+11 -1: Can't load 
+9 -1: ArrayList
+21 -1: negativeZeroFloatBits
+41 -1: (Ljava/lang/String;ILjava/util/Locale;)[C
+14 -1: ANSI_X3.4-1968
+39 -1: sun/reflect/annotation/AnnotationType$1
+3 -1: mod
+62 -1: Ljava/nio/Buffer;Ljava/lang/Comparable<Ljava/nio/ByteBuffer;>;
+29 -1: interpretWithArgumentsTracing
+6 -1: getDay
+47 -1: sun/reflect/generics/repository/ClassRepository
+19 -1: refKindDoesDispatch
+20 -1: getAnnotationsByType
+14 -1: needsExpansion
+18 -1: lastIndexOfSubList
+26 -1: JavaUtilZipFileAccess.java
+59 -1: (Ljava/lang/CharSequence;)Ljava/lang/AbstractStringBuilder;
+12 -1: ptypesOffset
+8 -1: hashcode
+18 -1: ([Ljava/net/URL;)V
+8 -1: iso-ir-6
+7 -1: jzentry
+52 -1:               only dump output if specified codebase
+31 -1: lambda$comparingLong$6043328a$1
+5 -1: MARCH
+14 -1: ANSI_X3.4-1986
+14 -1: isMalformed3_2
+7 -1: IS_TYPE
+68 -1: Ljava/lang/Object;Ljava/lang/Comparable<Ljava/nio/charset/Charset;>;
+30 -1: protocol doesn't support input
+17 -1: getExtClassLoader
+14 -1: setProxiedHost
+73 -1: ()Ljava/util/Map<Ljava/lang/String;Ljava/util/List<Ljava/lang/String;>;>;
+16 -1: traceInterpreter
+17 -1: (Ljava/net/URL;)I
+5 -1: expm1
+18 -1: createInheritedMap
+66 -1: java/util/concurrent/ConcurrentHashMap$ForEachTransformedEntryTask
+17 -1: getTimeOfDayValue
+15 -1: zeroLengthArray
+20 -1: invalid permission: 
+6 -1: REPORT
+15 -1: isNumericString
+78 -1: (Ljava/util/Locale;Ljava/lang/String;[Ljava/lang/Object;)Ljava/util/Formatter;
+6 -1: (TV;)Z
+25 -1: Lsun/misc/JavaLangAccess;
+29 -1: (I)Ljava/lang/reflect/Method;
+17 -1: (Ljava/net/URL;)V
+34 -1: ()Ljava/lang/Class$ReflectionData;
+50 -1: java.lang.invoke.MethodHandle.TRACE_METHOD_LINKAGE
+10 -1: copyWith: 
+17 -1: (Ljava/net/URL;)Z
+32 -1: ()Ljava/util/stream/Stream<TE;>;
+22 -1: quickCheckMemberAccess
+29 -1: ()Lsun/net/www/MessageHeader;
+19 -1: getAssignedCombiner
+8 -1: ([JIIJ)I
+17 -1: formatUnsignedInt
+68 -1: <V:Ljava/lang/Object;>Ljava/util/AbstractMap<Ljava/lang/String;TV;>;
+34 -1: java/nio/ByteBufferAsDoubleBufferB
+32 -1: ([I)Ljava/util/stream/IntStream;
+9 -1: init_lock
+18 -1: must be resolved: 
+42 -1: ()Ljava/nio/channels/spi/SelectorProvider;
+8 -1: ([JIIJ)V
+33 -1: IncompatibleClassChangeError.java
+34 -1: java/nio/ByteBufferAsDoubleBufferL
+31 -1: ()Ljava/util/function/Function;
+43 -1: Ljava/lang/Enum<Ljava/io/File$PathStatus;>;
+17 -1: availableCharsets
+49 -1: java/util/ArraysParallelSortHelpers$FJChar$Sorter
+22 -1: permission=<classname>
+22 -1: getAnnotatedSuperclass
+20 -1: isObjectPublicMethod
+15 -1: Attempt to get 
+10 -1: createLong
+14 -1: HASH_INCREMENT
+32 -1: sun/management/ManagementFactory
+13 -1: separatorChar
+15 -1: bad field type 
+8 -1: november
+27 -1: (F)Ljava/lang/StringBuffer;
+3 -1: EAT
+3 -1: mst
+54 -1: (Ljava/lang/reflect/Method;)Ljava/lang/reflect/Method;
+18 -1: Ljava/lang/Object;
+7 -1: ;:&=+$,
+12 -1: Handler.java
+7 -1: isDirty
+127 -1: <T:Ljava/lang/Object;>(Ljava/security/PrivilegedAction<TT;>;Ljava/security/AccessControlContext;[Ljava/security/Permission;)TT;
+14 -1: asTypeUncached
+5 -1: split
+200 -1: ([BLsun/reflect/ConstantPool;Ljava/lang/reflect/AnnotatedElement;Ljava/lang/Class;Ljava/lang/reflect/Type;Lsun/reflect/annotation/TypeAnnotation$TypeAnnotationTarget;)Ljava/lang/reflect/AnnotatedType;
+47 -1: java.lang.invoke.MethodHandle.TRACE_INTERPRETER
+22 -1: sun/invoke/empty/Empty
+66 -1: (Ljava/util/Map;Ljava/lang/Class;Ljava/lang/Class;)Ljava/util/Map;
+18 -1: jvm_update_version
+32 -1: (Ljava/util/Map;)Ljava/util/Map;
+14 -1: cacheLoadLimit
+8 -1: javaHome
+52 -1: (Ljava/lang/reflect/Field;)Ljava/lang/reflect/Field;
+20 -1: [[Ljava/lang/Object;
+19 -1: isJavaLetterOrDigit
+11 -1: loadLibrary
+32 -1: java/io/StreamCorruptedException
+14 -1: setAccessible0
+27 -1: sun/nio/cs/UTF_16LE$Encoder
+60 -1: (Ljava/lang/String;[Ljava/lang/Object;)Ljava/io/PrintStream;
+8 -1: segments
+10 -1: UTF_8.java
+3 -1: ECT
+5 -1: cp813
+5 -1: cp819
+61 -1: (Ljava/lang/Object;Ljava/lang/Object;Ljava/util/Comparator;)I
+5 -1: Cache
+4 -1: sinh
+32 -1: java/util/function/ToIntFunction
+10 -1: setFactory
+24 -1: Illegal mappings count: 
+16 -1: fileToEncodedURL
+38 -1: Ljava/lang/annotation/RetentionPolicy;
+27 -1: Ljava/net/SocketPermission;
+46 -1: (Ljava/lang/CharSequence;I)[Ljava/lang/String;
+11 -1: cardinality
+13 -1: getMonthValue
+64 -1: (Ljava/lang/invoke/MethodType;II)Ljava/lang/invoke/MethodHandle;
+6 -1: ENDTOT
+12 -1: getBytesUTF8
+9 -1: cacheLoad
+13 -1: packageAccess
+14 -1: sharedToString
+5 -1: merge
+29 -1: parameter type cannot be void
+27 -1: makePreparedFieldLambdaForm
+40 -1: Couldn't find 3-letter country code for 
+166 -1: (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/net/URL;Ljava/lang/ClassLoader;)V
+19 -1: (Ljava/util/Map;Z)V
+13 -1: setExecutable
+17 -1: objectFieldOffset
+57 -1: (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+128 -1: (Ljava/lang/Class<*>;ZLjava/lang/String;Ljava/lang/Class<*>;Ljava/lang/Class<*>;)Ljava/util/List<Ljava/lang/invoke/MemberName;>;
+61 -1: java/util/concurrent/ConcurrentHashMap$MapReduceKeysToIntTask
+6 -1: asType
+25 -1: java/io/ObjectStreamField
+15 -1: jvmMajorVersion
+124 -1: (Ljava/security/PrivilegedExceptionAction;Ljava/security/AccessControlContext;[Ljava/security/Permission;)Ljava/lang/Object;
+23 -1: (Ljava/lang/Class<*>;)C
+6 -1: andNot
+15 -1: getResponseCode
+59 -1: (Ljava/lang/StringBuffer;)Ljava/lang/AbstractStringBuilder;
+23 -1: (Ljava/lang/Class<*>;)I
+7 -1: seeAllp
+44 -1: (Ljava/lang/ClassLoader;[Ljava/lang/Class;)V
+13 -1: loadFromCache
+35 -1: sun/nio/cs/HistoricallyNamedCharset
+38 -1: (Ljava/lang/Class;[Ljava/lang/Class;)V
+19 -1: INVOKER_METHOD_TYPE
+16 -1: putShortVolatile
+12 -1: Asia/Karachi
+8 -1: cyrillic
+12 -1: getISO2Table
+23 -1: (Ljava/lang/Class<*>;)V
+3 -1: 1.4
+15 -1: LongBuffer.java
+6 -1: (IFZ)V
+23 -1: (Ljava/lang/Class<*>;)Z
+10 -1: initOutput
+9 -1: CELLSBUSY
+39 -1: java/security/PrivilegedActionException
+31 -1: sun/util/calendar/CalendarUtils
+202 -1: ([BLsun/reflect/ConstantPool;Ljava/lang/reflect/AnnotatedElement;Ljava/lang/Class;[Ljava/lang/reflect/Type;Lsun/reflect/annotation/TypeAnnotation$TypeAnnotationTarget;)[Ljava/lang/reflect/AnnotatedType;
+20 -1: Ljava/lang/Class<*>;
+5 -1: cp850
+25 -1: (JI)Ljava/nio/ByteBuffer;
+5 -1: cp852
+24 -1: Invalid parameter name "
+39 -1: ([CII)Ljava/lang/AbstractStringBuilder;
+5 -1: cp855
+11 -1: Deallocator
+5 -1: cp857
+5 -1: cp858
+7 -1: ([SI)[S
+37 -1: ([C)Ljava/lang/AbstractStringBuilder;
+27 -1: java/lang/SecurityException
+82 -1: (Ljava/lang/invoke/MethodHandle;Ljava/lang/Class;Ljava/lang/invoke/MethodHandle;)V
+38 -1: (Ljava/lang/String;)Ljava/lang/String;
+7 -1: connect
+7 -1: isEmpty
+11 -1: replaceNode
+19 -1: SuppliedThreadLocal
+12 -1: asFixedArity
+12 -1: fromIndex = 
+19 -1: createMemoryManager
+9 -1: List.java
+8 -1: FEBRUARY
+21 -1: UnicodeLittleUnmarked
+6 -1: a null
+30 -1: ()Ljava/util/Spliterator<TT;>;
+5 -1: cp862
+17 -1: ZoneInfoFile.java
+5 -1: cp866
+8 -1: BulkTask
+53 -1: java/util/concurrent/locks/AbstractQueuedSynchronizer
+20 -1: FileInputStream.java
+12 -1: java.vm.info
+10 -1: newDecoder
+5 -1: (JB)V
+8 -1: filePath
+17 -1: spreadArrayChecks
+44 -1: ([Ljava/lang/Object;Ljava/util/Comparator;)V
+33 -1: java/util/Collections$AsLIFOQueue
+32 -1: Ljava/util/LinkedList$Node<TE;>;
+5 -1: cp874
+78 -1: (Ljava/util/Locale;Ljava/lang/String;[Ljava/lang/Object;)Ljava/io/PrintStream;
+39 -1: (JLjava/util/function/Consumer<-TK;>;)V
+15 -1: appendCodePoint
+20 -1: primitiveReturnCount
+54 -1:               only dump output if specified permission
+20 -1: getGenericInterfaces
+41 -1: ([Ljava/lang/reflect/AccessibleObject;Z)V
+17 -1: nUnstartedThreads
+33 -1: (Ljava/lang/invoke/MemberName;Z)V
+24 -1: ARRAY_OBJECT_INDEX_SCALE
+40 -1: (Ljava/lang/String;ILjava/util/Locale;)I
+17 -1: java/io/Flushable
+22 -1: newConstructorAccessor
+26 -1: sun/misc/JavaUtilJarAccess
+6 -1: booted
+10 -1: setDoInput
+36 -1: (Ljava/lang/Class;)[Ljava/lang/Enum;
+19 -1: java/lang/Character
+52 -1: ([Ljava/net/URL;Ljava/net/URLStreamHandlerFactory;)V
+24 -1: (Ljava/nio/LongBuffer;)I
+16 -1: start > length()
+28 -1: (I)Ljava/lang/CharacterData;
+5 -1: val$c
+61 -1: (Ljava/lang/Throwable;Ljava/lang/String;[Ljava/lang/Object;)V
+13 -1: resolveOrNull
+9 -1: L_ESCAPED
+27 -1: MapReduceValuesToDoubleTask
+15 -1: getPreparedForm
+33 -1: (I)[Ljava/util/WeakHashMap$Entry;
+54 -1: ()Ljava/util/stream/Stream<+Ljava/util/zip/ZipEntry;>;
+16 -1: bad method type 
+5 -1: val$s
+17 -1: Null charset name
+36 -1: java/lang/invoke/LambdaForm$Compiled
+24 -1: (Ljava/util/SortedMap;)V
+19 -1: java/time/LocalTime
+29 -1: not invocable, no method type
+21 -1: recalculateWordsInUse
+6 -1: val$id
+39 -1: sun/security/util/ManifestEntryVerifier
+60 -1: ([Ljava/lang/Class<*>;I)Ljava/lang/reflect/Constructor<TT;>;
+45 -1: java/util/ArrayPrefixHelpers$LongCumulateTask
+5 -1: OfInt
+11 -1: environment
+60 -1: ([Ljava/lang/Class<*>;[B)[[Ljava/lang/annotation/Annotation;
+7 -1: (JJJZ)V
+10 -1: BufferPool
+6 -1: isUTF8
+12 -1: threadLocals
+35 -1: (Ljava/lang/String;)[Ljava/net/URL;
+21 -1: Ljava/nio/LongBuffer;
+15 -1: copyConstructor
+25 -1: setCallSiteTargetVolatile
+15 -1: getNumericValue
+26 -1: Ljava/security/CodeSource;
+18 -1: Null output stream
+14 -1: cloneWithIndex
+23 -1: LOCAL_LISTEN_PERMISSION
+6 -1: (TT;)I
+46 -1: (Ljava/security/PublicKey;Ljava/lang/String;)V
+6 -1: setCrc
+26 -1: java/io/FilterOutputStream
+10 -1: access$000
+10 -1: access$001
+10 -1: access$002
+6 -1: (TT;)V
+78 -1: <T:Ljava/lang/Object;U:Ljava/lang/Object;>([TU;IILjava/lang/Class<+[TT;>;)[TT;
+41 -1: java/util/concurrent/atomic/AtomicInteger
+8 -1: renameTo
+40 -1: (Ljava/lang/Class<*>;)Ljava/lang/Object;
+17 -1: getRawAnnotations
+29 -1: java/lang/VirtualMachineError
+37 -1: java/lang/management/MemoryPoolMXBean
+25 -1: (II)Ljava/util/List<TE;>;
+6 -1: utf_16
+23 -1: (Ljava/lang/String;[B)V
+19 -1: MIN_ARRAY_SORT_GRAN
+25 -1: array length is not legal
+45 -1: java/util/concurrent/locks/ReentrantLock$Sync
+36 -1: Ljava/security/AccessControlContext;
+48 -1: sun/reflect/generics/repository/MethodRepository
+24 -1: MethodHandleStatics.java
+24 -1: addThreadDumpForMonitors
+64 -1: <T:Ljava/lang/Object;>(Ljava/util/Set<TT;>;)Ljava/util/Set<TT;>;
+58 -1: (Ljava/lang/String;[Ljava/lang/Object;Ljava/lang/Object;)Z
+37 -1: (III)Lsun/util/calendar/CalendarDate;
+9 -1: createURI
+15 -1: unreserveMemory
+52 -1: (Lsun/reflect/MethodInfo;)Ljava/lang/reflect/Method;
+66 -1: (Ljava/lang/Class;[Ljava/lang/Class;)Ljava/lang/invoke/MethodType;
+51 -1: (Ljava/lang/reflect/Constructor;)Ljava/lang/String;
+23 -1: inheritableThreadLocals
+63 -1: ()Ljava/util/Map<Ljava/lang/String;Ljava/lang/reflect/Method;>;
+16 -1: setContentLength
+16 -1: LOWERCASE_LETTER
+4 -1: size
+25 -1: java.launcher.opt.hotspot
+19 -1: buildAnnotatedTypes
+26 -1: JAVAFX_LAUNCHER_CLASS_NAME
+11 -1: getAliasMap
+19 -1: CheckedNavigableSet
+15 -1: getAbsolutePath
+11 -1: doubleValue
+6 -1: utf_32
+22 -1: IMPLEMENTATION_VERSION
+3 -1: ne1
+11 -1: contentType
+8 -1: canWrite
+11 -1: Object.java
+14 -1: America/Denver
+8 -1: fileName
+13 -1: allPermDomain
+27 -1: ()Ljava/util/Iterator<TE;>;
+31 -1: (Lsun/reflect/MethodAccessor;)V
+11 -1: asTypeCache
+13 -1: lineSeparator
+9 -1: JarLoader
+15 -1: replacementNode
+18 -1: getContentEncoding
+12 -1: invoke_LLL_L
+22 -1: ()Ljava/util/TimeZone;
+17 -1: Reference Handler
+33 -1: java/lang/invoke/MethodHandleImpl
+47 -1: ()Ljava/util/concurrent/ConcurrentHashMap$Node;
+12 -1: invoke_LLL_V
+8 -1: form << 
+23 -1: (Ljava/lang/Object;JJ)J
+15 -1: isHighSurrogate
+31 -1: (Ljava/util/Collection<+TV;>;)Z
+36 -1: ([Ljava/util/HashMap$Node<TK;TV;>;)V
+23 -1: (Ljava/lang/Object;JJ)V
+12 -1: utf_32be_bom
+40 -1: sun/util/calendar/LocalGregorianCalendar
+27 -1: [Ljava/security/CodeSigner;
+15 -1: afterNodeAccess
+13 -1: nextThreadNum
+18 -1: INTERNED_ARGUMENTS
+11 -1: getMillisOf
+18 -1: offsetByCodePoints
+11 -1: writeObject
+48 -1: (Ljava/util/Locale$Category;Ljava/util/Locale;)V
+3 -1: nfe
+41 -1: (Ljava/util/Properties;Ljava/io/Reader;)V
+50 -1: (Ljava/util/concurrent/CountedCompleter;[C[CIIII)V
+15 -1: implFlushBuffer
+33 -1: (I)[Ljava/lang/invoke/MemberName;
+12 -1: Unicode.java
+17 -1: DMH.invokeVirtual
+5 -1: setup
+51 -1: (Ljava/util/Collection;[Ljava/lang/reflect/Field;)V
+3 -1: EST
+7 -1: TREEBIN
+7 -1: getFile
+10 -1: isLeapYear
+18 -1: LinkedHashIterator
+14 -1: DISPLAY_SCRIPT
+25 -1: privateGetDeclaredMethods
+68 -1: (Ljava/util/function/Function;Ljava/lang/Object;Ljava/lang/Object;)I
+22 -1: ()Ljava/util/Iterator;
+21 -1: sun/management/Sensor
+15 -1: getAvailableIDs
+51 -1: Lsun/util/PreHashedMap<Ljava/nio/charset/Charset;>;
+8 -1: elot_928
+6 -1: LATIN0
+45 -1: ([Ljava/lang/Object;II[Ljava/lang/Object;II)V
+10 -1: [Unlocked]
+15 -1: internArguments
+6 -1: LATIN9
+33 -1: (II)Ljava/lang/invoke/MethodType;
+12 -1: Version.java
+17 -1: setConnectTimeout
+75 -1: (Ljava/util/Locale;Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/String;
+13 -1: highSurrogate
+12 -1: Africa/Cairo
+21 -1: synchronizedSortedMap
+21 -1:  in java.library.path
+45 -1: sun/reflect/generics/tree/FormalTypeParameter
+24 -1: UncaughtExceptionHandler
+14 -1: previousOrSame
+24 -1: java/security/Permission
+9 -1: x-ISCII91
+5 -1: L_HEX
+35 -1: java/lang/invoke/DirectMethodHandle
+35 -1: java/util/ArrayDeque$DeqSpliterator
+14 -1: java/util/List
+11 -1: toLowerCase
+24 -1: java/nio/charset/Charset
+10 -1: MIN_NORMAL
+110 -1: (Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
+13 -1: regionMatches
+17 -1: newMethodAccessor
+26 -1: (Ljava/net/InetAddress;B)V
+68 -1: (Ljava/util/zip/ZipFile;Ljava/lang/String;J)Ljava/util/zip/ZipEntry;
+22 -1: ()Ljava/io/FileSystem;
+19 -1: primitiveSimpleName
+5 -1: MOVED
+9 -1: STATE_RED
+13 -1: linkToSpecial
+19 -1: AnnotationType.java
+20 -1: (II)Ljava/util/List;
+252 -1: (Ljava/util/concurrent/ConcurrentHashMap$BulkTask;III[Ljava/util/concurrent/ConcurrentHashMap$Node;Ljava/util/concurrent/ConcurrentHashMap$MapReduceMappingsToDoubleTask;Ljava/util/function/ToDoubleBiFunction;DLjava/util/function/DoubleBinaryOperator;)V
+12 -1: callSiteForm
+22 -1: isSiblingBindingBefore
+62 -1: <T:Ljava/lang/Object;>([TT;IITT;Ljava/util/Comparator<-TT;>;)I
+15 -1: buildEmptyNames
+11 -1: Thread.java
+30 -1: Ljava/lang/ref/Reference<TT;>;
+35 -1: sun/reflect/MethodAccessorGenerator
+152 -1: (Ljava/util/function/Function;Ljava/util/function/Function;Ljava/util/function/BinaryOperator;Ljava/util/function/Supplier;)Ljava/util/stream/Collector;
+5 -1: total
+242 -1: (Ljava/util/concurrent/ConcurrentHashMap$BulkTask;III[Ljava/util/concurrent/ConcurrentHashMap$Node;Ljava/util/concurrent/ConcurrentHashMap$MapReduceValuesToLongTask;Ljava/util/function/ToLongFunction;JLjava/util/function/LongBinaryOperator;)V
+27 -1: javax/security/auth/Subject
+43 -1: JVMTI_THREAD_STATE_BLOCKED_ON_MONITOR_ENTER
+17 -1: getSignerCertPath
+15 -1: registerNatives
+21 -1: sun/reflect/FieldInfo
+54 -1: (Ljava/nio/charset/Charset;Lsun/nio/cs/ISO_8859_1$1;)V
+17 -1: unwrapWithNoPrims
+17 -1: instanceof Long: 
+20 -1: hasRealParameterData
+23 -1: ()Ljava/time/LocalTime;
+14 -1: getAnnotations
+8 -1: optimize
+7 -1: setChar
+11 -1: OFFSET_MASK
+4 -1: TYPE
+177 -1: (Ljava/util/concurrent/ConcurrentHashMap$BulkTask;III[Ljava/util/concurrent/ConcurrentHashMap$Node;Ljava/util/function/BiFunction;Ljava/util/concurrent/atomic/AtomicReference;)V
+18 -1: removeShutdownHook
+27 -1: ()Ljava/security/Principal;
+29 -1: JAVAFX_APPLICATION_CLASS_NAME
+6 -1: digits
+37 -1: [Ljava/lang/reflect/Constructor<TT;>;
+45 -1: ()Ljava/lang/Thread$UncaughtExceptionHandler;
+7 -1: tryLock
+19 -1: java/net/Proxy$Type
+21 -1: setJavaSecurityAccess
+13 -1: tieBreakOrder
+3 -1: no 
+16 -1: Australia/Sydney
+13 -1: DAY_IN_MILLIS
+19 -1: ()Ljava/nio/Buffer;
+12 -1: Integer.java
+14 -1: isBmpCodePoint
+6 -1: daemon
+23 -1: Lsun/misc/JavaIOAccess;
+106 -1: <U:Ljava/lang/Object;>(JLjava/util/function/BiFunction<-TK;-TV;+TU;>;Ljava/util/function/Consumer<-TU;>;)V
+10 -1: getFloatAt
+15 -1: content/unknown
+52 -1: ()Ljava/util/Enumeration<+Ljava/util/zip/ZipEntry;>;
+123 -1: <T:Ljava/lang/Object;>(Ljava/lang/Class<*>;Lsun/reflect/annotation/AnnotationType;Lsun/reflect/annotation/AnnotationType;)Z
+4 -1: nsme
+12 -1: prefixLength
+9 -1: flagsMods
+95 -1: (BLjava/lang/invoke/MemberName;Ljava/lang/Class;Ljava/lang/Class;)Ljava/lang/invoke/MemberName;
+62 -1: (Ljava/lang/invoke/MethodHandle;)Ljava/lang/invoke/LambdaForm;
+16 -1: Illegal mode: 0x
+24 -1: java/io/FileOutputStream
+41 -1: [Pp][Ee][Rr][Mm][Ii][Ss][Ss][Ii][Oo][Nn]=
+24 -1: java/security/CodeSource
+16 -1: DUMP_CLASS_FILES
+25 -1: ([C)Ljava/nio/CharBuffer;
+12 -1: bindArgument
+50 -1: Ljava/lang/ref/FinalReference<Ljava/lang/Object;>;
+21 -1: unmodifiableSortedMap
+10 -1: jarHandler
+73 -1: (Ljava/lang/Class;[Ljava/lang/reflect/Method;)[Ljava/lang/reflect/Method;
+67 -1: ()Ljava/util/Map<Ljava/lang/Thread;[Ljava/lang/StackTraceElement;>;
+15 -1: threadSeqNumber
+18 -1: AutoCloseable.java
+9 -1: holdsLock
+25 -1: (Ljava/lang/Object;JJJJ)V
+7 -1: (IJII)I
+15 -1: copyToLongArray
+58 -1: Ljava/util/HashMap<Ljava/lang/String;Ljava/lang/Package;>;
+84 -1: (Ljava/lang/invoke/MethodHandle;I[Ljava/lang/Object;)Ljava/lang/invoke/MethodHandle;
+32 -1: getExecutableTypeAnnotationBytes
+17 -1: streamHandlerLock
+35 -1: java/lang/IndexOutOfBoundsException
+15 -1: moveRootToFront
+28 -1: ()Ljava/nio/file/FileSystem;
+14 -1: content-length
+61 -1: (Ljava/lang/invoke/CallSite;Ljava/lang/invoke/MethodHandle;)V
+7 -1: csASCII
+18 -1: staticIsConsistent
+21 -1: sharedToGenericString
+8 -1: linkLast
+21 -1: isUnicodeExtensionKey
+7 -1: readInt
+7 -1: compile
+32 -1: ()Ljava/lang/reflect/Executable;
+4 -1: Big5
+20 -1: Ljava/util/Set<TE;>;
+18 -1: ExpiringCache.java
+44 -1: (Ljava/lang/String;[BII)Ljava/lang/Class<*>;
+18 -1: LinkedHashMap.java
+32 -1: Ljava/lang/UnsatisfiedLinkError;
+13 -1: parameterType
+28 -1: (ID)Ljava/lang/StringBuffer;
+15 -1: synchronizedSet
+9 -1: implClose
+6 -1: member
+14 -1: MH_INVOKE_MODS
+21 -1: forOutputStreamWriter
+37 -1: Lsun/misc/JavaIOFileDescriptorAccess;
+28 -1: java/lang/ProcessEnvironment
+17 -1: setNormalizedYear
+14 -1: isMalformed4_2
+14 -1: isMalformed4_3
+38 -1: (Ljava/lang/Object;)Ljava/lang/String;
+16 -1: getJavaAWTAccess
+12 -1: isPrivileged
+30 -1: java/util/Collections$EmptyMap
+17 -1: LinkedKeyIterator
+7 -1: vmcount
+27 -1: java/lang/ref/WeakReference
+5 -1: march
+13 -1: addOldMapping
+58 -1: (Ljava/lang/Object;Ljava/lang/Runnable;)Lsun/misc/Cleaner;
+56 -1: (ILjava/lang/String;)[Ljava/lang/invoke/LambdaForm$Name;
+65 -1: java/util/concurrent/ConcurrentHashMap$MapReduceEntriesToLongTask
+55 -1: (Ljava/lang/invoke/SerializedLambda;)Ljava/lang/Object;
+17 -1: getEnclosingClass
+35 -1: (I)Lsun/util/calendar/BaseCalendar;
+13 -1: binarySearch0
+25 -1: ([J)Ljava/nio/LongBuffer;
+19 -1: java/util/Map$Entry
+22 -1: java/util/HashMap$Node
+26 -1: sun/reflect/MethodAccessor
+8 -1: LASTYEAR
+7 -1: disable
+36 -1: sun/launcher/LauncherHelper$FXHelper
+6 -1: toPath
+10 -1: shortValue
+6 -1: remove
+59 -1: ([Ljava/lang/String;[Ljava/lang/String;)Ljava/lang/Process;
+55 -1: java/util/concurrent/ConcurrentHashMap$ValueSpliterator
+64 -1: (Ljava/util/Collection;Ljava/lang/Object;)Ljava/util/Collection;
+15 -1: asPrimitiveType
+16 -1: PrintStream.java
+10 -1: image/jpeg
+22 -1: specificToStringHeader
+7 -1: class "
+38 -1: java/util/Collections$CheckedSortedMap
+19 -1: SUPPRESSED_SENTINEL
+16 -1: getEnumConstants
+54 -1: (ILjava/lang/CharSequence;II)Ljava/lang/StringBuilder;
+36 -1: Ljava/lang/Class<Ljava/lang/Short;>;
+13 -1: toThreadState
+38 -1: (Ljava/lang/Class;)Ljava/lang/Package;
+24 -1: (C)Ljava/lang/Character;
+19 -1: UNTREEIFY_THRESHOLD
+4 -1: NCPU
+23 -1: ()Ljava/lang/Exception;
+42 -1: (ITK;TV;Ljava/util/HashMap$Node<TK;TV;>;)V
+15 -1: nothingToVerify
+15 -1: setInitialValue
+15 -1: getTimeInMillis
+12 -1: getDoubleAt0
+18 -1: parameterTypeCache
+86 -1: (Ljava/nio/file/WatchService;[Ljava/nio/file/WatchEvent$Kind;)Ljava/nio/file/WatchKey;
+49 -1: java/util/concurrent/ConcurrentHashMap$ValuesView
+13 -1: <all actions>
+7 -1: exitVM.
+34 -1: Ljava/lang/ClassNotFoundException;
+68 -1: (Ljava/util/Map;Ljava/lang/Class;)[Ljava/lang/annotation/Annotation;
+28 -1: getCalendarDateFromFixedDate
+28 -1: UnsafeFieldAccessorImpl.java
+27 -1: java/lang/RuntimePermission
+74 -1: Ljava/lang/Object;Ljava/lang/Comparable<Lsun/util/locale/BaseLocale$Key;>;
+62 -1: ()Ljava/util/Iterator<Ljava/nio/charset/spi/CharsetProvider;>;
+13 -1: LanguageRange
+239 -1: (Ljava/util/concurrent/ConcurrentHashMap$BulkTask;III[Ljava/util/concurrent/ConcurrentHashMap$Node;Ljava/util/concurrent/ConcurrentHashMap$MapReduceValuesToIntTask;Ljava/util/function/ToIntFunction;ILjava/util/function/IntBinaryOperator;)V
+37 -1: DIRECTIONALITY_LEFT_TO_RIGHT_OVERRIDE
+62 -1: (Ljava/lang/invoke/MethodType;II)Ljava/lang/invoke/MethodType;
+16 -1: DMH.invokeStatic
+11 -1: (TK;TV;)TV;
+7 -1: ([FI)[F
+14 -1: newPerfCounter
+35 -1: ([JI)Ljava/util/Spliterator$OfLong;
+30 -1: java/util/AbstractList$ListItr
+5 -1: cp912
+5 -1: cp914
+45 -1: (Ljava/io/BufferedWriter;Ljava/lang/String;)V
+6 -1: LOCNAM
+8 -1: launcher
+5 -1: cp915
+14 -1: standardString
+26 -1: ()Ljava/lang/Thread$State;
+10 -1: L_ALPHANUM
+8 -1: (C[CII)I
+9 -1: SEPTEMBER
+20 -1: java/text/DateFormat
+38 -1: Ljava/lang/CloneNotSupportedException;
+5 -1: cp920
+23 -1: getConstructorSignature
+16 -1: ReferenceHandler
+19 -1: America/Puerto_Rico
+5 -1: cp923
+10 -1: typeString
+30 -1: Self-suppression not permitted
+25 -1: (Ljava/io/OutputStream;)V
+9 -1: implReset
+12 -1: fullAddCount
+34 -1: java/lang/invoke/LambdaForm$Hidden
+25 -1: ()Lsun/misc/JavaIOAccess;
+9 -1: Math.java
+9 -1: getAndSet
+7 -1: failure
+14 -1: LINE_SEPARATOR
+6 -1: parent
+30 -1: java/lang/BootstrapMethodError
+8 -1: indexMap
+9 -1: ALL_KINDS
+23 -1: desiredAssertionStatus0
+39 -1: (ILjava/lang/Object;)Ljava/lang/Object;
+22 -1: RuntimePermission.java
+21 -1: getContextClassLoader
+14 -1: VARARGS_INVOKE
+8 -1: zoneinfo
+130 -1: (Ljava/lang/invoke/MethodType;Ljava/lang/invoke/LambdaForm;Ljava/lang/invoke/MemberName;ILjava/lang/invoke/DirectMethodHandle$1;)V
+18 -1: java/lang/Readable
+11 -1: containsAll
+11 -1: newPosition
+57 -1: sun/reflect/InstantiationExceptionConstructorAccessorImpl
+13 -1: REVERSE_ORDER
+29 -1: Required array size too large
+6 -1: sunday
+44 -1: <T:Ljava/lang/Object;>()Ljava/util/Set<TT;>;
+10 -1: toIntExact
+33 -1: ([BIILjava/nio/charset/Charset;)V
+14 -1: indexOfSubList
+15 -1: tryAcquireNanos
+31 -1: java/lang/InvalidClassException
+22 -1: SecureClassLoader.java
+12 -1: proxiedHosts
+7 -1: ([CII)I
+11 -1: toHexString
+30 -1: sun/util/calendar/ZoneInfoFile
+31 -1: Ljava/util/jar/Attributes$Name;
+10 -1: L_USERINFO
+25 -1: (IB)Ljava/nio/ByteBuffer;
+11 -1: parseDouble
+7 -1: ([CII)V
+13 -1: Asia/Shanghai
+5 -1: [...]
+57 -1: ([Ljava/lang/Class;[B)[[Ljava/lang/annotation/Annotation;
+19 -1: java/nio/LongBuffer
+15 -1: getCertificates
+9 -1: comparing
+83 -1: (Ljava/lang/Class<*>;Ljava/lang/String;Ljava/lang/Class<*>;IILjava/lang/String;[B)V
+43 -1: Ljava/util/concurrent/atomic/AtomicInteger;
+23 -1: toFieldDescriptorString
+20 -1: Lsun/misc/MetaIndex;
+37 -1: java/util/Collections$UnmodifiableSet
+5 -1: (JC)V
+37 -1: nanosecond timeout value out of range
+26 -1: AbstractStringBuilder.java
+43 -1: java/lang/invoke/DirectMethodHandle$Special
+49 -1: ([Ljava/nio/file/LinkOption;)Ljava/nio/file/Path;
+15 -1: currentPosition
+14 -1: java/net/Proxy
+13 -1: asConstructor
+8 -1: userInfo
+14 -1: parseClassPath
+15 -1: legacyMergeSort
+34 -1: java/security/UnresolvedPermission
+97 -1: Lsun/util/locale/LocaleObjectCache<Lsun/util/locale/BaseLocale$Key;Lsun/util/locale/BaseLocale;>;
+9 -1: freeEntry
+19 -1: delimiterCodePoints
+34 -1: Should be non-empty if initialized
+23 -1: (I)Ljava/lang/Class<*>;
+11 -1: Reader.java
+26 -1: checkClassLoaderPermission
+27 -1: java/nio/DirectShortBufferS
+95 -1: Ljava/lang/Object;Ljava/security/PrivilegedExceptionAction<Lsun/misc/Launcher$ExtClassLoader;>;
+27 -1: java/nio/DirectShortBufferU
+20 -1: ()[Ljava/lang/Class;
+56 -1: ()[Ljava/util/concurrent/ConcurrentHashMap$Node<TK;TV;>;
+19 -1: sun/nio/cs/UTF_16BE
+24 -1: java/util/AbstractList$1
+10 -1: ([CIIIII)V
+60 -1: Ljava/lang/Object;Ljava/lang/Comparable<Ljava/lang/Object;>;
+37 -1: (Ljava/lang/invoke/LambdaForm$Name;)S
+9 -1: putDouble
+52 -1: ([Ljava/security/CodeSource;)Ljava/util/Enumeration;
+37 -1: (Ljava/lang/invoke/LambdaForm$Name;)Z
+22 -1: ()[Ljava/lang/Package;
+41 -1: java/lang/CharSequence$1CodePointIterator
+13 -1: auditSubclass
+24 -1: Ljava/util/jar/JarEntry;
+20 -1: findMethodHandleType
+15 -1: MAX_BUFFER_SIZE
+19 -1: FilePermission.java
+18 -1: WrappedPrintStream
+36 -1: (D)Ljava/lang/AbstractStringBuilder;
+11 -1: unfinalized
+10 -1: getFileURL
+37 -1: (Ljava/io/FileFilter;)[Ljava/io/File;
+54 -1: (Ljava/nio/ByteBuffer;I)Ljava/nio/charset/CoderResult;
+7 -1: ([ZI)[Z
+64 -1: (Ljava/security/CodeSource;)Ljava/security/PermissionCollection;
+6 -1: PUBLIC
+83 -1: (Ljava/util/jar/JarFile;Ljava/net/URL;Ljava/lang/String;)Ljava/security/CodeSource;
+17 -1: lockInterruptibly
+67 -1: ([Ljava/util/Hashtable$Entry;Ljava/lang/Object;Ljava/lang/Object;)V
+42 -1: java/util/ArraysParallelSortHelpers$FJChar
+21 -1: defaultCharBufferSize
+14 -1: unalignedKnown
+23 -1: ()Ljava/net/Proxy$Type;
+4 -1: TZDB
+14 -1: CharacterCache
+13 -1: lengthOfMonth
+13 -1: hasExtensions
+23 -1: Prefix string too short
+15 -1: Executable.java
+10 -1: forEachKey
+6 -1: getEra
+13 -1: appendEncoded
+21 -1: java/util/AbstractMap
+10 -1: access$100
+10 -1: access$102
+30 -1: javafx.application.Application
+46 -1: (Ljava/lang/Thread$UncaughtExceptionHandler;)V
+43 -1: Ljava/lang/invoke/LambdaForm$NamedFunction;
+101 -1: (Ljava/lang/Class;Ljava/lang/String;Ljava/lang/Class;IILjava/lang/String;[B)Ljava/lang/reflect/Field;
+9 -1: WILD_CHAR
+21 -1: SynchronizedSortedSet
+28 -1: (Ljava/util/Collection<*>;)Z
+29 -1: (I[C)Ljava/lang/StringBuffer;
+65 -1: (Ljava/lang/String;[Ljava/lang/Class;Z)Ljava/lang/reflect/Method;
+7 -1: putByte
+6 -1: H_MARK
+49 -1: (Ljava/lang/invoke/MemberName;)Ljava/lang/Object;
+98 -1: ([Ljava/lang/ClassValue$Entry<*>;ILjava/lang/ClassValue$Entry<*>;Z)Ljava/lang/ClassValue$Entry<*>;
+23 -1: array is not of length 
+52 -1: <T:Ljava/lang/Object;>(Ljava/util/List<TT;>;TT;TT;)Z
+41 -1: Ljava/util/Collections$EmptyListIterator;
+8 -1:         
+11 -1: updateCheck
+29 -1: getBootClassPathEntryForClass
+27 -1: sun/nio/cs/US_ASCII$Encoder
+10 -1: bindCaller
+18 -1: Ljava/util/BitSet;
+10 -1: checkRange
+77 -1: (Ljava/lang/Class<*>;Ljava/lang/String;Ljava/lang/Class<*>;Ljava/lang/Void;)V
+7 -1: Classes
+6 -1: store0
+23 -1: java/lang/Thread$Caches
+25 -1: Ljava/lang/CharacterData;
+7 -1: (JI[C)V
+49 -1: (Ljava/util/LinkedList;Ljava/util/LinkedList$1;)V
+16 -1: getGcInfoBuilder
+12 -1: counterCells
+14 -1: memoryLimitSet
+7 -1: , nojit
+9 -1: sharpsMap
+7 -1: october
+13 -1: isProxiedHost
+9 -1: rawOffset
+18 -1: toJavaFormatString
+19 -1: sun.boot.class.path
+60 -1: (ILjava/lang/CharSequence;)Ljava/lang/AbstractStringBuilder;
+11 -1: spliterator
+13 -1: contentLength
+18 -1: unixTimeToFileTime
+31 -1: Lsun/reflect/LangReflectAccess;
+16 -1:  while Java has 
+79 -1: (JLjava/util/function/ToIntBiFunction;ILjava/util/function/IntBinaryOperator;)I
+12 -1: timeEndOfDay
+17 -1: getCustomTimeZone
+25 -1: Ljava/lang/ref/Reference;
+20 -1: ()Ljava/util/Locale;
+64 -1: (Ljava/util/HashMap<TK;TV;>;[Ljava/util/HashMap$Node<TK;TV;>;Z)V
+13 -1: MAX_JVM_ARITY
+72 -1: (Ljava/lang/String;Ljava/lang/ClassLoader;)Ljava/lang/invoke/MethodType;
+11 -1: AsLIFOQueue
+84 -1: <T:Ljava/lang/Object;>(Ljava/util/List<TT;>;Ljava/lang/Object;)Ljava/util/List<TT;>;
+12 -1: mappingCount
+29 -1: (Ljava/io/FileOutputStream;)V
+50 -1: <T:Ljava/lang/Object;>(Ljava/util/List<-TT;>;TT;)V
+23 -1: Category cannot be NULL
+10 -1: normalized
+5 -1: CLASS
+28 -1: (IZ)Ljava/lang/StringBuffer;
+18 -1: java/lang/System$1
+18 -1: java/lang/System$2
+9 -1: getResult
+44 -1: ()Ljava/util/Collection<Ljava/lang/Thread;>;
+8 -1: isNative
+59 -1: Ljava/lang/Number;Ljava/lang/Comparable<Ljava/lang/Short;>;
+24 -1: [Ljava/lang/ThreadGroup;
+24 -1: (B)Ljava/nio/ByteBuffer;
+4 -1: READ
+44 -1: (Ljava/io/FilePermission;)Ljava/lang/String;
+93 -1: <E:Ljava/lang/Object;>Ljava/util/Collections$SynchronizedCollection<TE;>;Ljava/util/Set<TE;>;
+12 -1: compileClass
+12 -1: isProxyClass
+20 -1: isSystemDomainLoader
+74 -1: <T:Ljava/lang/Object;>(Ljava/util/List<TT;>;Ljava/util/Comparator<-TT;>;)V
+7 -1: getMask
+72 -1: (Ljava/lang/ClassLoader;Ljava/lang/SecurityManager;Ljava/lang/String;I)V
+20 -1: removeLastOccurrence
+64 -1: (Ljava/lang/reflect/Field;)Ljava/lang/invoke/DirectMethodHandle;
+57 -1: ()Ljava/util/Map<Ljava/lang/String;Ljava/lang/Class<*>;>;
+11 -1: parkBlocker
+40 -1: (Lsun/misc/JarIndex;Ljava/lang/String;)V
+33 -1: [Ljava/security/ProtectionDomain;
+14 -1: setContentType
+14 -1: getEnumeration
+18 -1: ProtectionDomain  
+76 -1: (Lsun/util/calendar/BaseCalendar$Date;)Lsun/util/calendar/BaseCalendar$Date;
+16 -1: setRequestMethod
+52 -1: (Ljava/util/List;Ljava/lang/Object;)Ljava/util/List;
+32 -1: Lsun/util/calendar/BaseCalendar;
+52 -1: (Ljava/net/URL;Ljava/lang/String;)Ljava/lang/String;
+5 -1: .:@[]
+7 -1: addLast
+21 -1: AnnotatedElement.java
+10 -1: defaultVal
+16 -1: getCanonicalPath
+17 -1: protection_domain
+9 -1: strictfp 
+19 -1: sun/nio/cs/UTF_16LE
+9 -1: readBytes
+18 -1: removeStaleEntries
+46 -1: java/util/Collections$UnmodifiableNavigableSet
+5 -1: cpath
+14 -1: COPY_THRESHOLD
+8 -1: permsMap
+8 -1: japanese
+33 -1: java/nio/charset/StandardCharsets
+47 -1: Lsun/reflect/DelegatingConstructorAccessorImpl;
+19 -1: NF_checkGenericType
+40 -1: java/util/Collections$UnmodifiableList$1
+4 -1: TERM
+48 -1: The following can be used with stack and domain:
+27 -1: ([BII)Ljava/nio/ByteBuffer;
+40 -1: Ljava/util/Vector<Ljava/lang/Class<*>;>;
+5 -1: digit
+7 -1: isFinal
+39 -1: (Ljava/lang/String;Ljava/lang/String;)I
+26 -1: memberDeclaringClassOrNull
+10 -1: ([CI[BII)I
+38 -1: (Ljava/lang/Class;I)Ljava/lang/Object;
+15 -1: isValidProtocol
+17 -1: (this Collection)
+11 -1: getTreeNode
+16 -1: ThreadLocal.java
+23 -1: java/nio/HeapLongBuffer
+39 -1: (Ljava/lang/String;Ljava/lang/String;)V
+12 -1: getNextEntry
+39 -1: (Ljava/lang/String;Ljava/lang/String;)Z
+28 -1: (C)Lsun/invoke/util/Wrapper;
+9 -1: readFloat
+21 -1: overrideFieldAccessor
+16 -1: fillInStackTrace
+16 -1: getDeclaredField
+5 -1: deflt
+8 -1: nextChar
+10 -1: primCounts
+22 -1: getAnnotatedInterfaces
+26 -1: ()Ljava/util/zip/Inflater;
+73 -1: <U:Ljava/lang/Object;>(JLjava/util/function/BiFunction<-TK;-TV;+TU;>;)TU;
+16 -1: traceMethodCalls
+10 -1: sun.nio.cs
+7 -1: marshal
+9 -1: ftypeKind
+77 -1: (Ljava/lang/invoke/MemberName;Ljava/lang/Class;)Ljava/lang/invoke/MemberName;
+11 -1: sun/misc/VM
+14 -1: GuardWithCatch
+40 -1: (Ljava/lang/Object;)Ljava/util/Iterator;
+13 -1: subtractExact
+42 -1: (Ljava/lang/Object;ILjava/lang/Object;II)V
+10 -1: isInfinite
+18 -1: sun.util.calendar.
+14 -1: java/lang/Math
+16 -1: java/lang/String
+10 -1: encodePath
+14 -1: compactAndTrim
+11 -1: getVariants
+4 -1: from
+47 -1: (Ljava/lang/ThreadLocal<*>;Ljava/lang/Object;)V
+35 -1: serializeAgentPropertiesToByteArray
+16 -1: computeIfPresent
+9 -1: EMPTY_MAP
+28 -1: java/lang/InstantiationError
+68 -1: (Ljava/util/Comparator;Ljava/util/Comparator;)Ljava/util/Comparator;
+14 -1: getDisplayName
+14 -1: limitedContext
+23 -1: usagetracker.properties
+52 -1: java/util/concurrent/locks/ReentrantLock$NonfairSync
+7 -1: public 
+17 -1: srcBegin > srcEnd
+48 -1: (ILjava/util/List;)Ljava/lang/invoke/MethodType;
+21 -1: java/lang/ThreadDeath
+9 -1: gregorian
+111 -1: (Ljava/util/HashMap;[Ljava/util/HashMap$Node;ILjava/lang/Object;Ljava/lang/Object;)Ljava/util/HashMap$TreeNode;
+52 -1: ([Ljava/util/concurrent/ConcurrentHashMap$Node;III)V
+12 -1: generateFile
+20 -1: appendParameterTypes
+22 -1: sun/misc/FileURLMapper
+13 -1: defaultDomain
+25 -1: (I)Ljava/time/ZoneOffset;
+21 -1: getBooleanAttributes0
+39 -1: (Ljava/lang/String;)Lsun/misc/Resource;
+43 -1: Ljava/lang/Thread$UncaughtExceptionHandler;
+23 -1: getTypeAnnotationBytes0
+8 -1: ELOT_928
+32 -1: sun/nio/cs/FastCharsetProvider$1
+34 -1: Ljava/nio/BufferOverflowException;
+14 -1: AppClassLoader
+10 -1: protected 
+12 -1: isAnnotation
+16 -1: PerfCounter.java
+51 -1: Ljava/util/Map<Ljava/io/File;Lsun/misc/MetaIndex;>;
+160 -1: (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
+11 -1: setLeapYear
+16 -1: parseContextSpec
+16 -1: setFieldAccessor
+8 -1: writeInt
+16 -1: java/lang/Number
+33 -1: java/util/AbstractMap$SimpleEntry
+9 -1: nextTable
+8 -1: getQuery
+14 -1: putOrderedLong
+93 -1: (Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)V
+52 -1: (Ljava/lang/String;)Lsun/reflect/generics/tree/Tree;
+85 -1: (ILjava/lang/Object;Ljava/lang/Object;Ljava/util/concurrent/ConcurrentHashMap$Node;)V
+9 -1: singleton
+9 -1: destroyed
+15 -1: iso_8859-2:1987
+12 -1: comparingInt
+29 -1: [Ljava/lang/OutOfMemoryError;
+39 -1: (Ljava/lang/String;Ljava/lang/Object;)V
+27 -1: ([Ljava/util/Enumeration;)V
+36 -1: Ljava/nio/charset/CodingErrorAction;
+16 -1: getCanonicalName
+41 -1: (Ljava/nio/LongBuffer;)Ljava/util/BitSet;
+15 -1: DISPLAY_COUNTRY
+32 -1: getFunctionalInterfaceMethodName
+66 -1: (Ljava/lang/String;Ljava/util/Map;Ljava/util/Map;Ljava/util/Map;)V
+24 -1: getUnresolvedPermissions
+66 -1: ([Ljava/lang/ClassValue$Entry<*>;I)Ljava/lang/ClassValue$Entry<*>;
+41 -1: ()Lsun/reflect/annotation/AnnotationType;
+7 -1: afIndex
+7 -1: csascii
+20 -1: ()Ljava/util/Vector;
+16 -1: EntrySpliterator
+53 -1: (Ljava/lang/Throwable;I)Ljava/lang/StackTraceElement;
+81 -1: (Ljava/lang/invoke/MethodHandle;Ljava/lang/Class;)Ljava/lang/invoke/MethodHandle;
+20 -1: classAssertionStatus
+7 -1: EXECUTE
+21 -1: ()Ljava/lang/Runtime;
+6 -1: cesu-8
+7 -1: offset 
+23 -1: Ljava/lang/SafeVarargs;
+34 -1: (Ljava/util/LinkedHashMap$Entry;)V
+8 -1: entryFor
+8 -1: getCache
+46 -1: (Ljava/lang/Object;I)Ljava/lang/reflect/Field;
+4 -1: skip
+8 -1: (II[CI)V
+4 -1: vart
+12 -1: InnerClasses
+68 -1: (Ljava/util/Map;Ljava/lang/Class;[Ljava/lang/String;)Ljava/util/Map;
+19 -1: currentClassLoader0
+34 -1: getDefaultUncaughtExceptionHandler
+11 -1: String.java
+117 -1: (Ljava/lang/ThreadLocal<*>;ILjava/lang/ThreadLocal$ThreadLocalMap$Entry;)Ljava/lang/ThreadLocal$ThreadLocalMap$Entry;
+7 -1: january
+31 -1: (ILjava/lang/String;IIIIIIIII)V
+24 -1: Lsun/misc/JavaAWTAccess;
+5 -1: .path
+15 -1: [Ljava/io/File;
+11 -1: Writer.java
+8 -1: , Size: 
+159 -1: (Ljava/util/concurrent/ConcurrentHashMap$BulkTask;III[Ljava/util/concurrent/ConcurrentHashMap$Node;Ljava/util/function/Function;Ljava/util/function/Consumer;)V
+32 -1: java/lang/invoke/LambdaForm$Name
+51 -1: (Ljava/util/ArrayList;Ljava/util/AbstractList;III)V
+7 -1: getZone
+14 -1: JIS_X0212-1990
+21 -1: (Ljava/util/Set<*>;)Z
+149 -1: (Lsun/util/locale/provider/LocaleServiceProviderPool$LocalizedObjectGetter;Ljava/util/Locale;Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/Object;
+21 -1: Illegal Load factor: 
+35 -1: ()Ljava/lang/invoke/MethodTypeForm;
+22 -1: ([Z)Ljava/lang/String;
+7 -1: setName
+48 -1: <T:Ljava/lang/Object;>(TT;Ljava/lang/String;)TT;
+9 -1: INTERFACE
+22 -1: ARRAY_CHAR_INDEX_SCALE
+19 -1: java/nio/ByteBuffer
+23 -1: Ljava/io/ExpiringCache;
+7 -1: streams
+44 -1: java/lang/invoke/DirectMethodHandle$Accessor
+36 -1: ([Ljava/security/ProtectionDomain;)V
+16 -1: afterNodeRemoval
+9 -1: prevIndex
+49 -1: java/util/concurrent/ConcurrentHashMap$KeySetView
+22 -1: getEnumConstantsShared
+17 -1: java/lang/Integer
+12 -1: getRawOffset
+36 -1: ()Ljava/nio/file/attribute/FileTime;
+7 -1: offsets
+10 -1: ] throw =>
+3 -1: [[B
+10 -1: hostsEqual
+18 -1: compareComparables
+35 -1: sun/reflect/ConstructorAccessorImpl
+8 -1: december
+36 -1: Invalid binary time-zone data: TZDB:
+24 -1: synchronizedNavigableMap
+72 -1: sun/util/locale/provider/LocaleServiceProviderPool$LocalizedObjectGetter
+19 -1: parseCustomTimeZone
+88 -1: (Ljava/lang/Class<*>;Ljava/lang/invoke/MemberName;)Ljava/lang/invoke/DirectMethodHandle;
+9 -1: findClass
+6 -1: OBJECT
+3 -1: GBK
+110 -1: (JLjava/util/function/ToLongFunction<Ljava/util/Map$Entry<TK;TV;>;>;JLjava/util/function/LongBinaryOperator;)J
+6 -1: UTF_16
+17 -1: <all permissions>
+13 -1: lookupCharset
+28 -1: ConstructorAccessorImpl.java
+62 -1: java/util/concurrent/ConcurrentHashMap$MapReduceKeysToLongTask
+30 -1: Ljava/lang/ClassCastException;
+56 -1: ()Ljava/util/Spliterator<Ljava/util/Map$Entry<TK;TV;>;>;
+11 -1: invokerType
+23 -1: java/lang/StringBuilder
+12 -1: deleteCharAt
+11 -1: CR_OVERFLOW
+14 -1: toExternalForm
+3 -1: out
+34 -1: Ljava/net/URLStreamHandlerFactory;
+15 -1: encodeArrayLoop
+30 -1: ()Ljava/util/Enumeration<TK;>;
+27 -1: java/io/SyncFailedException
+10 -1: checkedSet
+16 -1: checkedSortedSet
+22 -1: java/util/zip/ZipEntry
+43 -1: java/util/LinkedHashMap$LinkedValueIterator
+22 -1: UnmodifiableCollection
+32 -1: java/nio/ByteBufferAsCharBufferB
+11 -1: blockerLock
+27 -1: checkExtensionsDependencies
+11 -1: fromIndex: 
+32 -1: java/nio/ByteBufferAsCharBufferL
+6 -1: UTF_32
+30 -1: java/util/Hashtable$Enumerator
+92 -1: <K:Ljava/lang/Object;V:Ljava/lang/Object;>(Ljava/util/Map<+TK;+TV;>;)Ljava/util/Map<TK;TV;>;
+4 -1: tail
+5 -1: (BB)C
+16 -1: isValidSignature
+9 -1: addMillis
+9 -1: peekFirst
+86 -1: <E:Ljava/lang/Object;>(Ljava/util/Set<TE;>;Ljava/lang/Class<TE;>;)Ljava/util/Set<TE;>;
+5 -1: (BB)I
+15 -1: jvmMicroVersion
+28 -1: [Ljava/util/Hashtable$Entry;
+15 -1: iso_8859-5:1988
+14 -1: HOUR_IN_MILLIS
+67 -1: (Ljava/util/PrimitiveIterator$OfInt;I)Ljava/util/Spliterator$OfInt;
+5 -1: (BB)S
+21 -1: UnmodifiableSortedMap
+79 -1: (Ljava/lang/Object;Ljava/lang/Object;Ljava/util/concurrent/ConcurrentHashMap;)V
+56 -1: Ljava/util/Stack<Ljava/lang/ClassLoader$NativeLibrary;>;
+5 -1: (BB)Z
+10 -1: treeifyBin
+8 -1: isOpaque
+27 -1: java.launcher.ergo.message1
+27 -1: java.launcher.ergo.message2
+101 -1: Ljava/lang/Object;Ljava/io/Serializable;Ljava/lang/Cloneable;Ljava/lang/Comparable<Ljava/util/Date;>;
+31 -1: (Ljava/io/ObjectOutputStream;)V
+3 -1: GET
+13 -1: matchLocation
+13 -1: WrappedMember
+63 -1: NoSuchMethodException:\n  could not find proper constructor for 
+14 -1: gssloginconfig
+70 -1: (Ljava/util/LinkedList$Node<TE;>;TE;Ljava/util/LinkedList$Node<TE;>;)V
+57 -1: (Ljava/lang/Object;Ljava/lang/Object;Z)Ljava/lang/Object;
+11 -1: toByteArray
+49 -1: java/util/ArraysParallelSortHelpers$FJByte$Sorter
+13 -1: no protocol: 
+940 -1: aaaarababkaeaveafafrakakaamamhanargararaasasmavavaayaymazazebabakbebelbgbulbhbihbibisbmbambnbenbobodbrbrebsboscacatcechechchacocoscrcrecscescuchucvchvcycymdadandedeudvdivdzdzoeeeweelellenengeoepoesspaetesteueusfafasfffulfifinfjfijfofaofrfrafyfrygaglegdglaglglggngrngugujgvglvhahauhehebhihinhohmohrhrvhthathuhunhyhyehzheriainaidindieileigiboiiiiiikipkinindioidoisislititaiuikuiwhebjajpnjiyidjvjavkakatkgkonkikikkjkuakkkazklkalkmkhmknkankokorkrkaukskaskukurkvkomkwcorkykirlalatlbltzlgluglilimlnlinlolaoltlitlulublvlavmgmlgmhmahmimrimkmkdmlmalmnmonmomolmrmarmsmsamtmltmymyananaunbnobndndenenepngndonlnldnnnnononornrnblnvnavnynyaocociojojiomormororiososspapanpipliplpolpspusptporququermrohrnrunroronrurusrwkinsasanscsrdsdsndsesmesgsagsisinskslkslslvsmsmosnsnasosomsqsqisrsrpsssswstsotsusunsvsweswswatatamteteltgtgkththatitirtktuktltgltntsntotontrturtstsotttattwtwitytahuguigukukrururduzuzbvevenvivievovolwawlnwowolxhxhoyiyidyoyorzazhazhzhozuzul
+42 -1: java/util/LinkedHashMap$LinkedHashIterator
+39 -1: java/util/ArrayDeque$DescendingIterator
+15 -1: MODIFIER_LETTER
+21 -1: (Lsun/misc/Cleaner;)Z
+12 -1: PACKAGE_NAME
+14 -1: getMappedValue
+10 -1: interrupt0
+8 -1: LF_LIMIT
+17 -1: getDeclaringClass
+42 -1: (ZILjava/lang/String;)Ljava/lang/Class<*>;
+57 -1: (Ljava/lang/management/ThreadInfo;[Ljava/lang/Object;[I)V
+15 -1: java/nio/Bits$1
+61 -1: (Ljava/lang/String;ZLjava/lang/ClassLoader;)Ljava/lang/Class;
+28 -1: java/lang/ref/ReferenceQueue
+33 -1: [Ljava/security/cert/Certificate;
+44 -1: (Ljava/util/Hashtable;I)Ljava/util/Iterator;
+24 -1: java/security/CodeSigner
+19 -1: Non-positive length
+24 -1: [Ljava/util/Enumeration;
+38 -1: ()Ljava/security/PermissionCollection;
+16 -1: checkGenericType
+56 -1: java/util/concurrent/ConcurrentHashMap$ReduceEntriesTask
+7 -1: getYear
+5 -1: atime
+19 -1: Ljava/util/HashMap;
+125 -1: <U:Ljava/lang/Object;>(JLjava/util/function/Function<Ljava/util/Map$Entry<TK;TV;>;+TU;>;Ljava/util/function/Consumer<-TU;>;)V
+22 -1: STOP_THREAD_PERMISSION
+46 -1: (Ljava/util/Enumeration;)Ljava/util/ArrayList;
+16 -1: getPathSeparator
+10 -1: getMembers
+8 -1: getIntAt
+26 -1: java/io/File$TempDirectory
+48 -1: java/lang/invoke/MethodHandleImpl$GuardWithCatch
+25 -1: CaseInsensitiveComparator
+8 -1: pollLast
+21 -1: GET_POLICY_PERMISSION
+23 -1: uninitialized call site
+75 -1: (Ljava/util/function/Function;Ljava/util/Comparator;)Ljava/util/Comparator;
+9 -1: directory
+51 -1: (JLjava/util/function/BiFunction<-TV;-TV;+TV;>;)TV;
+41 -1: (Ljava/lang/ThreadLocal$ThreadLocalMap;)V
+42 -1: (Ljava/lang/Throwable;Ljava/lang/String;)V
+26 -1: (FLjava/lang/Appendable;)V
+5 -1: stop0
+9 -1: substring
+64 -1: (ILjava/lang/Object;Ljava/lang/Object;Ljava/util/HashMap$Node;)V
+5 -1: (JD)V
+9 -1: getShortB
+10 -1: nextOrSame
+24 -1: [ interpretWithArguments
+6 -1: GB2312
+32 -1: java/nio/BufferOverflowException
+23 -1: sun/nio/cs/ArrayEncoder
+4 -1: tanh
+9 -1: getShortL
+55 -1: (JLjava/util/function/BiFunction;)Ljava/util/Map$Entry;
+10 -1: getMessage
+12 -1: findTreeNode
+38 -1: DIRECTIONALITY_COMMON_NUMBER_SEPARATOR
+23 -1: [Ljava/lang/Comparable;
+17 -1: getCallSiteTarget
+55 -1: (Ljava/nio/ByteBuffer;II)Ljava/nio/charset/CoderResult;
+19 -1: java/util/ArrayList
+17 -1: java/io/DataInput
+13 -1: getPrincipals
+52 -1: <E:Ljava/lang/Object;>(TE;)Ljava/util/Iterator<TE;>;
+44 -1: sun/reflect/generics/tree/ClassTypeSignature
+6 -1: loaded
+20 -1: ()Ljava/lang/Object;
+36 -1: Ljava/util/concurrent/ConcurrentMap;
+13 -1: classValueMap
+33 -1: java/lang/SystemClassLoaderAction
+6 -1: loader
+31 -1: java/lang/annotation/Annotation
+5 -1: colon
+11 -1: Vector.java
+24 -1: CharacterDataLatin1.java
+12 -1: setUseCaches
+22 -1: getTypeAnnotationBytes
+9 -1: readShort
+24 -1: longPrimitiveReturnCount
+5 -1: get16
+34 -1: setDefaultUncaughtExceptionHandler
+17 -1: cachedInputStream
+29 -1: java/util/LinkedHashMap$Entry
+17 -1: java/lang/Boolean
+50 -1: ()[Lsun/reflect/generics/tree/FormalTypeParameter;
+14 -1: AnnotationData
+21 -1: Ljava/net/Proxy$Type;
+13 -1: invokeVirtual
+14 -1: Parameter.java
+21 -1: getDayOfWeekDateAfter
+92 -1: (Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
+56 -1: (Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
+46 -1: sun/util/locale/provider/LocaleProviderAdapter
+29 -1: Ljava/lang/StackTraceElement;
+47 -1: (TK;Ljava/util/function/Function<-TK;+TV;>;)TV;
+32 -1: enableContextClassLoaderOverride
+68 -1: (Ljava/lang/AbstractStringBuilder;)Ljava/lang/AbstractStringBuilder;
+51 -1: Ljava/util/concurrent/ConcurrentHashMap$KeySetView;
+9 -1: addAndGet
+5 -1: store
+7 -1: ([JII)V
+15 -1: signatureReturn
+18 -1: NF_reinvokerTarget
+22 -1: ()Ljava/nio/file/Path;
+25 -1: (C)Ljava/lang/Appendable;
+7 -1: expires
+18 -1: initializeInvokers
+19 -1: application/java-vm
+13 -1: stopOrSuspend
+13 -1: rawOffsetDiff
+6 -1: (JJZ)V
+9 -1: findValue
+5 -1: get32
+10 -1: asSubclass
+6 -1: forJRE
+3 -1: GMT
+7 -1: delete0
+69 -1: ([Ljava/security/cert/Certificate;[Ljava/security/cert/Certificate;)Z
+75 -1: (Ljava/util/LinkedList$Node;Ljava/lang/Object;Ljava/util/LinkedList$Node;)V
+6 -1: setEra
+24 -1: ()Ljava/util/Comparator;
+10 -1: access$200
+10 -1: access$202
+20 -1: retrieveDisplayNames
+3 -1: pae
+24 -1: java/lang/Byte$ByteCache
+7 -1: VM.java
+9 -1: TRANSIENT
+6 -1: setErr
+16 -1: jdkUpdateVersion
+10 -1: isResolved
+35 -1: sun/misc/JavaIOFileDescriptorAccess
+4 -1: char
+13 -1: Readable.java
+19 -1: UnixFileSystem.java
+43 -1: (Ljava/lang/ThreadLocal;)Ljava/lang/Object;
+40 -1: (Ljava/lang/String;[Ljava/lang/String;)V
+7 -1: profile
+11 -1: , version: 
+25 -1: PermissionCollection.java
+13 -1: setNormalized
+10 -1: access$210
+24 -1: (Ljava/lang/String;IJZ)J
+19 -1: isAnnotationPresent
+85 -1: (Ljava/util/Map;Ljava/lang/Class;Ljava/lang/Class;)[Ljava/lang/annotation/Annotation;
+19 -1: DMH.invokeInterface
+157 -1: <K:Ljava/lang/Object;V:Ljava/lang/Object;>Ljava/util/concurrent/ConcurrentHashMap$CollectionView<TK;TV;TV;>;Ljava/util/Collection<TV;>;Ljava/io/Serializable;
+94 -1: <E:Ljava/lang/Object;>Ljava/util/Collections$UnmodifiableCollection<TE;>;Ljava/util/List<TE;>;
+41 -1: java/lang/StringIndexOutOfBoundsException
+29 -1: java/net/UnknownHostException
+11 -1: (BBBBBBBB)J
+4 -1: iioe
+12 -1: (TK;TV;Z)TV;
+5 -1: ERROR
+31 -1: (Ljava/io/File;Ljava/io/File;)I
+32 -1: [Ljava/lang/invoke/MethodHandle;
+27 -1: lambda$comparing$77a9974f$1
+13 -1: toLowerCaseEx
+61 -1: (Ljava/lang/invoke/LambdaForm;Ljava/lang/invoke/MemberName;)V
+66 -1: (ILjava/lang/Object;Ljava/lang/Class;)Ljava/util/HashMap$TreeNode;
+43 -1: java/util/concurrent/ConcurrentHashMap$Node
+23 -1: latestUserDefinedLoader
+24 -1: buildAnnotatedSuperclass
+19 -1: compareToIgnoreCase
+31 -1: (Ljava/io/File;Ljava/io/File;)Z
+40 -1: (I[C)[Ljava/lang/invoke/LambdaForm$Name;
+16 -1: ROTATE_THRESHOLD
+18 -1: getClassAtIfLoaded
+37 -1: java/lang/IllegalThreadStateException
+5 -1: get64
+12 -1: writeReplace
+14 -1: DAYS_PER_CYCLE
+4 -1: _get
+6 -1: nChars
+26 -1: ()Ljava/net/SocketAddress;
+27 -1: setUncaughtExceptionHandler
+6 -1: jzfile
+42 -1: All subclasses should override this method
+3 2: Foo
+56 -1: (Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
+136 -1: <T:Ljava/lang/Object;>(Ljava/security/PrivilegedExceptionAction<TT;>;Ljava/security/AccessControlContext;[Ljava/security/Permission;)TT;
+3 -1: pdt
+44 -1: sun/util/locale/LocaleObjectCache$CacheEntry
+20 -1: java/util/Comparator
+9 -1: suspended
+11 -1: removeEntry
+10 -1: SPACE_FREE
+12 -1: processQueue
+9 -1: java.home
+5 -1: valid
+23 -1: printEnclosedStackTrace
+4 -1: push
+5 -1: guard
+23 -1: javaNetHttpCookieAccess
+36 -1: (Ljava/security/cert/Certificate;)[B
+9 -1: isWrapped
+12 -1: CharIterator
+26 -1: sun.io.useCanonPrefixCache
+42 -1: (Lsun/misc/Cleaner;Ljava/lang/Throwable;)V
+7 -1: prepare
+8 -1: parseInt
+13 -1: Invokers.java
+63 -1: (Ljava/net/URLClassLoader;)Ljava/security/AccessControlContext;
+18 -1: defaultWriteObject
+5 -1: class
+15 -1: EnclosingMethod
+23 -1: ([BI)Ljava/lang/String;
+16 -1: rangeCheckForAdd
+11 -1: getTimeImpl
+15 -1: arrayIndexScale
+5 -1: scalb
+5 -1: scale
+45 -1: (Ljava/lang/String;J)Ljava/util/zip/ZipEntry;
+8 -1: ([FIIF)I
+16 -1: runAllFinalizers
+27 -1: ()Lsun/net/ProgressMonitor;
+15 -1: MemberName.java
+27 -1: [Ljava/lang/reflect/Member;
+6 -1: length
+14 -1: genericInvoker
+8 -1: ([FIIF)V
+34 -1: Ljava/nio/file/attribute/FileTime;
+6 -1: number
+70 -1: (Ljava/lang/StringBuilder;Ljava/lang/String;)Ljava/lang/StringBuilder;
+12 -1: printLocales
+38 -1: java/lang/invoke/MethodHandleStatics$1
+8 -1: private 
+20 -1: invalid actions mask
+10 -1:  not found
+13 -1: parseClassSig
+22 -1: getAllAvailableLocales
+175 -1: (Ljava/util/concurrent/ConcurrentHashMap$BulkTask;III[Ljava/util/concurrent/ConcurrentHashMap$Node;Ljava/util/function/Function;Ljava/util/concurrent/atomic/AtomicReference;)V
+8 -1: ([BII)[B
+35 -1: (Ljava/util/HashMap$Node<TK;TV;>;)V
+132 -1: (Ljava/lang/Thread;ILjava/lang/Object;Ljava/lang/Thread;JJJJ[Ljava/lang/StackTraceElement;[Ljava/lang/Object;[I[Ljava/lang/Object;)V
+8 -1: ([BII)[C
+20 -1: DECIMAL_DIGIT_NUMBER
+40 -1: (Ljava/lang/String;[Ljava/lang/Object;)Z
+7 -1: ([CCI)I
+64 -1: (TV;)Ljava/util/concurrent/ConcurrentHashMap$KeySetView<TK;TV;>;
+8 -1: aliasMap
+8 -1: checkfpx
+44 -1: java/util/Comparators$NaturalOrderComparator
+48 -1: (Ljava/lang/ClassLoader;)Ljava/lang/ClassLoader;
+12 -1: Name is null
+10 -1: invoke_L_L
+8 -1: URL.java
+51 -1: (JILjava/time/ZoneOffset;)Ljava/time/LocalDateTime;
+25 -1: Lsun/invoke/util/Wrapper;
+19 -1: LauncherHelper.java
+10 -1: invoke_L_V
+5 -1: index
+30 -1: sun/security/x509/X509CertImpl
+8 -1: addClass
+46 -1: (I)Ljava/lang/invoke/LambdaForm$NamedFunction;
+20 -1: refKindIsConstructor
+10 -1: getFieldAt
+5 -1: log10
+13 -1: GetPerfAction
+15 -1: isLetterOrDigit
+27 -1: hasCheckedSpecialAttributes
+25 -1: MapReduceEntriesToIntTask
+17 -1: probeHomeLocation
+9 -1: image/png
+19 -1: checkSpreadArgument
+12 -1: LinkedKeySet
+13 -1: removeMapping
+39 -1: sun/security/util/SecurityConstants$AWT
+9 -1: MAX_RADIX
+28 -1: (F)Ljava/lang/StringBuilder;
+11 -1: ([C[B[I[I)Z
+36 -1: (Ljava/lang/Class;)Ljava/lang/Class;
+32 -1: java/lang/invoke/MagicLambdaImpl
+6 -1: monday
+16 -1: closeClassLoader
+41 -1: (Ljava/util/concurrent/locks/Condition;)I
+8 -1: reversed
+27 -1: sun/util/calendar/Gregorian
+49 -1: (Lsun/nio/cs/FastCharsetProvider;)Ljava/util/Map;
+42 -1: (Ljava/lang/String;Ljava/lang/Throwable;)V
+18 -1: java/util/Calendar
+8 -1: vmtarget
+17 -1: TREEIFY_THRESHOLD
+41 -1: (Ljava/util/concurrent/locks/Condition;)Z
+9 -1: deadChild
+8 -1: EntrySet
+5 -1: log1p
+58 -1: (Ljava/lang/String;Ljava/lang/String;Ljava/util/HashMap;)V
+9 -1: pageCount
+14 -1: slotToArgTable
+24 -1: toMethodDescriptorString
+25 -1: ([B)Ljava/nio/ByteBuffer;
+21 -1: twoToTheDoubleScaleUp
+32 -1: sun/misc/URLClassPath$FileLoader
+40 -1: (Ljava/util/List<Ljava/lang/String;>;Z)V
+6 -1: cache1
+6 -1: cache2
+27 -1: Ljava/net/URLStreamHandler;
+20 -1: Detect premature EOF
+94 -1: (Ljava/io/OutputStream;Ljava/lang/Object;Ljava/nio/charset/Charset;)Lsun/nio/cs/StreamEncoder;
+12 -1: NF_checkBase
+20 -1: (Ljava/nio/Buffer;)V
+45 -1: <E:Ljava/lang/Object;>Ljava/util/Vector<TE;>;
+4 -1: Sync
+12 -1: H_UNRESERVED
+32 -1: (Ljava/util/Map;)Ljava/util/Set;
+32 -1: (Ljava/util/Map$Entry<TK;TV;>;)Z
+25 -1: java/util/Locale$Category
+8 -1: receiver
+9 -1: MAX_VALUE
+4 -1: .RSA
+25 -1: Ljava/net/URLClassLoader;
+15 -1: Terminator.java
+26 -1: (Ljava/lang/String;TV;)TV;
+4 -1: null
+76 -1: (Ljava/lang/String;Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;)V
+9 -1: checkCast
+39 -1: ()Ljava/lang/AssertionStatusDirectives;
+15 -1: declaredMethods
+5 -1: clazz
+21 -1:    Retention policy: 
+20 -1: spreadArgElementType
+9 -1: WORD_MASK
+27 -1: ([IILjava/io/InputStream;)I
+11 -1: getMethodAt
+31 -1: (Ljava/net/URL;Ljava/net/URL;)Z
+13 -1: getLocaleName
+14 -1: isEnumConstant
+41 -1: (Ljava/lang/String;)Ljava/nio/CharBuffer;
+16 -1: newInvokeSpecial
+26 -1: (Ljava/nio/ByteBuffer;IS)V
+22 -1: sun/misc/JavaNioAccess
+184 -1: (Ljava/security/DomainCombiner;Ljava/lang/Class;Ljava/security/AccessControlContext;Ljava/security/AccessControlContext;[Ljava/security/Permission;)Ljava/security/AccessControlContext;
+96 -1: (Ljava/lang/invoke/MethodHandle;ILjava/lang/invoke/MethodHandle;)Ljava/lang/invoke/MethodHandle;
+5 -1: ([S)I
+30 -1: java/security/AccessController
+37 -1: sun/misc/Launcher$SharedArchiveLoader
+4 -1: pack
+5 -1: ([S)V
+51 -1: failure       before throwing exception, dump stack
+10 -1: dayOfMonth
+6 -1: CENSIG
+28 -1: java/util/function/Predicate
+26 -1: Malformed \\uxxxx encoding.
+9 -1: initIndex
+11 -1: invoke_LL_L
+23 -1: ConcurrentWeakInternSet
+14 -1: java/lang/Void
+42 -1: java/lang/String$CaseInsensitiveComparator
+38 -1: Ljava/lang/invoke/LambdaForm$Compiled;
+34 -1: java/util/Collections$SingletonSet
+142 -1: (Ljava/lang/invoke/MethodHandle;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/Object;Ljava/lang/Class;)Ljava/lang/invoke/CallSite;
+35 -1: too many bootstrap method arguments
+17 -1: maxDelimCodePoint
+13 -1: previousIndex
+3 -1: pop
+6 -1: CENSIZ
+7 -1: ([Z[Z)Z
+11 -1: invoke_LL_V
+3 -1: pos
+33 -1: java/nio/file/WatchEvent$Modifier
+3 -1: pow
+12 -1: nextClearBit
+15 -1: Dictionary.java
+27 -1: sun/reflect/CallerSensitive
+13 -1: signatureType
+10 -1: dstSavings
+13 -1: UnicodeLittle
+16 -1: America/New_York
+82 -1: (Lsun/util/locale/BaseLocale;Lsun/util/locale/LocaleExtensions;)Ljava/util/Locale;
+25 -1: referenceKindIsConsistent
+62 -1: Ljava/nio/Buffer;Ljava/lang/Comparable<Ljava/nio/LongBuffer;>;
+4 -1: INTS
+11 -1: LOAD_FACTOR
+40 -1: sun/reflect/DelegatingMethodAccessorImpl
+16 -1: accumulateAndGet
+21 -1: (B)Ljava/lang/String;
+6 -1: UNSAFE
+13 -1: resolveOrFail
+21 -1: MapReduceMappingsTask
+5 -1: arity
+77 -1: (Ljava/io/FileDescriptor;ZZLjava/lang/Object;)Ljava/nio/channels/FileChannel;
+5 -1: value
+61 -1: Ljava/util/concurrent/ConcurrentHashMap$EntrySetView<TK;TV;>;
+6 -1: forJar
+52 -1: <T:Ljava/lang/Object;>Ljava/lang/ref/Reference<TT;>;
+21 -1: CREATE_ACC_PERMISSION
+28 -1: sun/misc/NativeSignalHandler
+11 -1: defineClass
+5 -1: match
+93 -1: (Ljava/util/zip/ZipFile;Ljava/util/zip/ZipFile$ZipFileInputStream;Ljava/util/zip/Inflater;I)V
+11 -1: Double.java
+30 -1: America/Argentina/Buenos_Aires
+8 -1: previous
+37 -1: (Ljava/io/Writer;Ljava/lang/String;)V
+9 -1: Synthetic
+18 -1: isVMAnonymousClass
+62 -1: ([Ljava/lang/Object;Ljava/lang/Object;Ljava/util/Comparator;)I
+22 -1: (JI)Ljava/lang/String;
+49 -1: (Ljava/lang/CharSequence;II)Ljava/io/PrintStream;
+52 -1: ([Ljava/lang/Thread;)[[Ljava/lang/StackTraceElement;
+12 -1: setDayOfWeek
+11 -1: getManifest
+30 -1: java/util/Locale$FilteringMode
+54 -1: (Ljava/lang/String;)Lsun/util/calendar/CalendarSystem;
+12 -1: nextHashCode
+19 -1: ()Ljava/io/Console;
+42 -1: AccessControlContext invoking the Combiner
+19 -1: shouldBeInitialized
+17 -1: invocationCounter
+21 -1: IMPLEMENTATION_VENDOR
+7 -1: chararr
+60 -1: (Ljava/lang/String;)Ljava/util/Iterator<Ljava/lang/String;>;
+11 -1: asCollector
+52 -1: (ILjava/lang/CharSequence;)Ljava/lang/StringBuilder;
+9 -1: exception
+22 -1: newInstanceCallerCache
+20 -1: nativeLibraryContext
+24 -1: MODIFY_THREAD_PERMISSION
+4 -1: WRAP
+19 -1: Method name is null
+32 -1: sun/invoke/util/ValueConversions
+7 -1: APP_TAG
+24 -1: (Ljava/util/Hashtable;)I
+23 -1: METHOD_FORMAL_PARAMETER
+18 -1: inflationThreshold
+24 -1: java/io/DeleteOnExitHook
+3 -1: pst
+13 -1: BITS_PER_WORD
+25 -1: getConstructorAnnotations
+17 -1: CheckedCollection
+24 -1: (Ljava/util/Hashtable;)V
+7 -1: inClass
+5 -1: getFD
+8 -1: readLong
+19 -1: aliases_ISO_8859_13
+19 -1: invokeWithArguments
+19 -1: aliases_ISO_8859_15
+42 -1: ()Ljava/util/concurrent/ConcurrentHashMap;
+8 -1: expandTo
+23 -1: java/text/MessageFormat
+8 -1: classID0
+11 -1: replaceWith
+21 -1: Invalid port number :
+65 -1: (Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodHandle;)V
+19 -1: isGregorianLeapYear
+16 -1: asSpreaderChecks
+11 -1: withInitial
+10 -1: X-UTF-32BE
+57 -1: (Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)V
+12 -1: wrong type: 
+57 -1: (Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Z
+17 -1: sun/misc/Resource
+14 -1: getReadTimeout
+8 -1: safeTrim
+38 -1: DIRECTIONALITY_RIGHT_TO_LEFT_EMBEDDING
+14 -1: isElementIndex
+23 -1: FilterOutputStream.java
+6 -1: (IJI)V
+3 -1: put
+57 -1: (Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/String;
+43 -1: Expecting an absolute path of the library: 
+8 -1: checkRef
+53 -1: (Ljava/lang/String;)Ljava/lang/AbstractStringBuilder;
+74 -1: (Ljava/lang/Class<*>;[Ljava/lang/reflect/Field;)[Ljava/lang/reflect/Field;
+11 -1: user.script
+10 -1: H_ALPHANUM
+17 -1: getCalendarSystem
+48 -1: Ljava/util/concurrent/ConcurrentHashMap<TK;TV;>;
+17 -1: EnsureInitialized
+82 -1: (Ljava/lang/ThreadLocal$ThreadLocalMap;Ljava/lang/ThreadLocal;Ljava/lang/Object;)V
+243 -1: (Ljava/util/concurrent/ConcurrentHashMap$BulkTask;III[Ljava/util/concurrent/ConcurrentHashMap$Node;Ljava/util/concurrent/ConcurrentHashMap$MapReduceMappingsToIntTask;Ljava/util/function/ToIntBiFunction;ILjava/util/function/IntBinaryOperator;)V
+26 -1: getAnnotatedExceptionTypes
+10 -1: validIndex
+6 -1: ([CC)I
+8 -1: overflow
+5 -1: getID
+93 -1: (Ljava/io/InputStream;Ljava/lang/Object;Ljava/nio/charset/Charset;)Lsun/nio/cs/StreamDecoder;
+22 -1: java/util/StringJoiner
+9 -1: putFields
+18 -1: USE_SHARED_ARCHIVE
+8 -1: thursday
+8 -1: nanoTime
+59 -1: (Lsun/reflect/annotation/AnnotationType;Ljava/lang/Class;)V
+69 -1: (Ljava/nio/charset/CoderResult$Cache;I)Ljava/nio/charset/CoderResult;
+36 -1: (J)Ljava/lang/AbstractStringBuilder;
+16 -1: java/util/Arrays
+6 -1: ([CC)V
+25 -1: registerAsParallelCapable
+4 -1: slot
+24 -1: java/net/URLConnection$1
+6 -1: koi8-r
+19 -1:  should be of type 
+46 -1: java/util/concurrent/ConcurrentHashMap$TreeBin
+6 -1: koi8-u
+34 -1: data type scale not a power of two
+53 -1: Ljava/util/Map<Ljava/lang/String;Ljava/lang/Object;>;
+21 -1: AccessibleObject.java
+53 -1: <E:Ljava/lang/Object;>()Ljava/util/NavigableSet<TE;>;
+88 -1: (Ljava/util/List;Ljava/util/Collection;Ljava/util/Locale$FilteringMode;)Ljava/util/List;
+17 -1: getAnnotationType
+36 -1: Ljava/util/HashMap$TreeNode<TK;TV;>;
+14 -1: declaringClass
+10 -1: read,write
+5 -1: getId
+10 -1: BIG_ENDIAN
+20 -1: PolymorphicSignature
+16 -1: comparingByValue
+67 -1: (ILjava/lang/invoke/MethodType;)[Ljava/lang/invoke/LambdaForm$Name;
+19 -1: java/util/Hashtable
+20 -1: getUnicodeLocaleKeys
+27 -1: ()Ljava/util/LinkedHashMap;
+51 -1: (Ljava/lang/CharSequence;)Ljava/lang/StringBuilder;
+30 -1: java/util/HashMap$HashIterator
+22 -1: (IZ)Ljava/lang/String;
+5 -1: yield
+34 -1: java/lang/Throwable$SentinelHolder
+62 -1: (Ljava/lang/invoke/MethodType;ZI)Ljava/lang/invoke/LambdaForm;
+5 -1: (FD)F
+17 -1: java/util/SubList
+24 -1: (Ljava/io/PrintStream;)V
+7 -1: LF.zero
+25 -1: java/util/StringTokenizer
+15 -1: ISO8859_15_FDIS
+11 -1: powerOfTwoD
+11 -1: powerOfTwoF
+22 -1: WeakHashMapSpliterator
+15 -1: refKindIsGetter
+12 -1: setRawOffset
+11 -1: setProperty
+23 -1: (Ljava/lang/Object;IB)V
+45 -1: (ILjava/lang/Object;)Ljava/util/HashMap$Node;
+13 -1: applyAsDouble
+83 -1: (Lsun/misc/URLClassPath$FileLoader;Ljava/lang/String;Ljava/net/URL;Ljava/io/File;)V
+19 -1: MethodAccessor.java
+9 -1: WALL_TIME
+7 -1: INVOKES
+13 -1: java.ext.dirs
+9 -1: getStatic
+56 -1: (Ljava/lang/String;Ljava/lang/Object;)Ljava/lang/String;
+42 -1: (Ljava/util/function/UnaryOperator<TE;>;)V
+27 -1: java/lang/Class$MethodArray
+10 -1: H_USERINFO
+19 -1: PostVMInitHook.java
+7 -1: running
+32 -1: Warning: passing argument as-is 
+13 -1: EntryIterator
+22 -1: NF_checkSpreadArgument
+46 -1: ([DLjava/util/function/IntToDoubleFunction;I)V
+7 -1: .<init>
+13 -1: mappingLength
+20 -1: implOnMalformedInput
+53 -1: (Ljava/lang/String;ILjava/lang/reflect/Executable;I)V
+26 -1: cannot make variable arity
+18 -1: SharedSecrets.java
+27 -1: (Ljava/io/InputStream;IZ)[B
+9 -1: Asia/Gaza
+55 -1: Ljava/util/Map<Ljava/lang/String;Ljava/lang/Class<*>;>;
+5 -1: NTLM 
+19 -1: defaultCenturyStart
+18 -1: addElapsedTimeFrom
+38 -1: (Lsun/misc/Cleaner;)Lsun/misc/Cleaner;
+44 -1: (Ljava/io/OutputStream;ZLjava/lang/String;)V
+22 -1: (Ljava/lang/Object;B)V
+6 1: [LBar;
+7 -1: classes
+23 -1: java/net/URLClassLoader
+17 -1: sun/misc/Launcher
+163 -1: ([Ljava/util/concurrent/ConcurrentHashMap$Node<TK;TV;>;Ljava/util/concurrent/ConcurrentHashMap$Node<TK;TV;>;)[Ljava/util/concurrent/ConcurrentHashMap$Node<TK;TV;>;
+12 -1: updateAndGet
+90 -1: (Ljava/net/URL;Ljava/lang/String;Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;)V
+9 -1: increment
+27 -1: (Ljava/lang/CharSequence;)V
+16 -1: Ljava/io/Reader;
+27 -1: java/io/PushbackInputStream
+6 -1: (JFZ)V
+17 -1: getAppClassLoader
+35 -1: sun/reflect/generics/tree/Signature
+9 -1: elementAt
+27 -1: (Ljava/lang/CharSequence;)Z
+10 -1: readDouble
+37 -1: ([B)Ljava/nio/charset/CharsetEncoder;
+46 -1: (Ljava/lang/ThreadGroup;Ljava/lang/Runnable;)V
+4 -1: park
+36 -1: java/lang/NegativeArraySizeException
+49 -1: (Ljava/lang/invoke/MethodType;Ljava/lang/Class;)Z
+121 -1: <T:Ljava/lang/Object;U::Ljava/lang/Comparable<-TU;>;>(Ljava/util/function/Function<-TT;+TU;>;)Ljava/util/Comparator<TT;>;
+101 -1: (Ljava/lang/annotation/Annotation;Ljava/lang/annotation/Annotation;)Ljava/lang/annotation/Annotation;
+12 -1: .$|()[{^?*+\\
+6 -1: manRef
+3 -1: 437
+15 -1: newStringUnsafe
+15 -1: constantPoolOop
+10 -1: getPackage
+24 -1: FastCharsetProvider.java
+18 -1: getAnnotationBytes
+187 -1: (Ljava/security/DomainCombiner;Ljava/lang/Class<*>;Ljava/security/AccessControlContext;Ljava/security/AccessControlContext;[Ljava/security/Permission;)Ljava/security/AccessControlContext;
+33 -1: Cannot suppress a null exception.
+27 -1: sun/nio/cs/StandardCharsets
+33 -1: (BB)Ljava/lang/invoke/MemberName;
+61 -1: ([Ljava/lang/ClassValue$Entry;ILjava/lang/ClassValue$Entry;)I
+10 -1: X-UTF-32LE
+5 -1: toMap
+89 -1: (Ljava/lang/Class<*>;Ljava/util/List<Ljava/lang/Class<*>;>;)Ljava/lang/invoke/MethodType;
+46 -1: ([Ljava/lang/Object;IILjava/util/Comparator;)V
+66 -1: ([Ljava/lang/reflect/Constructor;)[Ljava/lang/reflect/Constructor;
+22 -1: ()Ljava/nio/ByteOrder;
+20 -1: isMethodHandleInvoke
+25 -1: sun/net/www/URLConnection
+89 -1: Ljava/util/concurrent/ConcurrentHashMap<Ljava/lang/String;Ljava/lang/invoke/LambdaForm;>;
+49 -1: (Ljava/nio/charset/Charset;FFLjava/lang/String;)V
+17 -1: MIN_LOW_SURROGATE
+23 -1: AbstractCollection.java
+19 -1: (Ljava/util/Date;)I
+19 -1: (Ljava/util/Date;)J
+12 -1: cldrdata.jar
+4 -1: path
+77 -1: (Ljava/lang/Class;Ljava/lang/String;Ljava/lang/Class;IILjava/lang/String;[B)V
+6 -1: MS1250
+37 -1: setJavaSecurityProtectionDomainAccess
+11 -1: isDelimiter
+5 -1: char0
+6 -1: MS1251
+5 -1: char1
+16 -1: getJavaNetAccess
+6 -1: MS1252
+6 -1: MS1253
+6 -1: MS1254
+51 -1: (Ljava/lang/Class;)Ljava/security/ProtectionDomain;
+6 -1: MS1257
+93 -1: (Ljava/lang/String;Ljava/nio/ByteBuffer;Ljava/security/ProtectionDomain;)Ljava/lang/Class<*>;
+10 -1: access$300
+5 -1: (JZ)C
+19 -1: (Ljava/util/Date;)Z
+5 -1: (JZ)D
+26 -1: java/lang/Character$Subset
+10 -1: access$302
+5 -1: (JZ)F
+9 -1: emptyList
+5 -1: (JZ)I
+5 -1: (JZ)J
+23 -1: (Ljava/lang/Runnable;)V
+27 -1: java/lang/invoke/MemberName
+29 -1: ()Ljava/util/Comparator<TT;>;
+18 -1: retrieveDirectives
+7 -1: ([F[F)Z
+40 -1: (Lsun/misc/URLClassPath;Ljava/net/URL;)V
+5 -1: (JZ)S
+40 -1: (Ljava/util/function/IntUnaryOperator;)I
+5 -1: (JZ)V
+16 -1: parseAnnotations
+21 -1: (C)Ljava/lang/String;
+73 -1: <K:Ljava/lang/Object;V:Ljava/lang/Object;>(TK;TV;)Ljava/util/Map<TK;TV;>;
+15 -1: charset encoder
+17 -1: getDomainCombiner
+9 -1: EmptyList
+15 -1: java.vm.version
+19 -1: getResourceAsStream
+94 -1: Ljava/lang/ThreadLocal<Ljava/lang/ref/SoftReference<Ljava/lang/StringCoding$StringEncoder;>;>;
+26 -1: java/util/HashMap$TreeNode
+22 -1: (Ljava/util/HashMap;)V
+23 -1: sun/misc/URLClassPath$1
+65 -1: ([Ljava/net/URL;Ljava/lang/ClassLoader;)Ljava/net/URLClassLoader;
+20 -1: Hashtable Enumerator
+23 -1: sun/misc/URLClassPath$2
+10 -1: Array.java
+8 -1: FT_LIMIT
+24 -1: ()[Ljava/lang/Throwable;
+23 -1: sun/misc/URLClassPath$3
+14 -1: java/io/Writer
+5 -1: chars
+76 -1: <K:Ljava/lang/Object;V:Ljava/lang/Object;>()Ljava/util/NavigableMap<TK;TV;>;
+6 -1:  final
+5 -1: error
+34 -1: java/lang/ApplicationShutdownHooks
+29 -1: Lsun/launcher/LauncherHelper;
+30 -1: ()Ljava/util/Enumeration<TE;>;
+47 -1: (Ljava/util/List;)Ljava/lang/invoke/MethodType;
+9 -1: scriptKey
+5 -1: BYTES
+12 -1: getException
+69 -1: (Ljava/lang/Class<*>;Ljava/lang/Object;)Ljava/lang/invoke/MethodType;
+14 -1: Illegal Load: 
+189 -1: (Ljava/util/concurrent/ConcurrentHashMap$BulkTask;III[Ljava/util/concurrent/ConcurrentHashMap$Node;Ljava/util/concurrent/ConcurrentHashMap$ReduceValuesTask;Ljava/util/function/BiFunction;)V
+66 -1: java/util/concurrent/ConcurrentHashMap$MapReduceMappingsToLongTask
+29 -1: getJavaIOFileDescriptorAccess
+11 -1: toCodePoint
+15 -1: setCreationTime
+18 -1: NULL_CAUSE_MESSAGE
+8 -1: elements
+32 -1: ()Ljava/nio/charset/CoderResult;
+8 -1: utf-32be
+7 -1: addDate
+4 -1: Cast
+23 -1: sun/misc/JavaLangAccess
+8 -1: 0{1,12}$
+27 -1: (Ljava/util/ArrayList;III)V
+11 -1: lastIndexOf
+14 -1: getCodeSources
+53 -1: (Lsun/util/calendar/CalendarDate;Ljava/lang/String;)V
+17 -1: cachedConstructor
+7 -1: forName
+74 -1: (Ljava/util/function/ToLongFunction;Ljava/lang/Object;Ljava/lang/Object;)I
+47 -1: (Ljava/lang/Object;)Lsun/reflect/FieldAccessor;
+8 -1: getDebug
+18 -1: currentClassLoader
+49 -1: Illegal leading minus sign on unsigned string %s.
+20 -1: toLowerCaseCharArray
+38 -1: java/util/concurrent/ConcurrentHashMap
+8 -1: isHidden
+92 -1: (Ljava/lang/Thread;ILjava/lang/Object;Ljava/lang/Thread;JJJJ[Ljava/lang/StackTraceElement;)V
+29 -1: java/lang/ArithmeticException
+26 -1: (Ljava/io/OutputStream;Z)V
+66 -1: (Ljava/lang/String;Ljava/lang/Object;)Ljava/lang/RuntimeException;
+38 -1: Ljava/util/Map<Ljava/lang/String;TT;>;
+16 -1: getFindClassTime
+34 -1: ([J)Ljava/util/Spliterator$OfLong;
+24 -1: UnmodifiableNavigableSet
+32 -1: java/lang/ClassNotFoundException
+3 -1: \\n 
+6 -1: SEALED
+14 -1: Flushable.java
+3 -1: HST
+24 -1: (Ljava/lang/Object;JII)Z
+13 -1: toSecondOfDay
+16 -1: thenComparingInt
+26 -1: java/lang/NoSuchFieldError
+18 -1: java/util/Locale$1
+25 -1: [Ljava/util/HashMap$Node;
+75 -1: ([Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
+11 -1: x-mswin-936
+32 -1: java/lang/management/MemoryUsage
+25 -1: (JS)Ljava/nio/ByteBuffer;
+25 -1: java/lang/ref/Finalizer$1
+25 -1: java/lang/ref/Finalizer$2
+21 -1: (Ljava/util/BitSet;)V
+25 -1: java/lang/ref/Finalizer$3
+83 -1: <T:Ljava/lang/Object;>(Ljava/util/Collection<+TT;>;Ljava/util/Comparator<-TT;>;)TT;
+24 -1: sun/nio/ch/Interruptible
+21 -1: (Ljava/util/BitSet;)Z
+72 -1: ([Ljava/security/ProtectionDomain;Ljava/security/AccessControlContext;)V
+54 -1: Ljava/util/AbstractSet<Ljava/util/Map$Entry<TK;TV;>;>;
+34 -1: getConstructorParameterAnnotations
+4 -1: name
+92 -1: <E:Ljava/lang/Enum<TE;>;>Ljava/lang/Object;Ljava/lang/Comparable<TE;>;Ljava/io/Serializable;
+11 -1: FORM_OFFSET
+13 -1: getAliasTable
+5 -1: (DD)D
+23 -1: reflectionFactoryAccess
+221 -1: (Ljava/util/concurrent/ConcurrentHashMap$BulkTask;III[Ljava/util/concurrent/ConcurrentHashMap$Node;Ljava/util/concurrent/ConcurrentHashMap$MapReduceValuesTask;Ljava/util/function/Function;Ljava/util/function/BiFunction;)V
+59 -1: (Ljava/lang/String;[Ljava/lang/String;)Ljava/nio/file/Path;
+6 -1: DELETE
+10 -1: returnType
+5 -1: (DD)I
+51 -1: ()Lsun/reflect/generics/repository/FieldRepository;
+8 -1: delegate
+12 -1: OTHER_LETTER
+18 -1: getTransitionIndex
+3 -1: HUP
+10 -1: (IIII[CI)V
+10 -1: ISO_8859-1
+17 -1: ArrayEncoder.java
+10 -1: ISO_8859-2
+34 -1: java/lang/reflect/AnnotatedElement
+10 -1: ISO_8859-4
+27 -1: sun/nio/cs/UTF_16LE$Decoder
+10 -1: ISO_8859-5
+13 -1: prefetchWrite
+9 -1: getFloatB
+10 -1: ISO_8859-7
+37 -1: (I)Ljava/lang/invoke/LambdaForm$Name;
+10 -1: ISO_8859-9
+10 -1: getActions
+11 -1: negateExact
+10 -1: isAbstract
+9 -1: getFloatL
+29 -1: java/lang/ClassValue$Identity
+14 -1: java/io/Reader
+8 -1: getOwner
+24 -1: java/lang/AssertionError
+17 -1: MethodHandle.java
+19 -1: classRedefinedCount
+10 -1: cachedYear
+15 -1: getAndIncrement
+26 -1: java.protocol.handler.pkgs
+14 -1: cleanSomeSlots
+27 -1: java/util/Spliterator$OfInt
+31 -1: getRawExecutableTypeAnnotations
+20 -1: ensureInitialization
+7 -1: os.arch
+57 -1: (Ljava/security/cert/CertPath;Ljava/security/Timestamp;)V
+21 -1: UNSAFE_COPY_THRESHOLD
+20 -1: toUnsignedBigInteger
+82 -1: (Ljava/util/concurrent/locks/Condition;)Ljava/util/Collection<Ljava/lang/Thread;>;
+15 -1: Reflection.java
+12 -1: decryptBlock
+3 -1: \\r 
+8 -1: newArray
+8 -1: Category
+36 -1: java/lang/reflect/GenericDeclaration
+22 -1: (Ljava/lang/String;Z)V
+8 -1: suspend0
+10 -1: getSigners
+22 -1: (Ljava/lang/String;Z)Z
+31 -1: Unable to create temporary file
+117 -1: <T:Ljava/lang/Object;>(Ljava/lang/ClassValue<TT;>;Ljava/lang/ClassValue$Entry<TT;>;)Ljava/lang/ClassValue$Entry<TT;>;
+17 -1: channelsAvailable
+9 -1: Date.java
+13 -1: toIndex < 0: 
+18 -1: mark > position: (
+11 -1: loadConvert
+4 -1: july
+42 -1: (Ljava/math/BigInteger;)Ljava/lang/String;
+6 -1: enable
+47 -1: (Ljava/util/zip/ZipEntry;)Ljava/io/InputStream;
+6 -1: unpack
+13 -1: setDayOfMonth
+19 -1: name can't be empty
+16 -1: getExtensionKeys
+15 -1: getAndDecrement
+36 -1: Ljava/lang/ClassValue$ClassValueMap;
+38 -1: (Ljava/lang/Class;Ljava/lang/String;)V
+11 -1: csISOlatin0
+9 -1: retention
+23 -1: system protocol handler
+9 -1: nullsLast
+15 -1: refKindIsStatic
+3 -1:  (\n
+48 -1: sun/launcher/LauncherHelper$ResourceBundleHolder
+29 -1: ()Ljava/util/LinkedList<TE;>;
+11 -1: csISOlatin9
+31 -1: ([CII)Ljava/lang/StringBuilder;
+29 -1: (II)Ljava/lang/StringBuilder;
+7 -1: pdcache
+4 -1: june
+12 -1: ;/?:@&=+$,[]
+141 -1: ([Ljava/lang/invoke/LambdaForm$Name;[Ljava/lang/invoke/LambdaForm$Name;Ljava/lang/invoke/LambdaForm$Name;)[Ljava/lang/invoke/LambdaForm$Name;
+32 -1: ()[Ljava/util/WeakHashMap$Entry;
+110 -1: (Ljava/lang/Class<*>;ILjava/lang/Class<*>;Ljava/lang/String;Ljava/lang/Object;)Ljava/lang/invoke/MethodHandle;
+81 -1: (Lsun/reflect/annotation/AnnotationType;Lsun/reflect/annotation/AnnotationType;)Z
+46 -1: String value %s exceeds range of unsigned int.
+6 -1: close0
+6 -1: (JDZ)V
+160 -1: <T:Ljava/lang/Object;>Ljava/lang/Object;Ljava/io/Serializable;Ljava/lang/reflect/GenericDeclaration;Ljava/lang/reflect/Type;Ljava/lang/reflect/AnnotatedElement;
+12 -1: LinkedValues
+24 -1: java/nio/HeapCharBufferR
+23 -1: jvmVersionInfoAvailable
+16 -1: classLoaderDepth
+33 -1: (Lsun/nio/ch/DirectBuffer;IIIII)V
+32 -1: java/nio/file/attribute/FileTime
+50 -1: java/util/concurrent/ConcurrentHashMap$CounterCell
+73 -1: (Lsun/misc/URLClassPath$JarLoader;Lsun/misc/JarIndex;)Lsun/misc/JarIndex;
+60 -1: (Ljava/net/URL;Ljava/lang/String;)Ljava/security/CodeSource;
+25 -1: Ljava/lang/ref/Finalizer;
+12 -1: utf-32be-bom
+40 -1: (Ljava/lang/String;ILjava/lang/Object;)V
+9 -1: THROW_UCS
+23 -1: java/util/AbstractMap$1
+23 -1: java/util/AbstractMap$2
+10 -1: x-utf-32be
+4 -1: (S)B
+60 -1: (Ljava/lang/invoke/MemberName;)Ljava/lang/invoke/LambdaForm;
+20 -1: ResourceBundleHolder
+4 -1: (S)I
+13 -1: getSuppressed
+17 -1: jdk_micro_version
+4 -1: (S)J
+9 -1: isNumeric
+10 -1: variantKey
+8 -1: utf-32le
+47 -1: java/util/concurrent/ConcurrentHashMap$MapEntry
+25 -1: ()Ljava/lang/Class<-TT;>;
+6 -1: closed
+4 -1: (S)S
+15 -1: setStandardTime
+10 -1: ShortCache
+4 -1: (S)V
+40 -1: sun/net/www/MessageHeader$HeaderIterator
+17 -1: jdk_major_version
+8 -1: FXHelper
+6 -1: CENTIM
+19 -1: java/security/Guard
+46 -1: java.lang.invoke.MethodHandle.DUMP_CLASS_FILES
+4 -1: ENUM
+27 -1: Ljava/lang/SecurityManager;
+39 -1: ([Ljava/lang/Class;[Ljava/lang/Class;)Z
+11 -1: getFieldAt0
+12 -1: user.variant
+28 -1: (Ljava/io/DataInputStream;)V
+44 -1: ([JLjava/util/function/LongBinaryOperator;)V
+7 -1: getRoot
+3 -1:  + 
+16 -1: identityHashCode
+25 -1: java/security/Permissions
+16 -1: Ljava/net/Proxy;
+23 -1: java/io/ExpiringCache$1
+5 -1:  more
+10 -1: formatList
+49 -1: (Ljava/lang/String;)Lsun/launcher/LauncherHelper;
+29 -1: Relative path in absolute URI
+11 -1: checkMapped
+8 -1: Checksum
+8 -1: " Radix:
+9 -1: getAndAdd
+9 -1: implReady
+16 -1: SynchronizedList
+30 -1: [Ljava/lang/StackTraceElement;
+5 -1: right
+13 -1: UTF_16BE.java
+4 -1: HEAD
+11 -1: isInvocable
+6 -1: ENDCOM
+15 -1: getPropertiesEx
+6 -1: Unsafe
+7 -1: IBM-819
+37 -1: : 0 <= i2 && i2 < names.length: 0 <= 
+19 -1: filterAndAddHeaders
+22 -1: nativeParkEventPointer
+18 -1: checkPositionIndex
+13 -1: invalid url: 
+25 -1:  out of range from input 
+9 -1: loadClass
+12 -1: encodingName
+9 -1: x-JIS0208
+38 -1: (Ljava/lang/Class;Ljava/lang/Object;)Z
+28 -1: (I)Ljava/lang/reflect/Field;
+17 -1: getJvmVersionInfo
+6 -1: LIJFDV
+21 -1: (D)Ljava/lang/String;
+7 -1: oomeMsg
+30 -1: java/io/InvalidObjectException
+25 -1: java/io/FilterInputStream
+32 -1: Ljava/net/ContentHandlerFactory;
+13 -1: toUnsignedInt
+17 -1: reconstitutionPut
+37 -1: (Ljava/lang/Object;)Ljava/lang/Class;
+14 -1: getContentType
+43 -1: java/util/Collections$SynchronizedSortedSet
+24 -1: (II)Ljava/nio/file/Path;
+25 -1: JAVAFX_APPLICATION_MARKER
+29 -1: (IC)Ljava/lang/StringBuilder;
+13 -1: java/util/Set
+10 -1: clearError
+64 -1: (Ljava/lang/invoke/MethodType;[I)Ljava/lang/invoke/MethodHandle;
+25 -1: java/io/FileInputStream$1
+8 -1: getFirst
+36 -1: (Lsun/reflect/ConstructorAccessor;)V
+84 -1: (Ljava/util/NavigableMap;Ljava/lang/Class;Ljava/lang/Class;)Ljava/util/NavigableMap;
+42 -1: (Ljava/lang/CharSequence;)Ljava/io/Writer;
+52 -1: ([Ljava/lang/Class<*>;)Ljava/lang/invoke/MethodType;
+6 -1: (IFI)V
+62 -1: <E:Ljava/lang/Object;>Ljava/lang/Object;Ljava/util/Queue<TE;>;
+17 -1: getHeaderFieldInt
+16 -1: CheckedSortedMap
+39 -1: (ZILjava/lang/String;)Ljava/lang/Class;
+10 -1: getterName
+10 -1: Asia/Tokyo
+4 -1: Node
+7 -1: rotate1
+13 -1: Stream closed
+7 -1: rotate2
+9 -1: checkExec
+17 -1: NF_checkExactType
+18 -1: ReverseComparator2
+18 -1: arrayElementGetter
+97 -1: (Ljava/util/ArrayPrefixHelpers$DoubleCumulateTask;Ljava/util/function/DoubleBinaryOperator;[DII)V
+9 -1: iso8859-1
+9 -1: iso8859-2
+9 -1: iso8859-4
+9 -1: iso8859-5
+9 -1: iso8859-7
+16 -1:  getPermissions 
+9 -1: iso8859-9
+9 -1: fromClass
+17 -1:  with modifiers "
+8 -1: isBooted
+24 -1: getCommonPoolParallelism
+10 -1: initialize
+47 -1: <T:Ljava/lang/Object;>(TT;)Ljava/util/Set<TT;>;
+17 -1: checkElementIndex
+14 -1: openConnection
+47 -1: (Ljava/lang/Thread;Lsun/nio/ch/Interruptible;)V
+12 -1: isAuthorized
+17 -1: ReduceEntriesTask
+7 -1: command
+24 -1: ArithmeticException.java
+24 -1: ensureOpenOrZipException
+67 -1: (Lsun/util/calendar/CalendarDate;I)Lsun/util/calendar/CalendarDate;
+39 -1: Ljava/lang/invoke/MethodHandles$Lookup;
+31 -1: Enclosing constructor not found
+34 -1: ()Lsun/util/calendar/BaseCalendar;
+25 -1: (JLjava/lang/Object;JJJ)V
+12 -1:  > toIndex: 
+22 -1: LocaleObjectCache.java
+19 -1: sun/misc/Launcher$1
+31 -1: java/util/HashMap$EntryIterator
+8 -1: contains
+60 -1: <K:Ljava/lang/Object;V:Ljava/lang/Object;>Ljava/lang/Object;
+53 -1: (I[Ljava/lang/Class<*>;)Ljava/lang/invoke/MethodType;
+19 -1: java/io/PrintStream
+42 -1: java/lang/Math$RandomNumberGeneratorHolder
+100 -1: <K:Ljava/lang/Object;>(I)Ljava/util/concurrent/ConcurrentHashMap$KeySetView<TK;Ljava/lang/Boolean;>;
+14 -1: getCapturedArg
+14 -1: getCodeSigners
+20 -1: mark() not supported
+56 -1: (Lsun/misc/URLClassPath;I)Lsun/misc/URLClassPath$Loader;
+20 -1: java/lang/StrictMath
+14 -1: annotationType
+3 -1: IET
+4 -1: lang
+13 -1: JarEntry.java
+9 -1: ([CIIII)I
+9 -1: canEncode
+5 -1: extra
+30 -1: java/lang/ref/ReferenceQueue$1
+37 -1: java/nio/channels/ReadableByteChannel
+73 -1: (Ljava/util/Map$Entry<Ljava/lang/String;Ljava/io/ExpiringCache$Entry;>;)Z
+24 -1: java/io/BufferedReader$1
+10 -1: x-utf-32le
+16 -1: methodDescriptor
+25 -1: (IJ)Ljava/nio/ByteBuffer;
+18 -1: getSystemResources
+46 -1: (Ljava/lang/String;I)Ljava/util/regex/Pattern;
+46 -1: (Ljava/net/URL;)Lsun/misc/URLClassPath$Loader;
+20 -1: calendars.properties
+25 -1: implOnUnmappableCharacter
+12 -1: signers_name
+40 -1: (ZILjava/util/Locale;)Ljava/lang/String;
+8 -1: (TE;)TE;
+50 -1: ()Lsun/util/locale/provider/LocaleProviderAdapter;
+11 -1: key is null
+7 -1: encrypt
+21 -1: millisUntilExpiration
+55 -1: Unable to parse property sun.reflect.inflationThreshold
+9 -1: checkExit
+5 -1: SHORT
+43 -1: java/util/Collections$UnmodifiableSortedSet
+10 -1: ISO8859_15
+16 -1: verifyParameters
+24 -1: buildAnnotatedInterfaces
+15 -1: refKindIsSetter
+45 -1: (JLjava/util/function/BiConsumer<-TK;-TV;>;)V
+23 -1: (Ljava/lang/Object;IC)V
+90 -1: <T:Ljava/lang/Object;>(Ljava/lang/ClassValue$Entry<TT;>;)Ljava/lang/ClassValue$Entry<TT;>;
+30 -1: (Ljava/net/URL;)Ljava/net/URI;
+60 -1: (BLjava/lang/Class<*>;Ljava/lang/String;Ljava/lang/Object;)V
+43 -1: (Ljava/util/Collection;Ljava/lang/Object;)I
+44 -1: (Ljava/net/URLConnection;)Ljava/lang/Object;
+17 -1: Stream not marked
+11 -1: targetCheck
+127 -1: (Ljava/lang/Class<*>;ILjava/lang/Class<*>;Ljava/lang/String;Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/invoke/MemberName;
+11 -1: debugString
+112 -1: (Ljava/lang/invoke/MethodHandle;Ljava/lang/Class;Ljava/lang/invoke/MethodHandle;)Ljava/lang/invoke/MethodHandle;
+43 -1: (Ljava/util/Collection;Ljava/lang/Object;)V
+15 -1: NF_staticOffset
+22 -1: CASE_INSENSITIVE_ORDER
+6 -1: unpark
+29 -1: (Ljava/lang/CharSequence;II)I
+59 -1: Ljava/util/concurrent/ConcurrentHashMap$KeySetView<TK;TV;>;
+19 -1: defaultFormatLocale
+7 -1: combine
+58 -1: (Ljava/util/Locale$LocaleKey;)Lsun/util/locale/BaseLocale;
+29 -1: (Ljava/lang/CharSequence;II)V
+35 -1: sun/util/calendar/BaseCalendar$Date
+57 -1: Ljava/util/concurrent/atomic/AtomicReferenceFieldUpdater;
+75 -1: ([Ljava/lang/reflect/Member;[Ljava/lang/String;)[Ljava/lang/reflect/Member;
+15 -1: MethodType_init
+5 -1: (JF)V
+20 -1: AUTOSELECT_FILTERING
+12 -1: invokeStatic
+18 -1: readFileDescriptor
+22 -1: java/lang/Terminator$1
+72 -1: (Ljava/lang/Object;Ljava/util/function/UnaryOperator;)Ljava/lang/Object;
+17 -1: EMPTY_STACK_TRACE
+13 -1: isSamePackage
+32 -1: ()Ljava/security/DomainCombiner;
+10 -1: decodeLoop
+30 -1: DIRECTIONALITY_EUROPEAN_NUMBER
+112 -1: (Ljava/util/List<Ljava/util/Locale$LanguageRange;>;Ljava/util/Collection<Ljava/lang/String;>;)Ljava/lang/String;
+33 -1: (Ljava/util/function/Predicate;)Z
+35 -1: logincontext  login context results
+17 -1: sun/nio/cs/UTF_16
+13 -1: SingletonList
+5 -1:  end=
+7 -1: getURLs
+17 -1: traceInstructions
+22 -1: generateCustomizedCode
+9 -1: NO_CHANGE
+11 -1: Number.java
+49 -1: <T:Ljava/lang/Object;>(ITT;)Ljava/util/List<TT;>;
+19 -1: checkSpecifyHandler
+8 -1: setValue
+30 -1: (Ljava/net/URL;)Ljava/net/URL;
+22 -1: (Ljava/lang/Object;C)V
+19 -1: Negative capacity: 
+24 -1: ArrayStoreException.java
+52 -1: (Ljava/lang/StringBuffer;II)Ljava/lang/StringBuffer;
+14 -1: linkMethodImpl
+42 -1: java/util/InvalidPropertiesFormatException
+4 -1: last
+19 -1: getLocalizedMessage
+65 -1: (Ljava/text/MessageFormat;[Ljava/lang/String;)[Ljava/lang/String;
+61 -1: Ljava/lang/Object;Ljava/util/Enumeration<Ljava/lang/Object;>;
+40 -1: (I[CII)Ljava/lang/AbstractStringBuilder;
+27 -1: java.launcher.opt.datamodel
+29 -1: (Ljava/lang/reflect/Method;)V
+19 -1: SPECIFICATION_TITLE
+123 -1: (Ljava/lang/Class;[Ljava/lang/Class;[Ljava/lang/Class;ILjava/lang/Class;)Lsun/reflect/SerializationConstructorAccessorImpl;
+32 -1: (I[CII)Ljava/lang/StringBuilder;
+29 -1: (Ljava/lang/reflect/Method;)Z
+23 -1: (Z[B)Ljava/lang/String;
+27 -1: sun/nio/cs/Surrogate$Parser
+32 -1: (Ljavax/security/auth/Subject;)Z
+29 -1: ()Ljava/lang/invoke/Invokers;
+7 -1: getPool
+7 -1: textOut
+12 -1: getEntryTime
+14 -1: classModifiers
+47 -1: (Ljava/util/Locale$Category;)Ljava/util/Locale;
+32 -1: getInheritedAccessControlContext
+4 -1: rcbt
+37 -1: (Ljava/lang/Class<*>;Ljava/io/File;)Z
+25 -1: AccessControlContext.java
+22 -1: FileURLConnection.java
+12 -1: NaturalOrder
+34 -1: sun/util/calendar/CalendarSystem$1
+94 -1: ([Ljava/lang/reflect/Method;Ljava/lang/String;[Ljava/lang/Class<*>;)Ljava/lang/reflect/Method;
+17 -1: No enum constant 
+7 -1: ([DII)V
+8 -1: register
+23 -1: FINAL_QUOTE_PUNCTUATION
+27 -1: ACCESS_CLIPBOARD_PERMISSION
+15 -1: verifyConstants
+20 -1: (Ljava/io/Writer;I)V
+45 -1: (Ljava/lang/String;)Ljava/lang/reflect/Field;
+24 -1: linkMethodHandleConstant
+43 -1: (Ljava/io/OutputStream;Ljava/lang/String;)V
+125 -1: <K:Ljava/lang/Object;V:Ljava/lang/Object;>(Ljava/util/Comparator<-TV;>;)Ljava/util/Comparator<Ljava/util/Map$Entry<TK;TV;>;>;
+14 -1: ALL_PERMISSION
+12 -1: createObject
+10 -1: CRC32.java
+14 -1: reservedMemory
+22 -1: ensureCapacityInternal
+11 -1: FormatData_
+9 -1: maxMemory
+27 -1: (I)Ljava/util/ListIterator;
+8 -1: UTF-16BE
+27 -1: AbstractSequentialList.java
+10 -1: access$400
+16 -1: Locale settings:
+10 -1: access$402
+12 -1: HashSet.java
+24 -1: java/lang/Long$LongCache
+30 -1: [Ljava/lang/reflect/Parameter;
+11 -1: single_step
+45 -1: (Ljava/lang/String;)Lsun/security/util/Debug;
+97 -1: (Ljava/lang/invoke/MethodType;Ljava/lang/invoke/LambdaForm;)Ljava/lang/invoke/SimpleMethodHandle;
+16 -1: indexOfBangSlash
+7 -1: region=
+26 -1: ([BII)Ljava/lang/Class<*>;
+8 -1: bitCount
+3 -1: INT
+67 -1: <T:Ljava/lang/Object;>([TT;Ljava/util/function/IntFunction<+TT;>;)V
+35 -1: newGetFloatIllegalArgumentException
+11 -1: ([DII[DII)V
+22 -1: makePreparedLambdaForm
+3 -1:  < 
+35 -1: sun/management/GarbageCollectorImpl
+4 -1: (*)*
+7 -1: getPort
+18 -1: java/io/FileSystem
+7 -1: getNode
+38 -1: (Ljava/lang/Object;I)Ljava/lang/Class;
+36 -1: $SwitchMap$java$util$Locale$Category
+18 -1: securityCheckCache
+5 -1: cdate
+10 -1: childValue
+19 -1: getMainClassFromJar
+70 -1: <T:Ljava/lang/Enum<TT;>;>(Ljava/lang/Class<TT;>;Ljava/lang/String;)TT;
+20 -1: unsuspendSomeThreads
+29 -1: sun.classloader.findClassTime
+11 -1: plusSeconds
+3 -1:  = 
+4 -1: Lock
+7 -1: regions
+38 -1: ()Ljava/lang/reflect/Constructor<TT;>;
+25 -1: parseParameterAnnotations
+20 -1: getSystemGMTOffsetID
+33 -1: [Cc][Oo][Dd][Ee][Bb][Aa][Ss][Ee]=
+37 -1: (Ljava/lang/String;I)Ljava/lang/Byte;
+10 -1: permission
+78 -1: (Ljava/lang/reflect/Constructor;)Lsun/reflect/generics/scope/ConstructorScope;
+28 -1: (Lsun/misc/JavaLangAccess;)V
+26 -1: GET_CLASSLOADER_PERMISSION
+24 -1: (J)Ljava/nio/ByteBuffer;
+20 -1: getUnresolvedActions
+49 -1: (I[BIILsun/security/util/ManifestEntryVerifier;)V
+5 -1: (ZJ)V
+14 -1: isClassOnlyJar
+35 -1: ()[Ljava/util/HashMap$Node<TK;TV;>;
+23 -1: ()Ljava/util/SortedMap;
+29 -1: HistoricallyNamedCharset.java
+30 -1: no leading reference parameter
+8 -1: csCESU-8
+3 -1:  > 
+13 -1: invoke_LLLL_L
+109 -1: <K:Ljava/lang/Object;V:Ljava/lang/Object;>(Ljava/util/NavigableMap<TK;+TV;>;)Ljava/util/NavigableMap<TK;TV;>;
+13 -1: invoke_LLLL_V
+46 -1: (Ljava/lang/Class;Z)[Ljava/lang/reflect/Field;
+5 -1: offer
+12 -1: isoLanguages
+61 -1: (Ljava/io/OutputStream;Ljava/lang/String;Ljava/lang/String;)V
+44 -1: sun/reflect/BootstrapConstructorAccessorImpl
+12 -1: BaseIterator
+58 -1: (IZ[Ljava/lang/Class;[Ljava/lang/Class;)Ljava/lang/String;
+23 -1: initializeOSEnvironment
+62 -1: ([Ljava/security/cert/Certificate;)[Ljava/security/CodeSigner;
+3 -1:  >>
+13 -1: searchEntries
+7 -1: setDate
+3 -1: red
+3 -1: ref
+10 -1: EMPTY_LIST
+3 -1: rem
+78 -1: <E:Ljava/lang/Object;>Ljava/util/AbstractCollection<TE;>;Ljava/util/List<TE;>;
+9 -1: scanToken
+6 -1: greek8
+9 -1: basicType
+12 -1: getFromClass
+43 -1: averageCharsPerByte exceeds maxCharsPerByte
+8 -1: isDaemon
+7 -1: nonNull
+17 -1: Invalid default: 
+45 -1: (Lsun/misc/URLClassPath;Ljava/lang/String;Z)V
+18 -1: java/lang/String$1
+11 -1: copyMethods
+15 -1: sun/misc/Signal
+5 -1: float
+18 -1: StreamDecoder.java
+19 -1: no such constructor
+14 -1: bad arity for 
+14 -1: divideUnsigned
+10 -1: CODING_END
+3 -1: IST
+14 -1: HeaderIterator
+9 -1: september
+20 -1: makeVarargsCollector
+6 -1: L_PATH
+45 -1: (Ljava/lang/String;)Ljava/io/File$PathStatus;
+24 -1: URI scheme is not "file"
+85 -1: (Ljava/lang/Class<TT;>;Ljava/lang/Class<TV;>;Ljava/lang/String;Ljava/lang/Class<*>;)V
+27 -1: java/util/function/Supplier
+17 -1: checkedCollection
+8 -1: MapEntry
+81 -1: (Ljava/lang/invoke/MethodHandle;ILjava/util/List;)Ljava/lang/invoke/MethodHandle;
+34 -1: java/security/ProtectionDomain$Key
+14 -1: List length = 
+39 -1: ([Ljava/lang/Object;)Ljava/lang/String;
+87 -1: (Ljava/lang/Object;Ljava/lang/Object;Ljava/util/function/BiFunction;)Ljava/lang/Object;
+7 -1: unparse
+28 -1: jarFileHasClassPathAttribute
+40 -1: (Lsun/misc/JavaIOFileDescriptorAccess;)V
+30 -1: (Ljava/lang/reflect/Field;ZZ)V
+22 -1: GetPropertyAction.java
+8 -1: RUNNABLE
+10 -1: exprString
+13 -1: getAnnotation
+19 -1: class can't be null
+22 -1: defaultAssertionStatus
+8 -1: getName0
+16 -1: quoteReplacement
+15 -1: getMemberVMInfo
+42 -1: (Ljava/lang/Class<*>;)Ljava/lang/Class<*>;
+16 -1: cachedLambdaForm
+16 -1: addFinalRefCount
+12 -1: getMethodAt0
+8 -1: ([ZII)[Z
+44 -1: (Ljava/lang/Object;TV;Ljava/lang/Object;)TV;
+4 -1: [TT;
+29 -1: Ljava/lang/invoke/LambdaForm;
+28 -1: java/util/DualPivotQuicksort
+61 -1: ()Ljava/util/concurrent/ConcurrentHashMap$KeySetView<TK;TV;>;
+17 -1: registerDirectory
+8 -1: jarFiles
+69 -1: (Ljava/lang/CharSequence;[Ljava/lang/CharSequence;)Ljava/lang/String;
+54 -1: [Ljava/util/concurrent/ConcurrentHashMap$Node<TK;TV;>;
+15 -1: getDefaultValue
+39 -1: sun/util/calendar/ZoneInfoFile$Checksum
+20 -1: DISABLE_JAR_CHECKING
+12 -1: CONTENT_TYPE
+46 -1: array type not assignable to trailing argument
+60 -1: <T:Ljava/lang/Object;>([TT;II)Ljava/util/stream/Stream<TT;>;
+47 -1: java.lang.invoke.MethodHandle.COMPILE_THRESHOLD
+9 -1: toInstant
+152 -1: <K:Ljava/lang/Object;V:Ljava/lang/Object;>(Ljava/util/NavigableMap<TK;TV;>;Ljava/lang/Class<TK;>;Ljava/lang/Class<TV;>;)Ljava/util/NavigableMap<TK;TV;>;
+7 -1: L_ALPHA
+29 -1: java/lang/AbstractMethodError
+29 -1: java/util/jar/Attributes$Name
+15 -1: LOCALE_SETTINGS
+19 -1: (J)Ljava/lang/Long;
+4 -1: jar:
+17 -1: ensureInitialized
+13 -1: LAST_MODIFIED
+40 -1: ([Ljava/lang/String;)[Ljava/lang/String;
+7 -1: shuffle
+70 -1: (Lsun/util/locale/LanguageTag;)Lsun/util/locale/InternalLocaleBuilder;
+19 -1: isPackageAccessible
+17 -1: compileToBytecode
+11 -1: getPackages
+237 -1: (Ljava/util/concurrent/ConcurrentHashMap$BulkTask;III[Ljava/util/concurrent/ConcurrentHashMap$Node;Ljava/util/concurrent/ConcurrentHashMap$MapReduceKeysToIntTask;Ljava/util/function/ToIntFunction;ILjava/util/function/IntBinaryOperator;)V
+53 -1: java/util/concurrent/ConcurrentHashMap$KeySpliterator
+26 -1: setURLStreamHandlerFactory
+47 -1: access        print all checkPermission results
+22 -1: sun/reflect/MethodInfo
+75 -1: (Ljava/lang/String;ZLjava/util/Set<Ljava/lang/String;>;)Lsun/misc/Resource;
+6 -1: KOI8-R
+14 -1: Character.java
+5 -1: (SS)I
+8 -1: unshared
+73 -1: (Ljava/lang/String;[BIILjava/security/ProtectionDomain;)Ljava/lang/Class;
+19 -1: replacementTreeNode
+10 -1: BA_REGULAR
+8 -1: UTF-16LE
+44 -1: (Ljava/lang/Class<*>;[Ljava/lang/Class<*>;)V
+22 -1: InputStreamReader.java
+17 -1: getInvocationType
+23 -1: getDeclaredConstructors
+48 -1: [Lsun/reflect/generics/tree/FormalTypeParameter;
+67 -1: (Ljava/util/Comparator;Ljava/util/Map$Entry;Ljava/util/Map$Entry;)I
+6 -1: koi8_r
+14 -1: ofTotalSeconds
+16 -1: content-encoding
+6 -1: koi8_u
+10 -1: getEncoded
+6 -1: ()[TT;
+43 -1: ([ILjava/util/function/IntUnaryOperator;I)V
+18 -1: getSecurityContext
+13 -1: LF_GEN_LINKER
+78 -1: (BLjava/lang/invoke/MemberName;Ljava/lang/Class;)Ljava/lang/invoke/MemberName;
+49 -1: (Ljava/util/HashMap;[Ljava/util/HashMap$Node;II)V
+19 -1: LinkedEntryIterator
+23 -1: Warning: JIT compiler "
+7 -1: static 
+100 -1: (Ljava/lang/Class;ZLjava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/Class;)Ljava/util/List;
+79 -1: <T:Ljava/lang/Object;>(Ljava/util/Collection<+TT;>;)Ljava/util/Collection<TT;>;
+10 -1: iso-ir-100
+10 -1: iso-ir-101
+13 -1: toUpperString
+31 -1: ()Ljava/util/Spliterator$OfInt;
+43 -1: Ljava/lang/StringIndexOutOfBoundsException;
+19 -1: Lsun/misc/JarIndex;
+7 -1: Version
+90 -1: (Ljava/lang/String;Ljava/nio/ByteBuffer;Ljava/security/ProtectionDomain;)Ljava/lang/Class;
+10 -1: getHandler
+45 -1: (ILjava/lang/Object;)Ljava/lang/StringBuffer;
+28 -1: getDeclaredAnnotationsByType
+46 -1: ([Ljava/lang/Class;Ljava/lang/StringBuilder;)V
+53 -1: ()Ljava/util/Enumeration<Ljava/security/Permission;>;
+15 -1: addAllNonStatic
+10 -1: iso-ir-110
+14 -1:     Using VM: 
+17 -1: casReflectionData
+26 -1: (Lsun/util/PreHashedMap;)I
+24 -1: removeByNameAndSignature
+4 -1: OS X
+85 -1: (Ljava/lang/ClassLoader;Ljava/lang/String;Ljava/lang/ClassLoader;Ljava/lang/String;)Z
+16 -1: JarEntryIterator
+38 -1: Ljava/lang/Class<Ljava/lang/Integer;>;
+37 -1: Ljava/lang/Class<Ljava/lang/Double;>;
+28 -1: ([Ljava/util/HashMap$Node;)V
+34 -1: java/lang/reflect/GenericArrayType
+14 -1: annotationData
+26 -1: (Lsun/util/PreHashedMap;)V
+22 -1: checkPackageDefinition
+30 -1: ACCUMULATED_DAYS_IN_MONTH_LEAP
+12 -1: lowestOneBit
+64 -1: (Ljava/lang/ThreadLocal$ThreadLocalMap;Ljava/lang/ThreadLocal;)V
+16 -1: asReadOnlyBuffer
+11 -1: getRealName
+17 -1: StringCoding.java
+10 -1: iso-ir-126
+11 -1: isSurrogate
+8 -1: setError
+138 -1: <T:Ljava/lang/Object;U:Ljava/lang/Object;>(Ljava/util/function/Function<-TT;+TU;>;Ljava/util/Comparator<-TU;>;)Ljava/util/Comparator<TT;>;
+8 -1: (TK;)TK;
+4 -1: java
+136 -1: <T:Ljava/lang/Object;>(Ljava/security/PrivilegedAction<TT;>;Ljava/security/AccessControlContext;Ljava/security/AccessControlContext;)TT;
+36 -1: ()Lsun/util/locale/LocaleExtensions;
+12 -1: doubleStream
+28 -1: ()Ljava/util/SimpleTimeZone;
+7 -1: :@&=+$,
+94 -1: Ljava/lang/ThreadLocal<Ljava/lang/ref/SoftReference<Ljava/lang/StringCoding$StringDecoder;>;>;
+64 -1: (Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method;
+19 -1: getSystemTimeZoneID
+18 -1: ReentrantLock.java
+8 -1: emptyMap
+16 -1: getSavedProperty
+249 -1: (Ljava/util/concurrent/ConcurrentHashMap$BulkTask;III[Ljava/util/concurrent/ConcurrentHashMap$Node;Ljava/util/concurrent/ConcurrentHashMap$MapReduceEntriesToDoubleTask;Ljava/util/function/ToDoubleFunction;DLjava/util/function/DoubleBinaryOperator;)V
+14 -1: KeySpliterator
+13 -1: findResources
+14 -1: forWrapperType
+8 -1: floorMod
+12 -1: isoCountries
+10 -1: CheckedSet
+21 -1: AbstractCalendar.java
+12 -1: IS_INVOCABLE
+45 -1: (Ljava/lang/Class;)Lsun/reflect/ConstantPool;
+19 -1: checkSecurityAccess
+13 -1: Invalid index
+28 -1: STACK_TRACE_ELEMENT_SENTINEL
+88 -1: (ILjava/lang/Object;Ljava/lang/Object;)Ljava/util/concurrent/ConcurrentHashMap$TreeNode;
+130 -1: (Ljava/lang/String;Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+14 -1: aliases_IBM437
+10 -1: iso-ir-144
+10 -1: iso-ir-148
+35 -1: ()Ljava/nio/charset/CharsetDecoder;
+95 -1: (Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodHandle;)Ljava/lang/invoke/MethodHandle;
+16 -1: UnmodifiableList
+40 -1: ()Ljava/util/concurrent/locks/Condition;
+9 -1: Path.java
+80 -1: ([BLsun/reflect/ConstantPool;Ljava/lang/Class;[Ljava/lang/Class;)Ljava/util/Map;
+36 -1: Ljava/lang/Class<Ljava/lang/Float;>;
+124 -1: (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/Object;)Ljava/lang/Object;
+20 -1: privateGetParameters
+24 -1: sun/nio/cs/StreamDecoder
+12 -1: getFreeSpace
+8 -1: US-ASCII
+22 -1: negativeZeroDoubleBits
+9 -1: putObject
+13 -1: linkToVirtual
+35 -1: Ljava/lang/Class<Ljava/lang/Long;>;
+15 -1: detectedCharset
+26 -1: java/lang/reflect/Modifier
+22 -1: JAVAFX_LAUNCH_MODE_JAR
+32 -1: java/net/URLStreamHandlerFactory
+7 -1: IBM-923
+80 -1: (Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/String;
+13 -1: TRANSFERINDEX
+34 -1: (Lsun/nio/cs/StandardCharsets$1;)V
+23 -1: ()Ljava/io/InputStream;
+16 -1: ()Ljava/io/File;
+41 -1: (Ljava/lang/String;)Ljava/nio/ByteBuffer;
+22 -1: sun/reflect/Reflection
+23 -1: java/lang/AutoCloseable
+25 -1: BootstrapMethodError.java
+19 -1: EnclosingMethodInfo
+13 -1: transferIndex
+18 -1: java/lang/Compiler
+4 -1: zero
+29 -1: java/util/Arrays$NaturalOrder
+9 -1: language=
+37 -1: Ljava/util/ArrayList<Ljava/net/URL;>;
+73 -1: ()Ljava/util/concurrent/locks/AbstractQueuedSynchronizer$ConditionObject;
+16 -1: balanceInsertion
+82 -1: (Ljava/lang/StringBuffer;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;I)V
+11 -1: asIntBuffer
+27 -1: (Ljava/util/LinkedList;II)V
+13 -1: ofEpochSecond
+19 -1: sunjce_provider.jar
+21 -1: (F)Ljava/lang/String;
+32 -1:  >> does not contain binding << 
+21 -1: declaredPublicMethods
+5 -1: FIELD
+17 -1: getPrimitiveClass
+127 -1: (Ljava/util/concurrent/atomic/AtomicReferenceFieldUpdater$AtomicReferenceFieldUpdaterImpl;Ljava/lang/Class;Ljava/lang/String;)V
+21 -1: replaceParameterTypes
+73 -1: Ljava/util/Map<Ljava/lang/Class<*>;Ljava/security/PermissionCollection;>;
+21 -1: (Ljava/lang/Double;)I
+5 -1: floor
+4 -1: halt
+14 -1: newConstructor
+48 -1: (Ljava/util/function/BiFunction<-TK;-TV;+TV;>;)V
+17 -1: packageAccessLock
+15 -1: America/Phoenix
+19 -1: Incoming arguments:
+11 -1: V_Monotonic
+14 -1: decrementExact
+15 -1: updatePositions
+14 -1: toLocaleString
+12 -1: appendEscape
+7 -1: DISPLAY
+27 -1: sun/nio/cs/US_ASCII$Decoder
+13 -1: LITTLE_ENDIAN
+6 -1: isNull
+13 -1: TempDirectory
+6 -1: LM_JAR
+39 -1: JVMTI_THREAD_STATE_WAITING_WITH_TIMEOUT
+10 -1: isCompiled
+36 -1: (Ljava/lang/AbstractStringBuilder;)Z
+3 -1: run
+17 -1: START_PUNCTUATION
+33 -1: Ljava/util/Stack<Ljava/net/URL;>;
+49 -1: (Ljava/lang/String;)Ljava/lang/invoke/LambdaForm;
+28 -1: java/security/DomainCombiner
+53 -1: (Ljava/lang/String;Ljava/util/Map;)Ljava/time/ZoneId;
+81 -1: ([BLsun/reflect/ConstantPool;Ljava/lang/Class;)[Ljava/lang/reflect/AnnotatedType;
+43 -1: java/lang/management/GarbageCollectorMXBean
+11 -1: toTitleCase
+12 -1: getHoldCount
+4 -1: ()[B
+4 -1: ()[C
+4 -1: ()[J
+20 -1: (Ljava/io/File;IZZ)Z
+18 -1: fileTimeToUnixTime
+23 -1: java/nio/HeapCharBuffer
+30 -1: Ljava/lang/ref/ReferenceQueue;
+6 -1: rt.jar
+69 -1: (Ljava/util/function/ToIntFunction<-TT;>;)Ljava/util/Comparator<TT;>;
+21 -1: java/lang/ThreadLocal
+25 -1: Ljava/lang/reflect/Field;
+44 -1: (Ljava/lang/String;Ljava/lang/Throwable;ZZ)V
+93 -1: (Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MemberName;)Ljava/lang/invoke/MethodHandle;
+22 -1: getDayOfWeekDateBefore
+37 -1: [Lsun/reflect/generics/tree/TypeTree;
+83 -1: <E:Ljava/lang/Object;>(Ljava/util/Map<TE;Ljava/lang/Boolean;>;)Ljava/util/Set<TE;>;
+10 -1: readFields
+42 -1: (Ljava/net/URL;)Ljava/security/CodeSource;
+44 -1: (Ljava/lang/String;IIJ)Ljava/nio/ByteBuffer;
+27 -1: Ljava/util/Collection<TE;>;
+13 -1: checkResource
+7 -1: rename0
+51 -1: (C)Ljava/lang/invoke/BoundMethodHandle$SpeciesData;
+47 -1: sun/reflect/generics/repository/FieldRepository
+11 -1: readBoolean
+134 -1: (ILjava/lang/Object;Ljava/lang/Object;Ljava/util/concurrent/ConcurrentHashMap$Node;Ljava/util/concurrent/ConcurrentHashMap$TreeNode;)V
+13 -1: getZoneOffset
+17 -1: getJdkVersionInfo
+21 -1: sun/misc/MessageUtils
+23 -1: defaultAllowArraySyntax
+31 -1: java/util/concurrent/locks/Lock
+24 -1: java/lang/reflect/Method
+7 -1: toUpper
+17 -1: sun/misc/Signal$1
+51 -1: (JLjava/util/function/BiFunction<-TK;-TK;+TK;>;)TK;
+28 -1: (Ljava/lang/ref/Reference;)Z
+8 -1: nthreads
+26 -1: MapReduceEntriesToLongTask
+27 -1: (Ljava/util/NavigableSet;)V
+10 -1: savedProps
+25 -1: Lsun/security/util/Debug;
+12 -1: CR_MALFORMED
+13 -1: com.sun.proxy
+17 -1: CharSequence.java
+24 -1: (ILjava/lang/String;II)Z
+13 -1: findNextValue
+108 -1: <K::Ljava/lang/Comparable<-TK;>;V:Ljava/lang/Object;>()Ljava/util/Comparator<Ljava/util/Map$Entry<TK;TV;>;>;
+5 -1: (FF)F
+9 -1: typeClass
+5 -1: (FF)I
+11 -1: segmentMask
+7 -1: (JJJJ)V
+14 -1: aliases_CESU_8
+85 -1: (Ljava/lang/Class;Ljava/lang/invoke/MemberName;)Ljava/lang/invoke/DirectMethodHandle;
+15 -1: getCalendarDate
+21 -1: getDeclaredAnnotation
+8 -1: ([BIIB)I
+15 -1: stripExtensions
+14 -1: getISO3Country
+5 -1: short
+47 -1: String value %s exceeds range of unsigned long.
+34 -1: ()Ljava/security/ProtectionDomain;
+8 -1: ([BIIB)V
+23 -1: (Ljava/lang/Object;ID)V
+47 -1: (Ljava/lang/String;Ljava/nio/charset/Charset;)V
+29 -1: RuntimeVisibleTypeAnnotations
+24 -1: (Ljava/io/InputStream;)V
+39 -1: (Ljava/lang/Class;Ljava/lang/String;Z)V
+16 -1: convertPrimitive
+8 -1: (TK;)TV;
+24 -1: (Ljava/io/InputStream;)Z
+6 -1: start 
+243 -1: (Ljava/util/concurrent/ConcurrentHashMap$BulkTask;III[Ljava/util/concurrent/ConcurrentHashMap$Node;Ljava/util/concurrent/ConcurrentHashMap$MapReduceEntriesToLongTask;Ljava/util/function/ToLongFunction;JLjava/util/function/LongBinaryOperator;)V
+9 -1: asSpecial
+20 -1: java/text/Normalizer
+17 -1: DAYS_0000_TO_1970
+9 -1: toCharset
+20 -1: REPLACEALL_THRESHOLD
+20 -1: sun/net/util/URLUtil
+16 -1: findLoadedClass0
+14 -1: localedata.jar
+6 -1: Parser
+6 -1: start0
+4 -1: hash
+83 -1: (Ljava/lang/Class;Ljava/lang/Class;[Ljava/lang/Class;)Ljava/lang/invoke/MethodType;
+29 -1: Ljava/lang/invoke/MemberName;
+8 -1: BOOT_TAG
+22 -1: MH_LINKER_ARG_APPENDED
+14 -1: comparingByKey
+47 -1: ([Ljava/lang/String;)Ljava/lang/ProcessBuilder;
+6 -1: start=
+18 -1: StringBuilder.java
+7 -1: getLong
+12 -1: copyElements
+16 -1: highResFrequency
+11 -1: toGMTString
+10 -1: ISO_8859_1
+10 -1: ISO_8859_2
+6 -1: result
+10 -1: ISO_8859_4
+10 -1: ISO_8859_5
+10 -1: ISO_8859_7
+15 -1: unmodifiableSet
+10 -1: ISO_8859_9
+25 -1: NoClassDefFoundError.java
+42 -1: (Ljava/lang/String;)Ljava/util/LinkedList;
+14 -1: parallelPrefix
+22 -1: ARRAY_LONG_INDEX_SCALE
+6 -1: resume
+36 -1: Ljava/lang/invoke/LambdaForm$Hidden;
+50 -1: java/util/Collections$UnmodifiableRandomAccessList
+130 -1: (Ljava/util/concurrent/ConcurrentHashMap$BulkTask;III[Ljava/util/concurrent/ConcurrentHashMap$Node;Ljava/util/function/Consumer;)V
+6 -1: getInt
+13 -1: getCachedYear
+21 -1: CONNECTOR_PUNCTUATION
+73 -1: (Ljava/lang/Class;Ljava/lang/Class;Ljava/lang/Object;ILjava/lang/Class;)V
+24 -1: [Lsun/util/calendar/Era;
+22 -1: (Ljava/lang/Object;D)V
+74 -1: (Ljava/security/AccessControlContext;)Ljava/security/AccessControlContext;
+109 -1: <T:Ljava/lang/Object;>(Ljava/lang/Class<*>;Ljava/lang/Class$AnnotationData;Ljava/lang/Class$AnnotationData;)Z
+6 -1: ([CZ)V
+74 -1: (Ljava/util/HashMap$Node;Ljava/util/HashMap$Node;)Ljava/util/HashMap$Node;
+19 -1: TRADITIONAL_CHINESE
+125 -1: (Ljava/lang/Throwable$PrintStreamOrWriter;[Ljava/lang/StackTraceElement;Ljava/lang/String;Ljava/lang/String;Ljava/util/Set;)V
+18 -1: unknown protocol: 
+57 -1: (Ljava/lang/reflect/Method;Lsun/reflect/MethodAccessor;)V
+13 -1: writeComments
+9 -1: Negotiate
+14 -1: Closeable.java
+10 -1: asSpreader
+122 -1: (Ljava/nio/file/WatchService;[Ljava/nio/file/WatchEvent$Kind;[Ljava/nio/file/WatchEvent$Modifier;)Ljava/nio/file/WatchKey;
+17 -1: jvm_minor_version
+9 -1: getDouble
+25 -1: Ljava/io/File$PathStatus;
+19 -1: averageCharsPerByte
+22 -1: getConstructorAccessor
+61 -1: (Ljava/lang/String;)Ljava/lang/management/MemoryManagerMBean;
+45 -1: (Ljava/lang/String;)Ljava/util/regex/Pattern;
+25 -1: (JC)Ljava/nio/ByteBuffer;
+20 -1: exclusiveOwnerThread
+85 -1: <T:Ljava/lang/Object;>(Ljava/lang/ClassValue<TT;>;Ljava/lang/ClassValue$Entry<TT;>;)V
+17 -1: getExtensionValue
+14 -1: getLoadAverage
+17 -1: GET_PD_PERMISSION
+6 -1: METHOD
+23 -1: sun/nio/cs/ISO_8859_1$1
+23 -1: (I[Ljava/lang/Object;)I
+4 1: zzz1
+13 -1: createWrapper
+4 1: zzz2
+4 1: zzz3
+33 -1:  greater than Character.MAX_RADIX
+37 -1: DIRECTIONALITY_RIGHT_TO_LEFT_OVERRIDE
+6 -1: BRIDGE
+20 -1: canonicalizeLanguage
+13 -1: setPermission
+46 -1: (Ljava/io/OutputStream;)Ljava/io/OutputStream;
+3 -1: 646
+14 -1: java/lang/Long
+55 -1: java/util/concurrent/ConcurrentHashMap$SearchValuesTask
+38 -1: (IC)Ljava/lang/invoke/LambdaForm$Name;
+37 -1: (Ljava/lang/Class;)Ljava/lang/String;
+17 -1: javaUtilJarAccess
+23 -1: registerMethodsToFilter
+34 -1: (Ljava/nio/charset/Charset;[CII)[B
+11 -1: % VERSION 2
+37 -1: ([DI)Ljava/util/Spliterator$OfDouble;
+16 -1:     Stack Size: 
+52 -1: (Ljava/lang/Class;)Ljava/lang/annotation/Annotation;
+17 -1: putDoubleVolatile
+10 -1: access$500
+10 -1: access$502
+28 -1: malformed input around byte 
+13 -1: LF_INVSPECIAL
+32 -1: Non-positive averageCharsPerByte
+14 -1: NF_fieldOffset
+10 -1: access$508
+48 -1: <T:Ljava/lang/Object;>(TT;)Ljava/util/List<TT;>;
+17 -1: defaultReadObject
+17 -1: java/util/TimSort
+13 -1: resolveClass0
+92 -1: (Ljava/lang/Class<*>;Ljava/lang/Class<*>;[Ljava/lang/Class<*>;)Ljava/lang/invoke/MethodType;
+20 -1: setLangReflectAccess
+30 -1: java/lang/reflect/TypeVariable
+56 -1: <T:Ljava/lang/Object;>([TT;)Ljava/util/Spliterator<TT;>;
+8 -1: VOLATILE
+10 -1: Big5-HKSCS
+23 -1: (Ljava/util/Locale$1;)V
+15 -1: ISO_8859-1:1987
+14 -1: no such method
+10 -1: null value
+8 -1: checkURL
+22 -1: ARRAY_BYTE_INDEX_SCALE
+27 -1: (Ljava/lang/ClassLoader;Z)V
+22 -1: java/lang/reflect/Type
+25 -1: java/net/JarURLConnection
+36 -1: java/util/WeakHashMap$KeySpliterator
+26 -1: ()Lsun/misc/JavaAWTAccess;
+50 -1: (I[Ljava/lang/Class;)Ljava/lang/invoke/MethodType;
+9 -1: toDegrees
+12 -1: lowSurrogate
+19 -1: getEnclosingMethod0
+7 -1: bytearr
+35 -1: (Ljava/util/List;Ljava/util/List;)I
+25 -1: [Ljava/lang/reflect/Type;
+34 -1: javaSecurityProtectionDomainAccess
+21 -1: java.launcher.X.usage
+37 -1: sun/util/locale/InternalLocaleBuilder
+33 -1: ()Ljava/lang/invoke/MethodHandle;
+3 -1: scl
+19 -1: synthesizeAllParams
+68 -1: Ljava/util/Map<Ljava/lang/String;[Ljava/security/cert/Certificate;>;
+6 -1: vclass
+35 -1: (Ljava/util/List;Ljava/util/List;)V
+59 -1: Ljava/lang/Number;Ljava/lang/Comparable<Ljava/lang/Float;>;
+13 -1: CallSite.java
+10 1: Bar loaded
+21 -1: ARRAY_INT_BASE_OFFSET
+49 -1: Ljava/util/concurrent/ConcurrentHashMap$TreeNode;
+32 -1: [Ljava/lang/reflect/Constructor;
+4 -1: type
+34 -1: <T:Ljava/lang/Object;>([TT;II)[TT;
+25 -1: BufferedOutputStream.java
+23 -1: java/util/zip/ZipFile$1
+24 -1: ()Ljava/lang/ClassValue;
+50 -1: (Ljava/util/concurrent/CountedCompleter;[F[FIIII)V
+16 -1: getQueuedThreads
+26 -1: getJavaNetHttpCookieAccess
+8 -1: setExtra
+8 -1: implRead
+20 -1: linkMethod => throw 
+76 -1: (Ljava/util/function/ToDoubleFunction;Ljava/lang/Object;Ljava/lang/Object;)I
+11 -1: KeyIterator
+39 -1: Ljava/util/List<Ljava/lang/Throwable;>;
+3 -1: " "
+36 -1: Ljava/lang/IllegalArgumentException;
+11 -1: checkBounds
+30 -1: sun/nio/cs/FastCharsetProvider
+11 -1: toLongArray
+107 -1: (Ljava/lang/Class<*>;Ljava/lang/String;Ljava/lang/Class<*>;IILjava/lang/String;[B)Ljava/lang/reflect/Field;
+8 -1:  (build 
+39 -1: (Ljava/nio/Buffer;ILjava/nio/Buffer;I)V
+31 -1: [Ljava/lang/reflect/Executable;
+3 -1: set
+21 -1: PhantomReference.java
+41 -1: java/util/Collections$CheckedNavigableMap
+53 -1: Can not make a java.lang.Class constructor accessible
+12 -1: ([CII[CIII)I
+55 -1: Ljava/util/HashMap<Ljava/lang/String;Ljava/lang/Void;>;
+12 -1: MICROSECONDS
+11 -1: writeBuffer
+52 -1:               and domain that didn't have permission
+37 -1: java/nio/channels/WritableByteChannel
+26 -1: getRawClassTypeAnnotations
+15 -1: putCharVolatile
+33 -1: java/security/InvalidKeyException
+19 -1: Ljava/io/Closeable;
+83 -1: Lsun/util/locale/LocaleObjectCache<Ljava/util/Locale$LocaleKey;Ljava/util/Locale;>;
+10 -1: SetFromMap
+24 -1: JavaFX-Application-Class
+13 -1: asShortBuffer
+10 -1: getReifier
+15 -1: isPositionIndex
+18 -1: SignalHandler.java
+3 -1: JST
+28 -1: (Ljava/io/FileDescriptor;I)I
+28 -1: getStackAccessControlContext
+16 -1: updateByteBuffer
+27 -1: ()Ljava/net/ContentHandler;
+25 -1: (JD)Ljava/nio/ByteBuffer;
+39 -1: ()Lsun/misc/JavaIOFileDescriptorAccess;
+107 -1: (Ljava/lang/ThreadLocal$ThreadLocalMap;Ljava/lang/ThreadLocal;)Ljava/lang/ThreadLocal$ThreadLocalMap$Entry;
+42 -1: sun/misc/PerfCounter$WindowsClientCounters
+8 -1: addExact
+28 -1: (Ljava/io/FileDescriptor;I)V
+36 -1: ([Ljava/lang/String;)Ljava/util/Map;
+21 -1: ()Ljava/lang/Process;
+4 -1: UTF8
+5 -1: mkdir
+10 -1: transient 
+3 -1: sgp
+15 -1: balanceDeletion
+161 -1: (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/net/URL;)Ljava/lang/Package;
+15 -1: SynchronizedMap
+40 -1: sun.misc.URLClassPath.disableJarChecking
+92 -1: <E:Ljava/lang/Object;>Ljava/util/AbstractSet<TE;>;Ljava/util/Set<TE;>;Ljava/io/Serializable;
+17 -1: removeEldestEntry
+35 -1: (I)Ljava/lang/Class$AnnotationData;
+24 -1: Ljava/util/Locale$Cache;
+13 -1: STANDARD_TIME
+17 -1: sun/nio/cs/MS1252
+99 -1: <K:Ljava/lang/Object;>()Ljava/util/concurrent/ConcurrentHashMap$KeySetView<TK;Ljava/lang/Boolean;>;
+23 -1: java/util/LinkedHashSet
+9 -1: iso8859_1
+9 -1: iso8859_2
+9 -1: iso8859_4
+66 -1: <A::Ljava/lang/annotation/Annotation;>(Ljava/lang/Class<TA;>;)[TA;
+9 -1: iso8859_5
+9 -1: iso8859_7
+9 -1: iso8859_9
+21 -1: Ljava/util/Formatter;
+6 -1: isPath
+21 -1: makeReferenceIdentity
+24 -1: sun/net/ApplicationProxy
+23 -1: ClassCastException.java
+11 -1: MethodArray
+12 -1: SingletonMap
+41 -1: java/util/ArrayPrefixHelpers$CumulateTask
+7 -1: seconds
+20 -1: ClassRepository.java
+14 -1: allocateMemory
+24 -1: java.launcher.jar.error1
+24 -1: java.launcher.jar.error2
+24 -1: java.launcher.jar.error3
+45 -1: (Ljava/lang/String;Ljava/util/jar/Manifest;)Z
+3 -1: sin
+7 -1: (J[II)I
+3 -1: Itr
+18 -1: findBootstrapClass
+10 -1: getElement
+15 -1: ISO_8859-4:1988
+34 -1: newGetLongIllegalArgumentException
+7 -1: pending
+17 -1: isNotContinuation
+6 -1: EXTSIG
+13 -1: searchMethods
+32 -1: Lsun/misc/JavaUtilZipFileAccess;
+33 -1: ([Ljava/lang/reflect/Parameter;)V
+31 -1: defaultUncaughtExceptionHandler
+89 -1: (Ljava/nio/file/WatchService;[Ljava/nio/file/WatchEvent$Kind<*>;)Ljava/nio/file/WatchKey;
+5 -1: ASCII
+28 -1: ()Lsun/reflect/ConstantPool;
+20 -1: isJavaIdentifierPart
+6 -1: EXTSIZ
+34 -1: Lsun/misc/JavaNetHttpCookieAccess;
+34 -1: ClassLoader object not initialized
+7 -1: CHECKED
+16 -1: encodeBufferLoop
+37 -1: (Ljava/time/Instant;)Ljava/util/Date;
+24 -1: (Ljava/util/Map$Entry;)Z
+50 -1: java/util/concurrent/ConcurrentHashMap$KeyIterator
+31 -1: ()[Ljava/lang/ClassValue$Entry;
+31 -1: java/lang/IllegalStateException
+43 -1: (Ljava/lang/Appendable;Ljava/util/Locale;)V
+6 -1: CENVEM
+53 -1: Ljava/util/ArrayList<Lsun/misc/URLClassPath$Loader;>;
+17 -1: getHeaderFieldKey
+71 -1: (Ljava/lang/CharSequence;Ljava/text/Normalizer$Form;)Ljava/lang/String;
+6 -1: CENVER
+17 -1: cleanStaleEntries
+9 -1: linkFirst
+57 -1: (Ljava/util/Comparator<-TT;>;)Ljava/util/Comparator<TT;>;
+8 -1: val$file
+27 -1: Invalid parameter modifiers
+6 -1: append
+57 -1: ()Lsun/reflect/generics/repository/ConstructorRepository;
+65 -1: java/util/concurrent/ConcurrentHashMap$MapReduceMappingsToIntTask
+39 -1: (Ljava/lang/String;)Ljava/lang/Integer;
+25 -1: lambda$parallelSetAll$191
+25 -1: lambda$parallelSetAll$192
+25 -1: lambda$parallelSetAll$193
+23 -1: INSERTIONSORT_THRESHOLD
+17 -1: java/time/Instant
+25 -1: lambda$parallelSetAll$194
+14 -1: dynamicInvoker
+9 -1: iso646-us
+8 -1: position
+29 -1: java/nio/channels/FileChannel
+27 -1: java/util/stream/Collectors
+64 -1: (Ljava/lang/CharSequence;Ljava/lang/Iterable;)Ljava/lang/String;
+10 -1: INDEX_NAME
+15 -1: getCommentBytes
+67 -1: (Ljava/io/FileOutputStream;Ljava/lang/String;)Ljava/io/PrintStream;
+22 -1: privateGetPublicFields
+32 -1: java/util/BitSet$1BitSetIterator
+12 -1: PERF_MODE_RO
+89 -1: ([Ljava/lang/ClassValue$Entry;ILjava/lang/ClassValue$Entry;Z)Ljava/lang/ClassValue$Entry;
+30 -1: java/security/PrivilegedAction
+18 -1: host can't be null
+26 -1: package name can't be null
+12 -1: PERF_MODE_RW
+10 -1: isEnqueued
+18 -1: argSlotToParameter
+37 -1: (II)Ljava/lang/AbstractStringBuilder;
+5 -1: tabAt
+53 -1: (Ljava/lang/Object;)Ljava/lang/AbstractStringBuilder;
+11 -1: PATH_OFFSET
+18 -1: unicodebigunmarked
+15 -1: ConditionObject
+6 -1: KOREAN
+13 -1: isNamePresent
+24 -1: ()Ljava/lang/Class<TE;>;
+14 -1: isStandardTime
+8 -1: ([IIII)I
+9 -1: WeakEntry
+12 -1: javaIOAccess
+17 -1: key can't be null
+129 -1: Ljava/lang/Object;Ljava/lang/Comparable<Ljava/nio/file/Path;>;Ljava/lang/Iterable<Ljava/nio/file/Path;>;Ljava/nio/file/Watchable;
+8 -1:  handler
+8 -1: ([IIII)V
+10 -1: atBugLevel
+18 -1: makeGuardWithCatch
+18 -1: currentLoadedClass
+11 -1: getCodeBase
+67 -1: <T:Ljava/lang/Object;>(Ljava/util/List<+TT;>;)Ljava/util/List<TT;>;
+12 -1: JarFile.java
+19 -1: (C)Ljava/io/Writer;
+22 -1: createURLStreamHandler
+23 -1: sun/nio/cs/ArrayDecoder
+13 -1: setAccessible
+18 -1: stripOffParameters
+101 -1: ([Ljava/security/ProtectionDomain;[Ljava/security/ProtectionDomain;)[Ljava/security/ProtectionDomain;
+18 -1: Ljava/util/Random;
+16 -1: Pacific/Honolulu
+13 -1: useOldMapping
+65 -1: (Ljava/lang/invoke/LambdaForm$NamedFunction;[Ljava/lang/Object;)V
+14 -1: filterArgument
+12 -1: LF_MH_LINKER
+25 -1: isDirectMemoryPageAligned
+49 -1: (Ljava/util/BitSet;)Ljava/util/function/Supplier;
+54 -1: (Ljava/util/concurrent/ConcurrentHashMap<TK;TV;>;TV;)V
+16 -1: java/time/ZoneId
+4 -1: zfot
+18 -1: isSameClassPackage
+6 -1: julian
+8 -1: (TT;)TT;
+22 -1: java/util/jar/Manifest
+7 -1: charOut
+16 -1: getOffsetsByWall
+19 -1: Illegal replacement
+139 -1: <E:Ljava/lang/Object;>Ljava/util/AbstractList<TE;>;Ljava/util/List<TE;>;Ljava/util/RandomAccess;Ljava/lang/Cloneable;Ljava/io/Serializable;
+96 -1: <T:Ljava/lang/Object;>(Ljava/lang/reflect/Constructor<TT;>;)Ljava/lang/reflect/Constructor<TT;>;
+15 -1: Annotation.java
+24 -1: (Ljava/lang/Class<*>;)[B
+6 -1: CODING
+34 -1: ([Ljava/lang/ClassValue$Entry;II)V
+6 -1: IGNORE
+62 -1: (Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/MethodHandle;
+29 -1: specificToGenericStringHeader
+12 -1: helpTransfer
+8 -1: fastTime
+62 -1: (Ljava/net/URLConnection;[Ljava/lang/Class;)Ljava/lang/Object;
+18 -1: Unhandled signal: 
+8 -1: isStrict
+15 -1: ISO_8859-7:1987
+13 -1: getWeekLength
+14 -1: jvmBuildNumber
+40 -1: (Ljava/lang/String;)Ljava/util/Iterator;
+6 -1: short0
+6 -1: short1
+73 -1: <T:Ljava/lang/Object;>(Ljava/security/PrivilegedExceptionAction<TT;>;)TT;
+10 -1: removeNode
+8 -1: setFloat
+18 -1: cspc862latinhebrew
+11 -1: setTimeZone
+34 -1: java/lang/reflect/AccessibleObject
+25 -1: MapReduceKeysToDoubleTask
+25 -1: java/lang/ref/Reference$1
+24 -1: java/nio/HeapByteBufferR
+15 -1: jdkMicroVersion
+117 -1: (Ljava/lang/Class;Ljava/lang/String;[Ljava/lang/Class;Ljava/lang/Class;[Ljava/lang/Class;IILjava/lang/String;[B[B[B)V
+8 -1: (TT;)TV;
+16 -1: Permissions.java
+22 -1: Ljava/util/Comparator;
+17 -1: getDaylightSaving
+25 -1: ([BIILjava/lang/String;)V
+9 -1: stillborn
+11 -1: maxPosition
+28 -1: java/util/ArrayPrefixHelpers
+73 -1: <K:Ljava/lang/Object;V:Ljava/lang/Object;>()Ljava/util/SortedMap<TK;TV;>;
+14 -1: useCanonCaches
+5 -1: clean
+16 -1: checkPermission2
+34 -1: sun.misc.launcher.useSharedArchive
+13 -1: shutdownHooks
+5 -1: clear
+240 -1: (Ljava/util/concurrent/ConcurrentHashMap$BulkTask;III[Ljava/util/concurrent/ConcurrentHashMap$Node;Ljava/util/concurrent/ConcurrentHashMap$MapReduceKeysToLongTask;Ljava/util/function/ToLongFunction;JLjava/util/function/LongBinaryOperator;)V
+67 -1: <U:Ljava/lang/Object;>(JLjava/util/function/Function<-TV;+TU;>;)TU;
+6 -1: cp1250
+6 -1: cp1251
+6 -1: cp1252
+13 -1: getZipMessage
+6 -1: cp1253
+28 -1: (J)Ljava/lang/ref/Reference;
+6 -1: cp1254
+54 -1: (ILjava/lang/String;)Ljava/lang/AbstractStringBuilder;
+6 -1: cp1257
+10 -1: deepEquals
+17 -1: WRITE_BUFFER_SIZE
+13 -1: copyFromArray
+40 -1: java/util/Collections$ReverseComparator2
+36 -1: sun/reflect/generics/visitor/Reifier
+19 -1: averageBytesPerChar
+13 -1: javaAWTAccess
+6 -1: cp5346
+61 -1: Ljava/util/Map<Ljava/lang/String;Ljava/nio/charset/Charset;>;
+6 -1: cp5347
+6 -1: cp5348
+6 -1: cp5349
+5 -1: field
+23 -1: ()Ljava/nio/LongBuffer;
+103 -1: (Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/WrongMethodTypeException;
+37 -1: (I)Ljava/lang/Character$UnicodeBlock;
+11 -1: offsetAfter
+79 -1: Ljava/util/HashMap<Ljava/security/CodeSource;Ljava/security/ProtectionDomain;>;
+27 -1: invocationHandlerReturnType
+17 -1: POSITIVE_INFINITY
+11 -1: maybeRebind
+3 -1: str
+18 -1: setSecurityManager
+9 -1: signature
+18 -1: corrupted jar file
+89 -1: <E:Ljava/lang/Object;>Ljava/lang/Object;Ljava/util/Collection<TE;>;Ljava/io/Serializable;
+87 -1: (Ljava/lang/invoke/MethodHandle;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
+6 -1: CENATT
+6 -1: cp5350
+12 -1: AF_GETSTATIC
+38 -1: (TE;Ljava/util/LinkedList$Node<TE;>;)V
+8 -1: isLoaded
+6 -1: CENATX
+6 -1: cp5353
+18 -1: Africa/Addis_Ababa
+35 -1: sun/usagetracker/UsageTrackerClient
+11 -1: toUpperCase
+22 -1: java/util/zip/Inflater
+10 -1: iso_8859-1
+10 -1: iso_8859-2
+3 -1: sum
+7 -1: x-Johab
+10 -1: iso_8859-4
+10 -1: iso_8859-5
+85 -1: (Ljava/security/DomainCombiner;Ljava/lang/Class;)Ljava/security/AccessControlContext;
+11 -1: activeCount
+49 -1: (Ljava/lang/ClassLoader;Ljava/lang/ClassLoader;)Z
+51 -1: (Ljava/util/List;Ljava/lang/Class;)Ljava/util/List;
+10 -1: iso_8859-7
+19 -1: appendVmErgoMessage
+10 -1: iso_8859-9
+14 -1: getClassLoader
+6 -1: (CJJ)Z
+15 -1: Lsun/misc/Perf;
+7 -1: getTree
+27 -1: Ljava/text/Normalizer$Form;
+11 -1: ISO-2022-JP
+69 -1: (Ljava/lang/invoke/MethodHandle;Ljava/lang/Object;)Ljava/lang/Object;
+50 -1: java/lang/invoke/DirectMethodHandle$StaticAccessor
+15 -1: fxLauncherClass
+35 -1: (Ljava/net/URL;Ljava/lang/String;)V
+16 -1: getAndAccumulate
+35 -1: (Ljava/net/URL;Ljava/lang/String;)Z
+32 -1: Non-positive averageBytesPerChar
+35 -1: Ljava/lang/Class<Ljava/lang/Void;>;
+15 -1: CLASS_MODIFIERS
+12 -1: checkedQueue
+13 -1: enumConstants
+10 -1: getFactory
+95 -1: Ljava/util/concurrent/ConcurrentMap<TK;Lsun/util/locale/LocaleObjectCache$CacheEntry<TK;TV;>;>;
+13 -1: Africa/Harare
+57 -1: (JLjava/util/TimeZone;)Lsun/util/calendar/Gregorian$Date;
+11 -1: ISO-2022-KR
+19 -1: $assertionsDisabled
+13 -1: PROXY_PACKAGE
+17 -1: copyFromLongArray
+81 -1: <K:Ljava/lang/Object;V:Ljava/lang/Object;>Ljava/util/LinkedHashMap$Entry<TK;TV;>;
+11 -1: checkDelete
+38 -1: sun/management/ManagementFactoryHelper
+7 -1: UTC1900
+20 -1: getBootstrapResource
+23 -1: ()Ljava/lang/Throwable;
+16 -1: CALLER_SENSITIVE
+26 -1: checkSystemClipboardAccess
+32 -1: Can't set default locale to NULL
+16 -1: fxLauncherMethod
+4 -1:  >= 
+8 -1: provider
+9 -1: Finalizer
+78 -1: (Ljava/io/FileDescriptor;ZZZLjava/lang/Object;)Ljava/nio/channels/FileChannel;
+13 -1: emptyIterator
+15 -1: getZipFileCount
+21 -1: isJavaIdentifierStart
+9 -1: connected
+11 -1: (ITK;TV;I)V
+16 -1: America/Honolulu
+22 -1: SynchronizedCollection
+28 -1: java/util/zip/ZipConstants64
+29 -1: inheritedAccessControlContext
+29 -1: ()[Ljava/security/CodeSigner;
+85 -1: (JLjava/lang/String;Ljava/lang/String;Ljava/lang/String;Lcom/sun/management/GcInfo;)V
+25 -1: (Ljava/nio/CharBuffer;Z)V
+15 -1: java/io/Console
+101 -1: (Ljava/lang/Class<*>;Lsun/reflect/annotation/AnnotationType;Lsun/reflect/annotation/AnnotationType;)Z
+9 -1: | resolve
+81 -1: (BLjava/lang/invoke/MemberName;Ljava/lang/Class<*>;)Ljava/lang/invoke/MemberName;
+33 -1: (Ljava/nio/charset/Charset;FF[B)V
+49 -1: (Ljava/lang/Class;Z)Ljava/lang/invoke/MethodType;
+66 -1: (Ljava/lang/String;Ljava/lang/String;Ljava/io/File;)Ljava/io/File;
+29 -1: ([Ljava/util/HashMap$Node;I)V
+13 -1: filterMethods
+4 -1: jcal
+61 -1: (Ljava/util/List<Ljava/lang/Class<*>;>;)[Ljava/lang/Class<*>;
+6 -1: which=
+46 -1: (Ljava/math/BigInteger;)Ljava/math/BigInteger;
+4 -1: date
+18 -1: internalMemberName
+6 -1: (JJI)Z
+30 -1: [Ljava/lang/invoke/LambdaForm;
+60 -1: <T:Ljava/lang/Object;V:Ljava/lang/Object;>Ljava/lang/Object;
+15 -1: ReservationNode
+42 -1: java/lang/ThreadLocal$ThreadLocalMap$Entry
+6 -1: setIn0
+4 -1: sort
+8 -1: ibm00858
+110 -1: (Ljava/lang/String;Ljava/nio/ByteBuffer;IILjava/security/ProtectionDomain;Ljava/lang/String;)Ljava/lang/Class;
+28 -1: Ljava/lang/ClassValue$Entry;
+22 -1: ensureExplicitCapacity
+6 -1: rotate
+14 -1: closeRequested
+30 -1: ([CII)Ljava/lang/StringBuffer;
+10 -1: LM_UNKNOWN
+15 -1: Comparable.java
+13 -1: getByteBuffer
+9 -1: getScheme
+15 -1: done with meta!
+17 -1: checkForTypeAlias
+7 -1: getKeys
+7 -1: SIG_DFL
+30 -1: Ljava/nio/charset/CoderResult;
+16 -1: returnTypesMatch
+19 -1: getClassAccessFlags
+18 -1: JavaNioAccess.java
+9 -1: setDouble
+23 -1: Ljava/util/zip/ZipFile;
+83 -1: (JLjava/util/function/ToIntFunction<-TK;>;ILjava/util/function/IntBinaryOperator;)I
+11 -1: ACCESS_READ
+15 -1: nativeByteOrder
+5 -1: hours
+7 -1: toArray
+7 -1: Encoder
+12 -1: resolveClass
+29 -1: (Ljava/io/FileDescriptor;JJ)V
+14 -1: redefinedCount
+8 -1: getTotal
+11 -1: iso_8859-13
+11 -1: iso_8859-15
+9 -1: expected 
+18 -1: getDeclaredMethods
+11 -1: elementData
+6 -1: intern
+10 -1: countryKey
+6 -1: setInt
+39 -1: Could not create extension class loader
+24 -1: SELF_SUPPRESSION_MESSAGE
+14 -1: argToSlotTable
+42 -1: Ljava/util/HashMap<TE;Ljava/lang/Object;>;
+5 -1:  \t\n\r\x0c
+4 -1: read
+12 -1: Objects.java
+7 -1: aliases
+29 -1: sun/reflect/LangReflectAccess
+6 -1: prefix
+15 -1: superInterfaces
+10 -1: getDoInput
+30 -1: java/nio/CharBufferSpliterator
+6 -1: KOI8_R
+12 -1: Asia/Kolkata
+6 -1: KOI8_U
+6 -1: LOCSIG
+15 -1: UA-Java-Version
+92 -1: <K:Ljava/lang/Object;V:Ljava/lang/Object;>Ljava/util/HashMap<TK;TV;>;Ljava/util/Map<TK;TV;>;
+14 -1: CertificateRep
+17 -1: getSystemResource
+85 -1: (JLjava/util/function/ToLongFunction<-TV;>;JLjava/util/function/LongBinaryOperator;)J
+27 -1: java/lang/reflect/Parameter
+5 -1: quote
+8 -1: not MH: 
+46 -1: java/util/Collections$UnmodifiableCollection$1
+6 -1: putVal
+6 -1: LOCSIZ
+6 -1: Atomic
+3 -1: 737
+38 -1: java/lang/UnsupportedClassVersionError
+27 -1: ()Ljava/lang/StringBuilder;
+41 -1: sun/net/www/protocol/jar/JarURLConnection
+60 -1: (Ljava/lang/String;[Ljava/lang/Object;)Ljava/util/Formatter;
+59 -1: <T:Ljava/lang/Object;>(TT;TT;Ljava/util/Comparator<-TT;>;)I
+54 -1: java/util/concurrent/locks/AbstractOwnableSynchronizer
+7 -1: getHost
+36 -1: (F)Ljava/lang/AbstractStringBuilder;
+69 -1: <U:Ljava/lang/Object;>(Ljava/lang/Class<TU;>;)Ljava/lang/Class<+TU;>;
+4 -1: Form
+103 -1: <K:Ljava/lang/Object;V:Ljava/lang/Object;>(Ljava/util/SortedMap<TK;+TV;>;)Ljava/util/SortedMap<TK;TV;>;
+6 -1: spread
+8 -1: addHours
+13 -1: contentEquals
+47 -1: (Ljava/lang/String;Ljava/security/Permission;)V
+12 -1: newCondition
+23 -1: (Ljava/lang/Object;IZ)V
+26 -1: (Ljava/util/LinkedList;I)V
+13 -1: ConstantValue
+18 -1: URLConnection.java
+12 -1: Boolean.java
+75 -1: ([Ljava/net/URL;Ljava/lang/ClassLoader;Ljava/net/URLStreamHandlerFactory;)V
+21 -1: EMPTY_THROWABLE_ARRAY
+153 -1: (Ljava/util/Map<Ljava/lang/Class<*>;[Ljava/lang/String;>;Ljava/lang/Class<*>;[Ljava/lang/String;)Ljava/util/Map<Ljava/lang/Class<*>;[Ljava/lang/String;>;
+18 -1: getUnresolvedCerts
+13 -1: Negative time
+28 -1: java/util/WeakHashMap$KeySet
+8 -1: slashify
+16 -1: isOtherLowercase
+17 -1: putObjectVolatile
+5 -1: ERASE
+12 -1: filterFields
+40 -1: Ljava/lang/ReflectiveOperationException;
+12 -1: VM settings:
+57 -1: (ILjava/lang/Object;)Ljava/util/HashMap$TreeNode<TK;TV;>;
+10 -1: access$600
+11 -1: ] return =>
+13 -1: user.timezone
+23 -1: USER_AGENT_JAVA_VERSION
+27 -1: (Ljava/util/HashMap$Node;)V
+19 -1: filterNTLMResponses
+28 -1: (Lsun/misc/VMNotification;)V
+37 -1: ()[[Ljava/lang/annotation/Annotation;
+45 -1: ()Lcom/sun/management/DiagnosticCommandMBean;
+3 -1: tan
+31 -1: getDirectlyAndIndirectlyPresent
+7 -1: prepend
+35 -1: (I)Lsun/util/calendar/CalendarDate;
+8 -1: val$dirs
+4 -1: test
+28 -1: Non-positive maxCharsPerByte
+83 -1: <K:Ljava/lang/Object;V:Ljava/lang/Object;>Ljava/lang/Object;Ljava/util/Map<TK;TV;>;
+12 -1: JAVA_VERSION
+24 -1: ([Ljava/lang/Thread;IZ)I
+70 -1: (Ljava/lang/invoke/LambdaForm$Name;)Ljava/lang/invoke/LambdaForm$Name;
+57 -1: <T:Ljava/lang/Object;>([TT;Ljava/util/Comparator<-TT;>;)V
+42 -1: (Ljava/lang/Class<*>;[I)Ljava/lang/Object;
+27 -1: java/lang/SecurityManager$1
+32 -1: java/security/SignatureException
+27 -1: java/lang/SecurityManager$2
+4 -1: .jar
+20 -1: parameterAnnotations
+31 -1: DIRECTIONALITY_BOUNDARY_NEUTRAL
+21 -1: hasClassPathAttribute
+17 -1: checkParentAccess
+35 -1: java/security/PermissionsEnumerator
+6 -1: FORMAT
+92 -1: <U:Ljava/lang/Object;>(JLjava/util/function/Function<Ljava/util/Map$Entry<TK;TV;>;+TU;>;)TU;
+3 -1: 775
+11 -1: PROBE_LIMIT
+111 -1: (Ljava/lang/Class;Ljava/lang/Class;Ljava/lang/String;)Ljava/util/concurrent/atomic/AtomicReferenceFieldUpdater;
+11 -1: AF_PUTFIELD
+22 -1: (Ljava/lang/Object;Z)V
+20 -1: getGenericReturnType
+9 -1: val$extcl
+13 -1: inClassLoader
+37 -1: sun.urlClassLoader.readClassBytesTime
+35 -1: (JLjava/util/function/BiConsumer;)V
+28 -1: getContentHandlerPkgPrefixes
+10 -1: getChannel
+78 -1: <T:Ljava/lang/Object;>(Ljava/util/List<+TT;>;TT;Ljava/util/Comparator<-TT;>;)I
+16 -1: parseMemberValue
+4 -1: regn
+47 -1: ([Ljava/lang/Object;III)Ljava/util/Spliterator;
+11 -1:  but found 
+6 -1: adjust
+11 -1: isLowerCase
+29 -1: sun/reflect/ReflectionFactory
+98 -1: <E:Ljava/lang/Object;>(Ljava/util/SortedSet<TE;>;Ljava/lang/Class<TE;>;)Ljava/util/SortedSet<TE;>;
+18 -1: [Ljava/lang/Error;
+5 -1: entry
+14 -1: refreshVersion
+8 -1: (IIIII)V
+22 -1: unmodifiableCollection
+6 -1: putAll
+22 -1: offsetByCodePointsImpl
+26 -1: (Ljava/lang/String;[BII)[C
+46 -1: (Ljava/net/URLClassLoader;Ljava/lang/String;)V
+4 -1: /LF=
+9 -1: Bits.java
+30 -1: [Ljava/util/WeakHashMap$Entry;
+19 -1: getLastModifiedTime
+44 -1: [Ljava/lang/Thread$UncaughtExceptionHandler;
+11 -1: getZoneInfo
+6 -1: lookup
+19 -1: MapReduceValuesTask
+18 -1: isVarargsCollector
+38 -1: java/util/jar/JarFile$JarEntryIterator
+11 -1: getJarIndex
+9 -1: getByName
+42 -1: (Ljava/lang/Object;JLjava/lang/Object;JJ)V
+71 -1: (Ljava/lang/invoke/LambdaForm$Name;I)Ljava/lang/invoke/LambdaForm$Name;
+30 -1: java/net/ContentHandlerFactory
+54 -1: (Ljava/lang/Class<*>;I)Ljava/lang/invoke/MethodHandle;
+12 -1: getSignature
+9 -1: parseLong
+25 -1: DEBUG_METHOD_HANDLE_NAMES
+15 -1: runFinalization
+13 -1: 0000000000000
+28 -1: ()[Ljava/lang/reflect/Field;
+37 -1: ([Ljava/lang/ClassValue$Entry<*>;II)V
+13 -1: gcInfoBuilder
+64 -1: (JLjava/util/function/BiFunction;Ljava/util/function/Consumer;)V
+5 -1: cnfe1
+8 -1: setShort
+28 -1: (C)Ljava/lang/StringBuilder;
+44 -1: (Ljava/nio/LongBuffer;)Ljava/nio/LongBuffer;
+70 -1: (Ljava/lang/String;[BIILjava/security/CodeSource;)Ljava/lang/Class<*>;
+33 -1: java/lang/TypeNotPresentException
+5 -1: \n    
+20 -1: acquireInterruptibly
+21 -1: (I)Ljava/lang/String;
+24 -1: (Ljava/io/PrintWriter;)V
+16 -1: convertArguments
+32 -1: Ljava/net/MalformedURLException;
+15 -1: linkToInterface
+39 -1: java/lang/Throwable$PrintStreamOrWriter
+10 -1: iso8859_13
+13 -1: hasPrimitives
+10 -1: iso8859_15
+145 -1: (Ljava/lang/invoke/MethodHandle;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/Object;Ljava/lang/Class<*>;)Ljava/lang/invoke/CallSite;
+8 -1: equalPDs
+28 -1: (Ljava/io/FileDescriptor;J)V
+7 -1: newLine
+43 -1: (Ljava/lang/Class<*>;I)Ljava/lang/Class<*>;
+8 -1: addEntry
+30 -1: java/util/WeakHashMap$EntrySet
+14 -1: USE_OLDMAPPING
+39 -1: (Ljava/io/DataInput;)Ljava/lang/String;
+12 -1: LF_EX_LINKER
+27 -1: java/lang/invoke/MethodType
+23 -1: JavaSecurityAccess.java
+23 -1: isLocalOrAnonymousClass
+19 -1: Expanded arguments:
+18 -1: sun/nio/cs/Unicode
+40 -1: ()Ljava/nio/charset/spi/CharsetProvider;
+23 -1: ([BLjava/lang/String;)V
+7 -1: default
+13 -1: highestOneBit
+9 -1: isDefault
+28 -1: (IF)Ljava/lang/StringBuffer;
+31 -1: ()Ljava/util/ListIterator<TE;>;
+4 -1: base
+23 -1: newPermissionCollection
+7 -1: version
+15 -1: Permission.java
+41 -1: java/lang/invoke/LambdaForm$NamedFunction
+8 -1: isQueued
+24 -1: ([Ljava/lang/Class<*>;)I
+64 -1: java/util/concurrent/ConcurrentHashMap$MapReduceEntriesToIntTask
+16 -1: checkInitialized
+37 -1: java/lang/ClassLoader$ParallelLoaders
+5 -1: ([B)I
+23 -1: Lsun/misc/URLClassPath;
+9 -1: usr_paths
+10 -1: Queue.java
+43 -1: (Ljava/io/File;Ljava/nio/charset/Charset;)V
+64 -1: (Ljava/lang/invoke/MethodTypeForm;)Ljava/lang/invoke/MemberName;
+3 -1: tid
+23 -1: JarIndex-Version: 1.0\n\n
+33 -1: (I)Ljava/nio/charset/CoderResult;
+5 -1: ([B)V
+10 -1: isInstance
+25 -1: unmappableCharacterAction
+11 -1: queueLength
+5 -1: ([B)Z
+10 -1: freeMemory
+47 -1: java/util/ArrayPrefixHelpers$DoubleCumulateTask
+52 -1: (Ljava/util/Map;Ljava/lang/Class;Ljava/lang/Class;)V
+41 -1: Ljava/util/Collections$ReverseComparator;
+16 -1: copyToShortArray
+206 -1: <K:Ljava/lang/Object;V:Ljava/lang/Object;>([Ljava/util/concurrent/ConcurrentHashMap$Node<TK;TV;>;ILjava/util/concurrent/ConcurrentHashMap$Node<TK;TV;>;Ljava/util/concurrent/ConcurrentHashMap$Node<TK;TV;>;)Z
+38 -1: sun/launcher/LauncherHelper$SizePrefix
+17 -1: ACCESS_PERMISSION
+17 -1: ReverseComparator
+30 -1: ()Ljava/lang/ClassValue$Entry;
+17 -1: AF_PUTSTATIC_INIT
+6 -1: (IIB)I
+19 -1: java/nio/file/Files
+35 -1: (Z)[Ljava/lang/reflect/Constructor;
+20 -1: INVALID_FIELD_OFFSET
+17 -1: initializeHeaders
+10 -1: management
+10 -1: targetType
+61 -1: (Ljava/util/SortedSet;Ljava/lang/Class;)Ljava/util/SortedSet;
+5 -1: ascii
+8 -1: validate
+78 -1: Ljava/util/concurrent/ConcurrentHashMap<Ljava/lang/String;Ljava/lang/Object;>;
+25 -1: sun/nio/cs/UTF_16$Decoder
+36 -1: sun/management/DiagnosticCommandImpl
+24 -1: unmodifiableNavigableMap
+18 -1: canonicalizeScript
+29 -1: Lsun/misc/JavaSecurityAccess;
+74 -1: ([JLjava/util/function/IntToLongFunction;)Ljava/util/function/IntConsumer;
+38 -1: DIRECTIONALITY_LEFT_TO_RIGHT_EMBEDDING
+11 -1: languageTag
+33 -1: java/lang/invoke/VolatileCallSite
+18 -1: setRequestProperty
+6 -1: 0x%02X
+20 -1: (Ljava/lang/Float;)I
+35 -1: (Ljava/nio/charset/CoderResult$1;)V
+24 -1: (Ljava/lang/Object;JJB)V
+21 -1: synchronizedSortedSet
+59 -1: (Ljava/util/function/ToLongFunction;)Ljava/util/Comparator;
+30 -1: av.length == arity: av.length=
+7 -1: $VALUES
+27 -1: RandomNumberGeneratorHolder
+3 -1: tlr
+43 -1: java/util/ArraysParallelSortHelpers$FJFloat
+28 -1: ()[Ljava/io/File$PathStatus;
+26 -1: invalid extra field length
+13 -1: getExtensions
+7 -1: PARAMS0
+7 -1: PARAMS1
+30 -1: [Ljava/lang/invoke/MemberName;
+7 -1: PARAMS2
+31 -1: java/lang/AbstractStringBuilder
+161 -1: (Ljava/util/concurrent/ConcurrentHashMap$BulkTask;III[Ljava/util/concurrent/ConcurrentHashMap$Node;Ljava/util/function/BiFunction;Ljava/util/function/Consumer;)V
+4 -1: repl
+19 -1: ()Ljava/lang/Class;
+24 -1: BufferedInputStream.java
+9 -1: sizeCache
+8 -1: READLINK
+9 -1: metaIndex
+18 -1: getLocalizedObject
+6 -1: filter
+58 -1: (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;I)V
+140 -1: (Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/invoke/MemberName;
+24 -1: Ljava/util/HashMap$Node;
+35 -1: (Lsun/nio/cs/FastCharsetProvider;)V
+8 -1: dispatch
+19 -1: sun.stderr.encoding
+130 -1: (Ljava/util/List<Ljava/util/Locale$LanguageRange;>;Ljava/util/Collection<Ljava/lang/String;>;)Ljava/util/List<Ljava/lang/String;>;
+51 -1: Ljava/util/concurrent/ConcurrentHashMap$ValuesView;
+30 -1: sun.reflect.inflationThreshold
+20 -1: MutableCallSite.java
+26 -1: invokeWithArgumentsTracing
+7 -1: getters
+38 -1: ()[Ljava/lang/reflect/TypeVariable<*>;
+46 -1: ([DLjava/util/function/DoubleBinaryOperator;)V
+5 -1: klass
+13 -1: publicMethods
+37 -1: ()[Ljava/lang/reflect/Constructor<*>;
+126 -1: (Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodHandle;)Ljava/lang/invoke/MethodHandle;
+6 -1: FJLong
+20 -1: canBeStaticallyBound
+17 -1: getTimezoneOffset
+9 -1: ALL_TYPES
+3 -1: toV
+8 -1: packages
+15 -1: codePointBefore
+10 -1: getCountry
+13 -1: getDSTSavings
+100 -1: (Ljava/io/InputStream;Ljava/lang/Object;Ljava/nio/charset/CharsetDecoder;)Lsun/nio/cs/StreamDecoder;
+50 -1: java/util/ArraysParallelSortHelpers$FJFloat$Sorter
+20 -1: hasNonVoidPrimitives
+7 -1: syncAll
+41 -1: domain        dump all domains in context
+59 -1: Ljava/util/Hashtable<Ljava/lang/Integer;Lsun/misc/Signal;>;
+16 -1: ForEachEntryTask
+18 -1: vminfoIsConsistent
+26 -1: (ZLjava/util/Comparator;)V
+9 -1: Lock.java
+31 -1: Lsun/reflect/ReflectionFactory;
+8 -1: (I[BII)I
+23 -1: doesExtendFXApplication
+87 -1: java/util/concurrent/atomic/AtomicReferenceFieldUpdater$AtomicReferenceFieldUpdaterImpl
+11 -1: windows-31j
+28 -1: sun/misc/URLClassPath$Loader
+17 -1: getEntryAfterMiss
+47 -1: ([Ljava/lang/reflect/Field;Ljava/lang/String;)J
+44 -1: Ljava/util/List<Ljava/security/Permission;>;
+10 -1: openStream
+31 -1: Ljava/net/UnknownHostException;
+19 -1: bad reference kind 
+29 -1: ()Ljava/nio/MappedByteBuffer;
+9 -1: H_UPALPHA
+37 -1: (Ljava/util/function/UnaryOperator;)V
+25 -1: FAKE_METHOD_HANDLE_INVOKE
+4 -1: peek
+25 -1: java/util/Hashtable$Entry
+18 -1: getMemberRefInfoAt
+37 -1: Ljava/util/WeakHashMap$Entry<TK;TV;>;
+14 -1: resolvedHandle
+27 -1: ()Ljava/util/Iterator<TV;>;
+61 -1: Ljava/util/Map<Ljava/lang/String;Ljava/lang/reflect/Method;>;
+6 -1: static
+50 -1: (Ljava/net/URLClassLoader;)Lsun/misc/URLClassPath;
+38 -1: (Ljava/lang/Class;)[Ljava/lang/Object;
+41 -1: (Ljava/util/SortedSet;Ljava/lang/Class;)V
+41 -1: java/lang/invoke/WrongMethodTypeException
+5 -1: group
+10 -1: readObject
+13 -1: getParentFile
+14 -1: daylightSaving
+15 -1: eagerValidation
+36 -1: (Ljava/io/File;)Lsun/misc/MetaIndex;
+18 -1: reduceEntriesToInt
+15 -1: INITIAL_ENTRIES
+18 -1: AtomicInteger.java
+7 -1: .length
+128 -1: Ljava/nio/Buffer;Ljava/lang/Comparable<Ljava/nio/CharBuffer;>;Ljava/lang/Appendable;Ljava/lang/CharSequence;Ljava/lang/Readable;
+6 -1: asList
+21 -1: unmodifiableSortedSet
+22 -1: ([B)Ljava/util/BitSet;
+5 -1: check
+64 -1: (Ljava/util/jar/JarFile;Lsun/misc/MetaIndex;)Lsun/misc/JarIndex;
+35 -1: ()Ljava/nio/charset/CharsetEncoder;
+4 -1: oome
+25 -1: ()Lsun/misc/URLClassPath;
+27 -1: (Ljava/io/FilePermission;)V
+29 -1: IllegalArgumentException.java
+17 -1: jvmSpecialVersion
+21 -1: Ljava/util/ArrayList;
+13 -1: packagePrefix
+27 -1: (Ljava/io/FilePermission;)Z
+11 -1: canonicalID
+82 -1: Ljava/lang/Object;Ljava/util/Comparator<Ljava/lang/String;>;Ljava/io/Serializable;
+24 -1: java.launcher.opt.footer
+13 -1: NativeLibrary
+37 -1: (Ljava/lang/String;J)Ljava/lang/Long;
+16 -1: longBitsToDouble
+6 -1: getKey
+22 -1: (JLjava/lang/String;)V
+14 -1: ensureCapacity
+69 -1: (Ljava/lang/Object;Ljava/util/function/BiFunction;)Ljava/lang/Object;
+22 -1: java/lang/Thread$State
+50 -1: ()Ljava/util/Iterator<Ljava/nio/charset/Charset;>;
+4 -1: 7bit
+46 -1: (Ljava/lang/Class<+Ljava/lang/ClassLoader;>;)Z
+76 -1: (ILjava/util/List<Ljava/lang/Class<*>;>;)[Ljava/lang/invoke/LambdaForm$Name;
+3 -1: ttb
+12 -1: UTF_16LE_BOM
+9 -1: remainder
+40 -1: ()Lsun/reflect/generics/visitor/Reifier;
+22 -1: [Ljava/lang/Throwable;
+17 -1: EMPTY_ENUMERATION
+13 -1: erasedInvoker
+46 -1: Ljava/nio/charset/IllegalCharsetNameException;
+10 -1: UnicodeBig
+53 -1: ()Ljava/util/Map<Ljava/io/File;Lsun/misc/MetaIndex;>;
+12 -1: MAX_EXPONENT
+10 -1: Enumerator
+15 -1: charset decoder
+11 -1: AF_GETFIELD
+12 -1: | getInvoker
+74 -1: (Ljava/nio/ByteBuffer;Ljava/nio/CharBuffer;)Ljava/nio/charset/CoderResult;
+14 -1: toUnsignedLong
+46 -1: java/lang/invoke/MethodHandleNatives$Constants
+29 -1: java version "1.8.0-internal"
+21 -1: ([Ljava/lang/Class;)I
+16 -1: UPPERCASE_LETTER
+22 -1: newConstantPerfCounter
+6 -1: signum
+9 -1: getField0
+38 -1: java/nio/charset/CoderMalfunctionError
+21 -1: [Ljava/lang/Runnable;
+11 -1: putIfAbsent
+30 -1: java/util/Collections$EmptySet
+22 -1: (I)[Ljava/lang/String;
+34 -1: Ljava/security/SecurityPermission;
+49 -1: ([Ljava/lang/Class;)Ljava/lang/invoke/MethodType;
+190 -1: (Ljava/util/concurrent/ConcurrentHashMap$BulkTask;III[Ljava/util/concurrent/ConcurrentHashMap$Node;Ljava/util/concurrent/ConcurrentHashMap$ReduceEntriesTask;Ljava/util/function/BiFunction;)V
+22 -1: Not an annotation type
+34 -1: java/io/ObjectInputStream$GetField
+50 -1: (Lsun/reflect/FieldInfo;)Ljava/lang/reflect/Field;
+32 -1: Ljava/lang/NoSuchFieldException;
+70 -1: (Ljava/lang/invoke/MethodHandle;[Ljava/lang/Object;)Ljava/lang/Object;
+9 -1: mergeSort
+77 -1: (ITK;TV;Ljava/util/HashMap$Node<TK;TV;>;)Ljava/util/HashMap$TreeNode<TK;TV;>;
+21 -1: sun/nio/cs/ISO_8859_1
+8 -1: DST_MASK
+21 -1: Ljava/lang/Throwable;
+108 -1: (Ljava/lang/ref/SoftReference<Ljava/lang/Class$ReflectionData<TT;>;>;I)Ljava/lang/Class$ReflectionData<TT;>;
+32 -1: java/util/Arrays$LegacyMergeSort
+59 -1: ()[Ljava/lang/reflect/TypeVariable<Ljava/lang/Class<TT;>;>;
+16 -1: parseUnsignedInt
+36 -1: ([D)Ljava/util/Spliterator$OfDouble;
+26 -1: SPECIFY_HANDLER_PERMISSION
+13 -1: primitiveType
+17 -1: threadStartFailed
+21 -1: (J)Ljava/lang/String;
+23 -1: setClassAssertionStatus
+28 -1: java/util/Hashtable$EntrySet
+28 -1: Non-positive maxBytesPerChar
+19 -1: getApplicationClass
+14 -1: SentinelHolder
+15 -1: staticFieldBase
+25 -1: setDefaultRequestProperty
+66 -1: java/util/concurrent/ConcurrentHashMap$ForEachTransformedValueTask
+8 -1: threadID
+9 -1: getFields
+15 -1: LineNumberTable
+38 -1: java/util/Collections$CheckedSortedSet
+14 -1: jdkBuildNumber
+6 -1: divide
+46 -1: (Ljava/io/BufferedWriter;Ljava/lang/String;Z)V
+20 -1: java/lang/Terminator
+92 -1: (Lsun/misc/URLClassPath$JarLoader;Ljava/lang/String;Ljava/net/URL;Ljava/util/jar/JarEntry;)V
+6 -1: force0
+10 -1: getThreads
+34 -1: java/util/IllformedLocaleException
+115 -1: (Ljava/lang/String;Lsun/reflect/generics/factory/GenericsFactory;)Lsun/reflect/generics/repository/FieldRepository;
+9 -1: testFlags
+11 -1: getLanguage
+36 -1: java/util/function/IntBinaryOperator
+12 -1: Suppressed: 
+10 -1: isMandated
+23 -1: MethodAccessorImpl.java
+28 -1: (Ljava/util/zip/ZipEntry;)[B
+27 -1: Ljava/util/Hashtable$Entry;
+5 -1: table
+10 -1: Short.java
+19 -1: ReferenceQueue.java
+8 -1: setTabAt
+26 -1: ()Lsun/invoke/empty/Empty;
+53 -1: (Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum;
+74 -1: (ILjava/lang/Object;)Ljava/util/concurrent/ConcurrentHashMap$Node<TK;TV;>;
+19 -1: changeParameterType
+61 -1: Ljava/util/Map<Ljava/lang/String;Ljava/security/Permission;>;
+23 -1: (Ljava/lang/Object;IF)V
+32 -1: (I)Ljava/util/ListIterator<TE;>;
+6 -1: unread
+12 -1: isSubclassOf
+6 -1: (JJJ)V
+3 -1: Key
+105 -1: (Ljava/util/HashMap<TK;TV;>;[Ljava/util/HashMap$Node<TK;TV;>;ITK;TV;)Ljava/util/HashMap$TreeNode<TK;TV;>;
+14 -1: x-utf-16le-bom
+22 -1: ()Ljava/nio/IntBuffer;
+104 -1: (Ljava/lang/Class;ILjava/lang/Class;Ljava/lang/String;Ljava/lang/Object;)Ljava/lang/invoke/MethodHandle;
+21 -1: removeFirstOccurrence
+21 -1: sun/misc/DoubleConsts
+23 -1: ()Ljava/util/SortedSet;
+11 -1: getManEntry
+23 -1: URI is not hierarchical
+7 -1: replace
+16 -1: getDisplayScript
+11 -1: ISO_8859-15
+16 -1: permuteArguments
+5 -1: (JI)C
+41 -1: Error decoding percent encoded characters
+22 -1: ([I)Ljava/lang/String;
+31 -1: java/lang/management/ThreadInfo
+9 -1: useCaches
+22 -1: withInternalMemberName
+5 -1: (JI)I
+5 -1: (JI)J
+67 -1: <E:Ljava/lang/Object;>Ljava/lang/Object;Ljava/util/Collection<TE;>;
+95 -1: (Ljava/util/jar/JarFile;[Ljava/security/CodeSource;)Ljava/util/Enumeration<Ljava/lang/String;>;
+7 -1: release
+56 -1: (Ljava/lang/Object;Ljava/lang/String;)Ljava/lang/String;
+5 -1: (JI)V
+46 -1: (Ljava/util/Iterator;I)Ljava/util/Spliterator;
+18 -1: verifyMemberAccess
+9 -1: warning: 
+23 -1: java/lang/reflect/Field
+67 -1: (Ljava/lang/String;Lsun/reflect/generics/factory/GenericsFactory;)V
+10 -1: SizePrefix
+15 -1: setJavaIOAccess
+6 -1: (JI)[B
+10 -1: ST_FLUSHED
+10 -1: resolution
+9 -1: ST_CODING
+16 -1: sun/misc/Cleaner
+21 -1: (Ljava/lang/Class;)[B
+18 -1: WeakReference.java
+41 -1: (Ljava/util/List;Ljava/util/Comparator;)V
+18 -1: printPropertyValue
+3 -1: 813
+22 -1: setRunFinalizersOnExit
+4 -1: init
+44 -1: ()Ljava/util/Iterator<Ljava/nio/file/Path;>;
+3 -1: 819
+12 -1: listIterator
+8 -1: , arity=
+24 -1: (Ljava/util/ArrayList;)I
+49 -1: (IJLjava/io/FileDescriptor;Ljava/lang/Runnable;)V
+10 -1: principals
+17 -1: x-ISO-2022-CN-CNS
+22 -1: (Ljava/lang/Object;F)V
+14 -1: setReadTimeout
+19 -1: getProtectionDomain
+13 -1: pathSeparator
+12 -1: getAndSetInt
+58 -1: (Ljava/util/List;Ljava/util/Collection;)Ljava/util/Locale;
+11 -1: setWritable
+4 -1: perf
+50 -1: ()Ljava/util/concurrent/ConcurrentHashMap<TK;TV;>;
+6 -1: LOCTIM
+6 -1: status
+11 -1: replaceName
+52 -1: (Ljava/lang/CharSequence;II)Ljava/lang/StringBuffer;
+9 -1: nextToken
+13 -1: dropArguments
+20 -1: RECOGNIZED_MODIFIERS
+14 -1: getInputStream
+9 -1: readFully
+25 -1: (CLjava/nio/CharBuffer;)I
+24 -1: sun/misc/PathPermissions
+10 -1: malformedN
+9 -1: n is null
+19 -1: instanceof Double: 
+13 -1: markSupported
+26 -1: fromMethodDescriptorString
+43 -1: [Ljava/util/concurrent/locks/ReentrantLock;
+17 -1: parseUnsignedLong
+33 -1: Lsun/misc/URLClassPath$JarLoader;
+26 -1: (Ljava/io/OutputStream;I)V
+6 -1: L_DASH
+17 -1: EmptyNavigableMap
+19 -1: CharsetDecoder.java
+18 -1: makeDynamicInvoker
+12 -1: URLUtil.java
+27 -1: ()Ljava/util/Iterator<TT;>;
+9 -1: initTable
+11 -1: getFragment
+7 -1: isLower
+20 -1: getMethodOrFieldType
+29 -1: java/util/function/BiFunction
+10 -1: access$700
+3 -1: 850
+7 -1: UTC2037
+43 -1: java/util/ArraysParallelSortHelpers$FJShort
+9 -1: toSTZTime
+10 -1: access$702
+3 -1: 852
+8 -1: hashCode
+3 -1: 855
+44 -1: (Ljava/lang/ThreadLocal;Ljava/lang/Object;)V
+3 -1: 857
+3 -1: 858
+5 -1: erase
+55 -1: ()Ljava/lang/Class<+Ljava/lang/annotation/Annotation;>;
+47 -1: (Ljava/util/Iterator;JI)Ljava/util/Spliterator;
+38 -1: Ljava/nio/charset/spi/CharsetProvider;
+22 -1: can't deserialize enum
+13 -1: LF_EX_INVOKER
+18 -1: java/text/Collator
+18 -1: Zero length string
+49 -1: <T:Ljava/lang/Object;>()Ljava/util/Iterator<TT;>;
+5 -1: amd64
+12 -1: getNameCount
+7 -1: inCheck
+52 -1: (Ljava/util/concurrent/ConcurrentHashMap$TreeNode;)V
+53 -1: (ILjava/lang/CharSequence;II)Ljava/lang/StringBuffer;
+21 -1: java/util/WeakHashMap
+8 -1:  throws 
+52 -1: (Ljava/util/concurrent/ConcurrentHashMap$TreeNode;)Z
+55 -1: Ljava/lang/ref/SoftReference<Ljava/util/jar/Manifest;>;
+16 -1: emptyEnumeration
+3 -1: 862
+89 -1: (Ljava/lang/Class;Ljava/lang/String;Ljava/lang/Object;ILjava/lang/Class;)Ljava/util/List;
+3 -1: 866
+55 -1: Lsun/reflect/generics/repository/ConstructorRepository;
+35 -1: (Ljava/lang/ref/ReferenceQueue$1;)V
+33 -1: java/util/Collections$CheckedList
+18 -1: prefetchReadStatic
+7 -1: (JI[I)I
+78 -1: (Ljava/lang/ThreadLocal$ThreadLocalMap;)Ljava/lang/ThreadLocal$ThreadLocalMap;
+7 -1: ordinal
+22 -1: FilterInputStream.java
+26 -1: java/util/zip/ZipConstants
+28 -1: JVM cannot find invoker for 
+43 -1: sun/reflect/generics/parser/SignatureParser
+51 -1: (Ljava/lang/invoke/MethodType;[Ljava/lang/Object;)V
+73 -1: (Ljava/util/function/ToIntFunction;Ljava/lang/Object;Ljava/lang/Object;)I
+17 -1: StringBuffer.java
+62 -1: ([Ljava/lang/Object;Ljava/lang/StringBuilder;Ljava/util/Set;)V
+6 -1: equals
+9 -1: formatter
+3 -1: 874
+35 -1: newGetShortIllegalArgumentException
+74 -1: (Ljava/nio/CharBuffer;Ljava/nio/ByteBuffer;)Ljava/nio/charset/CoderResult;
+3 -1: ucp
+60 -1: ([Ljava/lang/ClassValue$Entry;I)Ljava/lang/ClassValue$Entry;
+6 -1: create
+17 -1: makeReinvokerForm
+18 -1: csisolatincyrillic
+15 -1: incrementAndGet
+24 -1: maybeInstantiateVerifier
+33 -1: ()Ljava/nio/channels/FileChannel;
+6 -1: class 
+16 -1: getAnnotatedType
+43 -1: (Ljava/lang/reflect/Type;)Ljava/lang/Class;
+9 -1: HASH_BITS
+12 -1: placeInCache
+38 -1: java/util/Collections$SynchronizedList
+89 -1: (Ljava/net/URL;Ljava/util/jar/JarFile;Ljava/util/jar/JarEntry;)Ljava/security/CodeSource;
+22 -1: (Ljava/lang/String;I)B
+20 -1: Ljava/util/TimeZone;
+16 -1: sun.java.command
+28 -1: java/util/WeakHashMap$Values
+10 -1: X-UTF-16BE
+22 -1: (Ljava/lang/String;I)I
+26 -1: java/nio/DirectCharBufferS
+99 -1: (Ljava/lang/String;[BIILjava/lang/ClassLoader;Ljava/security/ProtectionDomain;)Ljava/lang/Class<*>;
+22 -1: (Ljava/lang/String;I)J
+26 -1: java/nio/DirectCharBufferU
+54 -1: (Ljava/util/function/Supplier;)Ljava/lang/ThreadLocal;
+21 -1: java/util/AbstractSet
+37 -1: (Ljava/lang/String;)Ljava/lang/Short;
+36 -1: Ljava/nio/charset/CoderResult$Cache;
+22 -1: (Ljava/lang/String;I)S
+15 -1: Ljava/util/Map;
+59 -1: (Ljava/lang/reflect/Type;)Ljava/lang/reflect/AnnotatedType;
+22 -1: (Ljava/lang/String;I)V
+10 -1: getAddress
+25 -1: java/nio/DirectIntBufferS
+11 -1: IMPL_LOOKUP
+3 -1: uee
+7 -1: addTime
+37 -1: sun/security/action/GetPropertyAction
+25 -1: java/nio/DirectIntBufferU
+21 -1: javaUtilZipFileAccess
+34 -1: java/util/Collections$CheckedQueue
+11 -1: readResolve
+22 -1: (Ljava/lang/String;I)Z
+11 -1: findVirtual
+63 -1: (Ljava/lang/String;Ljava/lang/String;)Lsun/security/util/Debug;
+22 -1: getDeclaredAnnotations
+16 -1: Collections.java
+18 -1: invalid entry size
+49 -1: java/util/concurrent/ConcurrentHashMap$TableStack
+32 -1: java/util/AbstractSequentialList
+4 -1: int0
+16 -1: MAXIMUM_CAPACITY
+53 -1: ()Ljava/util/concurrent/ConcurrentHashMap$KeySetView;
+4 -1: int1
+4 -1: int2
+4 -1: int3
+119 -1: (Ljava/lang/Class;Ljava/lang/String;[Ljava/lang/Class;Ljava/lang/Class;[Ljava/lang/Class;I)Lsun/reflect/MethodAccessor;
+7 -1: variant
+39 -1: Lsun/reflect/annotation/AnnotationType;
+11 -1: arrayOffset
+24 -1: ()Ljava/util/LinkedList;
+31 -1: java/lang/ClassCircularityError
+17 -1: java/lang/Package
+10 -1: ccsid00858
+27 -1: java/io/ExpiringCache$Entry
+16 -1: newFieldAccessor
+67 -1: (Ljava/lang/Object;Ljava/util/function/Function;)Ljava/lang/Object;
+99 -1: Lsun/reflect/generics/repository/GenericDeclRepository<Lsun/reflect/generics/tree/ClassSignature;>;
+53 -1: (Ljava/lang/invoke/MethodHandle;[Ljava/lang/Object;)V
+53 -1: Ljava/util/concurrent/ConcurrentHashMap$Node<TK;TV;>;
+58 -1: <T:Ljava/lang/Object;>([TT;)Ljava/util/stream/Stream<TT;>;
+54 -1: (Ljava/lang/CharSequence;Ljava/text/Normalizer$Form;)Z
+8 -1: Kerberos
+29 -1: ()Ljava/nio/channels/Channel;
+12 -1: java_version
+45 -1: (Lsun/reflect/DelegatingMethodAccessorImpl;)V
+10 -1: canConvert
+136 -1: (Ljava/lang/StringBuffer;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;)V
+26 -1: getDayOfWeekDateOnOrBefore
+7 -1: INVALID
+10 -1: TERMINATED
+41 -1: Ljava/security/cert/CertificateException;
+74 -1: (Ljava/lang/String;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/String;
+30 -1: [Ljava/lang/invoke/MethodType;
+13 -1: getMethodName
+7 -1: Factory
+34 -1: (Ljava/util/function/BiConsumer;)V
+11 -1: unlinkFirst
+37 -1: lambda$getDeclaredAnnotationsByType$0
+77 -1: (Ljava/nio/ByteBuffer;ILjava/nio/CharBuffer;II)Ljava/nio/charset/CoderResult;
+20 -1: java/util/ArrayDeque
+65 -1: (Ljava/lang/ThreadGroup;Ljava/lang/Runnable;Ljava/lang/String;J)V
+49 -1: (Ljava/util/ArrayDeque;Ljava/util/ArrayDeque$1;)V
+22 -1: (J)Ljava/time/Instant;
+17 -1: URLClassPath.java
+6 -1: 8859_1
+25 -1: stopRemoteManagementAgent
+6 -1: 8859_2
+17 -1: containsNullValue
+6 -1: 8859_4
+6 -1: 8859_5
+26 -1: (Ljava/nio/ByteBuffer;IC)V
+76 -1: (Ljava/lang/Runnable;Ljava/security/AccessControlContext;)Ljava/lang/Thread;
+6 -1: 8859_7
+6 -1: 8859_9
+8 -1: setHours
+9 -1: File.java
+21 -1: isIdentifierIgnorable
+5 -1: ([C)I
+4 -1: (B)I
+10 -1: codesource
+77 -1: ([Ljava/net/URL;Ljava/lang/ClassLoader;Ljava/security/AccessControlContext;)V
+4 -1: (B)J
+6 -1: isFair
+30 -1: java/lang/NullPointerException
+10 -1: IMPL_NAMES
+13 -1: Runnable.java
+26 -1: Ill-formed extension key: 
+17 -1: ()[Ljava/io/File;
+18 -1: javaSecurityAccess
+16 -1: equalsIgnoreCase
+4 -1: (B)V
+5 -1: ([C)V
+25 -1: Ljava/io/FileInputStream;
+14 -1: trackJavaUsage
+20 -1: Ljava/io/FileSystem;
+10 -1: iso_8859_1
+4 -1: (B)Z
+30 -1: java/lang/NoClassDefFoundError
+152 -1: <K:Ljava/lang/Object;V:Ljava/lang/Object;>(Ljava/util/HashMap$TreeNode<TK;TV;>;Ljava/util/HashMap$TreeNode<TK;TV;>;)Ljava/util/HashMap$TreeNode<TK;TV;>;
+19 -1: java/lang/Cloneable
+55 -1: ([Ljava/lang/Object;Ljava/util/function/IntFunction;I)V
+27 -1: (Ljava/nio/ByteBuffer;IJZ)V
+21 -1: ()Lsun/misc/JarIndex;
+72 -1: ([Ljava/security/CodeSource;)Ljava/util/Enumeration<Ljava/lang/String;>;
+64 -1: (Ljava/util/Collection;Ljava/util/Comparator;)Ljava/lang/Object;
+20 -1: invalid entry crc-32
+9 -1: BASECOUNT
+29 -1: java/security/BasicPermission
+52 -1: (Ljava/lang/String;Ljava/lang/String;)Ljava/io/File;
+7 -1: ([B[B)Z
+17 -1: EMPTY_ELEMENTDATA
+61 -1: (Ljava/security/PrivilegedExceptionAction;)Ljava/lang/Object;
+49 -1: (ILjava/lang/Class;)Ljava/lang/invoke/MethodType;
+29 -1: sun/reflect/MagicAccessorImpl
+121 -1: (Ljava/lang/String;Lsun/reflect/generics/factory/GenericsFactory;)Lsun/reflect/generics/repository/ConstructorRepository;
+6 -1: ([II)I
+40 -1: (Ljava/lang/String;I)Ljava/lang/Integer;
+25 -1: java.content.handler.pkgs
+63 -1: ()Ljava/util/Set<Ljava/util/Map$Entry<Ljava/lang/String;TV;>;>;
+13 -1: parameterList
+6 -1: rebind
+16 -1: isSuperInterface
+6 -1: ([II)V
+14 -1: currentRuntime
+9 -1: BA_EXISTS
+15 -1: END_PUNCTUATION
+15 -1: no such method 
+19 -1: getAndVerifyPackage
+4 -1: wrap
+24 -1: checkAwtEventQueueAccess
+7 -1: ibm-437
+4 -1: open
+47 -1: (Ljava/nio/ByteBuffer;[BI)Ljava/nio/ByteBuffer;
+21 -1: ADDRESS_BITS_PER_WORD
+13 -1: isConstructor
+12 -1: getUseCaches
+27 -1: sun/util/locale/LocaleUtils
+4 -1: koi8
+23 -1: getParameterAnnotations
+9 -1: providers
+57 -1: ([Ljava/lang/Object;Ljava/util/function/BinaryOperator;)V
+24 -1: Lsun/misc/JavaNetAccess;
+22 -1: ([J)Ljava/lang/String;
+7 -1: p-1022$
+6 -1: isType
+63 -1: Ljava/util/Map<Ljava/lang/String;Lsun/util/calendar/ZoneInfo;>;
+21 -1: pageAlignDirectMemory
+18 -1: getManifestDigests
+37 -1: [Ljava/lang/reflect/AccessibleObject;
+7 -1: decrypt
+54 -1: (Lsun/misc/URLClassPath$JarLoader;)Ljava/util/HashMap;
+32 -1: lambda$comparingByKey$bbdbfea9$1
+21 -1: hasGenericInformation
+31 -1: java/nio/charset/CharsetEncoder
+15 -1: setTargetNormal
+3 -1: ulp
+18 -1: argumentTypesMatch
+12 -1: getDayOfYear
+8 -1: closeAll
+76 -1: <K:Ljava/lang/Object;V:Ljava/lang/Object;>Ljava/lang/ref/SoftReference<TV;>;
+6 -1: concat
+9 -1: getLongAt
+16 -1: hasBeenFinalized
+32 -1: [Ljava/util/Hashtable$Entry<**>;
+23 -1: CheckedRandomAccessList
+106 -1: (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Ljava/net/URI;
+21 -1: defineClassInPackage.
+11 -1: ([III[III)V
+8 -1: toZoneId
+34 -1: SUBCLASS_IMPLEMENTATION_PERMISSION
+55 -1: java/util/concurrent/atomic/AtomicReferenceFieldUpdater
+8 -1: isDirect
+10 -1: ALL_ACCESS
+12 -1: isRegistered
+29 -1: ForEachTransformedMappingTask
+5 -1: LIJFD
+23 -1: (Ljava/util/TimeZone;)V
+23 -1: (Ljava/util/TimeZone;)Z
+20 -1: java/lang/Appendable
+29 -1: Lsun/util/calendar/Gregorian;
+9 -1: charValue
+8 -1: ONE_HOUR
+38 -1: (Ljava/util/Locale;)Ljava/lang/String;
+7 -1: script=
+10 -1: X-UTF-16LE
+7 -1: ENTRIES
+6 -1: detach
+38 -1: certpath      PKIX CertPathBuilder and
+14 -1: setLanguageTag
+13 -1: isAlphaString
+13 -1: interpretName
+9 -1: dayOfWeek
+88 -1: (Ljava/lang/Class;ZLjava/lang/String;Ljava/lang/Class;Ljava/lang/Class;)Ljava/util/List;
+91 -1: (Ljava/lang/invoke/MethodType;Ljava/lang/invoke/LambdaForm;)Ljava/lang/invoke/MethodHandle;
+13 -1: addTypeString
+17 -1: VectorSpliterator
+12 -1: MILLISECONDS
+6 -1: CENCOM
+10 -1: KeySetView
+18 -1: getTargetException
+7 -1: H_ALPHA
+32 -1: sun/util/locale/BaseLocale$Cache
+25 -1: [Ljava/lang/CharSequence;
+7 -1: Builder
+4 -1: left
+19 -1: BootClassPathHolder
+12 -1: publicFields
+11 -1: windows-437
+9 -1: EMPTY_SET
+26 -1: GET_STACK_TRACE_PERMISSION
+6 -1: copyOf
+14 -1: aliases_IBM737
+9 -1: writeLong
+35 -1: (JLjava/util/concurrent/TimeUnit;)Z
+70 -1: Ljava/lang/Object;Ljava/security/PrivilegedAction<Ljava/lang/String;>;
+22 -1: getAnnotatedReturnType
+41 -1: (Ljava/lang/String;[BII)Ljava/lang/Class;
+8 -1: ,maxpri=
+29 -1: handleParameterNumberMismatch
+26 -1: ts            timestamping
+11 -1: checkListen
+10 -1: SourceFile
+44 -1: (Ljava/lang/String;)Ljava/util/jar/JarEntry;
+17 -1: weakCompareAndSet
+9 -1: timestamp
+21 -1: (Z)Ljava/lang/String;
+12 -1: doneWithMeta
+20 -1: makeCollectArguments
+52 -1: (JLjava/util/function/BiFunction;)Ljava/lang/Object;
+10 -1: searchKeys
+48 -1: sun/reflect/SerializationConstructorAccessorImpl
+69 -1: (Ljava/lang/reflect/Constructor<*>;)Lsun/reflect/ConstructorAccessor;
+19 -1: ()Lsun/misc/Unsafe;
+11 -1: deepEquals0
+7 -1: GB18030
+13 -1: ValueIterator
+75 -1: (Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)V
+28 -1: java/util/PropertyPermission
+6 -1: CENCRC
+3 -1: url
+3 -1: L_L
+19 -1: Certificate too big
+33 -1: sun/reflect/DelegatingClassLoader
+16 -1: lambda$stream$57
+11 -1: BitSet.java
+13 -1: sun/misc/Perf
+26 -1: Ljava/util/SimpleTimeZone;
+7 -1: (BBBB)I
+24 -1: sun/misc/FloatingDecimal
+21 -1: sun/util/PreHashedMap
+8 -1: utf-16be
+11 -1: isInherited
+83 -1: (Ljava/util/Properties;Ljava/io/OutputStream;Ljava/lang/String;Ljava/lang/String;)V
+52 -1: (Ljava/lang/Class;Ljava/security/ProtectionDomain;)V
+11 -1: getDoOutput
+18 -1: asVarargsCollector
+22 -1: NoSuchMethodError.java
+18 -1: getStackTraceDepth
+30 -1: (Ljava/io/File;)Ljava/net/URL;
+15 -1: CURRENCY_SYMBOL
+24 -1: Lsun/misc/JavaNioAccess;
+6 -1: NATIVE
+22 -1: Lsun/misc/PerfCounter;
+63 -1: java/util/concurrent/ConcurrentHashMap$MapReduceValuesToIntTask
+30 -1:  less than Character.MIN_RADIX
+17 -1: isCallerSensitive
+8 -1: shutdown
+11 -1: STATE_GREEN
+4 -1: next
+10 -1: tryPresize
+16 -1: CONSTRUCTOR_NAME
+3 -1: MAY
+21 -1: java.security.manager
+20 -1: Lsun/misc/Contended;
+16 -1: DISPLAY_LANGUAGE
+6 -1: KeySet
+9 -1: getScript
+3 -1: utc
+17 -1: TRACE_INTERPRETER
+39 -1: (Ljava/lang/Object;I)Ljava/lang/String;
+19 -1: getFieldAtIfLoaded0
+15 -1: methodFilterMap
+54 -1: Ljava/util/Map<Ljava/lang/String;Ljava/lang/Boolean;>;
+45 -1: java/security/cert/Certificate$CertificateRep
+43 -1: (Ljava/lang/String;)Lsun/util/calendar/Era;
+34 -1: java/security/cert/X509Certificate
+19 -1: versionsInitialized
+11 -1: isDirectory
+14 -1: aliases_IBM775
+38 -1: (Ljava/util/function/Consumer<-TE;>;)V
+38 -1: (Ljava/lang/String;)Ljava/util/Locale;
+40 -1: (Ljava/lang/String;Z)Lsun/misc/Resource;
+14 -1: setDefaultZone
+14 -1: highResCounter
+78 -1: (Ljava/lang/ClassValue$Version;Ljava/lang/Object;)Ljava/lang/ClassValue$Entry;
+16 -1: defaultUseCaches
+40 -1: (Ljava/util/zip/ZipFile;)Ljava/util/Map;
+24 -1: isSupplementaryCodePoint
+15 -1: ISO_8859_1.java
+13 -1: multiplyExact
+126 -1: (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lsun/util/locale/LocaleExtensions;)Ljava/util/Locale;
+8 -1: classMap
+8 -1: wildcard
+19 -1: getSimpleBinaryName
+17 -1: illegal signature
+14 -1:  == basicType(
+17 -1: ZipConstants.java
+32 -1: [Ljava/lang/VirtualMachineError;
+27 -1: ()Ljava/util/stream/Stream;
+24 -1: (Ljava/lang/Exception;)V
+21 -1: java/util/Enumeration
+13 -1: newSetFromMap
+8 -1: getenv.*
+20 -1: sun/management/Agent
+21 -1: sun/nio/cs/US_ASCII$1
+7 -1: comment
+15 -1: appendAuthority
+11 -1: hasWrappers
+10 -1: dstOffset 
+24 -1: sun/reflect/ConstantPool
+75 -1: (Ljava/util/jar/JarFile;[Ljava/security/CodeSource;)Ljava/util/Enumeration;
+52 -1: (Ljava/util/jar/JarFile;)Ljava/util/jar/JarVerifier;
+70 -1: Ljava/lang/Object;Ljava/security/PrivilegedAction<Ljava/lang/Object;>;
+14 -1: previousSetBit
+17 -1: AF_GETSTATIC_INIT
+15 -1: ArrayDeque.java
+7 -1: boolean
+25 -1: (I)Ljava/math/BigInteger;
+146 -1: (Ljava/lang/ref/ReferenceQueue<Ljava/lang/Class<*>;>;Ljava/util/concurrent/ConcurrentMap<+Ljava/lang/ref/WeakReference<Ljava/lang/Class<*>;>;*>;)V
+8 -1: getClass
+8 -1: user.dir
+6 -1: VALUES
+5 -1: raise
+39 -1: (JLjava/util/function/Consumer<-TV;>;)V
+107 -1: <T:Ljava/lang/Object;V:Ljava/lang/Object;>Ljava/util/concurrent/atomic/AtomicReferenceFieldUpdater<TT;TV;>;
+5 -1: print
+8 -1: readChar
+55 -1: (JLjava/util/TimeZone;)Lsun/util/calendar/CalendarDate;
+56 -1: java/util/concurrent/ConcurrentHashMap$MapReduceKeysTask
+60 -1: Ljava/lang/Number;Ljava/lang/Comparable<Ljava/lang/Double;>;
+102 -1: <K:Ljava/lang/Object;V:Ljava/lang/Object;>(Ljava/util/SortedMap<TK;TV;>;)Ljava/util/SortedMap<TK;TV;>;
+23 -1: printModifiersIfNonzero
+14 -1: getterFunction
+15 -1: ISO-10646-UCS-2
+14 -1: canonizeString
+13 -1: getTotalSpace
+24 -1: synchronizedNavigableSet
+100 -1: <T:Ljava/lang/Object;>(Ljava/security/PrivilegedAction<TT;>;Ljava/security/AccessControlContext;)TT;
+4 -1: ioex
+24 -1: ()Ljava/nio/FloatBuffer;
+14 -1: toBinaryString
+7 -1: Segment
+34 -1: ()Lsun/misc/JavaUtilZipFileAccess;
+17 -1: setTargetVolatile
+22 -1: (Ljava/util/List<*>;)V
+51 -1: (ILjava/lang/CharSequence;)Ljava/lang/StringBuffer;
+22 -1: (Ljava/util/List<*>;)Z
+5 -1: (FI)F
+11 -1: parseMethod
+8 -1: Compiled
+19 -1: java/util/SortedMap
+7 -1: setByte
+12 -1: getFieldType
+8 -1: pageSize
+14 -1: getCallerClass
+9 -1: ensureObj
+18 -1: refKindHasReceiver
+10 -1: getZoneIds
+24 -1: (Ljava/nio/CharBuffer;)I
+47 -1: ()Lsun/reflect/generics/parser/SignatureParser;
+8 -1: getIndex
+24 -1: (Ljava/lang/Thread;TT;)V
+18 -1: (Ljava/util/Map;)V
+18 -1: (Ljava/util/Map;)Z
+6 -1: random
+10 -1: putAddress
+64 -1: (Ljava/util/function/Consumer<-Ljava/util/Map$Entry<TK;TV;>;>;)V
+24 -1: (Ljava/nio/CharBuffer;)V
+8 -1: canCache
+24 -1: (Ljava/nio/CharBuffer;)Z
+9 -1: getIntAt0
+4 -1: sqrt
+8 -1: makeLong
+54 -1: ([Ljava/lang/Object;Ljava/util/function/IntFunction;)V
+5 -1: (JJ)I
+26 -1: javaIOFileDescriptorAccess
+5 -1: (JJ)J
+15 -1:  != basicType: 
+36 -1: Ljava/lang/ref/ReferenceQueue<-TT;>;
+5 -1: words
+16 -1: sun.jnu.encoding
+32 -1: (Ljava/lang/invoke/LambdaForm;)V
+22 -1: ensureClassInitialized
+16 -1: Ljava/util/List;
+14 -1: varargsInvoker
+5 -1: (JJ)V
+20 -1: java/util/Properties
+12 -1: getImplClass
+21 -1: argumentTypesToString
+5 -1: (JJ)Z
+50 -1: java/util/Collections$SynchronizedRandomAccessList
+17 -1: makeWrappedMember
+21 -1: UnmodifiableSortedSet
+22 -1: (ILjava/lang/Class;Z)V
+3 -1: MIT
+13 -1: bootClassPath
+50 -1: (JLjava/util/function/Function;)Ljava/lang/Object;
+74 -1: <K:Ljava/lang/Object;V:Ljava/lang/Object;>Ljava/util/HashMap$Node<TK;TV;>;
+45 -1: (Ljava/util/Map$Entry;Ljava/util/Map$Entry;)I
+12 -1: advanceProbe
+10 -1: encoderFor
+14 -1: DataInput.java
+16 -1: java/lang/Double
+3 -1: 912
+3 -1: 914
+3 -1: abs
+3 -1: 915
+13 -1: currentThread
+17 -1: ClassDefiner.java
+32 -1: sun/misc/Launcher$ExtClassLoader
+16 -1: Current state = 
+12 -1: elementCount
+10 -1: unmaskNull
+8 -1: csibm857
+32 -1: java/net/UnknownServiceException
+10 -1: x-utf-16be
+3 -1: acc
+37 -1: Ljava/util/List<Ljava/io/Closeable;>;
+23 -1: ([Ljava/lang/Thread;Z)I
+17 -1: impliesIgnoreMask
+23 -1: getGenericComponentType
+51 -1: ()Lsun/reflect/generics/repository/ClassRepository;
+34 -1: " not found. Will use interpreter.
+38 -1: ()Ljava/security/AccessControlContext;
+6 -1: final 
+8 -1: utf-16le
+3 -1: 920
+3 -1: 923
+5 -1: (I)[C
+43 -1: (Ljava/nio/ByteOrder;)Ljava/nio/ByteBuffer;
+8 -1: csibm862
+34 -1: (Ljava/lang/ref/Reference<+TT;>;)Z
+8 -1: csibm866
+20 -1: getParameterizedType
+7 -1: (II[C)V
+20 -1: [Ljava/lang/Package;
+84 -1: (Ljava/lang/String;Ljava/security/ProtectionDomain;)Ljava/security/ProtectionDomain;
+28 -1: SynchronizedRandomAccessList
+18 -1: sun/misc/VMSupport
+61 -1: java/lang/invoke/MethodType$ConcurrentWeakInternSet$WeakEntry
+3 -1: add
+16 -1: ZipEntryIterator
+11 -1: next_target
+87 -1: (Ljava/lang/String;Ljava/nio/ByteBuffer;Ljava/security/CodeSource;)Ljava/lang/Class<*>;
+4 -1: amod
+12 -1: markedSkipLF
+55 -1: java/util/concurrent/ConcurrentHashMap$EntrySpliterator
+5 -1:  lim=
+29 -1: java/security/PermissionsHash
+50 -1: (Ljava/lang/CharSequence;II)Ljava/lang/Appendable;
+19 -1: makePairwiseConvert
+58 -1: <T:Ljava/lang/Object;>(Ljava/lang/ClassValue$Entry<TT;>;)V
+8 -1: contents
+11 -1: user.region
+17 -1: RandomAccess.java
+13 -1: singletonList
+13 -1: policy,access
+64 -1: Ljava/util/Map<Ljava/lang/String;Ljava/io/ExpiringCache$Entry;>;
+25 -1: (Ljava/lang/Appendable;)V
+19 -1: (Ljava/util/List;)V
+43 -1: (Ljava/lang/ClassLoader;Ljava/lang/Class;)V
+19 -1: (Ljava/util/List;)Z
+15 -1: America/Chicago
+25 -1: (II)Ljava/nio/CharBuffer;
+12 -1: getDayOfWeek
+8 -1: ([BIIZ)V
+28 -1: (Ljava/lang/reflect/Field;)I
+28 -1: (Ljava/lang/reflect/Field;)J
+18 -1: getDeclaringClass0
+11 -1: counterTime
+30 -1: (Ljava/util/Collection<TE;>;)V
+28 -1: (Ljava/lang/reflect/Field;)V
+31 -1: (IIIILjava/io/FileDescriptor;)V
+25 -1: java/net/SocketPermission
+20 -1: bad parameter count 
+18 -1: getHeaderFieldLong
+26 -1: GetReflectionFactoryAction
+19 -1: MIN_TRANSFER_STRIDE
+17 -1: java/nio/Bits$1$1
+7 -1: getSize
+33 -1: java/util/function/ToLongFunction
+46 -1: (IILjava/lang/String;)Ljava/lang/StringBuffer;
+10 -1: access$800
+65 -1: sun/misc/JavaSecurityProtectionDomainAccess$ProtectionDomainCache
+26 -1: (Ljava/lang/ClassLoader;)V
+25 -1: java/util/IdentityHashMap
+26 -1: (Ljava/lang/ClassLoader;)Z
+91 -1: <T:Ljava/lang/Object;>(Ljava/util/function/ToIntFunction<-TT;>;)Ljava/util/Comparator<TT;>;
+26 -1: ([CIILjava/lang/String;I)I
+12 -1: canonicalize
+3 -1: val
+8 -1: putCharB
+12 -1: UTF_32LE_BOM
+60 -1: (Ljava/security/CodeSource;)Ljava/security/ProtectionDomain;
+44 -1: (Lsun/misc/SignalHandler;Lsun/misc/Signal;)V
+26 -1: (Ljava/util/Enumeration;)V
+11 -1: INVALIDATED
+8 -1: putCharL
+22 -1: (II)Ljava/lang/String;
+7 -1: hasNext
+5 -1: WRITE
+20 -1: MIN_INITIAL_CAPACITY
+13 -1: propertyNames
+9 -1: Gregorian
+13 -1: getExpiration
+7 -1: minutes
+7 -1: ostream
+9 -1: java.lang
+17 -1: forceStandardTime
+9 -1: initWords
+41 -1: java/lang/Thread$UncaughtExceptionHandler
+9 -1: theUnsafe
+27 -1: ForEachTransformedEntryTask
+10 -1: forEncoder
+31 -1: needsClassLoaderPermissionCheck
+5 -1: ctime
+25 -1: ()Ljava/nio/DoubleBuffer;
+8 -1: getValue
+66 -1: (Lsun/util/locale/BaseLocale$Key;)Lsun/util/locale/BaseLocale$Key;
+42 -1: (Ljava/io/InputStream;Ljava/lang/String;)V
+6 -1: august
+14 -1: compileClasses
+13 -1: javaNetAccess
+22 -1: interpretWithArguments
+4 -1: url:
+14 -1: EMPTY_ITERATOR
+60 -1: Ljava/util/WeakHashMap<Ljava/io/Closeable;Ljava/lang/Void;>;
+24 -1: java/util/jar/Attributes
+12 -1: getOrDefault
+19 -1: Pacific/Guadalcanal
+33 -1: ()Ljava/lang/reflect/Constructor;
+38 -1: java/util/Collections$UnmodifiableList
+13 -1: basicTypeChar
+22 -1: (Ljava/lang/String;J)J
+14 -1: memberDefaults
+42 -1: (Ljava/lang/Class<*>;[Ljava/lang/String;)V
+38 -1: (Ljava/util/function/Consumer<-TK;>;)V
+16 -1: LF_NEWINVSPECIAL
+10 -1: classDepth
+28 -1: [Ljava/io/ObjectStreamField;
+46 -1: (Ljava/util/Collection;)Ljava/util/Collection;
+91 -1: ([Ljava/lang/reflect/Method;Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method;
+11 -1: getLocation
+39 -1: (Ljava/lang/Class;[Ljava/lang/Class;Z)V
+9 -1: loadTable
+55 -1: Directory separator should not appear in library name: 
+7 -1: setTime
+14 -1: getConstructor
+4 -1: urls
+25 -1: dispatchUncaughtException
+77 -1: (ILjava/lang/Object;Ljava/lang/Class<*>;)Ljava/util/HashMap$TreeNode<TK;TV;>;
+8 -1: modCount
+8 -1: Opening 
+6 -1: ENDHDR
+4 -1: cnfe
+34 -1: DIRECTIONALITY_PARAGRAPH_SEPARATOR
+10 -1: Asia/Amman
+3 -1: MST
+28 -1: DIRECTIONALITY_ARABIC_NUMBER
+3 -1: all
+4 -1: enum
+8 -1: copyWith
+12 -1: ([JI[IIJII)I
+29 -1: Ljava/lang/annotation/Target;
+7 -1: Thread-
+14 -1: x-utf-32le-bom
+38 -1: (ILjava/lang/management/MemoryUsage;)V
+33 -1: Signal already used by VM or OS: 
+27 -1: (I)Ljava/lang/StringBuffer;
+25 -1: java/text/Normalizer$Form
+10 -1: x-utf-16le
+34 -1:  can not access a member of class 
+27 -1: (Ljava/nio/ByteBuffer;IFZ)V
+28 -1: (Ljava/lang/ClassValue<*>;)V
+16 -1: INITIAL_CAPACITY
+23 -1: DirectMethodHandle.java
+18 -1: reduceValuesToLong
+41 -1: (Ljava/lang/String;Ljava/lang/Class<*>;)V
+6 -1: unwrap
+12 -1: threadStatus
+5 -1: (DI)D
+11 -1: fieldOffset
+52 -1: java/util/concurrent/ConcurrentHashMap$EntryIterator
+14 -1: ACCESS_EXECUTE
+44 -1: (Ljava/nio/ByteBuffer;)Ljava/nio/CharBuffer;
+29 -1: ([Ljava/lang/ThreadGroup;IZ)I
+18 -1: LocalVariableTable
+17 -1: ConstantPool.java
+26 -1: (Ljava/nio/ByteBuffer;ID)V
+4 -1: (C)B
+3 -1: and
+4 -1: head
+126 -1: (Ljava/lang/reflect/GenericDeclaration;Lsun/reflect/generics/scope/Scope;)Lsun/reflect/generics/factory/CoreReflectionFactory;
+4 -1: (C)C
+16 -1: Pacific/Auckland
+7 -1: Thread[
+5 -1: ([D)I
+4 -1: (C)I
+98 -1: <U:Ljava/lang/Object;>([Ljava/lang/reflect/Constructor<TU;>;)[Ljava/lang/reflect/Constructor<TU;>;
+11 -1: fileHandler
+30 -1: DIRECTIONALITY_NONSPACING_MARK
+10 -1: (this Map)
+14 -1: malformedCache
+5 -1: ([D)V
+4 -1: (C)V
+26 -1: getUnicodeLocaleAttributes
+4 -1: (C)Z
+81 -1: (JLjava/util/function/ToLongBiFunction;JLjava/util/function/LongBinaryOperator;)J
+46 -1: [Ljava/util/concurrent/ConcurrentHashMap$Node;
+20 -1:     Max. Heap Size: 
+24 -1: Ljava/lang/reflect/Type;
+13 -1: EmptyIterator
+8 -1: allocate
+7 -1: FLUSHED
+8 -1: exitVM.*
+59 -1: (Ljava/lang/String;)Lsun/util/locale/InternalLocaleBuilder;
+19 -1: reduceEntriesToLong
+15 -1: getISOLanguages
+13 -1: CONSTANT_ZERO
+23 -1: (I)Ljava/util/Iterator;
+96 -1: <K:Ljava/lang/Object;V:Ljava/lang/Object;>Ljava/util/AbstractMap<TK;TV;>;Ljava/util/Map<TK;TV;>;
+11 -1: isSynthetic
+7 -1: lineBuf
+30 -1: java/lang/annotation/Inherited
+65 -1: <E:Ljava/lang/Object;>Ljava/lang/Object;Ljava/lang/Iterable<TE;>;
+35 -1: (Ljava/lang/String;)[Ljava/io/File;
+27 -1: java/security/cert/CertPath
+26 -1: startRemoteManagementAgent
+9 -1: shiftLeft
+5 -1: stack
+42 -1: (Ljava/lang/Class<*>;[Ljava/lang/Object;)V
+11 -1: CheckedList
+10 -1: replaceAll
+86 -1: (Ljava/util/HashMap$TreeNode;Ljava/util/HashMap$TreeNode;)Ljava/util/HashMap$TreeNode;
+24 -1: (I)Ljava/nio/CharBuffer;
+13 -1: image/vnd.fpx
+15 -1: iso_8859-1:1987
+19 -1: (Ljava/lang/Long;)I
+22 -1: sun/misc/SignalHandler
+15 -1: ifModifiedSince
+42 -1: (Ljava/lang/Class;)Ljava/lang/ClassLoader;
+105 -1: (Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;ILjava/lang/Class;I[Ljava/lang/invoke/MemberName;)I
+22 -1: Negative timeout value
+38 -1: Ljava/io/UnsupportedEncodingException;
+11 -1: removeRange
+13 -1: Compiler.java
+6 -1: Sorter
+8 -1: aliasSet
+10 -1: UNASSIGNED
+34 -1: lambda$comparingByValue$1065357e$1
+34 -1: ()Ljava/lang/Class$AnnotationData;
+25 -1: sun/misc/Launcher$Factory
+15 -1: getLongVolatile
+8 -1: vmloader
+10 -1: unicodebig
+10 -1: closeables
+31 -1: JavaIOFileDescriptorAccess.java
+7 -1:  static
+3 -1: arg
+21 -1: library can't be null
+42 -1: java/util/ArraysParallelSortHelpers$FJByte
+22 -1: getDeclaringExecutable
+19 -1: runFinalizersOnExit
+20 -1: simpleTimeZoneParams
+13 -1: Modifier.java
+14 -1: pkcs11keystore
+6 -1: shared
+30 -1: java/net/MalformedURLException
+26 -1: ()[Lsun/util/calendar/Era;
+13 -1: x-MS950-HKSCS
+10 -1: relativize
+40 -1: (Ljava/lang/String;JJ)Ljava/lang/String;
+31 -1: java/util/HashMap$ValueIterator
+29 -1: MODIFY_THREADGROUP_PERMISSION
+38 -1: (Ljava/lang/String;)Ljava/lang/Object;
+7 -1: destroy
+37 -1: (Ljava/util/List;)[Ljava/lang/String;
+18 -1: (Ljava/io/File;Z)V
+32 -1: Ljava/util/HashMap$Node<TK;TV;>;
+18 -1: interfaceModifiers
+34 -1: java/util/LinkedList$LLSpliterator
+7 -1: REF_???
+23 -1: java/net/ContentHandler
+20 -1: <compiledLambdaForm>
+17 -1: [Ljava/lang/Byte;
+6 -1: exitVM
+3 -1: att
+27 -1: sun/nio/cs/UTF_16BE$Encoder
+6 -1: exists
+28 -1: Ljava/util/Collection<+TE;>;
+48 -1: (Ljava/lang/CharSequence;)Ljava/lang/Appendable;
+6 -1: getMap
+52 -1: ([Ljava/lang/Class;I)Ljava/lang/reflect/Constructor;
+11 -1: stackTrace[
+21 -1: slowCheckMemberAccess
+33 -1: ReflectiveOperationException.java
+9 -1: versionId
+56 -1: Wrong number of parameters in MethodParameters attribute
+14 -1: isLowSurrogate
+8 -1: csPCp852
+16 -1: copyConstructors
+25 -1: ()Ljava/util/Spliterator;
+9 -1: closeLock
+17 -1: readUnsignedShort
+7 -1: 8859_13
+7 -1: 8859_15
+13 -1: TARGET_OFFSET
+26 -1: (Ljava/util/Hashtable;IZ)V
+44 -1: (Ljava/nio/CharBuffer;)Ljava/nio/CharBuffer;
+46 -1: (Ljava/lang/reflect/Type;)Ljava/lang/Class<*>;
+25 -1: DEFAULT_CONCURRENCY_LEVEL
+36 -1: Ljava/util/List<Ljava/lang/String;>;
+29 -1: sun.nio.PageAlignDirectMemory
+37 -1: (Ljava/lang/Class;I)Ljava/lang/Class;
+12 -1: encryptBlock
+8 -1: parentOf
+5 -1: H_HEX
+11 -1: getFloatAt0
+24 -1: VirtualMachineError.java
+18 -1: getDeclaredClasses
+59 -1: (Ljava/lang/AbstractStringBuilder;)Ljava/lang/StringBuffer;
+42 -1: (Ljava/util/List<Ljava/lang/Class<*>;>;)[C
+37 -1: (Ljava/lang/String;)Ljava/lang/Float;
+13 -1: Iterator.java
+25 -1: (Ljava/io/PrintStream;I)V
+9 -1: getObject
+19 -1: [Ljava/lang/String;
+7 -1: SIZECTL
+11 -1: isUnderflow
+27 -1: sun.nio.MaxDirectMemorySize
+21 -1: isNonPublicProxyClass
+13 -1: toCalendarDOW
+9 -1: Type.java
+14 -1: aliases_IBM850
+17 -1: emptyNavigableMap
+14 -1: aliases_IBM852
+14 -1: aliases_IBM855
+14 -1: aliases_IBM857
+14 -1: aliases_IBM858
+15 -1: iso_8859-4:1988
+13 -1: UnicodeScript
+8 -1: getCharB
+17 -1: constructorMethod
+27 -1: java/util/function/Function
+20 -1: getProtectionDomain0
+8 -1: getCharL
+32 -1: ([Ljava/io/File;)[Ljava/net/URL;
+51 -1: (Ljava/lang/Class;Z)Ljava/lang/invoke/MethodHandle;
+7 -1: getenv.
+5 -1: stale
+14 -1: aliases_IBM862
+32 -1: java/util/spi/LocaleNameProvider
+14 -1: aliases_IBM866
+51 -1: ([Ljava/lang/Class;)Ljava/lang/reflect/Constructor;
+6 -1: CENDSK
+36 -1: java/util/Comparators$NullComparator
+34 -1: UnsafeStaticFieldAccessorImpl.java
+17 -1: staticFieldOffset
+12 -1: prefetchRead
+4 -1: help
+34 -1: (Ljava/util/concurrent/TimeUnit;)J
+8 -1: getChars
+19 -1: java/lang/Throwable
+34 -1: Annotation Type:\n   Member types: 
+55 -1: (Ljava/lang/Class<*>;Ljava/security/ProtectionDomain;)V
+14 -1: aliases_IBM874
+17 -1: getDisplayVariant
+24 -1: Ljava/net/NetPermission;
+15 -1: jvmMinorVersion
+11 -1: subSequence
+3 -1: x86
+6 -1: double
+14 -1: checkSlotCount
+20 -1: java/net/InetAddress
+14 -1: Principal.java
+8 -1: $,;:@&=+
+17 -1: ByteBuffered.java
+21 -1: sun.misc.Perf.getPerf
+11 -1: finishEntry
+27 -1: sun.timezone.ids.oldmapping
+26 -1: (ZLjava/io/OutputStream;)V
+41 -1: (Ljava/lang/String;Z)Ljava/lang/Class<*>;
+32 -1: generateSerializationConstructor
+6 -1: Value 
+40 -1: (Ljava/lang/String;Ljava/lang/String;Z)V
+16 -1: previousClearBit
+7 -1: theProp
+51 -1: (Ljava/io/OutputStream;Ljava/nio/charset/Charset;)V
+39 -1: com.sun.javafx.application.LauncherImpl
+21 -1: URLStreamHandler.java
+4 -1:  in 
+14 -1: BIT_INDEX_MASK
+125 -1: <K:Ljava/lang/Object;V:Ljava/lang/Object;>(Ljava/util/Comparator<-TK;>;)Ljava/util/Comparator<Ljava/util/Map$Entry<TK;TV;>;>;
+49 -1: (Ljava/util/Set;Ljava/lang/Class;)Ljava/util/Set;
+10 -1: scaleValue
+27 -1: (Ljava/nio/ByteBuffer;IDZ)V
+78 -1: (Ljava/util/Collection<Ljava/lang/reflect/Field;>;[Ljava/lang/reflect/Field;)V
+53 -1: <T:Ljava/lang/Object;>(I)Ljava/util/Enumeration<TT;>;
+38 -1: java/lang/IncompatibleClassChangeError
+60 -1: java/util/concurrent/ConcurrentHashMap$MapReduceMappingsTask
+23 -1: not a method or field: 
+35 -1: Ljava/nio/BufferUnderflowException;
+4 -1: i386
+13 -1: US_ASCII.java
+80 -1: ([BLsun/reflect/ConstantPool;Ljava/lang/Class;)Ljava/lang/reflect/AnnotatedType;
+21 -1: initializeSystemClass
+6 -1: ([CI)I
+18 -1: getBooleanProperty
+35 -1: java/util/function/ToDoubleFunction
+37 -1: (Ljava/lang/String;)Ljava/lang/Class;
+28 -1: MapReduceEntriesToDoubleTask
+38 -1: java/util/LinkedHashMap$LinkedEntrySet
+8 -1: copySign
+6 -1: ([CI)V
+55 -1: sun/util/calendar/ZoneInfoFile$ZoneOffsetTransitionRule
+12 -1: parseBoolean
+3 -1: NET
+3 -1: NEW
+6 -1: Values
+17 -1: getJavaLangAccess
+9 -1: LocaleKey
+3 -1: :-1
+30 -1: (Ljava/io/ObjectInputStream;)V
+3 -1: NFC
+3 -1: NFD
+18 -1: SIMPLIFIED_CHINESE
+28 -1: ()Ljava/lang/reflect/Method;
+9 -1: localInit
+37 -1: sun/util/locale/LocaleSyntaxException
+15 -1: LambdaForm.java
+5 -1: rtype
+8 -1:  field "
+50 -1: (Ljava/lang/Class;Ljava/lang/ref/ReferenceQueue;)V
+80 -1: (Ljava/lang/Class;[Ljava/lang/Class;[Ljava/lang/Class;IILjava/lang/String;[B[B)V
+36 -1: java/lang/invoke/MethodHandleNatives
+22 -1: (Ljava/util/Map<**>;)Z
+22 -1: Ljava/lang/Class<TV;>;
+7 -1: SubList
+14 -1: connect,accept
+19 -1: declaredAnnotations
+38 -1: Ljava/lang/ThreadLocal$ThreadLocalMap;
+11 -1: isSpaceChar
+10 -1: offerFirst
+20 -1: sun/nio/ByteBuffered
+17 -1: packageDefinition
+7 -1: trigger
+35 -1: [Ljava/lang/invoke/LambdaForm$Name;
+19 -1: sun.stdout.encoding
+5 -1: start
+26 -1: (Ljava/util/HashMap;IIII)V
+65 -1: (JLjava/util/function/Consumer<-Ljava/util/Map$Entry<TK;TV;>;>;)V
+6 -1: LOCVER
+3 -1: ://
+42 -1: (CLjava/lang/Class<*>;Ljava/lang/Object;)Z
+6 -1: friday
+45 -1: sun/reflect/DelegatingConstructorAccessorImpl
+15 -1: iso_8859-7:1987
+15 -1: newCalendarDate
+24 -1: getAnnotatedReceiverType
+32 -1: java/util/NoSuchElementException
+50 -1: (Ljava/util/NavigableSet;)Ljava/util/NavigableSet;
+26 -1: java/text/SimpleDateFormat
+16 -1: threadInitNumber
+55 -1: <T:Ljava/lang/Object;>(TT;)Ljava/util/Spliterator<TT;>;
+23 -1: (Ljava/lang/Object;)TT;
+3 1: bar
+37 -1: getFunctionalInterfaceMethodSignature
+5 -1: state
+39 -1: [Ljava/lang/reflect/GenericDeclaration;
+22 -1: sun/net/ProgressSource
+13 -1: LLSpliterator
+93 -1: <T:Ljava/lang/Object;>Ljava/lang/Object;Ljava/util/Enumeration<TT;>;Ljava/util/Iterator<TT;>;
+5 -1: JAPAN
+11 -1: SPACE_TOTAL
+20 -1: CharsetProvider.java
+12 -1: CR_UNDERFLOW
+7 -1: ITALIAN
+11 -1: getCallerPD
+13 -1: isMemberClass
+38 -1: (Ljava/util/function/Consumer<-TT;>;)V
+18 -1: malformedForLength
+14 -1: ReflectionData
+34 -1: (BZI)Ljava/lang/invoke/LambdaForm;
+16 -1: forPrimitiveType
+31 -1:  field found in java.lang.Class
+60 -1: (Ljava/lang/Void;Ljava/lang/ThreadGroup;Ljava/lang/String;)V
+18 -1: DescendingIterator
+25 -1: (IZLjava/lang/Runnable;)V
+35 -1: ()[Ljava/lang/reflect/TypeVariable;
+3 -1: bcp
+23 -1: (Ljava/lang/Object;)TV;
+25 -1: setDefaultAssertionStatus
+10 -1: ([BIIIII)V
+150 -1: <K:Ljava/lang/Object;V:Ljava/lang/Object;>(Ljava/util/concurrent/ConcurrentHashMap$Node<TK;TV;>;)Ljava/util/concurrent/ConcurrentHashMap$Node<TK;TV;>;
+14 -1:    Inherited: 
+17 -1: fileTimeToWinTime
+7 -1: ([BI)[B
+26 -1: java/io/OutputStreamWriter
+58 -1: <T:Ljava/lang/Object;>(Ljava/util/ListIterator<+TT;>;I)TT;
+39 -1: sun/reflect/annotation/AnnotationParser
+71 -1: (Ljava/nio/file/Path;Ljava/lang/String;)Ljava/nio/file/DirectoryStream;
+47 -1: Ljava/util/concurrent/locks/ReentrantLock$Sync;
+44 -1: (Ljava/util/Collection;[Ljava/lang/Object;)Z
+43 -1: ([ILjava/util/function/IntBinaryOperator;)V
+29 -1: reverseAllValidSurrogatePairs
+20 -1: Ljava/nio/file/Path;
+20 -1: getGenericSignature0
+42 -1: (Ljava/lang/String;Ljava/lang/Class<*>;Z)V
+8 -1: manEntry
+64 -1: (Ljava/lang/String;)Ljava/util/Enumeration<Lsun/misc/Resource;>;
+36 -1: newGetDoubleIllegalArgumentException
+63 -1: (Ljava/util/Collection;Ljava/lang/Class;)Ljava/util/Collection;
+30 -1:     Ergonomics Machine Class: 
+40 -1: ()Ljava/util/Map<Ljava/lang/String;TT;>;
+50 -1: (Ljava/lang/StringBuffer;)Ljava/lang/StringBuffer;
+30 -1: CHECK_MEMBER_ACCESS_PERMISSION
+22 -1: (Ljava/lang/Boolean;)I
+10 -1: V_Variable
+68 -1: java/util/concurrent/ConcurrentHashMap$ForEachTransformedMappingTask
+61 -1: (Ljava/lang/String;IILjava/lang/String;)Ljava/nio/ByteBuffer;
+21 -1: accessClassInPackage.
+44 -1: java/util/WeakHashMap$WeakHashMapSpliterator
+11 -1: nextElement
+9 -1: separator
+48 -1: java/util/zip/ZipFile$ZipFileInflaterInputStream
+44 -1: java/security/UnresolvedPermissionCollection
+10 -1: access$900
+44 -1: java/util/ArraysParallelSortHelpers$FJDouble
+84 -1: (Ljava/lang/invoke/MethodHandle;Ljava/lang/Class<*>;)Ljava/lang/invoke/MethodHandle;
+52 -1: (Ljava/lang/ref/Finalizer;)Ljava/lang/ref/Finalizer;
+19 -1: getDeclaredMethods0
+29 -1: (IJ)Ljava/lang/StringBuilder;
+11 -1: Member.java
+61 -1: (Ljava/lang/String;ZJJ)Ljava/lang/management/MemoryPoolMBean;
+35 -1: java/lang/AssertionStatusDirectives
+23 -1: Declaring class is null
+56 -1: (Ljava/lang/Class;Ljava/lang/Object;Ljava/lang/Object;)I
+12 -1: java/io/File
+41 -1: java/util/ConcurrentModificationException
+47 -1: (Ljava/lang/CharSequence;)Ljava/nio/CharBuffer;
+19 -1: parameterClassCache
+8 -1: US_ASCII
+15 -1: tryMonitorEnter
+22 -1: Ljava/util/Collection;
+67 -1: (Lsun/misc/Signal;Lsun/misc/SignalHandler;)Lsun/misc/SignalHandler;
+27 -1: (Lsun/misc/JavaNioAccess;)V
+9 -1: DigitOnes
+5 -1: write
+31 -1: NF_internalMemberNameEnsureInit
+18 -1: comparableClassFor
+20 -1: defineAnonymousClass
+49 -1: (Ljava/io/InputStream;Lsun/net/ProgressSource;J)V
+31 -1: java/lang/NumberFormatException
+17 -1: remainderUnsigned
+62 -1: <T::Ljava/lang/Comparable<-TT;>;>()Ljava/util/Comparator<TT;>;
+4 -1: Kind
+20 -1: (Ljava/lang/Short;)I
+8 -1: OfDouble
+51 -1: ([Ljava/lang/Object;Ljava/lang/invoke/MethodType;)Z
+22 -1: [Ljava/util/Map$Entry;
+13 -1: instanceClass
+29 -1: ()Ljava/lang/SecurityManager;
+12 -1: isUnmappable
+17 -1: getAllStackTraces
+19 -1: LinkedValueIterator
+7 -1: isDigit
+10 -1: rangeCheck
+89 -1: (Ljava/lang/Class<*>;Ljava/lang/Class<*>;)Ljava/util/List<Ljava/lang/invoke/MemberName;>;
+13 -1: getMethodType
+21 -1: FileOutputStream.java
+22 -1: ()Ljava/text/Collator;
+64 -1: <T:Ljava/lang/Object;>(Ljava/security/PrivilegedAction<TT;>;)TT;
+15 -1: defaultTimeZone
+14 -1: emptySortedMap
+35 -1: (Ljava/util/function/IntConsumer;)V
+45 -1: java/util/Collections$CheckedRandomAccessList
+11 -1: getPriority
+7 -1: signals
+6 -1: Subset
+15 -1: invokeInterface
+16 -1: nameRefsAreLegal
+38 -1: (Ljava/lang/Object;)Ljava/lang/Object;
+37 -1: Ljava/util/Collections$EmptyIterator;
+9 -1: Perf.java
+20 -1: java/math/BigInteger
+38 -1: java/util/WeakHashMap$ValueSpliterator
+71 -1: (Ljava/nio/charset/CodingErrorAction;)Ljava/nio/charset/CharsetEncoder;
+50 -1: (Ljava/lang/invoke/MemberName;Ljava/lang/Object;)V
+21 -1: [Ljava/lang/Class<*>;
+13 -1: getProperties
+63 -1: (Ljava/lang/Class<*>;[B[Ljava/lang/Object;)Ljava/lang/Class<*>;
+65 -1: (Ljava/util/Set<Ljava/lang/Class<*>;>;)[Ljava/lang/reflect/Field;
+21 -1: sun/util/calendar/Era
+21 -1: Ljava/nio/CharBuffer;
+23 -1: (Ljava/lang/Object;JS)V
+24 -1: oracle/jrockit/jfr/VMJFR
+15 -1: access allowed 
+34 -1: ()Lsun/util/calendar/CalendarDate;
+97 -1: (Ljava/security/PrivilegedExceptionAction;Ljava/security/AccessControlContext;)Ljava/lang/Object;
+44 -1: ([Ljava/lang/Object;[Ljava/lang/Object;III)V
+48 -1: (Ljava/util/Collection;I)Ljava/util/Spliterator;
+11 -1: SimpleEntry
+63 -1: (Ljava/net/URL;Ljava/net/URLStreamHandler;Ljava/util/HashMap;)V
+8 -1: putFloat
+20 -1: linkToCallSiteMethod
+8 -1: invokers
+35 -1: (J)Lsun/util/calendar/BaseCalendar;
+6 -1: FRENCH
+26 -1: getRawParameterAnnotations
+9 -1: wordIndex
+29 -1: JNI_COPY_FROM_ARRAY_THRESHOLD
+20 -1: sun/nio/cs/Surrogate
+48 -1: (Lsun/misc/JavaSecurityProtectionDomainAccess;)V
+45 -1: (Ljava/lang/ThreadLocal;Ljava/lang/Object;I)V
+3 -1: NST
+14 -1: getHeaderField
+27 -1: (Ljava/util/jar/JarFile;Z)V
+10 -1: findNative
+38 -1: The following can be used with access:
+29 -1: convertCertArrayToSignerArray
+4 -1: .DSA
+12 -1: dependencies
+12 -1: SYNCHRONIZED
+14 -1: x-euc-jp-linux
+28 -1: URLStreamHandlerFactory.java
+11 -1: checkPtypes
+68 -1: (Ljava/io/InputStream;Ljava/lang/Object;Ljava/nio/charset/Charset;)V
+13 -1: addDayOfMonth
+10 -1: isAncestor
+16 -1: entryDislocation
+12 -1: tableSizeFor
+29 -1: (ID)Ljava/lang/StringBuilder;
+19 -1: getLastRuleInstance
+24 -1: getGenericParameterTypes
+8 -1: ,offset=
+37 -1: (Ljava/net/URL;Ljava/lang/String;II)V
+13 -1: UTF_16LE.java
+12 -1: setTimestamp
+31 -1: AtomicReferenceFieldUpdaterImpl
+6 -1: L_URIC
+4 -1: (D)D
+14 -1: DeqSpliterator
+13 -1: LF_INVVIRTUAL
+171 -1: <U:Ljava/lang/Object;W:Ljava/lang/Object;>(Ljava/lang/Class<TU;>;Ljava/lang/Class<TW;>;Ljava/lang/String;)Ljava/util/concurrent/atomic/AtomicReferenceFieldUpdater<TU;TW;>;
+4 -1: (D)I
+22 -1: Ljava/lang/Class<TT;>;
+4 -1: (D)J
+196 -1: (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
+13 -1: ,transitions=
+30 -1: (Lsun/net/www/MessageHeader;)I
+4 -1: (D)V
+12 -1: segmentShift
+4 -1: (D)Z
+14 -1: Reference.java
+26 -1: [Ljava/lang/reflect/Field;
+26 -1: java/nio/DirectByteBufferR
+31 -1: lambda$thenComparing$36697e65$1
+30 -1: (Lsun/net/www/MessageHeader;)V
+55 -1: No java.nio.charset.StandardCharsets instances for you!
+59 -1: (Ljava/lang/Class<*>;Ljava/lang/Object;Ljava/lang/Object;)I
+19 -1: thenComparingDouble
+31 -1: ()Ljava/lang/invoke/LambdaForm;
+9 -1: getMillis
+21 -1: StandardCharsets.java
+15 -1: postDefineClass
+8 -1: getExtra
+3 -1: box
+10 -1: nextSetBit
+27 -1: java/util/ArrayList$SubList
+43 -1: (Ljava/util/concurrent/ConcurrentHashMap;)V
+52 -1: (Lsun/misc/URLClassPath;)Ljava/net/URLStreamHandler;
+7 -1: putLong
+40 -1: <T::Ljava/lang/Comparable<-TT;>;>([TT;)V
+8 -1: ([DII)[D
+8 -1: ([SIIS)I
+8 -1: argument
+12 -1: linkCallSite
+56 -1: <T:Ljava/lang/Object;>Ljava/lang/ref/WeakReference<TT;>;
+15 -1: returnSlotCount
+68 -1: (Ljava/lang/String;[Ljava/lang/Class<*>;Z)Ljava/lang/reflect/Method;
+20 -1: defaultDisplayLocale
+21 -1: (Ljava/util/Vector;)V
+49 -1: (Lsun/reflect/generics/visitor/TypeTreeVisitor;)V
+12 -1: isAlphabetic
+5 -1: perms
+13 -1: dosToJavaTime
+8 -1: ([SIIS)V
+7 -1: valueOf
+27 -1: Ljava/util/LinkedList$Node;
+41 -1: (Ljava/lang/String;I)Ljava/lang/Class<*>;
+38 -1: ()Ljava/nio/charset/CodingErrorAction;
+4 -1: MASK
+5 -1: Field
+101 -1: (Ljava/lang/Class;Ljava/nio/ByteBuffer;Lsun/reflect/ConstantPool;Ljava/lang/Class;)Ljava/lang/Object;
+56 -1: (Ljava/lang/String;Lsun/misc/Resource;)Ljava/lang/Class;
+9 -1: SURROGATE
+72 -1: (Ljava/lang/invoke/DirectMethodHandle$StaticAccessor;)Ljava/lang/Object;
+19 -1: PARAMETER_MODIFIERS
+48 -1: ([Ljava/lang/Object;II)Ljava/util/stream/Stream;
+56 -1: (TK;TV;Ljava/util/function/BiFunction<-TV;-TV;+TV;>;)TV;
+118 -1: <U:Ljava/lang/Object;>(JLjava/util/function/BiFunction<-TK;-TV;+TU;>;Ljava/util/function/BiFunction<-TU;-TU;+TU;>;)TU;
+29 -1: (Lsun/nio/ch/Interruptible;)V
+45 -1: Ljava/lang/ref/Reference<Ljava/lang/Object;>;
+17 -1: isHeldExclusively
+3 -1: NYI
+15 -1: getByteVolatile
+20 -1: compareAndSwapObject
+29 -1: (Z)[Ljava/lang/reflect/Field;
+7 -1: ([SII)V
+13 -1: toLanguageTag
+18 -1: StreamEncoder.java
+25 -1: (IF)Ljava/nio/ByteBuffer;
+18 -1: afterNodeInsertion
+11 -1: accessOrder
+60 -1: Lsun/reflect/annotation/TypeAnnotation$TypeAnnotationTarget;
+20 -1: META-INF/MANIFEST.MF
+18 -1: getSuperInterfaces
+26 -1: (Ljava/nio/ByteBuffer;IZ)C
+26 -1: (Ljava/nio/ByteBuffer;IZ)D
+9 -1: PROTECTED
+18 -1:  not visible from 
+26 -1: java/lang/RuntimeException
+26 -1: (Ljava/nio/ByteBuffer;IZ)F
+20 -1: java/net/FileNameMap
+15 -1: insertArguments
+8 -1: diagprop
+26 -1: (Ljava/nio/ByteBuffer;IZ)I
+26 -1: (Ljava/nio/ByteBuffer;IZ)J
+20 -1: createContentHandler
+11 -1: getProtocol
+26 -1: (Ljava/nio/ByteBuffer;IZ)S
+33 -1: Ljava/lang/NoSuchMethodException;
+19 -1: entry name too long
+40 -1: java/util/jar/JarVerifier$VerifierStream
+6 -1: server
+7 -1: OCTOBER
+26 -1: sun/invoke/util/VerifyType
+6 -1: domain
+9 -1: getUnsafe
+5 -1: ([Z)I
+4 -1: cons
+42 -1: ()Ljava/lang/ReflectiveOperationException;
+4 -1: byte
+29 -1: java/util/function/BiConsumer
+24 -1: getJavaUtilZipFileAccess
+37 -1: ([Ljava/lang/Object;)Ljava/util/List;
+27 -1: timeout can not be negative
+62 -1: Ljava/util/Map<Ljava/io/InputStream;Ljava/util/zip/Inflater;>;
+16 -1: getOurStackTrace
+34 -1: (J)Ljava/lang/ref/Reference<+TT;>;
+50 -1: Lsun/reflect/generics/repository/MethodRepository;
+20 -1: MIN_BYTE_BUFFER_SIZE
+3 -1: buf
+13 -1: getAttributes
+6 -1: GERMAN
+38 -1: java/security/NoSuchAlgorithmException
+20 -1: Direct buffer memory
+6 -1: (IIZ)V
+27 -1: JVMTI_THREAD_STATE_RUNNABLE
+26 -1: [Ljava/io/File$PathStatus;
+36 -1: (Ljava/util/List;Ljava/lang/Class;)V
+13 -1: JarIndex.java
+34 -1: java/lang/Character$CharacterCache
+8 -1: csIBM857
+10 -1: LineReader
+28 -1: Ljava/lang/RuntimeException;
+30 -1: ()Ljava/util/Enumeration<TV;>;
+12 -1: getSeparator
+124 -1: (Ljava/security/PrivilegedAction;Ljava/security/AccessControlContext;Ljava/security/AccessControlContext;)Ljava/lang/Object;
+97 -1: Ljava/util/Map<Ljava/lang/Integer;Ljava/lang/ref/WeakReference<Ljava/nio/charset/CoderResult;>;>;
+21 -1: ConstantCallSite.java
+40 -1: sun/util/locale/provider/LocaleResources
+101 -1: <U::Ljava/lang/Comparable<-TU;>;>(Ljava/util/function/Function<-TT;+TU;>;)Ljava/util/Comparator<TT;>;
+4 -1: copy
+23 -1: sun/security/util/Debug
+10 -1: isAbsolute
+34 -1: (Ljava/lang/invoke/MethodHandle;)V
+34 -1: (Ljava/lang/invoke/MethodHandle;)Z
+48 -1: ()[Ljava/util/concurrent/ConcurrentHashMap$Node;
+34 -1: (Lsun/reflect/LangReflectAccess;)V
+8 -1: csIBM862
+8 -1: csIBM866
+64 -1: java/util/concurrent/ConcurrentHashMap$MapReduceKeysToDoubleTask
+39 -1: No java.util.Objects instances for you!
+68 -1: (Ljava/util/PrimitiveIterator$OfInt;JI)Ljava/util/Spliterator$OfInt;
+19 -1: Illegal class name 
+24 -1: uncaughtExceptionHandler
+12 -1: runFinalizer
+23 -1: java/io/FileInputStream
+41 -1: (Ljava/lang/Class<*>;Ljava/lang/String;)V
+98 -1: (Ljava/util/HashMap$Node<TK;TV;>;Ljava/util/HashMap$Node<TK;TV;>;)Ljava/util/HashMap$Node<TK;TV;>;
+30 -1: java/nio/charset/CoderResult$1
+20 -1: SourceDebugExtension
+30 -1: java/nio/charset/CoderResult$2
+69 -1: ([Ljava/security/ProtectionDomain;[Ljava/security/ProtectionDomain;)Z
+14 -1: 1.8.0-internal
+16 -1: ReduceValuesTask
+13 -1: toLowerString
+14 -1: newPrintStream
+13 -1: unicodelittle
+19 -1: getClassLoadingLock
+9 -1: DigitTens
+80 -1: (Ljava/lang/Class<*>;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/MethodType;
+12 -1: sun_eu_greek
+21 -1: getBootstrapClassPath
+9 -1: volatile 
+15 -1: UnmodifiableMap
+21 -1: enumConstantDirectory
+10 -1: getContent
+4 -1: cosh
+74 -1: (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Ljava/util/Locale;
+11 -1: ([JII[JII)V
+28 -1: java/lang/reflect/Executable
+17 -1: MapReduceKeysTask
+6 -1: isOpen
+17 -1: AnnotationDefault
+17 -1: Already connected
+27 -1: (Lsun/misc/JavaNetAccess;)V
+34 -1: ([BILjava/nio/charset/Charset;Z)[B
+55 -1: Ljava/lang/invoke/DirectMethodHandle$EnsureInitialized;
+8 -1: Set.java
+10 -1: DEAD_ENTRY
+7 -1: nextInt
+3 -1: wtb
+23 -1: java/util/Collections$1
+86 -1: (Lsun/reflect/generics/factory/GenericsFactory;)Lsun/reflect/generics/visitor/Reifier;
+23 -1: java/util/Collections$2
+23 -1: java/util/Collections$3
+18 -1: DoubleCumulateTask
+22 -1: skip value is negative
+85 -1: (Ljava/io/InputStream;Ljava/lang/Object;Ljava/lang/String;)Lsun/nio/cs/StreamDecoder;
+5 -1: \\[\\]$
+34 -1: Ljava/util/NoSuchElementException;
+3 -1: NaN
+30 -1: sun/util/calendar/CalendarDate
+10 -1: V_Constant
+20 -1: java/lang/Comparable
+9 -1: text/html
+20 -1: -9223372036854775808
+20 -1: DataInputStream.java
+20 -1:    Member defaults: 
+35 -1: Ljava/lang/ref/ReferenceQueue$Lock;
+8 -1: getBytes
+17 -1: CodePointIterator
+11 -1: Signal.java
+19 -1: javaHomePrefixCache
+12 -1: replaceFirst
+37 -1: Ljava/lang/IndexOutOfBoundsException;
+39 -1: (Ljava/lang/String;ZI)Ljava/lang/Class;
+21 -1: initSystemClassLoader
+28 -1: America/Indiana/Indianapolis
+47 -1: (Ljava/security/Permission;Ljava/lang/Object;)V
+27 -1: java/net/URISyntaxException
+21 -1: createSecurityManager
+7 -1: suspend
+12 -1: L_UNRESERVED
+17 -1: checkMemberAccess
+12 -1: CoreCounters
+19 -1: java/net/HttpCookie
+8 -1: isGetter
+17 -1: setDaylightSaving
+27 -1: java.launcher.javafx.error1
+53 -1: java/util/concurrent/ConcurrentHashMap$CollectionView
+16 -1: readUnsignedByte
+47 -1: (Ljava/lang/String;)Ljava/net/URLStreamHandler;
+40 -1: (Z)[Ljava/lang/reflect/Constructor<TT;>;
+14 -1: javaLangAccess
+5 -1: apply
+8 -1: getFloat
+15 -1: getD3DAvailable
+25 -1: startLocalManagementAgent
+7 -1: RUNTIME
+22 -1: LocalVariableTypeTable
+8 -1: doOutput
+16 -1: CheckedSortedSet
+10 -1: zoneOffset
+64 -1: (Ljava/security/Permission;)Ljava/security/PermissionCollection;
+13 -1: hasUnresolved
+13 -1: user.language
+11 -1: getDoubleAt
+14 -1: getQueueLength
+37 -1: java/nio/DirectByteBuffer$Deallocator
+6 -1: ASHIFT
+9 -1: checkPath
+80 -1: (Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;Ljava/lang/Class;)V
+6 -1: CENEXT
+16 -1: doubleToLongBits
+11 -1: copyToArray
+9 -1: copyField
+36 -1: ()Lsun/util/calendar/Gregorian$Date;
+53 -1: (ILjava/lang/Object;)Ljava/util/HashMap$Node<TK;TV;>;
+44 -1: (TK;TV;Ljava/lang/ref/ReferenceQueue<TV;>;)V
+35 -1: java/util/Collections$EmptyIterator
+38 -1: sealing violation: can't seal package 
+37 -1: (Ljava/util/Queue;Ljava/lang/Class;)V
+129 -1: (Ljava/lang/Class<*>;Ljava/lang/String;[Ljava/lang/Class<*>;Ljava/lang/Class<*>;[Ljava/lang/Class<*>;IILjava/lang/String;[B[B[B)V
+18 -1: HashMapSpliterator
+8 -1: putShort
+23 -1: (Ljava/lang/Object;II)V
+7 -1: GERMANY
+13 -1: toTitleString
+102 -1: (Ljava/util/ArrayPrefixHelpers$CumulateTask;Ljava/util/function/BinaryOperator;[Ljava/lang/Object;II)V
+44 -1: ([Ljava/lang/Object;)Ljava/util/Spliterator;
+6 -1: unused
+25 -1: java/net/URLClassLoader$1
+25 -1: java/net/URLClassLoader$2
+25 -1: java/net/URLClassLoader$3
+25 -1: java/net/URLClassLoader$4
+12 -1: getFixedDate
+25 -1: java/net/URLClassLoader$5
+4 -1: time
+25 -1: java/net/URLClassLoader$6
+25 -1: java/net/URLClassLoader$7
+14 -1: historicalName
+12 -1: normalizeKey
+13 -1: MIN_SURROGATE
+41 -1: java/nio/charset/CharacterCodingException
+18 -1: java/lang/Iterable
+8 -1: ([JII)[J
+103 -1: Ljava/util/Map<Ljava/lang/Class<+Ljava/lang/annotation/Annotation;>;Ljava/lang/annotation/Annotation;>;
+18 -1: putBooleanVolatile
+5 -1: Entry
+11 -1: CounterCell
+12 -1: monitorEnter
+10 -1: skipBuffer
+15 -1: hasMoreElements
+24 -1: newIllegalStateException
+24 -1: Ljava/lang/LinkageError;
+12 -1: getEntryFlag
+44 -1: (Ljava/lang/Throwable$PrintStreamOrWriter;)V
+18 -1: java/nio/IntBuffer
+17 -1: jdk_minor_version
+28 -1: DIRECTIONALITY_RIGHT_TO_LEFT
+15 -1: getAndSetObject
+7 -1: sizeCtl
+15 -1: Ljava/util/Set;
+32 -1: (I)Ljava/lang/invoke/LambdaForm;
+16 -1: runFinalization0
+30 -1: Ljava/lang/reflect/Executable;
+15 -1: compareUnsigned
+5 -1: nkeys
+31 -1: Ljava/nio/channels/FileChannel;
+40 -1: sun/reflect/generics/tree/ClassSignature
+22 -1: (Ljava/lang/Object;I)B
+22 -1: (Ljava/lang/Object;I)C
+22 -1: (Ljava/lang/Object;I)D
+7 -1: JANUARY
+30 -1: newGetIllegalArgumentException
+22 -1: (Ljava/lang/Object;I)F
+22 -1: (Ljava/lang/Object;I)I
+22 -1: (Ljava/lang/Object;I)J
+28 -1: generateNamedFunctionInvoker
+38 -1: (Ljava/lang/String;)Ljava/time/ZoneId;
+19 -1: lambda$codePoints$2
+4 -1: Null
+7 -1: setEras
+15 -1: lambda$chars$15
+14 -1: CollectionView
+24 -1: (C)Ljava/io/PrintStream;
+22 -1: (Ljava/lang/Object;I)S
+96 -1: (Ljava/security/AccessControlContext;Lsun/security/util/Debug;Ljava/security/ProtectionDomain;)V
+17 -1: getJulianCalendar
+22 -1: (Ljava/lang/Object;I)V
+17 -1: getMethodAccessor
+9 -1: not owner
+35 -1: java/lang/reflect/ParameterizedType
+15 -1: ValueCollection
+22 -1: (Ljava/lang/Object;I)Z
+4 -1: utf8
+5 -1: CHINA
+9 -1: sprintf0d
+18 -1: java/nio/file/Path
+35 -1: DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC
+30 -1: URI has an authority component
+43 -1: (Ljava/lang/Object;)Ljava/util/Spliterator;
+15 -1: <no principals>
+62 -1: (Lsun/util/locale/BaseLocale$Key;)Lsun/util/locale/BaseLocale;
+6 -1: ([ZZ)V
+10 -1: getContext
+12 -1: content-type
+99 -1: (Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/ref/ReferenceQueue;ILjava/util/WeakHashMap$Entry;)V
+51 -1: (Ljava/util/concurrent/ConcurrentHashMap<TK;TV;>;)V
+27 -1: Invalid constant pool index
+20 -1: getUnicodeLocaleType
+43 -1: Ljava/nio/charset/CharacterCodingException;
+113 -1: (Ljava/net/URL;Ljava/net/URLStreamHandler;Ljava/util/HashMap<Ljava/lang/String;Lsun/misc/URLClassPath$Loader;>;)V
+56 -1: (Ljava/lang/String;Ljava/lang/String;)Ljava/util/Locale;
+24 -1: Lsun/misc/SignalHandler;
+8 -1: readlink
+125 -1: (Ljava/nio/file/WatchService;[Ljava/nio/file/WatchEvent$Kind<*>;[Ljava/nio/file/WatchEvent$Modifier;)Ljava/nio/file/WatchKey;
+11 -1: initStatics
+15 -1: unmappableCache
+13 -1: fixMethodType
+25 -1: sun/net/www/MessageHeader
+3 -1: cdt
+27 -1: Ljava/util/Locale$Category;
+59 -1: (ILjava/lang/Object;Ljava/lang/Object;ZZ)Ljava/lang/Object;
+8 -1: Accessor
+14 -1: mapLibraryName
+54 -1: (Lsun/misc/URLClassPath$JarLoader;)Lsun/misc/JarIndex;
+24 -1: ZoneOffsetTransitionRule
+7 -1: getChar
+3 -1: cee
+25 -1: MapReduceValuesToLongTask
+20 -1: declaredPublicFields
+44 -1: (Ljava/lang/ClassLoader;Ljava/lang/String;)J
+13 -1: removeElement
+20 -1: Ljava/nio/ByteOrder;
+8 -1: parallel
+67 -1: (Ljava/lang/reflect/Constructor;Lsun/reflect/ConstructorAccessor;)V
+22 -1: java/util/zip/ZipCoder
+8 -1: ([ZIIZ)V
+45 -1: (Ljava/lang/String;Ljava/lang/CharSequence;)Z
+8 -1: february
+40 -1: java/util/zip/ZipFile$ZipFileInputStream
+47 -1: java/nio/charset/Charset$ExtendedProviderHolder
+13 -1: IEEEremainder
+15 -1: sun/misc/Perf$1
+9 -1: abstract 
+20 -1: ()Ljava/time/ZoneId;
+7 -1: ONE_DAY
+92 -1: (Ljava/util/concurrent/ConcurrentHashMap$Node;)Ljava/util/concurrent/ConcurrentHashMap$Node;
+45 -1: (Ljava/util/ListIterator;I)Ljava/lang/Object;
+16 -1: Buffer size <= 0
+9 -1: checkName
+11 -1: getJarEntry
+20 -1: Replacement too long
+53 -1: (Ljava/lang/String;)Ljava/nio/charset/CharsetDecoder;
+12 -1: isWhitespace
+15 -1: csISOLatinGreek
+70 -1: (Ljava/lang/reflect/Constructor<*>;Lsun/reflect/ConstructorAccessor;)V
+20 -1: TypeAnnotationTarget
+3 -1: No 
+27 -1: Lsun/reflect/FieldAccessor;
+12 -1: doPrivileged
+83 -1: (JLjava/util/function/ToIntFunction<-TV;>;ILjava/util/function/IntBinaryOperator;)I
+60 -1: (Ljava/nio/file/attribute/FileTime;)Ljava/util/zip/ZipEntry;
+11 -1: changeEntry
+18 -1: MessageHeader.java
+51 -1: ([Ljava/lang/String;Ljava/util/Map;)Ljava/util/Map;
+9 -1: castEntry
+16 -1: ACCESS_MODIFIERS
+11 -1: newInstance
+24 -1: lastIndexOfSupplementary
+13 -1: JZENTRY_EXTRA
+5 -1: LONGS
+7 -1: domain 
+16 -1: protocolPathProp
+21 -1: java/util/Hashtable$1
+10 -1: isSameDate
+23 -1: (I)Ljava/nio/file/Path;
+33 -1: java/util/PrimitiveIterator$OfInt
+7 -1: ([II)[I
+8 -1: variant=
+29 -1: (Ljava/util/Collection<*>;Z)Z
+38 -1:  already loaded in another classloader
+10 -1: setSeconds
+9 -1: makeShort
+59 -1: ClassLoader.findLibrary failed to return an absolute path: 
+26 -1: java/util/jar/JarException
+9 -1: setCharAt
+13 -1: initCauseFrom
+13 -1: val$fieldName
+18 -1: MIN_HIGH_SURROGATE
+60 -1: (Lsun/util/calendar/ZoneInfoFile$ZoneOffsetTransitionRule;)B
+25 -1: getDayOfWeekFromFixedDate
+10 -1: maybeReBox
+9 -1: getHandle
+60 -1: (Lsun/util/calendar/ZoneInfoFile$ZoneOffsetTransitionRule;)I
+59 -1: ([Ljava/lang/Class<*>;)Ljava/lang/reflect/Constructor<TT;>;
+3 -1: cis
+11 -1: getIssuerDN
+9 -1: codebase=
+80 -1: (ILjava/lang/invoke/BoundMethodHandle$SpeciesData;)Ljava/lang/invoke/LambdaForm;
+29 -1: addThreadDumpForSynchronizers
+6 -1: FRANCE
+77 -1: <T:Ljava/lang/Object;U:Ljava/lang/Object;>([TU;ILjava/lang/Class<+[TT;>;)[TT;
+45 -1: <T:Ljava/lang/Object;>()Ljava/util/List<TT;>;
+16 -1: putFloatVolatile
+34 -1: (Ljava/lang/Class;)Ljava/util/Map;
+28 -1: ()Ljava/security/Permission;
+28 -1: (Ljava/lang/CharSequence;I)I
+51 -1: ()Ljava/util/Enumeration<Ljava/util/jar/JarEntry;>;
+25 -1: (II)Ljava/nio/ByteBuffer;
+20 -1: BasicPermission.java
+5 -1: isNaN
+48 -1: (Ljava/lang/Throwable;)Ljava/lang/InternalError;
+10 -1: ONE_MINUTE
+55 -1: (Ljava/lang/invoke/DirectMethodHandle$StaticAccessor;)J
+13 -1: DAYS_IN_MONTH
+7 -1: domains
+10 -1: isUnshared
+58 -1: Ljava/lang/Number;Ljava/lang/Comparable<Ljava/lang/Long;>;
+47 -1: (Ljava/util/List;)Ljava/security/cert/CertPath;
+28 -1: ()Ljava/util/Enumeration<*>;
+34 -1: sun/misc/Launcher$ExtClassLoader$1
+22 -1: (CLjava/lang/Object;)Z
+64 -1: Ljava/lang/ref/WeakReference<Ljava/nio/charset/CharsetDecoder;>;
+7 -1: ENGLISH
+27 -1: (Ljava/util/zip/Inflater;)V
+24 -1: makeArrayElementAccessor
+72 -1: (Ljava/lang/Class<*>;[Ljava/lang/Class<*>;)Ljava/lang/invoke/MethodType;
+48 -1: (Ljava/util/jar/JarFile;)Ljava/util/Enumeration;
+48 -1: (Ljava/lang/Class;)Ljava/lang/invoke/MethodType;
+15 -1: getNthDayOfWeek
+16 -1: printHelpMessage
+15 -1: getAbsoluteFile
+9 -1: OPEN_READ
+19 -1: willGMTOffsetChange
+28 -1: (Ljava/util/LinkedHashMap;)V
+37 -1: java/security/AllPermissionCollection
+5 -1: [id="
+34 -1: java/lang/invoke/BoundMethodHandle
+31 -1: ()Ljava/security/cert/CertPath;
+50 -1: java/util/ArraysParallelSortHelpers$FJShort$Sorter
+14 -1: StaticAccessor
+22 -1: synchronizedCollection
+53 -1: ([Ljava/io/File;)Ljava/security/AccessControlContext;
+37 -1: (Ljava/lang/Class;Ljava/lang/Class;)Z
+14 -1: codePointCount
+13 -1:  is param at 
+37 -1: Ljava/lang/invoke/MemberName$Factory;
+20 -1: annotationDataOffset
+31 -1: protocol doesn't support output
+11 -1: hostAddress
+12 -1: ,dstSavings=
+35 -1: java.lang.Integer.IntegerCache.high
+15 -1: ParallelLoaders
+48 -1: (Ljava/util/Locale;)Lsun/util/locale/BaseLocale;
+8 -1: getSize0
+22 -1: checkCreateClassLoader
+8 -1: transfer
+32 -1: (Lsun/misc/JavaSecurityAccess;)V
+26 -1: ()Ljava/net/URLConnection;
+3 -1: cmp
+9 -1: setMillis
+34 -1: sun/util/calendar/AbstractCalendar
+19 -1: getDirectBufferPool
+7 -1: ([FII)V
+28 -1: ([C)Ljava/lang/StringBuffer;
+54 -1: (Ljava/lang/Class<*>;)Ljava/security/ProtectionDomain;
+26 -1: (Ljava/nio/ByteBuffer;IF)V
+11 -1: discardMark
+71 -1: (Ljava/lang/Class;)Lsun/util/locale/provider/LocaleServiceProviderPool;
+30 -1: java/io/UTFDataFormatException
+53 -1: (Ljava/nio/CharBuffer;)Ljava/nio/charset/CoderResult;
+48 -1: java/util/concurrent/ConcurrentHashMap$Traverser
+5 -1: ([F)I
+37 -1: (IC)Ljava/lang/AbstractStringBuilder;
+27 -1: Ljava/util/jar/JarVerifier;
+22 -1: java/util/Spliterators
+32 -1: java/lang/invoke/MutableCallSite
+20 -1: java/io/Serializable
+5 -1: ([F)V
+35 -1: (Ljava/security/ProtectionDomain;)V
+33 -1: ()Ljava/lang/ref/Reference<+TT;>;
+10 -1: unlinkLast
+6 -1: (JSZ)V
+8 -1: isStatic
+14 -1: subclassAudits
+23 -1: (Ljava/lang/String;)TT;
+17 -1: java.awt.headless
+9 -1: <Unknown>
+39 -1: Lsun/util/locale/LocaleSyntaxException;
+8 -1: location
+3 -1: cos
+27 -1: createGarbageCollectorMBean
+20 -1: MAX_MH_INVOKER_ARITY
+75 -1: (Ljava/nio/ByteBuffer;Ljava/nio/CharBuffer;Z)Ljava/nio/charset/CoderResult;
+14 -1: Cloneable.java
+50 -1: (Lsun/reflect/DelegatingConstructorAccessorImpl;)V
+26 -1: sun/nio/ch/FileChannelImpl
+51 -1: (Ljava/lang/Class;Ljava/lang/reflect/Constructor;)V
+18 -1: Unknown byte order
+28 -1: ()[Lsun/invoke/util/Wrapper;
+21 -1: getReadClassBytesTime
+64 -1: (Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/MethodTypeForm;
+11 -1: setDelegate
+20 -1: (Ljava/util/List;)[C
+7 -1: usemmap
+22 -1: (CC)Ljava/lang/String;
+34 -1: sun/invoke/util/BytecodeDescriptor
+21 -1: getJavaSecurityAccess
+53 -1: (Ljava/nio/ByteBuffer;)Ljava/nio/charset/CoderResult;
+7 -1: ibm-737
+19 -1: (Ljava/lang/Enum;)I
+4 -1: rint
+11 -1: Constructor
+9 -1: arraycopy
+35 -1: ([D)Ljava/util/stream/DoubleStream;
+13 -1: comparingLong
+40 -1: <T:Ljava/lang/Object;>Ljava/lang/Object;
+23 -1: OutputStreamWriter.java
+8 -1: getShort
+17 -1: CLASSPATH_LASTOCC
+13 -1: createNewFile
+14 -1: internalValues
+34 -1: Ljava/lang/IllegalAccessException;
+23 -1: (JILjava/lang/Object;)V
+27 -1: sun.misc.URLClassPath.debug
+45 -1: [Ljava/lang/ThreadLocal$ThreadLocalMap$Entry;
+35 -1: ()Lsun/reflect/ConstructorAccessor;
+12 -1: classEnabled
+23 -1: cachedFixedDateNextJan1
+48 -1: (Ljava/util/Locale$LocaleKey;)Ljava/util/Locale;
+26 -1: (Ljava/lang/ThreadGroup;)V
+7 -1: setErr0
+6 -1: CENFLG
+26 -1: (Ljava/lang/ThreadGroup;)Z
+18 -1: sun/misc/MetaIndex
+3 -1: crc
+34 -1: (Z)Ljava/lang/invoke/MethodHandle;
+12 -1: replaceNames
+15 -1: java/util/Stack
+57 -1: Ljava/util/Vector<Ljava/lang/ClassLoader$NativeLibrary;>;
+89 -1: (JLjava/util/function/ToDoubleFunction<-TV;>;DLjava/util/function/DoubleBinaryOperator;)D
+24 -1: permission can't be null
+22 -1: Unable to connect to: 
+44 -1: (Ljava/nio/ByteBuffer;)Ljava/nio/ByteBuffer;
+14 -1: floatToIntBits
+11 -1: getLastRule
+6 -1: EXTCRC
+26 -1: java/net/InetSocketAddress
+36 -1: (Lsun/misc/URLClassPath$JarLoader;)V
+27 3: sun/launcher/LauncherHelper
+8 -1: ecma-118
+49 -1: (Ljava/net/URL;Ljava/lang/String;)[Ljava/net/URL;
+13 -1: hashCodeValue
+5 -1: CESU8
+21 -1: appendVmSelectMessage
+13 -1: bindImmediate
+12 -1: closeLoaders
+16 -1: emptySpliterator
+28 -1: (J)Ljava/time/LocalDateTime;
+22 -1: [Ljava/lang/Character;
+5 -1: certs
+6 -1: (null)
+25 -1: java/io/ObjectInputStream
+27 -1: ([Ljava/lang/ThreadGroup;)I
+3 -1: cst
+3 -1: csu
+20 -1: java/nio/ShortBuffer
+53 -1: (Ljava/lang/ClassValue;Ljava/lang/ClassValue$Entry;)V
+4 -1: (Z)I
+24 -1: Ljava/io/FileDescriptor;
+6 -1: cclass
+10 -1: , profile 
+39 -1: (Ljava/lang/String;)Ljava/lang/Process;
+4 -1: (Z)V
+5 -1: toURI
+24 -1: ConstructorAccessor.java
+5 -1: toURL
+18 -1: addAllIfNotPresent
+4 -1: (Z)Z
+5 -1: parse
+11 -1: isPrimitive
+42 -1: (Ljava/io/File;)Ljava/lang/ProcessBuilder;
+36 -1: (Ljava/util/Date;)Ljava/lang/String;
+23 -1: getFormalTypeParameters
+13 -1: Resource.java
+7 -1: ibm-775
+6 -1: isEnum
+24 -1: setJavaUtilZipFileAccess
+21 -1: \t[CIRCULAR REFERENCE:
+51 -1: (Ljava/lang/CharSequence;)Ljava/util/regex/Matcher;
+24 -1: (I)Ljava/nio/ByteBuffer;
+53 -1: sun/reflect/generics/repository/GenericDeclRepository
+3 -1: xor
+17 -1: invokeBasicMethod
+27 -1: java/lang/invoke/LambdaForm
+99 -1: (Ljava/util/jar/Manifest;Ljava/util/jar/JarEntry;Ljava/io/InputStream;Ljava/util/jar/JarVerifier;)V
+46 -1: Lsun/reflect/generics/factory/GenericsFactory;
+37 -1: sun/misc/Launcher$BootClassPathHolder
+10 -1: BindCaller
+24 -1: java/lang/reflect/Member
+32 -1: java/lang/management/ThreadState
+5 -1: (IB)V
+21 -1: RuntimeException.java
+5 -1: ended
+17 -1: java/util/TreeSet
+7 -1: : no !/
+16 -1: java/util/Vector
+9 -1: nextAfter
+22 -1: Ljava/lang/Deprecated;
+20 -1: requestedCharsetName
+37 -1: ([JIII)Ljava/util/Spliterator$OfLong;
+14 -1: internArgument
+11 -1: getTimeZone
+10 -1: isValidKey
+11 -1: LAST_RESULT
+43 -1: sun/reflect/annotation/TypeAnnotationParser
+13 -1: encodedInPath
+52 -1: (Ljava/security/PrivilegedAction;)Ljava/lang/Object;
+4 -1: LL_L
+34 -1: java/nio/charset/CodingErrorAction
+10 -1: copyFields
+11 -1: getConstant
+9 -1: threshold
+13 -1: aliases_UTF_8
+27 -1: (Ljava/util/ArrayDeque;II)V
+29 -1: Ljava/lang/ref/SoftReference;
+19 -1: indexedBinarySearch
+11 -1: containsKey
+81 -1: ([Ljava/lang/ClassValue$Entry;Ljava/lang/ClassValue;)Ljava/lang/ClassValue$Entry;
+86 -1: (Ljava/lang/invoke/MethodHandle;Ljava/lang/Class<*>;II)Ljava/lang/invoke/MethodHandle;
+15 -1: cannot convert 
+25 -1: getSystemResourceAsStream
+46 -1: (Ljava/util/Properties;)Ljava/util/Properties;
+12 -1: reverseOrder
+16 -1: getSystemPackage
+8 -1: ([SII)[S
+19 -1: makeAccessException
+100 -1: <U:Ljava/lang/Object;>(JLjava/util/function/Function<-TK;+TU;>;Ljava/util/function/Consumer<-TU;>;)V
+39 -1: ([Ljava/lang/Class;I)[Ljava/lang/Class;
+19 -1: Ljava/lang/Boolean;
+6 -1: Hidden
+47 -1: java/lang/invoke/MethodHandleImpl$ArrayAccessor
+5 -1: APRIL
+8 -1: emptySet
+11 -1: getCombiner
+58 -1: (Ljava/lang/String;I[Ljava/lang/invoke/LambdaForm$Name;I)V
+24 -1: java.system.class.loader
+14 -1: Can't handle: 
+16 -1: isNullConversion
+38 -1: ()Ljava/util/HashMap$TreeNode<TK;TV;>;
+29 -1: referenceKindIsConsistentWith
+11 -1: flushBuffer
+8 -1: putField
+27 -1: ()Ljava/security/PublicKey;
+53 -1: ()Ljava/util/stream/Stream<Ljava/util/jar/JarEntry;>;
+10 -1: pathToURLs
+26 -1: throwIllegalStateException
+10 -1: markedChar
+14 -1: isNativeMethod
+36 -1: (I)Ljava/lang/AbstractStringBuilder;
+54 -1: java/util/concurrent/ConcurrentHashMap$ReservationNode
+45 -1: Lsun/misc/JavaSecurityProtectionDomainAccess;
+62 -1: (Ljava/lang/Class<*>;Ljava/lang/Class<*>;Ljava/lang/Object;I)V
+7 -1: subList
+8 -1: UTF_32BE
+6 -1: U_None
+50 -1: sun/reflect/generics/repository/AbstractRepository
+62 -1: (Ljava/lang/Class<*>;Ljava/lang/Class<*>;Ljava/lang/Object;I)Z
+43 -1: sun/net/www/protocol/file/FileURLConnection
+13 -1: setZoneOffset
+43 -1: Underlying input stream returned zero bytes
+54 -1: [a-zA-Z_$][a-zA-Z0-9_$]*([.][a-zA-Z_$][a-zA-Z0-9_$]*)*
+13 -1: containsValue
+44 -1: (Ljava/nio/CharBuffer;)Ljava/nio/ByteBuffer;
+25 -1: isNullReferenceConversion
+38 -1: Ljava/util/Vector<Ljava/lang/String;>;
+6 -1: toFile
+7 -1: getSlot
+17 -1: (Ljava/net/URI;)V
+32 -1: java.security.cert.Certificate: 
+24 -1: ()Lsun/misc/PerfCounter;
+11 -1: Asia/Hebron
+16 -1: createMemoryPool
+10 -1: addToCache
+29 -1: Ljava/lang/invoke/DontInline;
+39 -1: java/lang/ref/Finalizer$FinalizerThread
+58 -1: (Ljava/lang/Class;)Lsun/reflect/generics/scope/ClassScope;
+20 -1: getBooleanAttributes
+15 -1: parallelLockMap
+34 -1: java/util/Vector$VectorSpliterator
+21 -1: createMemoryPoolMBean
+15 -1: no content-type
+41 -1: Couldn't find 3-letter language code for 
+5 -1: slash
+34 -1: Ljava/lang/annotation/ElementType;
+8 -1: isSetter
+26 -1: (ZLjava/lang/String;JJJZ)V
+6 -1: GMT_ID
+73 -1: ()[Ljava/lang/reflect/TypeVariable<Ljava/lang/reflect/Constructor<TT;>;>;
+56 -1: (Ljava/lang/String;Ljava/lang/String;)Ljava/lang/Object;
+32 -1: (Ljava/util/Set;)Ljava/util/Set;
+4 -1: stop
+62 -1: (Ljava/lang/String;IILjava/lang/String;I)Ljava/nio/ByteBuffer;
+11 -1: genericInfo
+11 -1: listToArray
+26 -1: ()Ljava/util/jar/Manifest;
+13 -1: putOrderedInt
+5 -1: flush
+13 -1: ArrayAccessor
+4 -1: Name
+95 -1: ([Ljava/util/concurrent/ConcurrentHashMap$Node;[Ljava/util/concurrent/ConcurrentHashMap$Node;)V
+68 -1: (Ljava/lang/Class;Ljava/lang/Object;)Ljava/lang/invoke/MethodHandle;
+23 -1: java/lang/ref/Reference
+37 -1: (Ljava/nio/file/attribute/FileTime;)J
+14 -1: MIN_CODE_POINT
+9 -1: ISO8859-1
+9 -1: ISO8859-2
+9 -1: ISO8859-5
+34 -1: ([CILjava/nio/charset/Charset;Z)[C
+9 -1: ISO8859-9
+9 -1: getUTF8At
+12 -1: java/net/URI
+67 -1: (Ljava/io/DataInput;Ljava/lang/String;)Lsun/util/calendar/ZoneInfo;
+22 -1: ListCompositionPattern
+67 -1: (Ljava/lang/String;[BIILjava/security/CodeSource;)Ljava/lang/Class;
+3 1: xxx
+12 -1: java/net/URL
+27 -1: Can't overwrite cause with 
+30 -1: sun/util/locale/BaseLocale$Key
+22 -1: forkSecondaryFinalizer
+23 -1: java/security/Principal
+8 -1: makeSite
+17 -1: NEGATIVE_INFINITY
+12 -1: addUnstarted
+12 -1: internalForm
+9 -1: cellsBusy
+23 -1: (Ljava/lang/Object;IJ)V
+13 -1: getParameters
+6 -1: H_PATH
+10 -1: L_REG_NAME
+139 -1: <K:Ljava/lang/Object;V:Ljava/lang/Object;>Ljava/util/AbstractMap<TK;TV;>;Ljava/util/Map<TK;TV;>;Ljava/lang/Cloneable;Ljava/io/Serializable;
+6 -1: latin0
+10 -1: addSeconds
+6 -1: latin1
+6 -1: latin2
+6 -1: latin4
+6 -1: latin5
+17 -1: getWaitingThreads
+6 -1: latin9
+21 -1: java/util/Comparators
+10 -1: trimToSize
+96 -1: <T:Ljava/lang/Object;>(Ljava/util/Collection<TT;>;Ljava/lang/Object;)Ljava/util/Collection<TT;>;
+43 -1: ([JLjava/util/function/IntToLongFunction;)V
+23 -1: GET_COMBINER_PERMISSION
+20 -1: lambda$replaceAll$14
+5 -1: KOREA
+20 -1: getJvmSpecialVersion
+9 -1: dumpStack
+16 -1: CACHE_LOAD_LIMIT
+43 -1:               GSS LoginConfigImpl debugging
+26 -1: (DLjava/lang/Appendable;)V
+8 -1: forName0
+35 -1: java/lang/ClassLoader$NativeLibrary
+46 -1: java/util/concurrent/ConcurrentHashMap$Segment
+24 -1: getDeclaredAnnotationMap
+89 -1: java/util/concurrent/atomic/AtomicReferenceFieldUpdater$AtomicReferenceFieldUpdaterImpl$1
+20 -1: java_runtime_version
+26 -1: java/util/stream/IntStream
+16 -1: Ljava/io/Writer;
+69 -1: ()Ljava/util/SortedMap<Ljava/lang/String;Ljava/nio/charset/Charset;>;
+15 -1: getClassLoader0
+6 -1: x86_64
+11 -1: isInterface
+15 -1: MODIFIER_SYMBOL
+44 -1: Note: Separate multiple options with a comma
+9 -1: recursive
+19 -1: java/nio/CharBuffer
+8 -1: capacity
+17 -1: validateMainClass
+50 -1: (Ljava/util/Set;Ljava/lang/Object;)Ljava/util/Set;
+22 -1: (Ljava/lang/Object;J)B
+22 -1: (Ljava/lang/Object;J)C
+22 -1: (Ljava/lang/Object;J)D
+17 -1: not a method type
+22 -1: (Ljava/lang/Object;J)F
+5 -1: count
+32 -1: AtomicReferenceFieldUpdater.java
+22 -1: (Ljava/lang/Object;J)I
+14 -1: methodAccessor
+22 -1: (Ljava/lang/Object;J)J
+7 -1: isError
+51 -1: (Ljava/nio/Buffer;II)Ljava/nio/charset/CoderResult;
+53 -1: (BZLjava/lang/Class<*>;)Ljava/lang/invoke/LambdaForm;
+25 -1:  <no signer certificates>
+5 -1: [pos=
+14 -1: lambda$chars$1
+9 -1: CELLVALUE
+16 -1: haveLeftoverChar
+22 -1: ([Ljava/lang/String;)V
+32 -1: java/lang/InstantiationException
+7 -1: SIG_IGN
+13 -1: ZipUtils.java
+50 -1: Ljava/lang/ref/ReferenceQueue<Ljava/lang/Object;>;
+22 -1: (Ljava/lang/Object;J)S
+69 -1: Ljava/util/HashMap<Ljava/lang/String;Lsun/misc/URLClassPath$Loader;>;
+16 -1: newInternalError
+22 -1: (Ljava/lang/Object;J)V
+9 -1: ABBR_MASK
+5 -1: array
+22 -1: (Ljava/lang/Object;J)Z
+13 -1: FilteringMode
+30 -1: java/util/stream/StreamSupport
+19 -1: retrieveDisplayName
+56 -1: (Ljava/util/TimeZone;)Lsun/util/calendar/Gregorian$Date;
+10 -1: val$values
+9 -1: normalize
+28 -1: (II)Ljava/lang/CharSequence;
+16 -1: serialVersionUID
+7 -1: getPath
+25 -1: (ILjava/lang/Class<*>;Z)V
+13 -1: thenComparing
+51 -1: (Ljava/lang/Object;)Ljava/lang/invoke/MethodHandle;
+36 -1: ()[Ljava/lang/annotation/Annotation;
+14 -1: MetaIndex.java
+8 -1: identity
+15 -1: findSystemClass
+24 -1: privateGetDeclaredFields
+27 -1: java/lang/ref/SoftReference
+19 -1: useCanonPrefixCache
+3 -1: dec
+3 -1: PLT
+8 -1: UTF_32LE
+17 -1: java/util/HashMap
+12 -1: toEpochMilli
+9 -1: intStream
+11 -1: Caused by: 
+31 -1: java/nio/charset/CharsetDecoder
+30 -1:               is being checked
+11 -1: parseHeader
+25 -1: ACCUMULATED_DAYS_IN_MONTH
+34 -1: newGetByteIllegalArgumentException
+21 -1: checkPropertiesAccess
+13 -1: StackMapTable
+8 -1: addCount
+51 -1: (Ljava/lang/invoke/MethodHandle;)Ljava/lang/String;
+9 -1: authority
+15 -1: iso-10646-ucs-2
+6 -1: SUNDAY
+22 -1: LocalGregorianCalendar
+9 -1: listRoots
+32 -1: Lsun/reflect/generics/tree/Tree;
+38 -1: [Ljava/util/WeakHashMap$Entry<TK;TV;>;
+11 -1: nativeOrder
+5 -1: long0
+5 -1: long1
+5 -1: long2
+5 -1: long3
+17 -1: capacityIncrement
+5 -1: long4
+97 -1: (Ljava/lang/String;Ljava/lang/invoke/MethodType;[Ljava/lang/Object;)Ljava/lang/invoke/MemberName;
+31 -1: Ljava/lang/CharacterDataLatin1;
+5 -1: long5
+5 -1: long6
+5 -1: long7
+17 -1: reduceValuesToInt
+13 -1: package2certs
+13 -1: isTypeVisible
+30 -1: java/lang/ref/PhantomReference
+47 -1: ()Ljava/util/stream/Stream<Ljava/lang/String;>;
+9 -1: longValue
+3 -1: PNT
+10 -1: storeToXML
+10 -1: getMethod0
+12 -1: constantZero
+7 -1: promise
+116 -1: (Ljava/lang/String;Lsun/reflect/generics/factory/GenericsFactory;)Lsun/reflect/generics/repository/MethodRepository;
+32 -1: DIRECTIONALITY_SEGMENT_SEPARATOR
+9 -1: byteOrder
+9 -1: isPromise
+4 -1: isOn
+6 -1: LOCCRC
+10 -1: setDefault
+9 -1: setHandle
+15 -1: java/nio/Buffer
+37 -1: (Ljava/lang/String;I)Ljava/lang/Long;
+10 -1: Float.java
+12 -1: showSettings
+27 -1: (Ljava/io/FileDescriptor;)I
+27 -1: (Ljava/io/FileDescriptor;)J
+21 -1: java/util/Spliterator
+22 -1: CodingErrorAction.java
+11 -1: isMalformed
+27 -1: java/util/PrimitiveIterator
+15 -1: THROW_EXCEPTION
+15 -1: copyToCharArray
+26 -1: ()Ljava/util/jar/JarEntry;
+27 -1: (Ljava/io/FileDescriptor;)V
+11 -1: getEncoding
+48 -1: (Ljava/lang/ThreadLocal<*>;Ljava/lang/Object;I)V
+17 -1: java.runtime.name
+28 -1: (Lsun/invoke/util/Wrapper;)Z
+20 -1: annotationTypeOffset
+27 -1: (J)Ljava/lang/StringBuffer;
+15 -1: METHOD_RECEIVER
+10 -1: startEntry
+29 -1: (I)Ljava/lang/reflect/Member;
+7 -1: setOut0
+10 -1: getMethods
+26 -1: ()Lsun/misc/JavaNioAccess;
+18 -1: linkToTargetMethod
+8 -1: INSTANCE
+3 -1: dir
+41 -1: ([Ljava/net/URL;Ljava/lang/ClassLoader;)V
+9 -1: unboxCast
+58 -1: <T:Ljava/lang/Throwable;>(TT;)Lsun/invoke/empty/Empty;^TT;
+12 -1: java.version
+50 -1: (Ljava/io/InputStream;Ljava/nio/charset/Charset;)V
+32 2: sun/net/www/protocol/jar/Handler
+20 -1: java/lang/Compiler$1
+9 -1: LongCache
+14 -1: FILL_THRESHOLD
+22 -1: getRawClassAnnotations
+9 -1: (JI[CII)I
+13 -1: hasMoreTokens
+13 -1: getSuperclass
+3 -1: PRC
+12 -1: MAX_PRIORITY
+14 -1: checkCacheLoad
+7 -1: lowMask
+8 -1: LM_CLASS
+7 -1: initIDs
+27 -1: Ljava/util/Collection<TV;>;
+3 -1: yes
+3 -1: PRT
+91 -1: (Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;)Ljava/lang/invoke/LambdaForm;
+27 -1: ()Lsun/security/util/Debug;
+8 -1: VM start
+9 -1: setMemory
+7 -1: getName
+10 -1: findSignal
+19 -1: startsWithLocHeader
+37 -1: java/util/Collections$SynchronizedMap
+51 -1: (ICLjava/lang/Object;)Ljava/lang/invoke/LambdaForm;
+62 -1: (Ljava/util/Locale;)Lsun/util/locale/provider/LocaleResources;
+17 -1: lastParameterType
+9 -1: NO_PTYPES
+116 -1: <T:Ljava/lang/Object;>([Ljava/lang/ClassValue$Entry<*>;Ljava/lang/ClassValue<TT;>;)Ljava/lang/ClassValue$Entry<TT;>;
+18 -1: DisplayNamePattern
+8 -1: getField
+5 -1: flags
+3 -1: PST
+17 -1: annotationDefault
+18 -1: java/nio/ByteOrder
+8 -1: highMask
+6 -1: ascii7
+29 -1: getGregorianYearFromFixedDate
+125 -1: (Ljava/lang/String;[Ljava/lang/invoke/LambdaForm$Name;[Ljava/lang/invoke/LambdaForm$Name;Ljava/lang/invoke/LambdaForm$Name;)V
+8 -1: =Lambda(
+29 -1: Ljava/util/WeakHashMap$Entry;
+18 -1: multiValueIterator
+74 -1: Ljava/util/LinkedHashMap<Ljava/lang/String;Ljava/io/ExpiringCache$Entry;>;
+13 -1: CONV_OP_LIMIT
+6 -1: sclSet
+81 -1: (Lsun/misc/URLClassPath$JarLoader;Ljava/util/jar/JarFile;)Ljava/util/jar/JarFile;
+14 -1: appendFragment
+46 -1: java/util/Collections$SynchronizedNavigableMap
+36 -1: application/x-java-serialized-object
+13 -1: setNativeName
+53 -1: java/util/concurrent/ConcurrentHashMap$ForwardingNode
+16 -1: setJavaAWTAccess
+30 -1: methodHandleInvokeLinkerMethod
+15 -1: reduceKeysToInt
+20 -1: ensureCapacityHelper
+23 -1: createFileURLConnection
+12 -1: d3dAvailable
+69 -1: (Ljava/lang/Object;Ljava/lang/invoke/MethodHandle;)Ljava/lang/String;
+49 -1: java/util/ArraysParallelSortHelpers$FJLong$Sorter
+21 -1: explicitCastArguments
+24 -1: JAVAFX_LAUNCH_MODE_CLASS
+9 -1: invoke_MT
+18 -1: ensureMemberAccess
+74 -1: (Ljava/util/concurrent/locks/AbstractQueuedSynchronizer$ConditionObject;)I
+36 -1: java/nio/charset/spi/CharsetProvider
+23 -1: (Ljava/lang/String;II)V
+14 -1: initProperties
+4 -1: (F)F
+35 -1: [[Ljava/lang/annotation/Annotation;
+4 -1: (F)I
+106 -1: Ljava/lang/Object;Ljava/io/Serializable;Ljava/lang/Comparable<Ljava/lang/String;>;Ljava/lang/CharSequence;
+46 -1: java/lang/invoke/BoundMethodHandle$SpeciesData
+74 -1: (Ljava/util/concurrent/locks/AbstractQueuedSynchronizer$ConditionObject;)Z
+24 -1: java.launcher.opt.header
+37 -1: ([Ljava/security/ProtectionDomain;Z)V
+10 -1: lineBuffer
+7 -1: ibm-813
+9 -1: isBuiltin
+4 -1: (F)V
+7 -1: ibm-819
+9 -1: H_ESCAPED
+4 -1: (F)Z
+20 -1: suppressedExceptions
+12 -1: UTF-32LE-BOM
+19 -1: CalendarSystem.java
+8 -1: readOnly
+81 -1: (JLjava/util/function/Function;Ljava/util/function/BiFunction;)Ljava/lang/Object;
+32 -1: sun/misc/Launcher$AppClassLoader
+78 -1: Ljava/lang/Object;Ljava/io/Serializable;Ljava/lang/Comparable<Ljava/io/File;>;
+17 -1: [Ljava/lang/Enum;
+41 -1: java/util/Collections$CheckedNavigableSet
+8 -1: asSetter
+3 -1: dom
+41 -1: (I)[Ljava/util/WeakHashMap$Entry<TK;TV;>;
+16 -1: localeExtensions
+21 -1: sun/net/www/ParseUtil
+21 -1: Ljava/nio/ByteBuffer;
+29 -1: java/util/concurrent/TimeUnit
+25 -1: java/lang/CharacterData00
+15 -1: sun/misc/Unsafe
+28 -1: java/io/ByteArrayInputStream
+3 -1: dow
+25 -1: java/lang/CharacterData01
+25 -1: java/lang/CharacterData02
+6 -1: STORED
+11 -1: isTransient
+8 -1: function
+16 -1: getCanonicalFile
+13 -1: ,useDaylight=
+24 -1: domain (context is null)
+12 -1: Cleaner.java
+17 -1: CalendarDate.java
+49 -1: Lsun/reflect/generics/repository/FieldRepository;
+14 -1: forInputString
+25 -1: java/lang/CharacterData0E
+67 -1: (Ljava/lang/Object;Ljava/util/function/Supplier;)Ljava/lang/Object;
+19 -1: codePointBeforeImpl
+6 -1: script
+21 -1: systemNativeLibraries
+38 -1: ([Ljava/lang/Class;)Ljava/lang/String;
+14 -1: CONTENT_LENGTH
+19 -1: HeapCharBuffer.java
+22 -1: ExtendedProviderHolder
+41 -1: java/lang/invoke/InvokerBytecodeGenerator
+12 -1: basicInvoker
+26 -1: ([Ljava/lang/Comparable;)V
+10 -1: val$tclass
+47 -1: (Ljava/lang/Throwable;)Lsun/invoke/empty/Empty;
+18 -1: isLegalReplacement
+22 -1: spliteratorUnknownSize
+15 -1: SynchronizedSet
+16 -1: MethodParameters
+22 -1: desiredAssertionStatus
+29 -1: ()Ljava/util/ArrayDeque<TE;>;
+36 -1: ()Ljava/lang/reflect/Constructor<*>;
+66 -1: ()Ljava/util/Map<Ljava/lang/String;Ljava/lang/invoke/LambdaForm;>;
+58 -1: (Ljava/lang/String;Ljava/lang/String;ILjava/lang/String;)V
+50 -1: (Ljava/util/concurrent/CountedCompleter;[J[JIIII)V
+14 -1: requireNonNull
+21 -1: java/lang/ThreadGroup
+95 -1: ([Ljava/util/concurrent/ConcurrentHashMap$Node;ILjava/util/concurrent/ConcurrentHashMap$Node;)V
+76 -1: (Ljava/nio/channels/WritableByteChannel;Ljava/nio/charset/CharsetEncoder;I)V
+14 -1: reflectionData
+33 -1: Ljava/lang/invoke/MethodTypeForm;
+7 -1: tuesday
+31 -1: ()Lsun/misc/JavaSecurityAccess;
+14 -1: fieldFilterMap
+28 -1: ([Ljava/lang/ThreadGroup;Z)I
+7 -1: ibm-850
+83 -1: (Ljava/lang/String;Ljava/lang/String;)Ljava/lang/management/GarbageCollectorMXBean;
+7 -1: ibm-852
+5 -1: cesu8
+14 -1: ForwardingNode
+29 -1: (Ljava/nio/ByteBuffer;IIIII)V
+7 -1: ibm-855
+12 -1: SingletonSet
+16 -1: isOtherUppercase
+15 -1: FIELD_UNDEFINED
+9 -1: makeEntry
+7 -1: ibm-857
+10 -1: extensions
+10 -1: longStream
+19 -1: getGenericSignature
+7 -1: newNode
+8 -1: jarNames
+25 -1: java/util/jar/JarVerifier
+49 -1: ()[Lsun/reflect/generics/tree/ClassTypeSignature;
+4 -1: wait
+115 -1: (Ljava/lang/String;Lsun/reflect/generics/factory/GenericsFactory;)Lsun/reflect/generics/repository/ClassRepository;
+56 -1: (Ljava/lang/String;Ljava/lang/Object;)Ljava/lang/Object;
+65 -1: (Lsun/util/locale/BaseLocale;Lsun/util/locale/LocaleExtensions;)V
+7 -1: ibm-862
+9 -1: ISO646-US
+7 -1: ibm-866
+16 -1: extendedProvider
+7 -1: ([C[C)Z
+93 -1: (Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/MethodHandle;
+8 -1: getHours
+21 -1: ()[Ljava/lang/String;
+187 -1: (Ljava/util/concurrent/ConcurrentHashMap$BulkTask;III[Ljava/util/concurrent/ConcurrentHashMap$Node;Ljava/util/concurrent/ConcurrentHashMap$ReduceKeysTask;Ljava/util/function/BiFunction;)V
+106 -1: <K:Ljava/lang/Object;V:Ljava/lang/Object;>Ljava/util/concurrent/locks/ReentrantLock;Ljava/io/Serializable;
+24 -1: java/nio/file/FileSystem
+14 -1: ForEachKeyTask
+13 -1: defaultLocale
+20 -1: constructorModifiers
+13 -1: asWrapperType
+42 -1: (Ljava/lang/String;ZI)Ljava/lang/Class<*>;
+17 -1: BaseCalendar.java
+76 -1: (Ljava/util/jar/JarFile;Ljava/util/jar/JarEntry;)[Ljava/security/CodeSigner;
+14 -1: isSynchronized
+27 -1: java/nio/DirectByteBuffer$1
+3 -1: ()B
+7 -1: ibm-874
+12 -1: exactInvoker
+3 -1: ()C
+39 -1: (Ljava/lang/Thread;Ljava/lang/Object;)V
+3 -1: ()D
+3 -1: ()F
+27 -1: [Ljava/lang/reflect/Method;
+10 -1: floatValue
+3 -1: ()I
+3 -1: ()J
+18 -1: getLocaleResources
+59 -1: java/util/concurrent/ConcurrentHashMap$MapReduceEntriesTask
+67 -1: (ILjava/lang/Object;Ljava/lang/Object;Ljava/util/Hashtable$Entry;)V
+11 -1: maxPriority
+11 -1: getStringAt
+60 -1: (Ljava/lang/ClassValue$Version;)Ljava/lang/ClassValue$Entry;
+3 -1: ()S
+49 -1: (Ljava/lang/String;)Ljava/io/ExpiringCache$Entry;
+42 -1: (III)Lsun/util/calendar/BaseCalendar$Date;
+82 -1: <T:Ljava/lang/Object;>(Ljava/util/Set<TT;>;Ljava/lang/Object;)Ljava/util/Set<TT;>;
+62 -1: ([BLsun/reflect/ConstantPool;Ljava/lang/Class;)Ljava/util/Map;
+3 -1: ()V
+24 -1: ()Ljava/nio/ShortBuffer;
+29 -1: file descriptor can't be null
+3 -1: ()Z
+7 -1: matcher
+66 -1: (Ljava/lang/reflect/Constructor;)Lsun/reflect/ConstructorAccessor;
+7 -1: matches
+12 -1: getAuthority
+16 -1: java/lang/Object
+5 -1: (IC)V
+17 -1: EmptyListIterator
+8 -1: charsets
+8 -1: sameFile
+47 -1: (TT;Ljava/util/function/UnaryOperator<TV;>;)TV;
+16 -1: overwrittenEntry
+15 -1: reinvokerTarget
+11 -1: isUpperCase
+5 -1: toUri
+9 -1: GMT+00:00
+33 -1: java/util/concurrent/ForkJoinPool
+10 -1: parseFloat
+53 -1: java/util/concurrent/ConcurrentHashMap$SearchKeysTask
+48 -1: (Ljava/lang/Object;Ljava/util/LinkedList$Node;)V
+82 -1: (Ljava/lang/Class<*>;Ljava/lang/Class<*>;Ljava/lang/Object;ILjava/lang/Class<*>;)V
+15 -1: reduceCacheLoad
+76 -1: (Ljava/lang/Class<*>;[Ljava/lang/reflect/Method;)[Ljava/lang/reflect/Method;
+20 -1: singletonSpliterator
+24 -1: getTransitionEpochSecond
+24 -1: MapReduceValuesToIntTask
+13 -1: ALLOWED_FLAGS
+55 -1: (Ljava/lang/reflect/Field;Z)Lsun/reflect/FieldAccessor;
+34 -1: [Ljava/lang/annotation/Annotation;
+10 -1: readBuffer
+21 -1: Illegal month value: 
+31 -1: java/security/SecureClassLoader
+12 -1: reinitialize
+5 -1: limit
+4 -1: grow
+15 -1: getCreationTime
+7 -1: , from 
+25 -1: (Ljava/lang/ClassValue;)V
+13 -1: java.compiler
+37 -1: ()Ljava/util/Set<Ljava/lang/String;>;
+6 -1: FJChar
+16 -1: getFieldAccessor
+4 -1: eras
+11 -1: isSupported
+24 -1: ()Ljava/text/DateFormat;
+32 -1: java/util/Collections$CopiesList
+32 -1: java/io/NotSerializableException
+15 -1: typeAnnotations
+27 -1: defaultAllowUserInteraction
+57 -1: (Ljava/lang/String;I[Ljava/lang/invoke/LambdaForm$Name;)V
+18 -1: checkArgumentTypes
+71 -1: (Lsun/util/calendar/BaseCalendar$Date;)Lsun/util/calendar/BaseCalendar;
+10 -1: isMirrored
+27 -1: (I)Ljava/lang/Thread$State;
+26 -1: (Ljava/util/Collection;Z)Z
+6 -1: ibm367
+16 -1: isAssignableFrom
+7 -1: readUTF
+35 -1: Ljava/lang/ref/ReferenceQueue<TV;>;
+56 -1: ([Ljava/util/HashMap$Node;Ljava/util/HashMap$TreeNode;)V
+8 -1: MANDATED
+18 -1: canonicalizeRegion
+11 -1: checkAccept
+44 -1: (Ljava/net/Proxy;)Lsun/net/ApplicationProxy;
+8 -1: ECMA-118
+22 -1: ReflectPermission.java
+4 -1: _put
+41 -1: java.lang.invoke.MethodHandle.DEBUG_NAMES
+47 -1: java/util/concurrent/ConcurrentHashMap$TreeNode
+44 -1: (Ljava/net/URL;[Ljava/security/CodeSigner;)V
+5 -1: april
+44 -1: ([Ljava/lang/Class<*>;Ljava/lang/Class<*>;)V
+150 -1: <K:Ljava/lang/Object;V:Ljava/lang/Object;>Ljava/util/concurrent/ConcurrentHashMap$CollectionView<TK;TV;TK;>;Ljava/util/Set<TK;>;Ljava/io/Serializable;
+91 -1: (ILjava/lang/Object;Ljava/lang/Object;Ljava/util/HashMap$Node;)Ljava/util/HashMap$TreeNode;
+26 -1: sun/net/util/IPAddressUtil
+8 -1: Modifier
+9 -1: isVarargs
+24 -1: -- listing properties --
+16 -1: hasAllPermission
+27 -1: MapReduceMappingsToLongTask
+29 -1: sharedGetParameterAnnotations
+9 -1: argCounts
+11 -1: toLocalTime
+89 -1: (Ljava/lang/invoke/LambdaForm;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/MemberName;
+4 -1: ROOT
+20 -1: sun.reflect.noCaches
+18 -1: UnicodeBigUnmarked
+4 -1: Lazy
+35 -1: java/lang/invoke/SimpleMethodHandle
+20 -1: (I)Ljava/nio/Buffer;
+48 -1: ()Lsun/reflect/generics/tree/ClassTypeSignature;
+31 -1: (I[CII)Ljava/lang/StringBuffer;
+17 -1: America/Anchorage
+7 -1: markpos
+9 -1: enumerate
+11 -1: parseLocale
+24 -1: java.launcher.cls.error1
+46 -1: (ILjava/lang/Object;)Ljava/lang/StringBuilder;
+24 -1: java.launcher.cls.error2
+24 -1: java.launcher.cls.error3
+24 -1: java.launcher.cls.error4
+21 -1: java/util/ArrayList$1
+17 -1: getExceptionTypes
+24 -1: java.launcher.cls.error5
+30 -1: java/util/Spliterator$OfDouble
+10 -1: forDecoder
+8 -1: getEntry
+10 -1: checkGuard
+12 -1: checkInitted
+34 -1: Lsun/util/locale/LocaleExtensions;
+41 -1: java/util/ArraysParallelSortHelpers$FJInt
+10 -1: findStatic
+22 -1: setConstructorAccessor
+34 -1: Lsun/misc/URLClassPath$FileLoader;
+20 -1: not a reinvoker MH: 
+16 -1: LongCumulateTask
+11 -1: checkAccess
+14 -1: SearchKeysTask
+36 -1: ()[Ljava/lang/reflect/AnnotatedType;
+11 -1: initDefault
diff --git a/test/hotspot/jtreg/runtime/appcds/FieldAnnotationsTest.java b/test/hotspot/jtreg/runtime/appcds/FieldAnnotationsTest.java
new file mode 100644
index 00000000000..88a346ff850
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/FieldAnnotationsTest.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+/*
+ * @test
+ * @summary Test for field annotations.
+ * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true)
+ * @library /test/lib
+ * @modules java.base/jdk.internal.misc
+ *          java.management
+ *          jdk.jartool/sun.tools.jar
+ * @compile test-classes/FieldAnnotationsApp.java test-classes/MyAnnotation.java
+ * @run main FieldAnnotationsTest
+ */
+
+import jdk.test.lib.process.OutputAnalyzer;
+
+// This is a test for the handling of multi-dimensional Arrays in MetaspaceClosure.
+//
+// We choose FieldAnnotations because they happen to be implemented as a multi-dimension
+// Array (Annotations::_fields_annotations, which is of type Array<Array<unsigned char>*>*,
+// and is handled by the template class PointerArrayRef<T> in metaspaceClosure.hpp).
+//
+// Specifically, we are testing the following C code, where _fields_annotations is non-NULL:
+//
+// void Annotations::metaspace_pointers_do(MetaspaceClosure* it) {
+//   ...
+//   it->push(&_fields_annotations);
+//
+// which will be matched with the function
+//
+// template <typename T> void MetaspaceClosure::push(Array<T*>** mpp, Writability w = _default)
+//
+public class FieldAnnotationsTest {
+    public static void main(String[] args) throws Exception {
+        String[] ARCHIVE_CLASSES = {"FieldAnnotationsApp", "MyAnnotation"};
+        String appJar = JarBuilder.build("FieldAnnotationsTest", ARCHIVE_CLASSES);
+
+        OutputAnalyzer dumpOutput = TestCommon.dump(
+                appJar, ARCHIVE_CLASSES);
+        TestCommon.checkDump(dumpOutput);
+
+        OutputAnalyzer execOutput = TestCommon.exec(appJar, "FieldAnnotationsApp");
+        TestCommon.checkExec(execOutput, "Field annotations are OK.");
+    }
+}
diff --git a/test/hotspot/jtreg/runtime/appcds/FreeUnusedMetadata.java b/test/hotspot/jtreg/runtime/appcds/FreeUnusedMetadata.java
new file mode 100644
index 00000000000..30f167cdf20
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/FreeUnusedMetadata.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+/*
+ * @test
+ * @summary Unused metadata created during dump time should be freed from the CDS archive.
+ * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true)
+ * @library /test/lib
+ * @modules jdk.jartool/sun.tools.jar
+ * @compile test-classes/MethodNoReturn.jasm test-classes/Hello.java
+ * @run main FreeUnusedMetadata
+ */
+
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import jdk.test.lib.process.OutputAnalyzer;
+
+public class FreeUnusedMetadata {
+    static byte iconst_1 =  4;
+    static byte pop      = 87;
+    static byte[] pattern = { // This has the same sequence as in test-classes/MethodNoReturn.jasm
+        iconst_1,
+        pop,
+        iconst_1,
+        pop,
+        iconst_1,
+        pop,
+        iconst_1,
+        pop,
+        iconst_1,
+        pop,
+        iconst_1,
+        pop,
+        iconst_1,
+        pop,
+        iconst_1,
+        pop,
+        iconst_1,
+        pop,
+        iconst_1,
+        pop,
+        iconst_1,
+        iconst_1,
+        iconst_1,
+        iconst_1,
+        iconst_1,
+        iconst_1,
+        iconst_1,
+        iconst_1,
+        pop,
+        pop,
+        pop,
+        pop,
+        pop,
+        pop,
+        pop,
+        pop
+    };
+
+    public static void main(String[] args) throws Exception {
+        String[] ARCHIVE_CLASSES = {"Hello", "MethodNoReturn"};
+        String appJar = JarBuilder.build("FreeUnusedMetadata", ARCHIVE_CLASSES);
+
+        OutputAnalyzer dumpOutput = TestCommon.dump(
+                appJar, ARCHIVE_CLASSES);
+        TestCommon.checkDump(dumpOutput, "Loading classes to share");
+
+        OutputAnalyzer execOutput = TestCommon.exec(appJar, "Hello");
+        TestCommon.checkExec(execOutput, "Hello World");
+
+
+        String archive = TestCommon.getCurrentArchiveName();
+        System.out.println("Checking for pattern inside " + archive + "...");
+
+        byte[] data = Files.readAllBytes(Paths.get(archive));
+        int max = data.length - pattern.length;
+        for (int i=0; i<max; i++) {
+            if (data[i+0] == iconst_1 && data[i+1] == pop &&
+                data[i+2] == iconst_1 && data[i+3] == pop) {
+                boolean match = true;
+                for (int x=4; x<pattern.length; x++) {
+                    if (data[i+x] != pattern[x]) {
+                        match = false;
+                        break;
+                    }
+                }
+
+                if (match) {
+                    throw new RuntimeException("method of unverifiable class should have been " +
+                        "removed from the archive " + archive +
+                        " , but was found at offset " + i);
+                }
+            }
+        }
+        System.out.println("Not found: method from unverifiable class has been removed");
+    }
+}
diff --git a/test/hotspot/jtreg/runtime/appcds/HelloExtTest.java b/test/hotspot/jtreg/runtime/appcds/HelloExtTest.java
new file mode 100644
index 00000000000..3dba32bfbd0
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/HelloExtTest.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+/*
+ * @test
+ * @summary a simple test for loading a class using the ext class loader in AppCDS
+ * AppCDS does not support uncompressed oops
+ * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true)
+ * @library /test/lib
+ * @modules java.base/jdk.internal.misc
+ *          java.management
+ *          jdk.jartool/sun.tools.jar
+ *          jdk.internal.jvmstat/sun.jvmstat.monitor
+ * @compile test-classes/HelloExt.java
+ * @build sun.hotspot.WhiteBox
+ * @run main ClassFileInstaller sun.hotspot.WhiteBox
+ * @run main HelloExtTest
+ */
+
+import jdk.test.lib.process.OutputAnalyzer;
+
+public class HelloExtTest {
+
+  public static void main(String[] args) throws Exception {
+    JarBuilder.build("helloExt", "HelloExt");
+
+    String appJar = TestCommon.getTestJar("helloExt.jar");
+    JarBuilder.build(true, "WhiteBox", "sun/hotspot/WhiteBox");
+    String whiteBoxJar = TestCommon.getTestJar("WhiteBox.jar");
+    String bootClassPath = "-Xbootclasspath/a:" + whiteBoxJar;
+
+    TestCommon.dump(appJar,
+        TestCommon.list("org/omg/CORBA/ORB", "[Ljava/lang/Comparable;"),
+        bootClassPath, "-verbose:class", "--add-modules", "java.corba");
+
+    OutputAnalyzer output = TestCommon.execCommon("-XX:+UnlockDiagnosticVMOptions", "-XX:+WhiteBoxAPI",
+        "-cp", appJar, bootClassPath, "-verbose:class", "--add-modules", "java.corba", "HelloExt");
+
+    String prefix = ".class.load. ";
+    String class_pattern = ".*LambdaForm[$]MH[/][0123456789].*";
+    String suffix = ".*source: shared objects file.*";
+    String pattern = prefix + class_pattern + suffix;
+    output.shouldNotMatch(pattern);
+
+    output = TestCommon.execCommon("-XX:+UnlockDiagnosticVMOptions", "-XX:+WhiteBoxAPI",
+        "-cp", appJar, bootClassPath, "-verbose:class",
+        "-XX:+PrintSharedArchiveAndExit", "-XX:+PrintSharedDictionary",
+        "--add-modules", "java.corba", "HelloExt");
+    output.shouldNotMatch(class_pattern);
+  }
+}
diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/windbg/ia64/WindbgIA64ThreadFactory.java b/test/hotspot/jtreg/runtime/appcds/HelloTest.java
similarity index 60%
rename from src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/windbg/ia64/WindbgIA64ThreadFactory.java
rename to test/hotspot/jtreg/runtime/appcds/HelloTest.java
index ee2080de713..eee29f575ad 100644
--- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/windbg/ia64/WindbgIA64ThreadFactory.java
+++ b/test/hotspot/jtreg/runtime/appcds/HelloTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2003, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2017, 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
@@ -22,23 +22,23 @@
  *
  */
 
-package sun.jvm.hotspot.debugger.windbg.ia64;
+/*
+ * @test
+ * @summary Hello World test for AppCDS
+ * AppCDS does not support uncompressed oops
+ * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true)
+ * @library /test/lib
+ * @modules java.base/jdk.internal.misc
+ *          java.management
+ *          jdk.jartool/sun.tools.jar
+ * @compile test-classes/Hello.java
+ * @run main HelloTest
+ */
 
-import sun.jvm.hotspot.debugger.*;
-import sun.jvm.hotspot.debugger.windbg.*;
+public class HelloTest {
 
-public class WindbgIA64ThreadFactory implements WindbgThreadFactory {
-  private WindbgDebugger debugger;
-
-  public WindbgIA64ThreadFactory(WindbgDebugger debugger) {
-    this.debugger = debugger;
-  }
-
-  public ThreadProxy createThreadWrapper(Address threadIdentifierAddr) {
-    return new WindbgIA64Thread(debugger, threadIdentifierAddr);
-  }
-
-  public ThreadProxy createThreadWrapper(long id) {
-    return new WindbgIA64Thread(debugger, id);
+  public static void main(String[] args) throws Exception {
+      TestCommon.test(JarBuilder.getOrCreateHelloJar(),
+          TestCommon.list("Hello"), "Hello");
   }
 }
diff --git a/test/hotspot/jtreg/runtime/appcds/IgnoreEmptyClassPaths.java b/test/hotspot/jtreg/runtime/appcds/IgnoreEmptyClassPaths.java
new file mode 100644
index 00000000000..8bf4dec9326
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/IgnoreEmptyClassPaths.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+/*
+ * @test
+ * @summary Test the -XX:+IgnoreEmptyClassPaths flag
+ * AppCDS does not support uncompressed oops
+ * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true)
+ * @library /test/lib
+ * @modules java.base/jdk.internal.misc
+ *          java.management
+ *          jdk.jartool/sun.tools.jar
+ * @compile test-classes/Hello.java
+ * @compile test-classes/HelloMore.java
+ * @run main IgnoreEmptyClassPaths
+ */
+
+import java.io.File;
+import jdk.test.lib.process.OutputAnalyzer;
+
+public class IgnoreEmptyClassPaths {
+
+  public static void main(String[] args) throws Exception {
+    String jar1 = JarBuilder.getOrCreateHelloJar();
+    String jar2 = JarBuilder.build("IgnoreEmptyClassPaths_more", "HelloMore");
+
+    String sep = File.pathSeparator;
+    String cp_dump = jar1 + sep + jar2 + sep;
+    String cp_exec = sep + jar1 + sep + sep + jar2 + sep;
+
+    TestCommon.testDump(cp_dump, TestCommon.list("Hello", "HelloMore"),
+                        "-XX:+TraceClassPaths", "-XX:+IgnoreEmptyClassPaths");
+
+    OutputAnalyzer output = TestCommon.execCommon(
+        "-verbose:class",
+        "-cp", cp_exec,
+        "-XX:+IgnoreEmptyClassPaths", // should affect classpath even if placed after the "-cp" argument
+        "-XX:+TraceClassPaths",
+        "HelloMore");
+    TestCommon.checkExec(output);
+  }
+}
diff --git a/test/hotspot/jtreg/runtime/appcds/JarBuilder.java b/test/hotspot/jtreg/runtime/appcds/JarBuilder.java
new file mode 100644
index 00000000000..bb3921e7281
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/JarBuilder.java
@@ -0,0 +1,235 @@
+/*
+ * Copyright (c) 2015, 2017, 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.
+ *
+ */
+
+/*
+ * @summary Simple jar builder
+ *   Input: jarName className1 className2 ...
+ *     do not specify extensions, just the names
+ *     E.g. prot_domain ProtDomainA ProtDomainB
+ *   Output: A jar containing compiled classes, placed in a test classes folder
+ * @library /open/test/lib
+ */
+
+import jdk.test.lib.JDKToolFinder;
+import jdk.test.lib.process.OutputAnalyzer;
+import jdk.test.lib.process.ProcessTools;
+import java.io.File;
+import java.util.ArrayList;
+import sun.tools.jar.Main;
+
+public class JarBuilder {
+    // to turn DEBUG on via command line: -DJarBuilder.DEBUG=[true, TRUE]
+    private static final boolean DEBUG = Boolean.parseBoolean(System.getProperty("JarBuilder.DEBUG", "false"));
+    private static final String classDir = System.getProperty("test.classes");
+
+    public static String getJarFilePath(String jarName) {
+        return classDir + File.separator + jarName + ".jar";
+    }
+
+    // jar all files under dir, with manifest file man, with an optional versionArgs
+    // for generating a multi-release jar.
+    // The jar command is as follows:
+    // jar cmf \
+    //  <path to output jar> <path to the manifest file>\
+    //   -C <path to the base classes> .\
+    //    --release 9 -C <path to the versioned classes> .
+    // the last line begins with "--release" corresponds to the optional versionArgs.
+    public static void build(String jarName, File dir, String man, String ...versionArgs)
+        throws Exception {
+        ArrayList<String> args = new ArrayList<String>();
+        if (man != null) {
+            args.add("cfm");
+        } else {
+            args.add("cf");
+        }
+        args.add(classDir + File.separator + jarName + ".jar");
+        if (man != null) {
+            args.add(man);
+        }
+        args.add("-C");
+        args.add(dir.getAbsolutePath());
+        args.add(".");
+        for (String verArg : versionArgs) {
+            args.add(verArg);
+        }
+        createJar(args);
+    }
+
+    public static String build(String jarName, String ...classNames)
+        throws Exception {
+
+        return createSimpleJar(classDir, getJarFilePath(jarName), classNames);
+    }
+
+    public static String build(boolean classesInWorkDir, String jarName, String ...classNames)
+        throws Exception {
+        if (classesInWorkDir) {
+            return createSimpleJar(".", getJarFilePath(jarName), classNames);
+        } else {
+            return build(jarName, classNames);
+        }
+    }
+
+
+    public static String buildWithManifest(String jarName, String manifest,
+        String jarClassesDir, String ...classNames) throws Exception {
+        String jarPath = getJarFilePath(jarName);
+        ArrayList<String> args = new ArrayList<String>();
+        args.add("cvfm");
+        args.add(jarPath);
+        args.add(System.getProperty("test.src") + File.separator + "test-classes"
+            + File.separator + manifest);
+        addClassArgs(args, jarClassesDir, classNames);
+        createJar(args);
+
+        return jarPath;
+    }
+
+
+    // Execute: jar uvf $jarFile -C $dir .
+    static void update(String jarFile, String dir) throws Exception {
+        String jarExe = JDKToolFinder.getJDKTool("jar");
+
+        ArrayList<String> args = new ArrayList<>();
+        args.add(jarExe);
+        args.add("uvf");
+        args.add(jarFile);
+        args.add("-C");
+        args.add(dir);
+        args.add(".");
+
+        executeProcess(args.toArray(new String[1]));
+    }
+
+
+    private static String createSimpleJar(String jarclassDir, String jarName,
+        String[] classNames) throws Exception {
+
+        ArrayList<String> args = new ArrayList<String>();
+        args.add("cf");
+        args.add(jarName);
+        addClassArgs(args, jarclassDir, classNames);
+        createJar(args);
+
+        return jarName;
+    }
+
+    private static void addClassArgs(ArrayList<String> args, String jarclassDir,
+        String[] classNames) {
+
+        for (String name : classNames) {
+            args.add("-C");
+            args.add(jarclassDir);
+            args.add(name + ".class");
+        }
+    }
+
+    private static void createJar(ArrayList<String> args) {
+        if (DEBUG) printIterable("createJar args: ", args);
+
+        Main jarTool = new Main(System.out, System.err, "jar");
+        if (!jarTool.run(args.toArray(new String[1]))) {
+            throw new RuntimeException("jar operation failed");
+        }
+    }
+
+    // Many AppCDS tests use the same simple "Hello.jar" which contains
+    // simple Hello.class and does not specify additional attributes.
+    // For this common use case, use this method to get the jar path.
+    // The method will check if the jar already exists
+    // (created by another test or test run), and will create the jar
+    // if it does not exist
+    public static String getOrCreateHelloJar() throws Exception {
+        String jarPath = getJarFilePath("hello");
+
+        File jarFile = new File(jarPath);
+        if (jarFile.exists()) {
+            return jarPath;
+        } else {
+            return build("hello", "Hello");
+        }
+    }
+
+    public static void compile(String dstPath, String source, String... extraArgs) throws Exception {
+        ArrayList<String> args = new ArrayList<String>();
+        args.add(JDKToolFinder.getCompileJDKTool("javac"));
+        args.add("-d");
+        args.add(dstPath);
+        if (extraArgs != null) {
+            for (String s : extraArgs) {
+                args.add(s);
+            }
+        }
+        args.add(source);
+
+        if (DEBUG) printIterable("compile args: ", args);
+
+        ProcessBuilder pb = new ProcessBuilder(args);
+        OutputAnalyzer output = new OutputAnalyzer(pb.start());
+        output.shouldHaveExitValue(0);
+    }
+
+    public static void signJar() throws Exception {
+        String keyTool = JDKToolFinder.getJDKTool("keytool");
+        String jarSigner = JDKToolFinder.getJDKTool("jarsigner");
+        String classDir = System.getProperty("test.classes");
+        String FS = File.separator;
+
+        executeProcess(keyTool,
+            "-genkey", "-keystore", "./keystore", "-alias", "mykey",
+            "-storepass", "abc123", "-keypass", "abc123",
+            "-dname", "CN=jvmtest")
+            .shouldHaveExitValue(0);
+
+        executeProcess(jarSigner,
+           "-keystore", "./keystore", "-storepass", "abc123", "-keypass",
+           "abc123", "-signedjar", classDir + FS + "signed_hello.jar",
+           classDir + FS + "hello.jar", "mykey")
+           .shouldHaveExitValue(0);
+    }
+
+    private static OutputAnalyzer executeProcess(String... cmds)
+        throws Exception {
+
+        JarBuilder.printArray("executeProcess: ", cmds);
+        return ProcessTools.executeProcess(new ProcessBuilder(cmds));
+    }
+
+    // diagnostic
+    public static void printIterable(String msg, Iterable<String> l) {
+        StringBuilder sum = new StringBuilder();
+        for (String s : l) {
+            sum.append(s).append(' ');
+        }
+        System.out.println(msg + sum.toString());
+    }
+
+    public static void printArray(String msg, String[] l) {
+        StringBuilder sum = new StringBuilder();
+        for (String s : l) {
+            sum.append(s).append(' ');
+        }
+        System.out.println(msg + sum.toString());
+    }
+}
diff --git a/test/hotspot/jtreg/runtime/appcds/JvmtiAddPath.java b/test/hotspot/jtreg/runtime/appcds/JvmtiAddPath.java
new file mode 100644
index 00000000000..12d41a2c066
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/JvmtiAddPath.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+/*
+ * @test
+ * @summary JvmtiEnv::AddToBootstrapClassLoaderSearch and JvmtiEnv::AddToSystemClassLoaderSearch should disable AppCDS
+ * AppCDS does not support uncompressed oops
+ * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true)
+ * @bug 8060592
+ * @library /test/lib
+ * @modules java.base/jdk.internal.misc
+ *          java.management
+ *          jdk.jartool/sun.tools.jar
+ * @build sun.hotspot.WhiteBox
+ * @run main ClassFileInstaller sun.hotspot.WhiteBox
+ * @compile test-classes/Hello.java
+ * @compile test-classes/JvmtiApp.java
+ * @run main JvmtiAddPath
+ */
+
+import java.io.File;
+import jdk.test.lib.process.OutputAnalyzer;
+import sun.hotspot.WhiteBox;
+
+public class JvmtiAddPath {
+    static String use_whitebox_jar;
+    static String[] no_extra_matches = {};
+    static String[] check_appcds_enabled = {
+        "[class,load] ExtraClass source: shared object"
+    };
+    static String[] check_appcds_disabled = {
+        "[class,load] ExtraClass source: file:"
+    };
+
+    static void run(String cp, String... args) throws Exception {
+        run(no_extra_matches, cp, args);
+    }
+
+    static void run(String[] extra_matches, String cp, String... args) throws Exception {
+        String[] opts = {"-cp", cp, "-XX:+UnlockDiagnosticVMOptions", "-XX:+WhiteBoxAPI", use_whitebox_jar};
+        opts = TestCommon.concat(opts, args);
+        OutputAnalyzer output = TestCommon.execCommon(opts);
+        TestCommon.checkExec(output, extra_matches);
+    }
+
+    public static void main(String[] args) throws Exception {
+        JarBuilder.build("jvmti_addboot", "Hello");
+        JarBuilder.build("jvmti_addapp", "Hello");
+        JarBuilder.build("jvmti_app", "JvmtiApp", "ExtraClass");
+        JarBuilder.build(true, "WhiteBox", "sun/hotspot/WhiteBox");
+
+        // In all the test cases below, appJar does not contain Hello.class. Instead, we
+        // append JAR file(s) that contain Hello.class to the boot classpath, the app
+        // classpath, or both, and verify that Hello.class is loaded by the expected ClassLoader.
+        String appJar = TestCommon.getTestJar("jvmti_app.jar");         // contains JvmtiApp.class
+        String addappJar = TestCommon.getTestJar("jvmti_addapp.jar");   // contains Hello.class
+        String addbootJar = TestCommon.getTestJar("jvmti_addboot.jar"); // contains Hello.class
+        String twoAppJars = appJar + File.pathSeparator + addappJar;
+        String wbJar = TestCommon.getTestJar("WhiteBox.jar");
+        use_whitebox_jar = "-Xbootclasspath/a:" + wbJar;
+
+        TestCommon.testDump(appJar, TestCommon.list("JvmtiApp", "ExtraClass"), use_whitebox_jar);
+
+        System.out.println("Test case 1: not adding any paths - Hello.class should not be found");
+        run(check_appcds_enabled, appJar, "-Xlog:class+load", "JvmtiApp", "noadd"); // appcds should be enabled
+
+        System.out.println("Test case 2: add to boot classpath only - should find Hello.class in boot loader");
+        run(check_appcds_disabled, appJar, "-Xlog:class+load", "JvmtiApp", "bootonly", addbootJar); // appcds should be disabled
+
+        System.out.println("Test case 3: add to app classpath only - should find Hello.class in app loader");
+        run(appJar, "JvmtiApp", "apponly", addappJar);
+
+        System.out.println("Test case 4: add to boot and app paths - should find Hello.class in boot loader");
+        run(appJar, "JvmtiApp", "appandboot", addbootJar, addappJar);
+
+        System.out.println("Test case 5: add to app using -cp, but add to boot using JVMTI - should find Hello.class in boot loader");
+        run(twoAppJars, "JvmtiApp", "bootonly", addappJar);
+
+        System.out.println("Test case 6: add to app using AppCDS, but add to boot using JVMTI - should find Hello.class in boot loader");
+        TestCommon.testDump(twoAppJars, TestCommon.list("JvmtiApp", "ExtraClass", "Hello"), use_whitebox_jar);
+        run(twoAppJars, "JvmtiApp", "bootonly", addappJar);
+
+        System.out.println("Test case 7: add to app using AppCDS, no JVMTI calls - should find Hello.class in app loader");
+        run(twoAppJars, "JvmtiApp", "noadd-appcds");
+    }
+}
diff --git a/test/hotspot/jtreg/runtime/appcds/MismatchedUseAppCDS.java b/test/hotspot/jtreg/runtime/appcds/MismatchedUseAppCDS.java
new file mode 100644
index 00000000000..085e79e622d
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/MismatchedUseAppCDS.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+/*
+ * @test
+ * @summary Try different combination of mismatched UseAppCDS between dump time and run time.
+ * (Note: AppCDS does not support uncompressed oops.)
+ * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true)
+ * @library /test/lib
+ * @modules java.base/jdk.internal.misc
+ *          java.management
+ *          jdk.jartool/sun.tools.jar
+ * @compile test-classes/CheckIfShared.java
+ * @build sun.hotspot.WhiteBox
+ * @run main ClassFileInstaller sun.hotspot.WhiteBox
+ * @run main MismatchedUseAppCDS
+ */
+
+import jdk.test.lib.process.OutputAnalyzer;
+
+public class MismatchedUseAppCDS {
+  public static void main(String[] args) throws Exception {
+    String wbJar = JarBuilder.build(true, "WhiteBox", "sun/hotspot/WhiteBox");
+    String use_whitebox_jar = "-Xbootclasspath/a:" + wbJar;
+
+    String appJar = JarBuilder.build("MismatchedUseAppCDS", "CheckIfShared");
+
+    OutputAnalyzer output;
+
+    // (1): dump with -XX:+UseAppCDS, but run with -XX:-UseAppCDS
+    TestCommon.testDump(appJar, TestCommon.list("CheckIfShared"),
+                        // command-line arguments ...
+                        "-XX:+UseAppCDS",
+                        use_whitebox_jar);
+
+    output = TestCommon.exec(appJar,
+                             // command-line arguments ...
+                             use_whitebox_jar,
+                             "-XX:-UseAppCDS",
+                             "-XX:+UnlockDiagnosticVMOptions",
+                             "-XX:+WhiteBoxAPI",
+                             "CheckIfShared", "false");
+    TestCommon.checkExec(output);
+
+    // (2): dump with -XX:-UseAppCDS, but run with -XX:+UseAppCDS
+    TestCommon.testDump(appJar, TestCommon.list("CheckIfShared"),
+                        // command-line arguments ...
+                        "-XX:-UseAppCDS",
+                        use_whitebox_jar);
+
+    output = TestCommon.exec(appJar,
+                             // command-line arguments ...
+                             use_whitebox_jar,
+                             "-XX:+UseAppCDS",
+                             "-XX:+UnlockDiagnosticVMOptions",
+                             "-XX:+WhiteBoxAPI",
+                             "CheckIfShared", "false");
+    TestCommon.checkExec(output);
+  }
+}
diff --git a/test/hotspot/jtreg/runtime/appcds/MissingSuperTest.java b/test/hotspot/jtreg/runtime/appcds/MissingSuperTest.java
new file mode 100644
index 00000000000..fa9bfb93493
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/MissingSuperTest.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+/*
+ * @test
+ * @summary When super class is missing during dumping, no crash should happen.
+ * AppCDS does not support uncompressed oops
+ * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true)
+ * @library /test/lib
+ * @modules java.base/jdk.internal.misc
+ *          java.management
+ *          jdk.jartool/sun.tools.jar
+ * @compile test-classes/MissingSuper.java
+ * @run main MissingSuperTest
+ */
+
+public class MissingSuperTest {
+
+  public static void main(String[] args) throws Exception {
+    // The classes "MissingSuperSup" and "MissingSuperIntf" are intentionally not
+    // included into the jar to provoke the test condition
+    JarBuilder.build("missing_super", "MissingSuper",
+        "MissingSuperSub", "MissingSuperImpl");
+
+    String appJar = TestCommon.getTestJar("missing_super.jar");
+    TestCommon.test(appJar, TestCommon.list("MissingSuper",
+        "MissingSuperSub",
+        "MissingSuperImpl"),
+        "MissingSuper");
+  }
+}
diff --git a/test/hotspot/jtreg/runtime/appcds/MultiProcessSharing.java b/test/hotspot/jtreg/runtime/appcds/MultiProcessSharing.java
new file mode 100644
index 00000000000..25861868344
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/MultiProcessSharing.java
@@ -0,0 +1,144 @@
+/*
+ * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+/*
+ * @test
+ * @summary Run multiple processes with the same archive, ensure they share
+ * AppCDS does not support uncompressed oops
+ * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true)
+ * @library /test/lib
+ * @modules java.base/jdk.internal.misc
+ *          java.management
+ *          jdk.jartool/sun.tools.jar
+ * @build sun.hotspot.WhiteBox
+ * @run main ClassFileInstaller sun.hotspot.WhiteBox
+ * @compile test-classes/MultiProcClass.java
+ * @run main MultiProcessSharing
+ */
+
+import java.io.File;
+import jdk.test.lib.Asserts;
+import jdk.test.lib.Platform;
+import jdk.test.lib.process.OutputAnalyzer;
+import sun.hotspot.WhiteBox;
+
+
+public class MultiProcessSharing {
+    static String useWbJar;
+    static String sharedClass1Jar;
+    static boolean checkPmap = false;
+
+    public static void main(String[] args) throws Exception {
+        String wbJar = JarBuilder.build(true, "WhiteBox", "sun/hotspot/WhiteBox");
+        useWbJar = "-Xbootclasspath/a:" + wbJar;
+        sharedClass1Jar = JarBuilder.build("shared_class1", "MultiProcClass");
+
+        // create an archive
+        OutputAnalyzer out = TestCommon.dump(sharedClass1Jar,
+            TestCommon.list("MultiProcClass"), useWbJar);
+        TestCommon.checkDump(out);
+
+        // determine whether OK to use pmap for extra test verification
+        long myPid = ProcessHandle.current().pid();
+        checkPmap = (Platform.isLinux() && (MultiProcClass.runPmap(myPid, false) == 0));
+        System.out.println("MultiProcessSharing: checkPmap is " + checkPmap);
+
+        // use an archive in several processes concurrently
+        int numProcesses = 3;
+        Thread[] threads = new Thread[numProcesses];
+        ProcessHandler[] processHandlers = new ProcessHandler[numProcesses];
+        for (int i = 0; i < numProcesses; i++) {
+            processHandlers[i] = new ProcessHandler(i);
+            threads[i] = new Thread(processHandlers[i]);
+        }
+
+        for (Thread t : threads) {
+            t.start();
+        }
+
+        for (Thread t : threads) {
+            try {
+                t.join();
+            } catch (InterruptedException ie) {
+                throw ie;
+            }
+        }
+
+        // check results
+        for (ProcessHandler ph : processHandlers) {
+            TestCommon.checkExec(ph.out);
+            if (checkPmap && !TestCommon.isUnableToMap(ph.out)) {
+                checkPmapOutput(ph.out.getOutput());
+            }
+        }
+    }
+
+
+    static class ProcessHandler implements Runnable {
+        int processNumber;
+        OutputAnalyzer out;
+
+        ProcessHandler(int processNumber) {
+            this.processNumber = processNumber;
+        }
+
+        @Override
+        public void run() {
+            try {
+                out = TestCommon.exec(sharedClass1Jar,
+                   "-XX:+UnlockDiagnosticVMOptions", "-XX:+WhiteBoxAPI", useWbJar,
+                   "MultiProcClass", "" + processNumber, "" + checkPmap);
+            } catch (Exception e) {
+                throw new RuntimeException("Error occurred when using archive, exec()" + e);
+            }
+        }
+    }
+
+
+    private static void checkPmapOutput(String stdio) {
+        System.out.println("Checking pmap output ...");
+        String[] lines = stdio.split("\n");
+
+        boolean foundJsa = false;
+        boolean foundReadOnlyJsaSection = false;
+
+        for (String line : lines) {
+            if (line.contains(TestCommon.getCurrentArchiveName()))
+                System.out.println(line);
+                foundJsa = true;
+                if (line.contains("r--")) {
+                    foundReadOnlyJsaSection = true;
+                }
+
+                // On certain ARM platforms system maps r/o memory mapped files
+                // as r/x; see JDK-8145694 for details
+                if ( (Platform.isARM() || Platform.isAArch64()) && line.contains("r-x") ) {
+                    foundReadOnlyJsaSection = true;
+                }
+        }
+
+        Asserts.assertTrue(foundJsa && foundReadOnlyJsaSection);
+    }
+
+}
diff --git a/test/hotspot/jtreg/runtime/appcds/MultiReleaseJars.java b/test/hotspot/jtreg/runtime/appcds/MultiReleaseJars.java
new file mode 100644
index 00000000000..fdc6ef06492
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/MultiReleaseJars.java
@@ -0,0 +1,238 @@
+/*
+ * Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+/*
+ * @test MultiReleaseJars
+ * @bug 8170105
+ * @summary Test multi-release jar with AppCDS.
+ * AppCDS does not support uncompressed oops
+ * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true)
+ * @library /test/lib
+ * @modules java.base/jdk.internal.misc
+ *          jdk.jartool/sun.tools.jar
+ * @run main/othervm MultiReleaseJars
+ */
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.PrintStream;
+import java.io.IOException;
+import jdk.test.lib.process.OutputAnalyzer;
+
+public class MultiReleaseJars {
+
+    static final int MAJOR_VERSION = Runtime.version().major();
+    static final String MAJOR_VERSION_STRING = String.valueOf(Runtime.version().major());
+
+    static String[] getMain() {
+        String[] sts = {
+            "package version;",
+            "public class Main {",
+            "    public static void main(String[] args) {",
+            "        Version version = new Version();",
+            "        System.out.println(\"I am running on version \" + version.getVersion());",
+            "    }",
+            "}"
+        };
+        return sts;
+    }
+
+    static String[] getVersion(int version) {
+        String[] sts = {
+            "package version;",
+            "public class Version {",
+            "    public int getVersion(){ return " + version + "; }",
+            "}"
+        };
+        return sts;
+    }
+
+    static void writeFile(File file, String... contents) throws Exception {
+        if (contents == null) {
+            throw new java.lang.RuntimeException("No input for writing to file" + file);
+        }
+        FileOutputStream fos = new FileOutputStream(file);
+        PrintStream ps = new PrintStream(fos);
+        for (String str : contents) {
+            ps.println(str);
+        }
+        ps.close();
+        fos.close();
+    }
+
+    /* version.jar entries and files:
+     * META-INF/
+     * META-INF/MANIFEST.MF
+     * version/
+     * version/Main.class
+     * version/Version.class
+     * META-INF/versions/
+     * META-INF/versions/<major-version>/
+     * META-INF/versions/<major-version>/version/
+     * META-INF/versions/<major-version>/version/Version.class
+     */
+    static void createClassFilesAndJar() throws Exception {
+        String tempDir = System.getProperty("test.classes");
+        File baseDir = new File(tempDir + File.separator + "base");
+        File vDir    = new File(tempDir + File.separator + MAJOR_VERSION_STRING);
+
+        baseDir.mkdirs();
+        vDir.mkdirs();
+
+        File fileMain = TestCommon.getOutputSourceFile("Main.java");
+        writeFile(fileMain, getMain());
+
+        File fileVersion = TestCommon.getOutputSourceFile("Version.java");
+        writeFile(fileVersion, getVersion(7));
+        JarBuilder.compile(baseDir.getAbsolutePath(), fileVersion.getAbsolutePath(), "--release", "7");
+        JarBuilder.compile(baseDir.getAbsolutePath(), fileMain.getAbsolutePath(),
+            "-cp", baseDir.getAbsolutePath(), "--release", MAJOR_VERSION_STRING);
+
+        String[] meta = {
+            "Multi-Release: true",
+            "Main-Class: version.Main"
+        };
+        File metainf = new File(tempDir, "mf.txt");
+        writeFile(metainf, meta);
+
+        fileVersion = TestCommon.getOutputSourceFile("Version.java");
+        writeFile(fileVersion, getVersion(MAJOR_VERSION));
+        JarBuilder.compile(vDir.getAbsolutePath(), fileVersion.getAbsolutePath(), "--release", MAJOR_VERSION_STRING);
+
+        JarBuilder.build("version", baseDir, metainf.getAbsolutePath(),
+            "--release", MAJOR_VERSION_STRING, "-C", vDir.getAbsolutePath(), ".");
+
+        // the following jar file is for testing case-insensitive "Multi-Release"
+        // attibute name
+        String[] meta2 = {
+            "multi-Release: true",
+            "Main-Class: version.Main"
+        };
+        metainf = new File(tempDir, "mf2.txt");
+        writeFile(metainf, meta2);
+        JarBuilder.build("version2", baseDir, metainf.getAbsolutePath(),
+            "--release", MAJOR_VERSION_STRING, "-C", vDir.getAbsolutePath(), ".");
+    }
+
+    static void checkExecOutput(OutputAnalyzer output, String expectedOutput) throws Exception {
+        try {
+            TestCommon.checkExec(output, expectedOutput);
+        } catch (java.lang.RuntimeException re) {
+            String cause = re.getMessage();
+            if (!expectedOutput.equals(cause)) {
+                throw re;
+            }
+        }
+    }
+
+    public static void main(String... args) throws Exception {
+        // create version.jar which contains Main.class and Version.class.
+        // Version.class has two versions: 8 and the current version.
+        createClassFilesAndJar();
+
+        String mainClass          = "version.Main";
+        String loadInfo           = "[class,load] version.Version source: shared objects file";
+        String appClasses[]       = {"version/Main", "version/Version"};
+        String appJar             = TestCommon.getTestJar("version.jar");
+        String appJar2            = TestCommon.getTestJar("version2.jar");
+        String verboseMode        = "-verbose:class";
+        String enableMultiRelease = "-Djdk.util.jar.enableMultiRelease=true";
+        String jarVersion         = null;
+        String expectedOutput     = null;
+
+        // 1. default to highest version
+        //    if META-INF/versions exists, no other commandline options like -Djdk.util.jar.version and
+        //    -Djdk.util.jar.enableMultiRelease passed to vm
+        OutputAnalyzer output = TestCommon.dump(appJar, appClasses);
+        output.shouldContain("Loading classes to share: done.");
+        output.shouldHaveExitValue(0);
+
+        output = TestCommon.exec(appJar, verboseMode, mainClass);
+        checkExecOutput(output, "I am running on version " + MAJOR_VERSION_STRING);
+
+        // 2. Test versions 7 and the current major version.
+        //    -Djdk.util.jar.enableMultiRelease=true (or force), default is true.
+        //    a) -Djdk.util.jar.version=7 does not exist in jar.
+        //        It will fallback to the root version which is also 7 in this test.
+        //    b) -Djdk.util.jar.version=MAJOR_VERSION exists in the jar.
+        for (int i : new int[] {7, MAJOR_VERSION}) {
+            jarVersion = "-Djdk.util.jar.version=" + i;
+            expectedOutput = "I am running on version " + i;
+            output = TestCommon.dump(appJar, appClasses, enableMultiRelease, jarVersion);
+            output.shouldContain("Loading classes to share: done.");
+            output.shouldHaveExitValue(0);
+
+            output = TestCommon.exec(appJar, verboseMode, mainClass);
+            checkExecOutput(output, expectedOutput);
+        }
+
+        // 3. For unsupported version, 5 and current major version + 1, the multiversion
+        // will be turned off, so it will use the default (root) version.
+        for (int i : new int[] {5, MAJOR_VERSION + 1}) {
+            jarVersion = "-Djdk.util.jar.version=" + i;
+            output = TestCommon.dump(appJar, appClasses, enableMultiRelease, jarVersion);
+            output.shouldHaveExitValue(0);
+            // With the fix for 8172218, multi-release jar is being handled in
+            // jdk corelib which doesn't emit the following warning message.
+            //output.shouldContain("JDK" + i + " is not supported in multiple version jars");
+
+            output = TestCommon.exec(appJar, verboseMode, mainClass);
+            if (i == 5)
+                checkExecOutput(output, "I am running on version 7");
+            else
+                checkExecOutput(output, "I am running on version " + MAJOR_VERSION_STRING);
+        }
+
+        // 4. If explicitly disabled from command line for multiversion jar, it will use default
+        //    version at root regardless multiversion versions exists.
+        //    -Djdk.util.jar.enableMultiRelease=false (not 'true' or 'force')
+        for (int i = 6; i < MAJOR_VERSION + 1; i++) {
+            jarVersion = "-Djdk.util.jar.version=" + i;
+            output = TestCommon.dump(appJar, appClasses, "-Djdk.util.jar.enableMultiRelease=false", jarVersion);
+            output.shouldHaveExitValue(0);
+
+            output = TestCommon.exec(appJar, verboseMode, mainClass);
+            expectedOutput = "I am running on version 7";
+            checkExecOutput(output, expectedOutput);
+        }
+
+        // 5. Sanity test with -Xbootclasspath/a
+        //    AppCDS behaves the same as the non-AppCDS case. A multi-release
+        //    jar file in the -Xbootclasspath/a will be ignored.
+        output = TestCommon.dump(appJar, appClasses, "-Xbootclasspath/a:" + appJar, enableMultiRelease, jarVersion);
+        output.shouldContain("Loading classes to share: done.");
+        output.shouldHaveExitValue(0);
+
+        output = TestCommon.exec(appJar, "-Xbootclasspath/a:" + appJar, verboseMode, mainClass);
+        checkExecOutput(output, "I am running on version 7");
+
+        // 6. Sanity test case-insensitive "Multi-Release" attribute name
+        output = TestCommon.dump(appJar2, appClasses);
+        output.shouldContain("Loading classes to share: done.");
+        output.shouldHaveExitValue(0);
+
+        output = TestCommon.exec(appJar2, verboseMode, mainClass);
+        checkExecOutput(output, "I am running on version " + MAJOR_VERSION_STRING);
+    }
+}
diff --git a/test/hotspot/jtreg/runtime/appcds/OldClassTest.java b/test/hotspot/jtreg/runtime/appcds/OldClassTest.java
new file mode 100644
index 00000000000..d41faf33608
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/OldClassTest.java
@@ -0,0 +1,167 @@
+/*
+ * Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+/*
+ * @test
+ * @summary classes with major version < JDK_1.5 (48) should not be included in CDS
+ * AppCDS does not support uncompressed oops
+ * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true)
+ * @library /test/lib
+ * @modules java.base/jdk.internal.org.objectweb.asm
+ *          java.base/jdk.internal.misc
+ *          java.management
+ *          jdk.jartool/sun.tools.jar
+ * @compile test-classes/Hello.java
+ * @run build TestCommon JarBuilder
+ * @run main OldClassTest
+ */
+
+import java.io.File;
+import java.io.FileOutputStream;
+import jdk.test.lib.process.OutputAnalyzer;
+import java.nio.file.Files;
+
+import java.util.*;
+import jdk.internal.org.objectweb.asm.*;
+
+public class OldClassTest implements Opcodes {
+
+  public static void main(String[] args) throws Exception {
+    File jarSrcFile = new File(JarBuilder.getOrCreateHelloJar());
+
+    File dir = new File(System.getProperty("test.classes", "."));
+    File jarFile = new File(dir, "OldClassTest_old.jar");
+    String jar = jarFile.getPath();
+
+    if (!jarFile.exists() || jarFile.lastModified() < jarSrcFile.lastModified()) {
+      createTestJarFile(jarSrcFile, jarFile);
+    } else {
+      System.out.println("Already up-to-date: " + jarFile);
+    }
+
+    String appClasses[] = TestCommon.list("Hello");
+
+    // CASE 1: pre-JDK 1.5 compiled classes should be excluded from the dump
+    OutputAnalyzer output = TestCommon.dump(jar, appClasses);
+    TestCommon.checkExecReturn(output, 0, true, "Pre JDK 1.5 class not supported by CDS");
+
+    output = TestCommon.execCommon(
+        "-cp", jar,
+        "-verbose:class",
+        "Hello");
+    TestCommon.checkExecReturn(output, 0, true, "Hello Unicode world (Old)");
+
+    // CASE 2: if we exlcude old version of this class, we should not pick up
+    //         the newer version of this class in a subsequent classpath element.
+    String classpath = jar + File.pathSeparator + jarSrcFile.getPath();
+    output = TestCommon.dump(classpath, appClasses);
+    TestCommon.checkExecReturn(output, 0, true, "Pre JDK 1.5 class not supported by CDS");
+
+    output = TestCommon.execCommon(
+        "-cp", classpath,
+        "-verbose:class",
+        "Hello");
+    TestCommon.checkExecReturn(output, 0, true, "Hello Unicode world (Old)");
+  }
+
+  static void createTestJarFile(File jarSrcFile, File jarFile) throws Exception {
+    jarFile.delete();
+    Files.copy(jarSrcFile.toPath(), jarFile.toPath());
+
+    File dir = new File(System.getProperty("test.classes", "."));
+    File outdir = new File(dir, "old_class_test_classes");
+    outdir.delete();
+    outdir.mkdir();
+
+    writeClassFile(new File(outdir, "Hello.class"), makeOldHello());
+
+    JarBuilder.update(jarFile.getPath(), outdir.getPath());
+  }
+
+  static void writeClassFile(File file, byte bytecodes[]) throws Exception {
+    try (FileOutputStream fos = new FileOutputStream(file)) {
+        fos.write(bytecodes);
+      }
+  }
+
+/* makeOldHello() was obtained using JDK8. We use a method name > 128 that would
+   trigger a call to java.lang.Character.isJavaIdentifierStart() during class
+   file parsing.
+
+cat > Hello.java <<EOF
+public class Hello {
+    public static void main(String args[]) {
+        System.out.println(\u1234());
+    }
+    static String \u1234() {
+        return "Hello Unicode world (Old)";
+    }
+}
+EOF
+javac Hello.java
+java jdk.internal.org.objectweb.asm.util.ASMifier Hello.class
+
+ */
+
+  static byte[] makeOldHello() throws Exception {
+    ClassWriter cw = new ClassWriter(0);
+    FieldVisitor fv;
+    MethodVisitor mv;
+    AnnotationVisitor av0;
+
+//WAS cw.visit(V1_6, ACC_PUBLIC + ACC_SUPER, "Hello", null, "java/lang/Object", null);
+      cw.visit(V1_4, ACC_PUBLIC + ACC_SUPER, "Hello", null, "java/lang/Object", null);
+
+    {
+      mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
+      mv.visitCode();
+      mv.visitVarInsn(ALOAD, 0);
+      mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);
+      mv.visitInsn(RETURN);
+      mv.visitMaxs(1, 1);
+      mv.visitEnd();
+    }
+    {
+      mv = cw.visitMethod(ACC_PUBLIC + ACC_STATIC, "main", "([Ljava/lang/String;)V", null, null);
+      mv.visitCode();
+      mv.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
+      mv.visitMethodInsn(INVOKESTATIC, "Hello", "\u1234", "()Ljava/lang/String;", false);
+      mv.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false);
+      mv.visitInsn(RETURN);
+      mv.visitMaxs(2, 1);
+      mv.visitEnd();
+    }
+    {
+      mv = cw.visitMethod(ACC_STATIC, "\u1234", "()Ljava/lang/String;", null, null);
+      mv.visitCode();
+      mv.visitLdcInsn("Hello Unicode world (Old)");
+      mv.visitInsn(ARETURN);
+      mv.visitMaxs(1, 0);
+      mv.visitEnd();
+    }
+    cw.visitEnd();
+
+    return cw.toByteArray();
+  }
+}
diff --git a/test/hotspot/jtreg/runtime/appcds/PackageSealing.java b/test/hotspot/jtreg/runtime/appcds/PackageSealing.java
new file mode 100644
index 00000000000..00651ee0a3f
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/PackageSealing.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+/*
+ * @test
+ * @summary AppCDS handling of package.
+ * AppCDS does not support uncompressed oops
+ * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true)
+ * @library /test/lib
+ * @modules java.base/jdk.internal.misc
+ *          java.management
+ * @compile test-classes/C1.java
+ * @compile test-classes/C2.java
+ * @compile test-classes/PackageSealingTest.java
+ * @run main PackageSealing
+ */
+
+import jdk.test.lib.process.OutputAnalyzer;
+
+public class PackageSealing {
+    public static void main(String args[]) throws Exception {
+        String[] classList = {"sealed/pkg/C1", "pkg/C2", "PackageSealingTest"};
+        String appJar = ClassFileInstaller.writeJar("pkg_seal.jar",
+            ClassFileInstaller.Manifest.fromSourceFile("test-classes/package_seal.mf"),
+            "PackageSealingTest", "sealed/pkg/C1", "pkg/C2");
+
+        // test shared package from -cp path
+        TestCommon.testDump(appJar, TestCommon.list(classList));
+        OutputAnalyzer output;
+        output = TestCommon.exec(appJar, "PackageSealingTest");
+        TestCommon.checkExec(output, "OK");
+
+        // test shared package from -Xbootclasspath/a
+        TestCommon.dump(appJar, TestCommon.list(classList),
+                        "-Xbootclasspath/a:" + appJar);
+        output = TestCommon.exec(appJar, "-Xbootclasspath/a:" + appJar, "PackageSealingTest");
+        TestCommon.checkExec(output, "OK");
+    }
+}
diff --git a/test/hotspot/jtreg/runtime/appcds/ParallelLoad2.java b/test/hotspot/jtreg/runtime/appcds/ParallelLoad2.java
new file mode 100644
index 00000000000..6c07c6bebce
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/ParallelLoad2.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+/*
+ * @test
+ * @summary Load app classes from CDS archive in parallel threads. Similar to ParallelLoad.java, but each class in its own JAR
+ * AppCDS does not support uncompressed oops
+ * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true)
+ * @library /test/lib
+ * @modules java.base/jdk.internal.misc
+ *          java.management
+ *          jdk.jartool/sun.tools.jar
+ * @compile test-classes/ParallelLoad.java
+ * @compile test-classes/ParallelClasses.java
+ * @run main ParallelLoad2
+ */
+
+import java.io.File;
+
+public class ParallelLoad2 {
+  public static int MAX_CLASSES = 40;
+  public static void main(String[] args) throws Exception {
+    JarBuilder.build("parallel_load2", "ParallelLoad", "ParallelLoadThread", "ParallelLoadWatchdog");
+    for (int i=0; i<MAX_CLASSES; i++) {
+      JarBuilder.build("parallel_load2_" + i, "ParallelClass" + i);
+    }
+
+    String cp = TestCommon.getTestJar("parallel_load2.jar");
+    for (int i=0; i<MAX_CLASSES; i++) {
+      cp += File.pathSeparator + TestCommon.getTestJar("parallel_load2_" + i + ".jar");
+    }
+
+    String[] class_list = new String[MAX_CLASSES + 2];
+    for (int i=0; i<MAX_CLASSES; i++) {
+      class_list[i] = "ParallelClass" + i;
+    }
+    class_list[class_list.length - 1] = "ParallelLoad";
+    class_list[class_list.length - 2] = "ParallelLoadThread";
+
+    TestCommon.test(cp, class_list,
+                          "ParallelLoad");
+  }
+}
diff --git a/test/hotspot/jtreg/runtime/appcds/ParallelLoadTest.java b/test/hotspot/jtreg/runtime/appcds/ParallelLoadTest.java
new file mode 100644
index 00000000000..b5dd75a0a7c
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/ParallelLoadTest.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+/*
+ * @test
+ * @summary Load app classes from CDS archive in parallel threads
+ * AppCDS does not support uncompressed oops
+ * @library /test/lib
+ * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true)
+ * @modules java.base/jdk.internal.misc
+ *          java.management
+ *          jdk.jartool/sun.tools.jar
+ * @compile test-classes/ParallelLoad.java
+ * @compile test-classes/ParallelClasses.java
+ * @run main ParallelLoadTest
+ */
+
+public class ParallelLoadTest {
+    public static final int MAX_CLASSES = 40;
+
+    public static void main(String[] args) throws Exception {
+        JarBuilder.build("parallel_load", getClassList(true));
+        String appJar = TestCommon.getTestJar("parallel_load.jar");
+        TestCommon.test(appJar, getClassList(false), "ParallelLoad");
+    }
+
+    private static String[] getClassList(boolean includeWatchdog) {
+        int extra = includeWatchdog ? 3 : 2;
+        String[] classList = new String[MAX_CLASSES + extra];
+
+        int i;
+        for (i=0; i<MAX_CLASSES; i++) {
+            classList[i] = "ParallelClass" + i;
+        }
+
+        classList[i++] = "ParallelLoad";
+        classList[i++] = "ParallelLoadThread";
+        if (includeWatchdog)
+            classList[i++] = "ParallelLoadWatchdog";
+
+        return classList;
+    }
+}
diff --git a/test/hotspot/jtreg/runtime/appcds/PrintSharedArchiveAndExit.java b/test/hotspot/jtreg/runtime/appcds/PrintSharedArchiveAndExit.java
new file mode 100644
index 00000000000..e3072bba759
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/PrintSharedArchiveAndExit.java
@@ -0,0 +1,148 @@
+/*
+ * Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+/*
+ * @test
+ * @summary test the -XX:+PrintSharedArchiveAndExit flag
+ * AppCDS does not support uncompressed oops
+ * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true)
+ * @library /test/lib
+ * @modules java.base/jdk.internal.misc
+ *          java.management
+ *          jdk.jartool/sun.tools.jar
+ * @compile test-classes/Hello.java
+ * @compile test-classes/HelloMore.java
+ * @run main/othervm/timeout=3600 PrintSharedArchiveAndExit
+ */
+
+import java.io.File;
+import jdk.test.lib.process.OutputAnalyzer;
+
+public class PrintSharedArchiveAndExit {
+  private static void check(OutputAnalyzer output, int ret, boolean checkContain, String... matches) throws Exception {
+    // Tests specific to this test
+    TestCommon.checkExecReturn(output, ret, checkContain, matches);
+
+    // In all test case, we should never print out the following due to
+    // PrintSharedArchiveAndExit. JVM should have been terminated
+    // before reaching these outputs.
+    TestCommon.checkExecReturn(output, ret, false,
+                               "Usage:",            // JVM help message
+                               "java version",      // JVM version
+                               "Hello World");      // output from the Hello.class in hello.jar
+  }
+
+  private static void log(String msg) {
+    System.out.println(">---------------------------------------------------------------------");
+    System.out.println(msg);
+    System.out.println("<---------------------------------------------------------------------");
+  }
+
+  public static void main(String[] args) throws Exception {
+    String appJar = JarBuilder.getOrCreateHelloJar();
+    String appJar2 = JarBuilder.build("PrintSharedArchiveAndExit-more", "HelloMore");
+
+    String cp = appJar + File.pathSeparator + appJar2;
+    String lastCheckMsg = "checking shared classpath entry: " + appJar2; // the last JAR to check
+
+    TestCommon.testDump(cp, TestCommon.list("Hello"));
+
+    OutputAnalyzer output;
+
+    log("Normal execution -- all the JAR paths should be checked");
+    output = TestCommon.execCommon(
+        "-cp", cp,
+        "-XX:+PrintSharedArchiveAndExit");
+    check(output, 0, true, lastCheckMsg);
+
+    output = TestCommon.execCommon(
+        "-cp", cp,
+        "-XX:+PrintSharedArchiveAndExit",
+        "-XX:+PrintSharedDictionary");  // Test PrintSharedDictionary as well.
+    check(output, 0, true, lastCheckMsg, "java.lang.Object");
+
+    log("Normal execution -- Make sure -version, help message and app main()\n" +
+        "class are not invoked. These are checked inside check().");
+    output = TestCommon.execCommon("-cp", cp, "-XX:+PrintSharedArchiveAndExit", "-version");
+    check(output, 0, true, lastCheckMsg);
+
+    output = TestCommon.execCommon("-cp", cp, "-XX:+PrintSharedArchiveAndExit", "-help");
+    check(output, 0, true, lastCheckMsg);
+
+    output = TestCommon.execCommon("-cp", cp, "-XX:+PrintSharedArchiveAndExit", "Hello");
+    check(output, 0, true, lastCheckMsg);
+
+    log("Execution with simple errors -- with 'simple' errors like missing or modified\n" +
+        "JAR files, the VM should try to continue to print the remaining information.\n" +
+        "Use an invalid Boot CP -- all the JAR paths should be checked");
+    output = TestCommon.execCommon(
+        "-cp", cp,
+        "-Xbootclasspath/a:foo.jar",
+        "-XX:+PrintSharedArchiveAndExit");
+    check(output, 1, true, lastCheckMsg, "[BOOT classpath mismatch, ");
+
+    log("Use an App CP shorter than the one at dump time -- all the JAR paths should be checked");
+    output = TestCommon.execCommon(
+        "-cp", ".",
+        "-XX:+PrintSharedArchiveAndExit");
+    check(output, 1, true, lastCheckMsg, "Run time APP classpath is shorter than the one at dump time: .");
+
+    log("Use an invalid App CP -- all the JAR paths should be checked");
+    String invalidCP = "non-existing-dir" + File.pathSeparator + cp;
+    output = TestCommon.execCommon(
+        "-cp", invalidCP,
+        "-XX:+PrintSharedArchiveAndExit");
+    check(output, 1, true, lastCheckMsg, "APP classpath mismatch, actual: -Djava.class.path=" + invalidCP);
+
+    log("Changed modification time of hello.jar -- all the JAR paths should be checked");
+    (new File(appJar)).setLastModified(System.currentTimeMillis() + 2000);
+    output = TestCommon.execCommon(
+        "-cp", cp,
+        "-XX:+PrintSharedArchiveAndExit");
+    check(output, 1, true, lastCheckMsg, "[Timestamp mismatch]");
+
+    log("Even if hello.jar is out of date, we should still be able to print the dictionary.");
+    output = TestCommon.execCommon(
+        "-cp", cp,
+        "-XX:+PrintSharedArchiveAndExit",
+        "-XX:+PrintSharedDictionary");  // Test PrintSharedDictionary as well.
+    check(output, 1, true, lastCheckMsg, "java.lang.Object");
+
+
+    log("Remove hello.jar -- all the JAR paths should be checked");
+    (new File(appJar)).delete();
+    output = TestCommon.execCommon(
+        "-cp", cp,
+        "-XX:+PrintSharedArchiveAndExit");
+    check(output, 1, true, lastCheckMsg, "[Required classpath entry does not exist: " + appJar + "]");
+
+    log("Execution with major errors -- with 'major' errors like the JSA file\n" +
+        "is missing, we should stop immediately to avoid crashing the JVM.");
+    output = TestCommon.execCommon(
+        "-cp", cp,
+        "-XX:+PrintSharedArchiveAndExit",
+        "-XX:SharedArchiveFile=./no-such-fileappcds.jsa");
+    check(output, 1, false, lastCheckMsg);
+  }
+}
diff --git a/test/hotspot/jtreg/runtime/appcds/ProhibitedPackage.java b/test/hotspot/jtreg/runtime/appcds/ProhibitedPackage.java
new file mode 100644
index 00000000000..92e7227c9c5
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/ProhibitedPackage.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+/*
+ * @test
+ * @summary AppCDS handling of prohibited package.
+ * AppCDS does not support uncompressed oops
+ * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true)
+ * @requires vm.cds
+ * @library /test/lib
+ * @modules java.base/jdk.internal.misc
+ *          java.management
+ *          jdk.jartool/sun.tools.jar
+ * @compile test-classes/ProhibitedHelper.java test-classes/Prohibited.jasm
+ * @run main ProhibitedPackage
+ */
+
+import jdk.test.lib.Platform;
+import jdk.test.lib.process.OutputAnalyzer;
+
+public class ProhibitedPackage {
+
+    public static void main(String[] args) throws Exception {
+        JarBuilder.build("prohibited_pkg", "java/lang/Prohibited", "ProhibitedHelper");
+
+        String appJar = TestCommon.getTestJar("prohibited_pkg.jar");
+
+        // Test support for customer loaders
+        if (Platform.areCustomLoadersSupportedForCDS()) {
+            String classlist[] = new String[] {
+                "java/lang/Object id: 1",
+                "java/lang/Prohibited id: 2 super: 1 source: " + appJar
+            };
+
+            // Make sure a class in a prohibited package for a custom loader
+            // will be ignored during dumping.
+            TestCommon.dump(appJar,  classlist, "-Xlog:cds")
+                .shouldContain("Dumping")
+                .shouldContain("[cds] Prohibited package for non-bootstrap classes: java/lang/Prohibited.class")
+                .shouldHaveExitValue(0);
+        }
+
+
+        // Make sure a class in a prohibited package for a non-custom loader
+        // will be ignored during dumping.
+        TestCommon.dump(appJar,
+                        TestCommon.list("java/lang/Prohibited", "ProhibitedHelper"),
+                        "-Xlog:class+load")
+            .shouldContain("Dumping")
+            .shouldNotContain("[info][class,load] java.lang.Prohibited source: ")
+            .shouldHaveExitValue(0);
+
+        // Try loading the class in a prohibited package with various -Xshare
+        // modes. The class shouldn't be loaded and appropriate exceptions
+        // are expected.
+
+        OutputAnalyzer output;
+
+        // -Xshare:on
+        output = TestCommon.execCommon(
+            "-XX:+UnlockDiagnosticVMOptions", "-XX:+WhiteBoxAPI",
+            "-cp", appJar, "-Xlog:class+load=info", "ProhibitedHelper");
+        TestCommon.checkExec(output, "Prohibited package name: java.lang");
+
+        // -Xshare:auto
+        output = TestCommon.execAuto(
+            "-XX:+UnlockDiagnosticVMOptions", "-XX:+WhiteBoxAPI",
+            "-cp", appJar, "-Xlog:class+load=info", "ProhibitedHelper");
+        TestCommon.checkExec(output, "Prohibited package name: java.lang");
+
+        // -Xshare:off
+        output = TestCommon.execOff(
+            "-XX:+UnlockDiagnosticVMOptions", "-XX:+WhiteBoxAPI",
+            "-cp", appJar, "-Xlog:class+load=info", "ProhibitedHelper");
+        output.shouldContain("Prohibited package name: java.lang");
+    }
+}
diff --git a/test/hotspot/jtreg/runtime/appcds/ProtectionDomain.java b/test/hotspot/jtreg/runtime/appcds/ProtectionDomain.java
new file mode 100644
index 00000000000..2ff23525a1a
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/ProtectionDomain.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+/*
+ * @test
+ * @summary AppCDS handling of protection domain.
+ * AppCDS does not support uncompressed oops
+ * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true)
+ * @library /test/lib
+ * @modules java.base/jdk.internal.misc
+ *          java.management
+ *          jdk.jartool/sun.tools.jar
+ * @compile test-classes/ProtDomain.java
+ * @compile test-classes/ProtDomainB.java
+ * @compile test-classes/JimageClassProtDomain.java
+ * @run main ProtectionDomain
+ */
+
+import jdk.test.lib.process.OutputAnalyzer;
+
+public class ProtectionDomain {
+  public static void main(String[] args) throws Exception {
+    JarBuilder.build("prot_domain", "ProtDomain", "ProtDomainB", "ProtDomainOther",
+                     "ProtDomainBOther", "JimageClassProtDomain");
+
+    String appJar = TestCommon.getTestJar("prot_domain.jar");
+    TestCommon.testDump(appJar,
+         TestCommon.list("ProtDomain",
+                         "ProtDomainBOther",
+                         "java/util/Dictionary",
+                         "sun/tools/javac/Main",
+                         "jdk/nio/zipfs/ZipInfo",
+                         "java/net/URL",
+                         "sun/rmi/rmic/Main",
+                         "com/sun/jndi/dns/DnsName"));
+
+    OutputAnalyzer output;
+
+    // First class is loaded from CDS, second class is loaded from JAR
+    output = TestCommon.exec(appJar, "-verbose:class", "ProtDomain");
+    TestCommon.checkExec(output, "Protection Domains match");
+
+    // First class is loaded from JAR, second class is loaded from CDS
+    output = TestCommon.exec(appJar, "-verbose:class", "ProtDomainB");
+    TestCommon.checkExec(output, "Protection Domains match");
+
+    // Test ProtectionDomain for application and extension module classes from the
+    // "modules" jimage
+    output = TestCommon.exec(appJar, "-verbose:class", "JimageClassProtDomain");
+    output.shouldNotContain("Failed: Protection Domains do not match");
+  }
+}
diff --git a/test/hotspot/jtreg/runtime/appcds/RewriteBytecodesTest.java b/test/hotspot/jtreg/runtime/appcds/RewriteBytecodesTest.java
new file mode 100644
index 00000000000..d49587d6bce
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/RewriteBytecodesTest.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+/*
+ * @test
+ * @summary Use ClassLoader.defineClass() to load a class with rewritten bytecode. Make sure
+ *          the archived class with the same name is not loaded.
+ * AppCDS does not support uncompressed oops
+ * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true)
+ * @library /test/lib
+ * @modules java.base/jdk.internal.misc
+ *          java.management
+ *          jdk.jartool/sun.tools.jar
+ * @compile test-classes/RewriteBytecodes.java test-classes/Util.java test-classes/Super.java test-classes/Child.java
+ * @build sun.hotspot.WhiteBox
+ * @run main ClassFileInstaller sun.hotspot.WhiteBox
+ * @run main RewriteBytecodesTest
+ */
+
+import java.io.File;
+import jdk.test.lib.process.OutputAnalyzer;
+
+public class RewriteBytecodesTest {
+  public static void main(String[] args) throws Exception {
+    String wbJar = JarBuilder.build(true, "WhiteBox", "sun/hotspot/WhiteBox");
+    String use_whitebox_jar = "-Xbootclasspath/a:" + wbJar;
+
+    String appJar = JarBuilder.build("dynamic_define", "RewriteBytecodes", "Util", "Super", "Child");
+    String superClsFile = (new File(System.getProperty("test.classes", "."), "Super.class")).getPath();
+
+    TestCommon.dump(appJar, TestCommon.list("RewriteBytecodes", "Super", "Child"),
+                    // command-line arguments ...
+                    use_whitebox_jar);
+
+    OutputAnalyzer output = TestCommon.exec(appJar,
+                    // command-line arguments ...
+                    "--add-opens=java.base/java.lang=ALL-UNNAMED",
+                    use_whitebox_jar,
+                    "-XX:+UnlockDiagnosticVMOptions",
+                    "-XX:+WhiteBoxAPI",
+                    "RewriteBytecodes", superClsFile);
+    TestCommon.checkExec(output);
+  }
+}
diff --git a/test/hotspot/jtreg/runtime/appcds/SharedArchiveConsistency.java b/test/hotspot/jtreg/runtime/appcds/SharedArchiveConsistency.java
new file mode 100644
index 00000000000..e7b1dac98ed
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/SharedArchiveConsistency.java
@@ -0,0 +1,386 @@
+/*
+ * Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+/*
+ *  @test
+ *  @summary SharedArchiveConsistency
+ *   AppCDS does not support uncompressed oops
+ *  @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true)
+ *  @library /test/lib
+ *  @modules java.base/jdk.internal.misc
+ *           java.compiler
+ *           java.management
+ *           jdk.jartool/sun.tools.jar
+ *           jdk.internal.jvmstat/sun.jvmstat.monitor
+ *  @build sun.hotspot.WhiteBox
+ *  @compile test-classes/Hello.java
+ *  @run main ClassFileInstaller sun.hotspot.WhiteBox
+ *  @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI SharedArchiveConsistency
+ */
+import jdk.test.lib.process.OutputAnalyzer;
+import jdk.test.lib.Utils;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.channels.FileChannel;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import static java.nio.file.StandardCopyOption.REPLACE_EXISTING;
+import java.nio.file.StandardOpenOption;
+import static java.nio.file.StandardOpenOption.READ;
+import static java.nio.file.StandardOpenOption.WRITE;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Random;
+import sun.hotspot.WhiteBox;
+
+public class SharedArchiveConsistency {
+    public static WhiteBox wb;
+    public static int offset_magic;    // FileMapHeader::_magic
+    public static int sp_offset_crc;   // FileMapHeader::space_info::_crc
+    public static int file_header_size = -1;// total size of header, variant, need calculation
+    public static int space_info_size; // size of space_info
+    public static int sp_offset;       // offset of FileMapHeader::space_info
+    public static int sp_used_offset;  // offset of space_info::_used
+    public static int size_t_size;     // size of size_t
+
+    public static File jsa;        // will be updated during test
+    public static File orgJsaFile; // kept the original file not touched.
+    public static String[] shared_region_name = {"MiscCode", "ReadWrite", "ReadOnly", "MiscData"};
+    public static int num_regions = shared_region_name.length;
+    public static String[] matchMessages = {
+        "Unable to use shared archive",
+        "An error has occurred while processing the shared archive file.",
+        "Checksum verification failed.",
+        "The shared archive file has been truncated."
+    };
+
+    public static void getFileOffsetInfo() throws Exception {
+        wb = WhiteBox.getWhiteBox();
+        offset_magic = wb.getOffsetForName("FileMapHeader::_magic");
+        sp_offset_crc = wb.getOffsetForName("space_info::_crc");
+        try {
+            int nonExistOffset = wb.getOffsetForName("FileMapHeader::_non_exist_offset");
+            System.exit(-1); // should fail
+        } catch (Exception e) {
+            // success
+        }
+
+        sp_offset = wb.getOffsetForName("FileMapHeader::_space[0]") - offset_magic;
+        sp_used_offset = wb.getOffsetForName("space_info::_used") - sp_offset_crc;
+        size_t_size = wb.getOffsetForName("size_t_size");
+        space_info_size  = wb.getOffsetForName("space_info_size");
+    }
+
+    public static int getFileHeaderSize(FileChannel fc) throws Exception {
+        if (file_header_size != -1) {
+            return file_header_size;
+        }
+        // this is not real header size, it is struct size
+        file_header_size = wb.getOffsetForName("file_header_size");
+        int offset_path_misc_info = wb.getOffsetForName("FileMapHeader::_paths_misc_info_size") -
+            offset_magic;
+        int path_misc_info_size   = (int)readInt(fc, offset_path_misc_info, size_t_size);
+        file_header_size += path_misc_info_size; //readInt(fc, offset_path_misc_info, size_t_size);
+        System.out.println("offset_path_misc_info = " + offset_path_misc_info);
+        System.out.println("path_misc_info_size   = " + path_misc_info_size);
+        System.out.println("file_header_size      = " + file_header_size);
+        file_header_size = (int)align_up_page(file_header_size);
+        System.out.println("file_header_size (aligned to page) = " + file_header_size);
+        return file_header_size;
+    }
+
+    public static long align_up_page(long l) throws Exception {
+        // wb is obtained in getFileOffsetInfo() which is called first in main() else we should call
+        // WhiteBox.getWhiteBox() here first.
+        int pageSize = wb.getVMPageSize();
+        return (l + pageSize -1) & (~ (pageSize - 1));
+    }
+
+    private static long getRandomBetween(long start, long end) throws Exception {
+        if (start > end) {
+            throw new IllegalArgumentException("start must be less than end");
+        }
+        Random aRandom = Utils.getRandomInstance();
+        int d = aRandom.nextInt((int)(end - start));
+        if (d < 1) {
+            d = 1;
+        }
+        return start + d;
+    }
+
+    public static long readInt(FileChannel fc, long offset, int nbytes) throws Exception {
+        ByteBuffer bb = ByteBuffer.allocate(nbytes);
+        bb.order(ByteOrder.nativeOrder());
+        fc.position(offset);
+        fc.read(bb);
+        return  (nbytes > 4 ? bb.getLong(0) : bb.getInt(0));
+    }
+
+    public static void writeData(FileChannel fc, long offset, ByteBuffer bb) throws Exception {
+        fc.position(offset);
+        fc.write(bb);
+        fc.force(true);
+    }
+
+    public static FileChannel getFileChannel() throws Exception {
+        List<StandardOpenOption> arry = new ArrayList<StandardOpenOption>();
+        arry.add(READ);
+        arry.add(WRITE);
+        return FileChannel.open(jsa.toPath(), new HashSet<StandardOpenOption>(arry));
+    }
+
+    public static void modifyJsaContentRandomly() throws Exception {
+        FileChannel fc = getFileChannel();
+        // corrupt random area in the data areas (MiscCode, ReadWrite, ReadOnly, MiscData)
+        long[] used    = new long[num_regions];       // record used bytes
+        long start0, start, end, off;
+        int used_offset, path_info_size;
+
+        int bufSize;
+        System.out.printf("%-12s%-12s%-12s%-12s%-12s\n", "Space Name", "Offset", "Used bytes", "Reg Start", "Random Offset");
+        start0 = getFileHeaderSize(fc);
+        for (int i = 0; i < num_regions; i++) {
+            used_offset = sp_offset + space_info_size * i + sp_used_offset;
+            // read 'used'
+            used[i] = readInt(fc, used_offset, size_t_size);
+            start = start0;
+            for (int j = 0; j < i; j++) {
+                start += align_up_page(used[j]);
+            }
+            end = start + used[i];
+            off = getRandomBetween(start, end);
+            System.out.printf("%-12s%-12d%-12d%-12d%-12d\n", shared_region_name[i], used_offset, used[i], start, off);
+            if (end - off < 1024) {
+                bufSize = (int)(end - off + 1);
+            } else {
+                bufSize = 1024;
+            }
+            ByteBuffer bbuf = ByteBuffer.wrap(new byte[bufSize]);
+            writeData(fc, off, bbuf);
+        }
+        if (fc.isOpen()) {
+            fc.close();
+        }
+    }
+
+    public static void modifyJsaContent() throws Exception {
+        FileChannel fc = getFileChannel();
+        byte[] buf = new byte[4096];
+        ByteBuffer bbuf = ByteBuffer.wrap(buf);
+
+        long total = 0L;
+        long used_offset = 0L;
+        long[] used = new long[num_regions];
+        System.out.printf("%-12s%-12s\n", "Space name", "Used bytes");
+        for (int i = 0; i < num_regions; i++) {
+            used_offset = sp_offset + space_info_size* i + sp_used_offset;
+            // read 'used'
+            used[i] = readInt(fc, used_offset, size_t_size);
+            System.out.printf("%-12s%-12d\n", shared_region_name[i], used[i]);
+            total += used[i];
+        }
+        System.out.printf("%-12s%-12d\n", "Total: ", total);
+        long corrupt_used_offset =  getFileHeaderSize(fc);
+        System.out.println("Corrupt RO section, offset = " + corrupt_used_offset);
+        while (used_offset < used[0]) {
+            writeData(fc, corrupt_used_offset, bbuf);
+            bbuf.clear();
+            used_offset += 4096;
+        }
+        fc.force(true);
+        if (fc.isOpen()) {
+            fc.close();
+        }
+    }
+
+    public static void modifyJsaHeader() throws Exception {
+        FileChannel fc = getFileChannel();
+        // screw up header info
+        byte[] buf = new byte[getFileHeaderSize(fc)];
+        ByteBuffer bbuf = ByteBuffer.wrap(buf);
+        writeData(fc, 0L, bbuf);
+        if (fc.isOpen()) {
+            fc.close();
+        }
+    }
+
+    public static void copyFile(File from, File to) throws Exception {
+        if (to.exists()) {
+            if(!to.delete()) {
+                throw new IOException("Could not delete file " + to);
+            }
+        }
+        to.createNewFile();
+        setReadWritePermission(to);
+        Files.copy(from.toPath(), to.toPath(), REPLACE_EXISTING);
+    }
+
+    // Copy file with bytes deleted or inserted
+    // del -- true, deleted, false, inserted
+    public static void copyFile(File from, File to, boolean del) throws Exception {
+        FileChannel inputChannel = null;
+        FileChannel outputChannel = null;
+        try {
+            inputChannel = new FileInputStream(from).getChannel();
+            outputChannel = new FileOutputStream(to).getChannel();
+            long size = inputChannel.size();
+            int init_size = getFileHeaderSize(inputChannel);
+            outputChannel.transferFrom(inputChannel, 0, init_size);
+            int n = (int)getRandomBetween(0, 1024);
+            if (del) {
+                System.out.println("Delete " + n + " bytes at data start section");
+                inputChannel.position(init_size + n);
+                outputChannel.transferFrom(inputChannel, init_size, size - init_size - n);
+            } else {
+                System.out.println("Insert " + n + " bytes at data start section");
+                outputChannel.position(init_size);
+                outputChannel.write(ByteBuffer.wrap(new byte[n]));
+                outputChannel.transferFrom(inputChannel, init_size + n , size - init_size);
+            }
+        } finally {
+            inputChannel.close();
+            outputChannel.close();
+        }
+    }
+
+    public static void restoreJsaFile() throws Exception {
+        Files.copy(orgJsaFile.toPath(), jsa.toPath(), REPLACE_EXISTING);
+    }
+
+    public static void setReadWritePermission(File file) throws Exception {
+        if (!file.canRead()) {
+            if (!file.setReadable(true)) {
+                throw new IOException("Cannot modify file " + file + " as readable");
+            }
+        }
+        if (!file.canWrite()) {
+            if (!file.setWritable(true)) {
+                throw new IOException("Cannot modify file " + file + " as writable");
+            }
+        }
+    }
+
+    public static void testAndCheck(String[] execArgs) throws Exception {
+        OutputAnalyzer output = TestCommon.execCommon(execArgs);
+        String stdtxt = output.getOutput();
+        System.out.println("Note: this test may fail in very rare occasions due to CRC32 checksum collision");
+        for (String message : matchMessages) {
+            if (stdtxt.contains(message)) {
+                // match any to return
+                return;
+            }
+        }
+        TestCommon.checkExec(output);
+    }
+
+    // dump with hello.jsa, then
+    // read the jsa file
+    //   1) run normal
+    //   2) modify header
+    //   3) keep header correct but modify content
+    //   4) update both header and content, test
+    //   5) delete bytes in data begining
+    //   6) insert bytes in data begining
+    //   7) randomly corrupt data in four areas: RO, RW. MISC DATA, MISC CODE
+    public static void main(String... args) throws Exception {
+        // must call to get offset info first!!!
+        getFileOffsetInfo();
+        Path currentRelativePath = Paths.get("");
+        String currentDir = currentRelativePath.toAbsolutePath().toString();
+        System.out.println("Current relative path is: " + currentDir);
+        // get jar file
+        String jarFile = JarBuilder.getOrCreateHelloJar();
+
+        // dump (appcds.jsa created)
+        TestCommon.testDump(jarFile, null);
+
+        // test, should pass
+        System.out.println("1. Normal, should pass but may fail\n");
+        String[] execArgs = {"-cp", jarFile, "Hello"};
+
+        OutputAnalyzer output = TestCommon.execCommon(execArgs);
+
+        try {
+            TestCommon.checkExecReturn(output, 0, true, "Hello World");
+        } catch (Exception e) {
+            TestCommon.checkExecReturn(output, 1, true, matchMessages[0]);
+        }
+
+        // get current archive name
+        jsa = new File(TestCommon.getCurrentArchiveName());
+        if (!jsa.exists()) {
+            throw new IOException(jsa + " does not exist!");
+        }
+
+        setReadWritePermission(jsa);
+
+        // save as original untouched
+        orgJsaFile = new File(new File(currentDir), "appcds.jsa.bak");
+        copyFile(jsa, orgJsaFile);
+
+
+        // modify jsa header, test should fail
+        System.out.println("\n2. Corrupt header, should fail\n");
+        modifyJsaHeader();
+        output = TestCommon.execCommon(execArgs);
+        output.shouldContain("The shared archive file has the wrong version");
+        output.shouldNotContain("Checksum verification failed");
+
+        // modify content
+        System.out.println("\n3. Corrupt Content, should fail\n");
+        copyFile(orgJsaFile, jsa);
+        modifyJsaContent();
+        testAndCheck(execArgs);
+
+        // modify both header and content, test should fail
+        System.out.println("\n4. Corrupt Header and Content, should fail\n");
+        copyFile(orgJsaFile, jsa);
+        modifyJsaHeader();
+        modifyJsaContent();  // this will not be reached since failed on header change first
+        output = TestCommon.execCommon(execArgs);
+        output.shouldContain("The shared archive file has the wrong version");
+        output.shouldNotContain("Checksum verification failed");
+
+        // delete bytes in data sectoin
+        System.out.println("\n5. Delete bytes at begining of data section, should fail\n");
+        copyFile(orgJsaFile, jsa, true);
+        testAndCheck(execArgs);
+
+        // insert bytes in data sectoin forward
+        System.out.println("\n6. Insert bytes at begining of data section, should fail\n");
+        copyFile(orgJsaFile, jsa, false);
+        testAndCheck(execArgs);
+
+        System.out.println("\n7. modify Content in random areas, should fail\n");
+        copyFile(orgJsaFile, jsa);
+        modifyJsaContentRandomly();
+        testAndCheck(execArgs);
+    }
+}
diff --git a/test/hotspot/jtreg/runtime/appcds/SharedArchiveFile.java b/test/hotspot/jtreg/runtime/appcds/SharedArchiveFile.java
new file mode 100644
index 00000000000..f18b5f21880
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/SharedArchiveFile.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+/*
+ * @test
+ * @summary The diagnostic option, -XX:SharedArchiveFile can be unlocked using -XX:+UseAppCDS
+ * AppCDS does not support uncompressed oops
+ * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true)
+ * @library /test/lib
+ * @modules java.base/jdk.internal.misc
+ *          java.management
+ *          jdk.jartool/sun.tools.jar
+ * @compile test-classes/Hello.java
+ * @run main SharedArchiveFile
+ */
+
+import jdk.test.lib.Platform;
+import jdk.test.lib.cds.CDSTestUtils;
+import jdk.test.lib.process.OutputAnalyzer;
+import jdk.test.lib.process.ProcessTools;
+import java.util.Properties;
+
+public class SharedArchiveFile {
+    public static void main(String[] args) throws Exception {
+        boolean isProduct = !Platform.isDebugBuild();
+        String appJar = JarBuilder.getOrCreateHelloJar();
+
+        // 1) Using -XX:SharedArchiveFile without -XX:+UseAppCDS should fail
+        //    on product binary without -XX:+UnlockDiagnosticVMOptions.
+        if (isProduct) {
+            ProcessBuilder pb = ProcessTools.createJavaProcessBuilder(true,
+                "-XX:SharedArchiveFile=./SharedArchiveFile.jsa", "-Xshare:dump");
+            OutputAnalyzer out = CDSTestUtils.executeAndLog(pb, "dump");
+            out.shouldContain("Error: VM option 'SharedArchiveFile' is diagnostic and must be enabled via -XX:+UnlockDiagnosticVMOptions.");
+        }
+
+        // 2) Dumping with -XX:+UnlockDiagnosticVMOptions -XX:SharedArchiveFile
+        //    should always succeed.
+        CDSTestUtils.createArchive("-XX:+UnlockDiagnosticVMOptions")
+            .shouldContain("Dumping");
+
+        // 3) Using -XX:SharedArchiveFile with -XX:+UseAppCDS should work
+        //    on product binary by default.
+        OutputAnalyzer output3 = TestCommon.dump(appJar, TestCommon.list("Hello"));
+        output3.shouldContain("Dumping");
+        output3 = TestCommon.exec(appJar, "Hello");
+        TestCommon.checkExec(output3, "Hello World");
+
+        // 4) Using -XX:+UseAppCDS should not affect other diagnostic flags,
+        //    such as LogEvents
+        OutputAnalyzer output4 = TestCommon.exec(appJar, "-XX:+LogEvents", "Hello");
+        if (isProduct) {
+            output4.shouldContain("Error: VM option 'LogEvents' is diagnostic and must be enabled via -XX:+UnlockDiagnosticVMOptions.");
+        } else {
+            TestCommon.checkExec(output4, "Hello World");
+        }
+
+        // 5) 8066921 - Extra -XX:+UseAppCDS
+        TestCommon.testDump(appJar, TestCommon.list("Hello"), "-XX:+UseAppCDS");
+        OutputAnalyzer output5 = TestCommon.exec(appJar, "-XX:+UseAppCDS", "Hello");
+        TestCommon.checkExec(output5);
+    }
+}
diff --git a/test/hotspot/jtreg/runtime/appcds/SharedBaseAddress.java b/test/hotspot/jtreg/runtime/appcds/SharedBaseAddress.java
new file mode 100644
index 00000000000..b317aac0d8f
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/SharedBaseAddress.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+/*
+ * @test SharedBaseAddress
+ * @summary Test variety of values for SharedBaseAddress, in AppCDS mode,
+ *          making sure VM handles normal values as well as edge values
+ *          w/o a crash.
+ * AppCDS does not support uncompressed oops
+ * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true)
+ * @library /test/lib
+ * @modules java.base/jdk.internal.misc
+ *          java.management
+ *          jdk.jartool/sun.tools.jar
+ * @compile test-classes/Hello.java
+ * @run main/timeout=240 SharedBaseAddress
+ */
+
+import jdk.test.lib.process.OutputAnalyzer;
+
+public class SharedBaseAddress {
+
+    // shared base address test table
+    private static final String[] testTable = {
+        "1g", "8g", "64g","512g", "4t",
+        "32t", "128t", "0",
+        "1", "64k", "64M"
+    };
+
+    public static void main(String[] args) throws Exception {
+        String appJar = JarBuilder.getOrCreateHelloJar();
+
+        for (String testEntry : testTable) {
+            System.out.println("sharedBaseAddress = " + testEntry);
+
+            OutputAnalyzer dumpOutput = TestCommon.dump(
+                appJar, new String[] {"Hello"}, "-XX:SharedBaseAddress=" + testEntry);
+            TestCommon.checkDump(dumpOutput, "Loading classes to share");
+
+            OutputAnalyzer execOutput = TestCommon.exec(appJar, "Hello");
+            TestCommon.checkExec(execOutput, "Hello World");
+        }
+    }
+}
diff --git a/test/hotspot/jtreg/runtime/appcds/SharedPackages.java b/test/hotspot/jtreg/runtime/appcds/SharedPackages.java
new file mode 100644
index 00000000000..c748d53c39c
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/SharedPackages.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+/*
+ * @test
+ * @summary AppCDS handling of package.
+ * AppCDS does not support uncompressed oops
+ * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true)
+ * @library /test/lib
+ * @modules java.base/jdk.internal.misc
+ *          java.management
+ *          jdk.jartool/sun.tools.jar
+ * @compile test-classes/PackageTest.java
+ * @compile test-classes/JimageClassPackage.java
+ * @run main SharedPackages
+ */
+
+import jdk.test.lib.process.OutputAnalyzer;
+
+public class SharedPackages {
+    public static void main(String[] args) throws Exception {
+        JarBuilder.build("pkg", "p/PackageTest", "JimageClassPackage");
+
+        String appJar = TestCommon.getTestJar("pkg.jar");
+        TestCommon.testDump(appJar, TestCommon.list("p/PackageTest",
+                                                    "java/util/Dictionary",
+                                                    "sun/tools/javac/Main",
+                                                    "jdk/nio/zipfs/ZipInfo",
+                                                    "java/net/URL",
+                                                    "sun/rmi/rmic/Main",
+                                                    "com/sun/jndi/dns/DnsName"));
+
+        OutputAnalyzer output;
+
+        // Test 1: shared class from Jar on the -cp
+        output = TestCommon.exec(appJar, "-verbose:class", "p.PackageTest");
+        TestCommon.checkExec(output, "Expected package");
+        if (!TestCommon.isUnableToMap(output))
+            output.shouldContain("Package is not sealed");
+
+        // Test 2: shared classes from "modules" jimage
+        output = TestCommon.exec(appJar, "-verbose:class",
+                                 "JimageClassPackage");
+        if (!TestCommon.isUnableToMap(output)) {
+            output.shouldNotContain("Unexpected package");
+            output.shouldNotContain("Package is not sealed");
+        }
+
+        // Test 3: shared class from Jar on the -Xbootclasspath/a
+        TestCommon.dump(
+            appJar, TestCommon.list("p/PackageTest"), "-Xbootclasspath/a:" + appJar);
+        output = TestCommon.exec(appJar, "-Xbootclasspath/a:" + appJar, "p.PackageTest");
+        if (!TestCommon.isUnableToMap(output)) {
+            output.shouldNotContain("Unexpected package");
+            output.shouldContain("Package is not sealed");
+        }
+    }
+}
diff --git a/test/hotspot/jtreg/runtime/appcds/SignedJar.java b/test/hotspot/jtreg/runtime/appcds/SignedJar.java
new file mode 100644
index 00000000000..7c3391ea034
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/SignedJar.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+/*
+ * @test
+ * @summary AppCDS handling of signed JAR.
+ * AppCDS does not support uncompressed oops
+ * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true)
+ * @library /test/lib
+ * @modules java.base/jdk.internal.misc
+ *          java.management
+ *          jdk.jartool/sun.tools.jar
+ * @compile test-classes/Hello.java
+ * @run main SignedJar
+ */
+
+import jdk.test.lib.process.OutputAnalyzer;
+import java.io.File;
+
+public class SignedJar {
+    public static void main(String[] args) throws Exception {
+        String unsignedJar = JarBuilder.getOrCreateHelloJar();
+        JarBuilder.signJar();
+
+        // Test class exists in signed JAR
+        String signedJar = TestCommon.getTestJar("signed_hello.jar");
+        OutputAnalyzer output;
+        output = TestCommon.dump(signedJar, TestCommon.list("Hello"));
+        TestCommon.checkDump(output, "Preload Warning: Skipping Hello from signed JAR");
+
+        // At runtime, the Hello class should be loaded from the jar file
+        // instead of from the shared archive since a class from a signed
+        // jar shouldn't be dumped into the archive.
+        output = TestCommon.exec(signedJar, "-verbose:class", "Hello");
+        String expectedOutput = ".class,load. Hello source: file:.*signed_hello.jar";
+
+        try {
+            output.shouldMatch(expectedOutput);
+        } catch (Exception e) {
+            TestCommon.checkCommonExecExceptions(output, e);
+        }
+
+        // Test class exists in both signed JAR and unsigned JAR
+        String jars = signedJar + System.getProperty("path.separator") + unsignedJar;
+        output = TestCommon.dump(jars, TestCommon.list("Hello"));
+        TestCommon.checkDump(output, "Preload Warning: Skipping Hello from signed JAR");
+    }
+}
diff --git a/test/hotspot/jtreg/runtime/appcds/SpecifySysLoaderProp.java b/test/hotspot/jtreg/runtime/appcds/SpecifySysLoaderProp.java
new file mode 100644
index 00000000000..bea6a0ac3f0
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/SpecifySysLoaderProp.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+/*
+ *  @test
+ *  @summary If -Djava.system.class.loader=xxx is specified in command-line, disable UseAppCDS
+ *  AppCDS does not support uncompressed oops
+ *  @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true)
+ *  @library /test/lib
+ *  @modules java.base/jdk.internal.misc
+ *      jdk.jartool/sun.tools.jar
+ *  @compile test-classes/TestClassLoader.java
+ *  @compile test-classes/ReportMyLoader.java
+ *  @compile test-classes/TrySwitchMyLoader.java
+ *  @run main SpecifySysLoaderProp
+ */
+
+import java.io.*;
+import jdk.test.lib.process.OutputAnalyzer;
+
+public class SpecifySysLoaderProp {
+
+  public static void main(String[] args) throws Exception {
+    JarBuilder.build("sysloader", "TestClassLoader", "ReportMyLoader", "TrySwitchMyLoader");
+
+    String jarFileName = "sysloader.jar";
+    String appJar = TestCommon.getTestJar(jarFileName);
+    TestCommon.testDump(appJar, TestCommon.list("ReportMyLoader"));
+    String warning = "VM warning: UseAppCDS is disabled because the java.system.class.loader property is specified";
+
+
+    // (0) Baseline. Do not specify -Djava.system.class.loader
+    //     The test class should be loaded from archive
+    OutputAnalyzer output = TestCommon.execCommon(
+        "-verbose:class",
+        "-cp", appJar,
+        "ReportMyLoader");
+    TestCommon.checkExec(output,
+                         "[class,load] ReportMyLoader source: shared objects file",
+                         "ReportMyLoader's loader = jdk.internal.loader.ClassLoaders$AppClassLoader@");
+
+    // (1) Try to execute the archive with -Djava.system.class.loader=no.such.Klass,
+    //     it should fail
+    output = TestCommon.execCommon(
+        "-cp", appJar,
+        "-Djava.system.class.loader=no.such.Klass",
+        "ReportMyLoader");
+    try {
+        output.shouldContain(warning);
+        output.shouldContain("ClassNotFoundException: no.such.Klass");
+    } catch (Exception e) {
+        TestCommon.checkCommonExecExceptions(output, e);
+    }
+
+    // (2) Try to execute the archive with -Djava.system.class.loader=TestClassLoader,
+    //     it should run, but AppCDS should be disabled
+    output = TestCommon.execCommon(
+        "-verbose:class",
+        "-cp", appJar,
+        "-Djava.system.class.loader=TestClassLoader",
+        "ReportMyLoader");
+    TestCommon.checkExec(output,
+                         "ReportMyLoader's loader = jdk.internal.loader.ClassLoaders$AppClassLoader@", //<-this is still printed because TestClassLoader simply delegates to Launcher$AppLoader, but ...
+                         "TestClassLoader.called = true", //<-but this proves that TestClassLoader was indeed called.
+                         "TestClassLoader: loadClass(\"ReportMyLoader\","); //<- this also proves that TestClassLoader was indeed called.
+    try {
+        output.shouldMatch(".class,load. TestClassLoader source: file:");
+        output.shouldMatch(".class,load. ReportMyLoader source: file:.*" + jarFileName);
+    } catch (Exception e) {
+        TestCommon.checkCommonExecExceptions(output, e);
+    }
+
+    // (3) Try to change the java.system.class.loader programmatically after
+    //     the app's main method is executed. This should have no effect in terms of
+    //     changing or switching the actual system class loader that's already in use.
+    output = TestCommon.execCommon(
+        "-verbose:class",
+        "-cp", appJar,
+        "TrySwitchMyLoader");
+    TestCommon.checkExec(output,
+                         "[class,load] ReportMyLoader source: shared objects file",
+                         "TrySwitchMyLoader's loader = jdk.internal.loader.ClassLoaders$AppClassLoader@",
+                         "ReportMyLoader's loader = jdk.internal.loader.ClassLoaders$AppClassLoader@",
+                         "TestClassLoader.called = false");
+  }
+}
diff --git a/test/hotspot/jtreg/runtime/appcds/TestCommon.java b/test/hotspot/jtreg/runtime/appcds/TestCommon.java
new file mode 100644
index 00000000000..ad6f8e272cb
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/TestCommon.java
@@ -0,0 +1,324 @@
+/*
+ * Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+import jdk.test.lib.Utils;
+import jdk.test.lib.JDKToolFinder;
+import jdk.test.lib.Platform;
+import jdk.test.lib.cds.CDSOptions;
+import jdk.test.lib.cds.CDSTestUtils;
+import jdk.test.lib.process.ProcessTools;
+import jdk.test.lib.process.OutputAnalyzer;
+import java.io.File;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Date;
+
+/**
+ * This is a test utility class for common AppCDS test functionality.
+ *
+ * Various methods use (String ...) for passing VM options. Note that the order
+ * of the VM options are important in certain cases. Many methods take arguments like
+ *
+ *    (String prefix[], String suffix[], String... opts)
+ *
+ * Note that the order of the VM options is:
+ *
+ *    prefix + opts + suffix
+ */
+public class TestCommon extends CDSTestUtils {
+    private static final String JSA_FILE_PREFIX = System.getProperty("user.dir") +
+        File.separator + "appcds-";
+
+    private static final SimpleDateFormat timeStampFormat =
+        new SimpleDateFormat("HH'h'mm'm'ss's'SSS");
+
+    private static final String timeoutFactor =
+        System.getProperty("test.timeout.factor", "1.0");
+
+    private static String currentArchiveName;
+
+    // Call this method to start new archive with new unique name
+    public static void startNewArchiveName() {
+        deletePriorArchives();
+        currentArchiveName = JSA_FILE_PREFIX +
+            timeStampFormat.format(new Date()) + ".jsa";
+    }
+
+    // Call this method to get current archive name
+    public static String getCurrentArchiveName() {
+        return currentArchiveName;
+    }
+
+    // Attempt to clean old archives to preserve space
+    // Archives are large artifacts (20Mb or more), and much larger than
+    // most other artifacts created in jtreg testing.
+    // Therefore it is a good idea to clean the old archives when they are not needed.
+    // In most cases the deletion attempt will succeed; on rare occasion the
+    // delete operation will fail since the system or VM process still holds a handle
+    // to the file; in such cases the File.delete() operation will silently fail, w/o
+    // throwing an exception, thus allowing testing to continue.
+    public static void deletePriorArchives() {
+        File dir = new File(System.getProperty("user.dir"));
+        String files[] = dir.list();
+        for (String name : files) {
+            if (name.startsWith("appcds-") && name.endsWith(".jsa")) {
+                if (!(new File(dir, name)).delete())
+                    System.out.println("deletePriorArchives(): delete failed for file " + name);
+            }
+        }
+    }
+
+
+    // Create AppCDS archive using most common args - convenience method
+    // Legacy name preserved for compatibility
+    public static OutputAnalyzer dump(String appJar, String appClasses[],
+                                               String... suffix) throws Exception {
+        return createArchive(appJar, appClasses, suffix);
+    }
+
+
+    // Create AppCDS archive using most common args - convenience method
+    public static OutputAnalyzer createArchive(String appJar, String appClasses[],
+                                               String... suffix) throws Exception {
+        AppCDSOptions opts = (new AppCDSOptions()).setAppJar(appJar)
+            .setAppClasses(appClasses);
+        opts.addSuffix(suffix);
+        return createArchive(opts);
+    }
+
+
+    // Create AppCDS archive using appcds options
+    public static OutputAnalyzer createArchive(AppCDSOptions opts)
+        throws Exception {
+
+        ArrayList<String> cmd = new ArrayList<String>();
+        File classList = makeClassList(opts.appClasses);
+        startNewArchiveName();
+
+        for (String p : opts.prefix) cmd.add(p);
+
+        if (opts.appJar != null) {
+            cmd.add("-cp");
+            cmd.add(opts.appJar);
+        } else {
+            cmd.add("-cp");
+            cmd.add("\"\"");
+        }
+
+        cmd.add("-Xshare:dump");
+        cmd.add("-Xlog:cds,cds+hashtables");
+        cmd.add("-XX:+UseAppCDS");
+        cmd.add("-XX:ExtraSharedClassListFile=" + classList.getPath());
+
+        if (opts.archiveName == null)
+            opts.archiveName = getCurrentArchiveName();
+
+        cmd.add("-XX:SharedArchiveFile=" + opts.archiveName);
+
+        for (String s : opts.suffix) cmd.add(s);
+
+        String[] cmdLine = cmd.toArray(new String[cmd.size()]);
+        ProcessBuilder pb = ProcessTools.createJavaProcessBuilder(true, cmdLine);
+        return executeAndLog(pb, "dump");
+    }
+
+
+    // Execute JVM using AppCDS archive with specified AppCDSOptions
+    public static OutputAnalyzer runWithArchive(AppCDSOptions opts)
+        throws Exception {
+
+        ArrayList<String> cmd = new ArrayList<String>();
+
+        for (String p : opts.prefix) cmd.add(p);
+
+        cmd.add("-Xshare:" + opts.xShareMode);
+        cmd.add("-XX:+UseAppCDS");
+        cmd.add("-showversion");
+        cmd.add("-XX:SharedArchiveFile=" + getCurrentArchiveName());
+        cmd.add("-Dtest.timeout.factor=" + timeoutFactor);
+
+        if (opts.appJar != null) {
+            cmd.add("-cp");
+            cmd.add(opts.appJar);
+        }
+
+        for (String s : opts.suffix) cmd.add(s);
+
+        String[] cmdLine = cmd.toArray(new String[cmd.size()]);
+        ProcessBuilder pb = ProcessTools.createJavaProcessBuilder(true, cmdLine);
+        return executeAndLog(pb, "exec");
+    }
+
+
+    public static OutputAnalyzer execCommon(String... suffix) throws Exception {
+        AppCDSOptions opts = (new AppCDSOptions());
+        opts.addSuffix(suffix);
+        return runWithArchive(opts);
+    }
+
+
+    public static OutputAnalyzer exec(String appJar, String... suffix) throws Exception {
+        AppCDSOptions opts = (new AppCDSOptions()).setAppJar(appJar);
+        opts.addSuffix(suffix);
+        return runWithArchive(opts);
+    }
+
+
+    public static OutputAnalyzer execAuto(String... suffix) throws Exception {
+        AppCDSOptions opts = (new AppCDSOptions());
+        opts.addSuffix(suffix).setXShareMode("auto");
+        return runWithArchive(opts);
+    }
+
+    public static OutputAnalyzer execOff(String... suffix) throws Exception {
+        AppCDSOptions opts = (new AppCDSOptions());
+        opts.addSuffix(suffix).setXShareMode("off");
+        return runWithArchive(opts);
+    }
+
+    public static OutputAnalyzer execModule(String prefix[], String upgrademodulepath, String modulepath,
+                                            String mid, String... testClassArgs)
+        throws Exception {
+
+        AppCDSOptions opts = (new AppCDSOptions());
+
+        opts.addPrefix(prefix);
+        if (upgrademodulepath == null) {
+            opts.addSuffix("-p", modulepath, "-m", mid);
+        } else {
+            opts.addSuffix("--upgrade-module-path", upgrademodulepath,
+                           "-p", modulepath, "-m", mid);
+        }
+        opts.addSuffix(testClassArgs);
+
+        return runWithArchive(opts);
+    }
+
+
+    // A common operation: dump, then check results
+    public static OutputAnalyzer testDump(String appJar, String appClasses[],
+                                          String... suffix) throws Exception {
+        OutputAnalyzer output = dump(appJar, appClasses, suffix);
+        output.shouldContain("Loading classes to share");
+        output.shouldHaveExitValue(0);
+        return output;
+    }
+
+
+    /**
+     * Simple test -- dump and execute appJar with the given appClasses in classlist.
+     */
+    public static OutputAnalyzer test(String appJar, String appClasses[], String... args)
+        throws Exception {
+        testDump(appJar, appClasses);
+
+        OutputAnalyzer output = exec(appJar, args);
+        return checkExec(output);
+    }
+
+
+    public static OutputAnalyzer checkExecReturn(OutputAnalyzer output, int ret,
+                           boolean checkContain, String... matches) throws Exception {
+        try {
+            for (String s : matches) {
+                if (checkContain) {
+                    output.shouldContain(s);
+                } else {
+                    output.shouldNotContain(s);
+                }
+            }
+            output.shouldHaveExitValue(ret);
+        } catch (Exception e) {
+            checkCommonExecExceptions(output, e);
+        }
+
+        return output;
+    }
+
+
+    // Convenience concatenation utils
+    public static String[] list(String ...args) {
+        return args;
+    }
+
+
+    public static String[] list(String arg, int count) {
+        ArrayList<String> stringList = new ArrayList<String>();
+        for (int i = 0; i < count; i++) {
+            stringList.add(arg);
+        }
+
+        String outputArray[] = stringList.toArray(new String[stringList.size()]);
+        return outputArray;
+    }
+
+
+    public static String[] concat(String... args) {
+        return list(args);
+    }
+
+
+    public static String[] concat(String prefix[], String... extra) {
+        ArrayList<String> list = new ArrayList<String>();
+        for (String s : prefix) {
+            list.add(s);
+        }
+        for (String s : extra) {
+            list.add(s);
+        }
+
+        return list.toArray(new String[list.size()]);
+    }
+
+
+    // ===================== Concatenate paths
+    public static String concatPaths(String... paths) {
+        String prefix = "";
+        String s = "";
+        for (String p : paths) {
+            s += prefix;
+            s += p;
+            prefix = File.pathSeparator;
+        }
+        return s;
+    }
+
+
+    public static String getTestJar(String jar) {
+        File jarFile = CDSTestUtils.getTestArtifact(jar, true);
+        if (!jarFile.isFile()) {
+            throw new RuntimeException("Not a regular file: " + jarFile.getPath());
+        }
+        return jarFile.getPath();
+    }
+
+
+    public static String getTestDir(String d) {
+        File dirFile = CDSTestUtils.getTestArtifact(d, true);
+        if (!dirFile.isDirectory()) {
+            throw new RuntimeException("Not a directory: " + dirFile.getPath());
+        }
+        return dirFile.getPath();
+    }
+}
diff --git a/test/hotspot/jtreg/runtime/appcds/TraceLongClasspath.java b/test/hotspot/jtreg/runtime/appcds/TraceLongClasspath.java
new file mode 100644
index 00000000000..b3f73278dba
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/TraceLongClasspath.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+/*
+ * @test
+ * @summary ensure -XX:+TraceClassPaths showing entire expecting app classpath
+ * AppCDS does not support uncompressed oops
+ * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true)
+ * @library /test/lib
+ * @modules java.base/jdk.internal.misc
+ *          java.management
+ *          jdk.jartool/sun.tools.jar
+ * @compile test-classes/Hello.java
+ * @run main TraceLongClasspath
+ */
+
+import java.io.File;
+import jdk.test.lib.process.OutputAnalyzer;
+
+public class TraceLongClasspath {
+
+    final static String ps = File.pathSeparator;
+
+    public static void main(String[] args) throws Exception {
+        String appJar = JarBuilder.getOrCreateHelloJar();
+
+        String longClassPath =
+            "/scratch/xxxx/yyyy/ZZZZZZ/aaaaaaaaaa/xx/abc/abc/modules/user-patch.jar" + ps +
+            "/scratch/xxxx/yyyy/ZZZZZZ/aaaaaaaaaa/xx/abc/abc/modules/abc-startup.jar" + ps +
+            "/scratch/xxxx/yyyy/ZZZZZZ/aaaaaaaaaa/xx/foobar_common/modules/features/com.foobar.db.jdbc7-dms.jar" + ps +
+            "/scratch/xxxx/yyyy/ZZZZZZ/aaaaaaaaaa/jdk/lib/tools.jar" + ps +
+            "/scratch/xxxx/yyyy/ZZZZZZ/aaaaaaaaaa/xx/aaserver/server/lib/someapps.jar" + ps +
+            "/scratch/xxxx/yyyy/ZZZZZZ/aaaaaaaaaa/xx/aaserver/../foobar_common/modules/net.xy.batcontrib_1.1.0.0_1-0b3/lib/bat-contrib.jar" + ps +
+            "/scratch/xxxx/yyyy/ZZZZZZ/aaaaaaaaaa/xx/aaserver/modules/features/foobar.aas.common.kkkkkkkkkkk.jar" + ps +
+            "/scratch/xxxx/yyyy/ZZZZZZ/aaaaaaaaaa/xx/abc/abc/modules/foobar.abc.common.adapters_11.1.1/foobar.abc.common.adapters.jar" + ps +
+            "/scratch/xxxx/yyyy/ZZZZZZ/aaaaaaaaaa/xx/abc/abc/modules/foobar.plane.adapter_12.1.3/foobar.plane.adapter.jar" + ps +
+            "/scratch/xxxx/yyyy/ZZZZZZ/aaaaaaaaaa/xx/abc/lib/ccccccccar-common.jar" + ps +
+            "/scratch/xxxx/yyyy/ZZZZZZ/aaaaaaaaaa/xx/foobar_common/communications/modules/config.jar" + ps +
+            "/scratch/xxxx/yyyy/ZZZZZZ/aaaaaaaaaa/xx/foobar_common/communications/modules/userprefs-config.jar" + ps +
+            "/scratch/xxxx/yyyy/XXXXXX/aaaaaaaa/xxxxxxx/xxxxxxxx.us.foobar.com/CommonDomain/config/abc-infra" + ps +
+            "/scratch/xxxx/yyyy/ZZZZZZ/aaaaaaaaaa/xx/abc/abc/modules/qqqqqq-all-1.6.5.jar" + ps +
+            "/scratch/xxxx/yyyy/ZZZZZZ/aaaaaaaaaa/xx/abc/abc/modules/foobar.abc.thread_11.1.1/foobar.abc.thread.jar" + ps +
+            "/scratch/xxxx/yyyy/ZZZZZZ/aaaaaaaaaa/xx/abc/abc/modules/foobar.abc.thread_11.1.1/thread-rrrrrrr-ext-aas.jar" + ps +
+            "/scratch/xxxx/yyyy/ZZZZZZ/aaaaaaaaaa/xx/abc/abc/modules/foobar.abc.adapter_11.1.1/foobar.abc.adapter.jar" + ps +
+            "/scratch/xxxx/yyyy/ZZZZZZ/aaaaaaaaaa/xx/abc/abc/modules/foobar.abc.ccc_11.1.1/foobar.abc.ccc.jar" + ps +
+            "/scratch/xxxx/yyyy/ZZZZZZ/aaaaaaaaaa/xx/bbb/lib/commons-configuration.jar" + ps +
+            "/scratch/xxxx/yyyy/ZZZZZZ/aaaaaaaaaa/xx/bbb/lib/commons-lang.jar" + ps +
+            "/scratch/xxxx/yyyy/ZZZZZZ/aaaaaaaaaa/xx/bbb/lib/commons-logging.jar" + ps +
+            "/scratch/xxxx/yyyy/ZZZZZZ/aaaaaaaaaa/xx/foobar_common/modules/foobar.wccore/foobar-ppppppp-api.jar" + ps +
+            "/scratch/xxxx/yyyy/ZZZZZZ/aaaaaaaaaa/xx/foobar_common/modules/foobar.ooo_12.1.3/ooo-manifest.jar" + ps +
+            "/scratch/xxxx/yyyy/ZZZZZZ/aaaaaaaaaa/xx/foobar_common/modules/internal/features/rrr_aaxyxx_foobar.rrr.aas.classpath.jar" + ps +
+            "/scratch/xxxx/yyyy/ZZZZZZ/aaaaaaaaaa/xx/abc/abc/modules/foobar.abc.thread_11.1.1/rrrrrrrr-api.jar" + ps +
+            "/scratch/xxxx/yyyy/ZZZZZZ/aaaaaaaaaa/xx/abc/abc/modules/commons-xxx-1.1.jar" + ps +
+            "/scratch/xxxx/yyyy/ZZZZZZ/aaaaaaaaaa/xx/abc/abc/modules/foobar.abc.mgmt_11.1.1/abc-infra-mgmt.jar" + ps +
+            "/scratch/xxxx/yyyy/ZZZZZZ/aaaaaaaaaa/xx/foobar_common/eee/archives/eee-eee.jar" + ps +
+            "/scratch/xxxx/yyyy/ZZZZZZ/aaaaaaaaaa/xx/aaserver/common/march/lib/marchnet.jar" + ps +
+            "/scratch/xxxx/yyyy/ZZZZZZ/aaaaaaaaaa/xx/aaserver/common/march/lib/marchclient.jar" + ps +
+            "/scratch/xxxx/yyyy/ZZZZZZ/aaaaaaaaaa/xx/aaserver/common/march/lib/march.jar" + ps +
+            "/scratch/xxxx/yyyy/ZZZZZZ/aaaaaaaaaa/xx/wwcontent/cde/iii/jlib/iiiloader.jar" + ps +
+            "/scratch/xxxx/yyyy/ZZZZZZ/aaaaaaaaaa/xx/wwcontent/cde/iii/components/xxxxxxyyzzzzz/classes-xxxxxxyyzzzzz.jar" + ps +
+            "/scratch/xxxx/yyyy/ZZZZZZ/aaaaaaaaaa/xx/wwcontent/cde/iii/components/mmmmmmm/lib/abc_core.jar" + ps +
+            "/scratch/xxxx/yyyy/ZZZZZZ/aaaaaaaaaa/xx/wwcontent/cde/iii/components/mmmmmmm/lib/abc_codec.jar" + ps +
+            "/scratch/xxxx/yyyy/ZZZZZZ/aaaaaaaaaa/xx/wwcontent/cde/iii/components/mmmmmmm/lib/abc_imageio.jar" + ps +
+            "/scratch/xxxx/yyyy/ZZZZZZ/aaaaaaaaaa/jdk/lib/tools.jar" + ps +
+            "/scratch/xxxx/yyyy/ZZZZZZ/aaaaaaaaaa/xx/foobar_common/modules/foobar.ooo_12.1.3/ooo-manifest.jar";
+
+        longClassPath += ps + appJar;
+        // Dump an archive with a specified JAR file in -classpath
+        TestCommon.testDump(longClassPath, TestCommon.list("Hello"));
+
+        // Then try to execute the archive with a different classpath and with -XX:+TraceClassPaths.
+        // The diagnosis "expecting" app classpath trace should show the entire classpath.
+        OutputAnalyzer output = TestCommon.execCommon(
+            "-XX:+TraceClassPaths",
+            "-cp", appJar,
+            "Hello");
+        output.shouldContain("Unable to use shared archive");
+        output.shouldContain("shared class paths mismatch");
+        // the "expecting" app classpath from -XX:+TraceClassPaths should not
+        // be truncated
+        output.shouldContain(longClassPath);
+        output.shouldHaveExitValue(1);
+    }
+}
diff --git a/test/hotspot/jtreg/runtime/appcds/UseAppCDS.java b/test/hotspot/jtreg/runtime/appcds/UseAppCDS.java
new file mode 100644
index 00000000000..639c06de59c
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/UseAppCDS.java
@@ -0,0 +1,228 @@
+/*
+ * Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+/*
+ * @test
+ * @summary Testing use of UseAppCDS flag
+ * AppCDS does not support uncompressed oops
+ * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true)
+ * @library /test/lib
+ * @modules java.base/jdk.internal.misc
+ *          java.management
+ *          jdk.jartool/sun.tools.jar
+ * @build UseAppCDS_Test
+ * @run main UseAppCDS
+ */
+
+import jdk.test.lib.JDKToolLauncher;
+import jdk.test.lib.cds.CDSTestUtils;
+import jdk.test.lib.process.OutputAnalyzer;
+import jdk.test.lib.process.ProcessTools;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.io.*;
+
+public class UseAppCDS {
+
+    // Class UseAppCDS_Test is loaded by the App loader
+
+    static final String TEST_OUT = "UseAppCDS_Test.main--executed";
+
+    private static final String TESTJAR = "./test.jar";
+    private static final String TESTNAME = "UseAppCDS_Test";
+    private static final String TESTCLASS = TESTNAME + ".class";
+
+    private static final String CLASSES_DIR = System.getProperty("test.classes", ".");
+    private static final String CLASSLIST_FILE = "./UseAppCDS.classlist";
+    private static final String ARCHIVE_FILE = "./shared.jsa";
+    private static final String BOOTCLASS = "java.lang.Class";
+
+    public static void main(String[] args) throws Exception {
+
+        // First create a jar file for the application "test" class
+        JDKToolLauncher jar = JDKToolLauncher.create("jar")
+            .addToolArg("-cf")
+            .addToolArg(TESTJAR)
+            .addToolArg("-C")
+            .addToolArg(CLASSES_DIR)
+            .addToolArg(TESTCLASS);
+
+        ProcessBuilder pb = new ProcessBuilder(jar.getCommand());
+        TestCommon.executeAndLog(pb, "jar01").shouldHaveExitValue(0);
+
+        pb = new ProcessBuilder(jar.getCommand());
+        TestCommon.executeAndLog(pb, "jar02").shouldHaveExitValue(0);
+
+        // In all tests the BOOTCLASS should be loaded/dumped/used
+
+        // Test 1: No AppCDS - dumping loaded classes excludes the "test" classes
+        dumpLoadedClasses(false, new String[] { BOOTCLASS },
+                          new String[] { TESTNAME });
+
+        // Test 2:    AppCDS - dumping loaded classes includes "test" classes
+        dumpLoadedClasses(true, new String[] { BOOTCLASS, TESTNAME },
+                          new String[0]);
+
+        // Next tests rely on the classlist we just dumped
+
+        // Test 3: No AppCDS - "test" classes in classlist ignored when dumping
+        dumpArchive(false, new String[] { BOOTCLASS },
+                    new String[] { TESTNAME});
+
+        // Test 4:    AppCDS - "test" classes in classlist are dumped
+        dumpArchive(true, new String[] { BOOTCLASS, TESTNAME },
+                    new String[0]);
+
+        // Next tests rely on the archive we just dumped
+
+        // Test 5: No AppCDS - Using archive containing "test" classes ignores them
+        useArchive(false, new String[] { BOOTCLASS },
+                   new String[] { TESTNAME });
+
+        // Test 6:    AppCDS - Using archive containing "test" classes loads them
+        useArchive(true, new String[] { BOOTCLASS, TESTNAME },
+                   new String[0]);
+    }
+
+    public static List<String> toClassNames(String filename) throws IOException {
+        ArrayList<String> classes = new ArrayList<>();
+        BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(filename)));
+        for (; ; ) {
+            String line = br.readLine();
+            if (line == null)
+                break;
+            classes.add(line.replaceAll("/", "."));
+        }
+        return classes;
+    }
+
+    static void dumpLoadedClasses(boolean useAppCDS, String[] expectedClasses,
+                                  String[] unexpectedClasses) throws Exception {
+        ProcessBuilder pb = ProcessTools.createJavaProcessBuilder(
+            true,
+            "-XX:DumpLoadedClassList=" + CLASSLIST_FILE,
+            "-cp",
+            TESTJAR,
+            useAppCDS ? "-XX:+UseAppCDS" : "-XX:-UseAppCDS",
+            TESTNAME,
+            TEST_OUT);
+
+        OutputAnalyzer output = TestCommon.executeAndLog(pb, "dump-loaded-classes")
+            .shouldHaveExitValue(0).shouldContain(TEST_OUT);
+
+        List<String> dumpedClasses = toClassNames(CLASSLIST_FILE);
+
+        for (String clazz : expectedClasses) {
+            if (!dumpedClasses.contains(clazz)) {
+                throw new RuntimeException(clazz + " missing in " +
+                                           CLASSLIST_FILE);
+            }
+        }
+        for (String clazz : unexpectedClasses) {
+            if (dumpedClasses.contains(clazz)) {
+                throw new RuntimeException("Unexpectedly found " + clazz +
+                                           " in " + CLASSLIST_FILE);
+            }
+        }
+    }
+
+    static void dumpArchive(boolean useAppCDS, String[] expectedClasses,
+                            String[] unexpectedClasses) throws Exception {
+        ProcessBuilder pb = ProcessTools.createJavaProcessBuilder(
+            true,
+            useAppCDS ? "-XX:-UnlockDiagnosticVMOptions" :
+                        "-XX:+UnlockDiagnosticVMOptions",
+            "-cp",
+            TESTJAR,
+            useAppCDS ? "-XX:+UseAppCDS" : "-XX:-UseAppCDS",
+            "-XX:SharedClassListFile=" + CLASSLIST_FILE,
+            "-XX:SharedArchiveFile=" + ARCHIVE_FILE,
+            "-Xlog:cds",
+            "-Xshare:dump");
+
+        OutputAnalyzer output = TestCommon.executeAndLog(pb, "dump-archive")
+            .shouldHaveExitValue(0);
+
+        for (String clazz : expectedClasses) {
+            String failed = "Preload Warning: Cannot find " + clazz;
+            output.shouldNotContain(failed);
+        }
+        for (String clazz : unexpectedClasses) {
+            String failed = "Preload Warning: Cannot find " + clazz;
+            output.shouldContain(failed);
+        }
+    }
+
+    static void useArchive(boolean useAppCDS, String[] expectedClasses,
+                           String[] unexpectedClasses) throws Exception {
+        ProcessBuilder pb = ProcessTools.createJavaProcessBuilder(
+            true,
+            useAppCDS ? "-XX:-UnlockDiagnosticVMOptions" :
+                        "-XX:+UnlockDiagnosticVMOptions",
+            "-cp",
+            TESTJAR,
+            useAppCDS ? "-XX:+UseAppCDS" : "-XX:-UseAppCDS",
+            "-XX:SharedArchiveFile=" + ARCHIVE_FILE,
+            "-verbose:class",
+            "-Xshare:on",
+            TESTNAME,
+            TEST_OUT );
+
+        OutputAnalyzer output = TestCommon.executeAndLog(pb, "use-archive");
+        if (CDSTestUtils.isUnableToMap(output))
+            System.out.println("Unable to map: test case skipped");
+        else
+            output.shouldHaveExitValue(0).shouldContain(TEST_OUT);
+
+        // Quote the class name in the regex as it may contain $
+        String prefix = ".class,load. ";
+        String archive_suffix = ".*source: shared objects file.*";
+        String jar_suffix = ".*source: .*\\.jar";
+
+        for (String clazz : expectedClasses) {
+            String pattern = prefix + clazz + archive_suffix;
+            try {
+                output.shouldMatch(pattern);
+            } catch (Exception e) {
+                TestCommon.checkCommonExecExceptions(output, e);
+            }
+        }
+
+        for (String clazz : unexpectedClasses) {
+            String pattern = prefix + clazz + archive_suffix;
+            try {
+                output.shouldNotMatch(pattern);
+            } catch (Exception e) {
+                TestCommon.checkCommonExecExceptions(output, e);
+            }
+            pattern = prefix + clazz + jar_suffix;
+            try {
+                output.shouldMatch(pattern);
+            } catch (Exception e) {
+                TestCommon.checkCommonExecExceptions(output, e);
+            }
+        }
+    }
+}
diff --git a/test/hotspot/jtreg/runtime/appcds/UseAppCDS_Test.java b/test/hotspot/jtreg/runtime/appcds/UseAppCDS_Test.java
new file mode 100644
index 00000000000..438c614445b
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/UseAppCDS_Test.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2014, 2017, 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.
+ *
+ */
+public class UseAppCDS_Test {
+    // args are from UseAppCDS:
+    // args[0] = TEST_OUT
+    public static void main(String[] args) {
+        System.out.println(args[0]);
+    }
+}
diff --git a/test/hotspot/jtreg/runtime/appcds/VerifierTest.java b/test/hotspot/jtreg/runtime/appcds/VerifierTest.java
new file mode 100644
index 00000000000..ba7f5d1b831
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/VerifierTest.java
@@ -0,0 +1,343 @@
+/*
+ * Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+import java.io.File;
+import java.io.FileOutputStream;
+import jdk.test.lib.process.OutputAnalyzer;
+import java.nio.file.Files;
+
+import java.util.*;
+import jdk.internal.org.objectweb.asm.*;
+
+/**
+ * The testsets contained in this class are executed by ./VerifierTest_*.java, so that
+ * individual testsets can be executed in parallel to shorten the total time required.
+ */
+public class VerifierTest implements Opcodes {
+    // Test verification settings for dumping & runtime
+    static final String VFY_ALL = "-Xverify:all";
+    static final String VFY_REMOTE = "-Xverify:remote"; // default
+    static final String VFY_NONE = "-Xverify:none";
+
+    static final String ERR =
+        "ERROR: class VerifierTestC was loaded unexpectedly";
+    static final String MAP_FAIL =
+        "shared archive file was created with less restrictive verification setting";
+    static final String VFY_ERR = "java.lang.VerifyError";
+
+    enum Testset1Part {
+        A, B
+    }
+
+    public static void main(String[] args) throws Exception {
+        String subCaseId = args[0];
+        String jarName_verifier_test_tmp = "verifier_test_tmp" + "_" + subCaseId;
+        String jarName_verifier_test = "verifier_test" + "_" + subCaseId;
+        String jarName_greet = "greet" + "_" + subCaseId;
+        String jarName_hi = "hi" + "_" + subCaseId;
+
+
+        JarBuilder.build(jarName_verifier_test_tmp, "VerifierTest0", "VerifierTestA",
+                         "VerifierTestB", "VerifierTestC", "VerifierTestD", "VerifierTestE",
+                         "UnverifiableBase", "UnverifiableIntf", "UnverifiableIntfSub");
+        JarBuilder.build(jarName_greet, "Greet");
+        JarBuilder.build(jarName_hi, "Hi", "Hi$MyClass");
+
+        File dir = new File(System.getProperty("test.classes", "."));
+        File jarSrcFile = new File(dir, jarName_verifier_test_tmp + ".jar");
+        File jarFile = new File(dir, jarName_verifier_test + ".jar");
+        String jar = jarFile.getPath();
+
+        if (!jarFile.exists() || jarFile.lastModified() < jarSrcFile.lastModified()) {
+            createTestJarFile(jarSrcFile, jarFile);
+        } else {
+            System.out.println("Already up-to-date: " + jarFile);
+        }
+
+        String noAppClasses[] = TestCommon.list("");
+        String appClasses[] = TestCommon.list("UnverifiableBase",
+                                              "UnverifiableIntf",
+                                              "UnverifiableIntfSub",
+                                              "VerifierTestA",
+                                              "VerifierTestB",
+                                              "VerifierTestC",
+                                              "VerifierTestD",
+                                              "VerifierTestE",
+                                              "VerifierTest0");
+
+
+        switch (subCaseId) {
+        case "0":         testset_0(jar, noAppClasses, appClasses);                 return;
+        case "1A":        testset_1(jar, noAppClasses, appClasses, Testset1Part.A); return;
+        case "1B":        testset_1(jar, noAppClasses, appClasses, Testset1Part.B); return;
+        case "2":         testset_2(jarName_greet, jarName_hi);                   return;
+        default:
+            throw new RuntimeException("Unknown option: " + subCaseId);
+        }
+    }
+
+    static void testset_0(String jar, String[] noAppClasses, String[] appClasses) throws Exception {
+        // Dumping should fail if the IgnoreUnverifiableClassesDuringDump
+        // option is not enabled.
+        OutputAnalyzer output = TestCommon.dump(jar, appClasses,
+                            "-XX:+UnlockDiagnosticVMOptions",
+                            "-XX:-IgnoreUnverifiableClassesDuringDump");
+        output.shouldContain("Please remove the unverifiable classes");
+        output.shouldHaveExitValue(1);
+
+        // By default, bad classes should be ignored during dumping.
+        TestCommon.testDump(jar, appClasses);
+    }
+
+    static void testset_1(String jar, String[] noAppClasses, String[] appClasses, Testset1Part part)
+        throws Exception
+    {
+        String config[][] = {
+            // {dump_list, dumptime_verification_setting,
+            //  runtime_verification_setting, runtime_output},
+
+            // Dump app/ext with -Xverify:remote
+            {"app",   VFY_REMOTE, VFY_REMOTE, VFY_ERR},
+            {"app",   VFY_REMOTE, VFY_ALL,    MAP_FAIL},
+            {"app",   VFY_REMOTE, VFY_NONE,   ERR },
+            // Dump app/ext with -Xverify:all
+            {"app",   VFY_ALL,    VFY_REMOTE, VFY_ERR },
+            {"app",   VFY_ALL,    VFY_ALL,    VFY_ERR },
+            {"app",   VFY_ALL,    VFY_NONE,   ERR },
+            // Dump app/ext with -Xverify:none
+            {"app",   VFY_NONE,   VFY_REMOTE, MAP_FAIL},
+            {"app",   VFY_NONE,   VFY_ALL,    MAP_FAIL},
+            {"app",   VFY_NONE,   VFY_NONE,   ERR },
+            // Dump sys only with -Xverify:remote
+            {"noApp", VFY_REMOTE, VFY_REMOTE, VFY_ERR},
+            {"noApp", VFY_REMOTE, VFY_ALL,    VFY_ERR},
+            {"noApp", VFY_REMOTE, VFY_NONE,   ERR},
+            // Dump sys only with -Xverify:all
+            {"noApp", VFY_ALL, VFY_REMOTE,    VFY_ERR},
+            {"noApp", VFY_ALL, VFY_ALL,       VFY_ERR},
+            {"noApp", VFY_ALL, VFY_NONE,      ERR},
+            // Dump sys only with -Xverify:none
+            {"noApp", VFY_NONE, VFY_REMOTE,   VFY_ERR},
+            {"noApp", VFY_NONE, VFY_ALL,      VFY_ERR},
+            {"noApp", VFY_NONE, VFY_NONE,     ERR},
+        };
+
+        int loop_start, loop_stop;
+
+        // Further break down testset_1 into two parts (to be invoked from VerifierTest_1A.java
+        // and VerifierTest_1B.java) to improve parallel test execution time.
+        switch (part) {
+        case A:
+            loop_start = 0;
+            loop_stop  = 9;
+            break;
+        case B:
+        default:
+            assert part == Testset1Part.B;
+            loop_start = 9;
+            loop_stop  = config.length;
+            break;
+        }
+
+        String prev_dump_setting = "";
+        for (int i = loop_start; i < loop_stop; i ++) {
+            String dump_list[] = config[i][0].equals("app") ? appClasses :
+                noAppClasses;
+            String dump_setting = config[i][1];
+            String runtime_setting = config[i][2];
+            String runtime_output = config[i][3];
+            System.out.println("Test case [" + i + "]: dumping " + config[i][0] +
+                               " with " + dump_setting +
+                               ", run with " + runtime_setting);
+            if (!dump_setting.equals(prev_dump_setting)) {
+                OutputAnalyzer dumpOutput = TestCommon.dump(
+                                                            jar, dump_list, dump_setting,
+                                                            // FIXME: the following options are for working around a GC
+                                                            // issue - assert failure when dumping archive with the -Xverify:all
+                                                            "-Xms256m",
+                                                            "-Xmx256m");
+            }
+            OutputAnalyzer runtimeOutput = TestCommon.execCommon(
+                                                                 "-cp", jar,
+                                                                 runtime_setting,
+                                                                 "VerifierTest0");
+            try {
+                runtimeOutput.shouldContain(runtime_output);
+            } catch (RuntimeException re) {
+                // Check if the failure is due to archive mapping failure.
+                // If not, a RuntimeException will be thrown.
+                runtimeOutput.shouldContain("Unable to use shared archive");
+            }
+            prev_dump_setting = dump_setting;
+        }
+    }
+
+    static void testset_2(String jarName_greet, String jarName_hi) throws Exception {
+        String appClasses[];
+        String jar;
+
+        // The following section is for testing the scenarios where
+        // the classes are verifiable during dump time.
+        appClasses = TestCommon.list("Hi",
+                                     "Greet",
+                                     "Hi$MyClass");
+        jar = TestCommon.getTestJar(jarName_hi + ".jar") + File.pathSeparator +
+            TestCommon.getTestJar(jarName_greet + ".jar");
+        final String PASS_RESULT = "Hi, how are you?";
+        String config2[][] = {
+            // {dump_list, dumptime_verification_setting,
+            //  runtime_verification_setting, runtime_output},
+
+            // Dump app/ext with -Xverify:remote
+            {"app",   VFY_REMOTE, VFY_REMOTE, PASS_RESULT},
+            {"app",   VFY_REMOTE, VFY_ALL,    MAP_FAIL},
+            {"app",   VFY_REMOTE, VFY_NONE,   PASS_RESULT },
+            // Dump app/ext with -Xverify:all
+            {"app",   VFY_ALL,    VFY_REMOTE, PASS_RESULT },
+            {"app",   VFY_ALL,    VFY_ALL,    PASS_RESULT },
+            {"app",   VFY_ALL,    VFY_NONE,   PASS_RESULT },
+            // Dump app/ext with -Xverify:none
+            {"app",   VFY_NONE,   VFY_REMOTE, MAP_FAIL},
+            {"app",   VFY_NONE,   VFY_ALL,    MAP_FAIL},
+            {"app",   VFY_NONE,   VFY_NONE,   PASS_RESULT },
+        };
+        for (int i = 0; i < config2.length; i ++) {
+            // config2[i][0] is always set to "app" in this test
+            String dump_setting = config2[i][1];
+            String runtime_setting = config2[i][2];
+            String runtime_output = config2[i][3];
+            System.out.println("Test case [" + i + "]: dumping " + config2[i][0] +
+                               " with " + dump_setting +
+                               ", run with " + runtime_setting);
+            OutputAnalyzer dumpOutput = TestCommon.dump(
+                                                        jar, appClasses, dump_setting,
+                                                        "-XX:+UnlockDiagnosticVMOptions",
+                                                        // FIXME: the following options are for working around a GC
+                                                        // issue - assert failure when dumping archive with the -Xverify:all
+                                                        "-Xms256m",
+                                                        "-Xmx256m");
+            OutputAnalyzer runtimeOutput = TestCommon.execCommon(
+                                                                 "-cp", jar,
+                                                                 runtime_setting,
+                                                                 "Hi");
+            try {
+                runtimeOutput.shouldContain(runtime_output);
+            } catch (RuntimeException re) {
+                // Check if the failure is due to archive mapping failure.
+                // If not, a RuntimeException will be thrown.
+                runtimeOutput.shouldContain("Unable to use shared archive");
+            }
+        }
+
+    }
+
+    static void createTestJarFile(File jarSrcFile, File jarFile) throws Exception {
+        jarFile.delete();
+        Files.copy(jarSrcFile.toPath(), jarFile.toPath());
+
+        File dir = new File(System.getProperty("test.classes", "."));
+        File outdir = new File(dir, "verifier_test_classes");
+        outdir.mkdir();
+
+        writeClassFile(new File(outdir, "UnverifiableBase.class"), makeUnverifiableBase());
+        writeClassFile(new File(outdir, "UnverifiableIntf.class"), makeUnverifiableIntf());
+
+        JarBuilder.update(jarFile.getPath(), outdir.getPath());
+    }
+
+    static void writeClassFile(File file, byte bytecodes[]) throws Exception {
+        try (FileOutputStream fos = new FileOutputStream(file)) {
+            fos.write(bytecodes);
+        }
+    }
+
+    // This was obtained using JDK8: java jdk.internal.org.objectweb.asm.util.ASMifier tmpclasses/UnverifiableBase.class
+    static byte[] makeUnverifiableBase() throws Exception {
+        ClassWriter cw = new ClassWriter(0);
+        FieldVisitor fv;
+        MethodVisitor mv;
+        AnnotationVisitor av0;
+
+        cw.visit(V1_6, ACC_SUPER, "UnverifiableBase", null, "java/lang/Object", null);
+        {
+            fv = cw.visitField(ACC_FINAL + ACC_STATIC, "x", "LVerifierTest;", null, null);
+            fv.visitEnd();
+        }
+        {
+            mv = cw.visitMethod(0, "<init>", "()V", null, null);
+            mv.visitCode();
+            mv.visitVarInsn(ALOAD, 0);
+            mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);
+            mv.visitInsn(RETURN);
+            mv.visitMaxs(1, 1);
+            mv.visitEnd();
+        }
+        {
+            mv = cw.visitMethod(ACC_STATIC, "<clinit>", "()V", null, null);
+            mv.visitCode();
+            //WAS mv.visitTypeInsn(NEW, "VerifierTest");
+            mv.visitTypeInsn(NEW, "java/lang/Object");
+            mv.visitInsn(DUP);
+            mv.visitMethodInsn(INVOKESPECIAL, "VerifierTest0", "<init>", "()V", false);
+            mv.visitFieldInsn(PUTSTATIC, "UnverifiableBase", "x", "LVerifierTest;");
+            mv.visitInsn(RETURN);
+            mv.visitMaxs(2, 0);
+            mv.visitEnd();
+        }
+        cw.visitEnd();
+
+        return cw.toByteArray();
+    }
+
+    // This was obtained using JDK8: java jdk.internal.org.objectweb.asm.util.ASMifier tmpclasses/UnverifiableIntf.class
+    static byte[] makeUnverifiableIntf() throws Exception {
+        ClassWriter cw = new ClassWriter(0);
+        FieldVisitor fv;
+        MethodVisitor mv;
+        AnnotationVisitor av0;
+
+        cw.visit(V1_6, ACC_ABSTRACT + ACC_INTERFACE, "UnverifiableIntf", null, "java/lang/Object", null);
+
+        {
+            fv = cw.visitField(ACC_PUBLIC + ACC_FINAL + ACC_STATIC, "x", "LVerifierTest0;", null, null);
+            fv.visitEnd();
+        }
+        {
+            mv = cw.visitMethod(ACC_STATIC, "<clinit>", "()V", null, null);
+            mv.visitCode();
+            //WAS mv.visitTypeInsn(NEW, "VerifierTest");
+            mv.visitTypeInsn(NEW, "java/lang/Object");
+            mv.visitInsn(DUP);
+            mv.visitMethodInsn(INVOKESPECIAL, "VerifierTest0", "<init>", "()V", false);
+            mv.visitFieldInsn(PUTSTATIC, "UnverifiableIntf", "x", "LVerifierTest0;");
+            mv.visitInsn(RETURN);
+            mv.visitMaxs(2, 0);
+            mv.visitEnd();
+        }
+        cw.visitEnd();
+
+        return cw.toByteArray();
+    }
+
+}
diff --git a/test/hotspot/jtreg/runtime/appcds/VerifierTest_0.java b/test/hotspot/jtreg/runtime/appcds/VerifierTest_0.java
new file mode 100644
index 00000000000..6ed2f6d35d5
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/VerifierTest_0.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+/*
+ * @test
+ * @summary Unverfiable app classes should not be archived.
+ * AppCDS does not support uncompressed oops
+ * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true)
+ * @library /test/lib
+ * @modules java.base/jdk.internal.misc
+ * @modules jdk.jartool/sun.tools.jar
+ *          java.base/jdk.internal.org.objectweb.asm
+ * @compile test-classes/Greet.java
+ * @compile test-classes/Hi.java
+ * @compile test-classes/VerifierTest0.java
+ * @run main/othervm/timeout=3600 VerifierTest 0
+ */
diff --git a/test/hotspot/jtreg/runtime/appcds/VerifierTest_1A.java b/test/hotspot/jtreg/runtime/appcds/VerifierTest_1A.java
new file mode 100644
index 00000000000..8a5430e2263
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/VerifierTest_1A.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+/*
+ * @test
+ * @summary Unverfiable app classes should not be archived.
+ * AppCDS does not support uncompressed oops
+ * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true)
+ * @library /test/lib
+ * @modules java.base/jdk.internal.misc
+ * @modules jdk.jartool/sun.tools.jar
+ *          java.base/jdk.internal.org.objectweb.asm
+ * @compile test-classes/Greet.java
+ * @compile test-classes/Hi.java
+ * @compile test-classes/VerifierTest0.java
+ * @run main/othervm/timeout=3600 VerifierTest 1A
+ */
diff --git a/test/hotspot/jtreg/runtime/appcds/VerifierTest_1B.java b/test/hotspot/jtreg/runtime/appcds/VerifierTest_1B.java
new file mode 100644
index 00000000000..7a721d13ba8
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/VerifierTest_1B.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+/*
+ * @test
+ * @summary Unverfiable app classes should not be archived.
+ * AppCDS does not support uncompressed oops
+ * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true)
+ * @library /test/lib
+ * @modules java.base/jdk.internal.misc
+ * @modules jdk.jartool/sun.tools.jar
+ *          java.base/jdk.internal.org.objectweb.asm
+ * @compile test-classes/Greet.java
+ * @compile test-classes/Hi.java
+ * @compile test-classes/VerifierTest0.java
+ * @run main/othervm/timeout=3600 VerifierTest 1B
+ */
diff --git a/test/hotspot/jtreg/runtime/appcds/VerifierTest_2.java b/test/hotspot/jtreg/runtime/appcds/VerifierTest_2.java
new file mode 100644
index 00000000000..c7bbe8793f7
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/VerifierTest_2.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+/*
+ * @test
+ * @summary Unverfiable app classes should not be archived.
+ * AppCDS does not support uncompressed oops
+ * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true)
+ * @library /test/lib
+ * @modules java.base/jdk.internal.misc
+ * @modules jdk.jartool/sun.tools.jar
+ *          java.base/jdk.internal.org.objectweb.asm
+ * @compile test-classes/Greet.java
+ * @compile test-classes/Hi.java
+ * @compile test-classes/VerifierTest0.java
+ * @run main/othervm/timeout=3600 VerifierTest 2
+ */
diff --git a/test/hotspot/jtreg/runtime/appcds/WideIloadTest.java b/test/hotspot/jtreg/runtime/appcds/WideIloadTest.java
new file mode 100644
index 00000000000..9323924449d
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/WideIloadTest.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+/**
+ * @test
+ * @summary Test 'iload_w' bytecode in shared class
+ * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true)
+ * @library /test/lib
+ * @modules java.base/jdk.internal.misc
+ *          java.management
+ *          jdk.jartool/sun.tools.jar
+ * @compile test-classes/Iloadw.jasm
+ * @compile test-classes/IloadwMain.java
+ * @run main WideIloadTest
+ */
+
+import jdk.test.lib.process.OutputAnalyzer;
+
+public class WideIloadTest {
+    public static void main(String args[]) throws Exception {
+        JarBuilder.build("iload_w", "Iloadw", "IloadwMain");
+        String appJar = TestCommon.getTestJar("iload_w.jar");
+        OutputAnalyzer dumpOutput = TestCommon.dump(appJar, TestCommon.list(
+                                        "Iloadw", "IloadwMain"));
+        TestCommon.checkDump(dumpOutput);
+        OutputAnalyzer execOutput = TestCommon.exec(appJar, "IloadwMain");
+        TestCommon.checkExec(execOutput, "Passed");
+    }
+}
diff --git a/test/hotspot/jtreg/runtime/appcds/WrongClasspath.java b/test/hotspot/jtreg/runtime/appcds/WrongClasspath.java
new file mode 100644
index 00000000000..d3069c33cd9
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/WrongClasspath.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+/*
+ * @test
+ * @summary classpath mismatch between dump time and execution time
+ * AppCDS does not support uncompressed oops
+ * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true)
+ * @library /test/lib
+ * @modules java.base/jdk.internal.misc
+ *          java.management
+ *          jdk.jartool/sun.tools.jar
+ * @compile test-classes/Hello.java
+ * @run main WrongClasspath
+ */
+
+import jdk.test.lib.process.OutputAnalyzer;
+
+public class WrongClasspath {
+
+  public static void main(String[] args) throws Exception {
+    String appJar = JarBuilder.getOrCreateHelloJar();
+
+    // Dump an archive with a specified JAR file in -classpath
+    TestCommon.testDump(appJar, TestCommon.list("Hello"));
+
+    // Then try to execute the archive without -classpath -- it should fail
+    OutputAnalyzer output = TestCommon.execCommon(
+        /* "-cp", appJar, */ // <- uncomment this and the execution should succeed
+        "Hello");
+    output.shouldContain("Unable to use shared archive");
+    output.shouldContain("shared class paths mismatch");
+    output.shouldHaveExitValue(1);
+  }
+}
diff --git a/test/hotspot/jtreg/runtime/appcds/XShareAutoWithChangedJar.java b/test/hotspot/jtreg/runtime/appcds/XShareAutoWithChangedJar.java
new file mode 100644
index 00000000000..d3c5c8ef50c
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/XShareAutoWithChangedJar.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+/*
+ * @test
+ * @summary Test -Xshare:auto for AppCDS
+ * AppCDS does not support uncompressed oops
+ * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true)
+ * @library /test/lib
+ * @modules java.base/jdk.internal.misc
+ *          java.management
+ *          jdk.jartool/sun.tools.jar
+ * @compile test-classes/Hello.java
+ * @run main XShareAutoWithChangedJar
+ */
+
+import jdk.test.lib.process.OutputAnalyzer;
+
+public class XShareAutoWithChangedJar {
+  public static void main(String[] args) throws Exception {
+    String appJar = JarBuilder.build("XShareAutoWithChangedJar", "Hello");
+
+    // 1. dump
+    OutputAnalyzer output = TestCommon.dump(appJar, TestCommon.list("Hello"));
+    TestCommon.checkDump(output);
+
+    // 2. change the jar
+    JarBuilder.build("XShareAutoWithChangedJar", "Hello");
+
+    // 3. exec
+    output = TestCommon.execAuto("-cp", appJar, "Hello");
+    output.shouldContain("Hello World");
+  }
+}
diff --git a/test/hotspot/jtreg/runtime/appcds/cacheObject/CheckCachedResolvedReferences.java b/test/hotspot/jtreg/runtime/appcds/cacheObject/CheckCachedResolvedReferences.java
new file mode 100644
index 00000000000..6bd96c2abfd
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/cacheObject/CheckCachedResolvedReferences.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+/*
+ * @test
+ * @summary Test resolved_references
+ * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true)
+ * @requires vm.cds.custom.loaders
+ * @requires (vm.gc=="null")
+ * @library /test/lib /test/hotspot/jtreg/runtime/appcds
+ * @modules java.base/jdk.internal.misc
+ * @modules java.management
+ *          jdk.jartool/sun.tools.jar
+ * @build sun.hotspot.WhiteBox
+ * @compile CheckCachedResolvedReferencesApp.java
+ * @compile ../test-classes/Hello.java
+ * @run main ClassFileInstaller -jar app.jar CheckCachedResolvedReferencesApp
+ * @run main ClassFileInstaller -jar hello.jar Hello
+ * @run main ClassFileInstaller -jar WhiteBox.jar sun.hotspot.WhiteBox
+ * @run main CheckCachedResolvedReferences
+ */
+
+import jdk.test.lib.process.OutputAnalyzer;
+import sun.hotspot.WhiteBox;
+
+public class CheckCachedResolvedReferences {
+    public static void main(String[] args) throws Exception {
+        String wbJar = ClassFileInstaller.getJarPath("WhiteBox.jar");
+        String use_whitebox_jar = "-Xbootclasspath/a:" + wbJar;
+        String appJar = ClassFileInstaller.getJarPath("app.jar");
+        String helloJarPath = ClassFileInstaller.getJarPath("hello.jar");
+
+        String classlist[] = new String[] {
+            "CheckCachedResolvedReferencesApp",            // built-in app loader
+            "java/lang/Object id: 1",                      // boot loader
+            "Hello id: 2 super: 1 source: " + helloJarPath // custom loader
+        };
+
+        TestCommon.testDump(appJar, classlist, use_whitebox_jar);
+        OutputAnalyzer output = TestCommon.exec(appJar, use_whitebox_jar,
+                                                "-XX:+UnlockDiagnosticVMOptions",
+                                                "-XX:+WhiteBoxAPI",
+                                                "CheckCachedResolvedReferencesApp",
+                                                helloJarPath);
+        TestCommon.checkExec(output);
+    }
+}
diff --git a/test/hotspot/jtreg/runtime/appcds/cacheObject/CheckCachedResolvedReferencesApp.java b/test/hotspot/jtreg/runtime/appcds/cacheObject/CheckCachedResolvedReferencesApp.java
new file mode 100644
index 00000000000..0db69dd3391
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/cacheObject/CheckCachedResolvedReferencesApp.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+import java.io.File;
+import java.net.URL;
+import java.net.URLClassLoader;
+import sun.hotspot.WhiteBox;
+
+public class CheckCachedResolvedReferencesApp {
+    public static void main(String args[]) throws Exception {
+        String path = args[0];
+        URL url = new File(path).toURI().toURL();
+        URL[] urls = new URL[] {url};
+
+        URLClassLoader loader = new URLClassLoader(urls);
+        Class hello = loader.loadClass("Hello");
+        System.out.println("Loaded " + hello + " from " + url + " using loader " + loader);
+
+        WhiteBox wb = WhiteBox.getWhiteBox();
+
+        if (!wb.areOpenArchiveHeapObjectsMapped()) {
+            System.out.println("Archived open_archive_heap objects are not mapped.");
+            System.out.println("This may happen during normal operation. Test Skipped.");
+            return;
+        }
+
+        // CheckCachedResolvedReferencesApp is shared class and loaded by the
+        // AppClassLoader. It should have cached resolved_references.
+        if (wb.isSharedClass(CheckCachedResolvedReferencesApp.class)) {
+            Object refs1 = wb.getResolvedReferences(CheckCachedResolvedReferencesApp.class);
+            if (refs1 != null && wb.isShared(refs1)) {
+                System.out.println(
+                    "resolved references from CheckCachedResolvedReferencesApp is cached");
+            } else {
+                throw new RuntimeException(
+                    "FAILED. CheckCachedResolvedReferencesApp has no cached resolved references");
+            }
+        }
+
+        // Hello is shared class and loaded by the 'loader' defined in current app.
+        // It should not have cached resolved_references.
+        if (wb.isSharedClass(hello)) {
+            Object refs2 = wb.getResolvedReferences(hello);
+            if (refs2 != null) {
+                if (!wb.isShared(refs2)) {
+                    System.out.println("resolved references from hello is not cached");
+                } else {
+                    throw new RuntimeException(
+                        "FAILED. Hello has unexpected cached resolved references");
+                }
+            } else {
+                throw new RuntimeException("FAILED. Hello has no resolved references");
+            }
+        }
+    }
+}
diff --git a/test/hotspot/jtreg/runtime/appcds/cacheObject/DumpTimeVerifyFailure.config.txt b/test/hotspot/jtreg/runtime/appcds/cacheObject/DumpTimeVerifyFailure.config.txt
new file mode 100644
index 00000000000..fb6912f2c3c
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/cacheObject/DumpTimeVerifyFailure.config.txt
@@ -0,0 +1,3 @@
+VERSION: 1.0
+@SECTION: String
+26: shared_string_from_MyInner
diff --git a/test/hotspot/jtreg/runtime/appcds/cacheObject/DumpTimeVerifyFailure.java b/test/hotspot/jtreg/runtime/appcds/cacheObject/DumpTimeVerifyFailure.java
new file mode 100644
index 00000000000..12be0931edf
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/cacheObject/DumpTimeVerifyFailure.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+/*
+ * @test
+ * @summary Dump time should not crash if any class with shared strings fails verification due to missing dependencies.
+ * @bug 8186789
+ * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true)
+ * @requires (sun.arch.data.model != "32") & (os.family != "windows")
+ * @requires (vm.gc=="null")
+ * @library /test/lib /test/hotspot/jtreg/runtime/appcds
+ * @modules java.base/jdk.internal.misc
+ * @modules java.management
+ *          jdk.jartool/sun.tools.jar
+ * @compile MyOuter.java MyException.java
+ * @run main DumpTimeVerifyFailure
+ */
+
+import jdk.test.lib.process.OutputAnalyzer;
+
+public class DumpTimeVerifyFailure {
+    public static void main(String[] args) throws Exception {
+        // App classes (see MyOuter.java):
+        //   MyOuter
+        //   MyInnder$MyOuter extends MyOuter
+        //   MyException
+        //
+        // MyOuter$MyInner.test() throws MyException.
+        // The missingMyException.jar file only includes MyOuter and
+        // MyOuter$MyInner classes, but not the MyException class.
+        // At dump time, MyOuter and MyOuter$MyInner classes fail
+        // verification due to missing MyException class.
+        String[] ARCHIVE_CLASSES = {"MyOuter", "MyOuter$MyInner"};
+        String appJar = JarBuilder.build("missingMyException", ARCHIVE_CLASSES);
+
+        OutputAnalyzer dumpOutput = TestCommon.dump(
+                appJar, ARCHIVE_CLASSES,
+                "-Xlog:verification",
+                "-XX:SharedArchiveConfigFile=" + TestCommon.getSourceFile("DumpTimeVerifyFailure.config.txt"));
+        TestCommon.checkDump(dumpOutput, "Loading classes to share");
+    }
+}
diff --git a/test/hotspot/jtreg/runtime/appcds/cacheObject/GCStress.config.txt b/test/hotspot/jtreg/runtime/appcds/cacheObject/GCStress.config.txt
new file mode 100644
index 00000000000..af3e33bb977
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/cacheObject/GCStress.config.txt
@@ -0,0 +1,3 @@
+VERSION: 1.0
+@SECTION: String
+25: GCStressApp_shared_string
diff --git a/test/hotspot/jtreg/runtime/appcds/cacheObject/GCStressApp.java b/test/hotspot/jtreg/runtime/appcds/cacheObject/GCStressApp.java
new file mode 100644
index 00000000000..636da040d6b
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/cacheObject/GCStressApp.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+import java.io.*;
+import java.util.*;
+import sun.hotspot.WhiteBox;
+
+// All strings in archived classes are shared
+public class GCStressApp {
+    static WhiteBox wb = WhiteBox.getWhiteBox();
+    static int[] arr;
+
+    static String get_shared_string() {
+        String shared_str = "GCStressApp_shared_string";
+        return shared_str;
+    }
+
+    static String get_shared_string1() {
+        String shared_str1 = "GCStressApp_shared_string1";
+        return shared_str1;
+    }
+
+    static void allocAlot() {
+        try {
+            Random random = new Random();
+            for (int i = 0; i < 1024 * 1024; i++) {
+                int len = random.nextInt(10000);
+                arr = new int[len];
+            }
+        } catch (java.lang.OutOfMemoryError e) { }
+    }
+
+    static void runGC() {
+        wb.fullGC();
+    }
+
+    public static void main(String args[]) throws Exception {
+        if (!wb.isSharedClass(GCStressApp.class)) {
+           System.out.println("GCStressApp is not shared. Possibly there was a mapping failure.");
+           return;
+        }
+
+        if (wb.areSharedStringsIgnored()) {
+          System.out.println("Shared strings are ignored.");
+          return;
+        }
+
+        Object refs = wb.getResolvedReferences(GCStressApp.class);
+        if (wb.isShared(refs)) {
+            String shared_str = get_shared_string();
+            String shared_str1 = get_shared_string1();
+
+            if (!wb.isShared(shared_str)) {
+                throw new RuntimeException("FAILED. GCStressApp_shared_string is not shared");
+            }
+
+            if (!wb.isShared(shared_str1)) {
+                throw new RuntimeException("FAILED. GCStressApp_shared_string1 is not shared");
+            }
+
+            allocAlot();
+            runGC();
+            runGC();
+            runGC();
+
+            System.out.println("Passed");
+        } else {
+            System.out.println(
+                "No cached resolved references. Open archive heap data is not used.");
+        }
+    }
+}
diff --git a/test/hotspot/jtreg/runtime/appcds/cacheObject/GCStressTest.java b/test/hotspot/jtreg/runtime/appcds/cacheObject/GCStressTest.java
new file mode 100644
index 00000000000..591de9b3e67
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/cacheObject/GCStressTest.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+/*
+ * @test
+ * @summary
+ * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true)
+ * @requires (sun.arch.data.model != "32") & (os.family != "windows")
+ * @requires (vm.gc=="null")
+ * @library /test/lib /test/hotspot/jtreg/runtime/appcds
+ * @modules java.base/jdk.internal.misc
+ * @modules java.management
+ *          jdk.jartool/sun.tools.jar
+ * @build sun.hotspot.WhiteBox
+ * @compile GCStressApp.java
+ * @run main ClassFileInstaller -jar gcstress.jar GCStressApp
+ * @run main ClassFileInstaller -jar WhiteBox.jar sun.hotspot.WhiteBox
+ * @run main GCStressTest
+ */
+
+import jdk.test.lib.process.OutputAnalyzer;
+
+public class GCStressTest {
+    public static void main(String[] args) throws Exception {
+        String wbJar = ClassFileInstaller.getJarPath("WhiteBox.jar");
+        String use_whitebox_jar = "-Xbootclasspath/a:" + wbJar;
+        String appJar = ClassFileInstaller.getJarPath("gcstress.jar");
+        String appClasses[] = TestCommon.list("GCStressApp");
+
+        OutputAnalyzer output = TestCommon.dump(appJar, appClasses,
+                                                use_whitebox_jar,
+                                                "-Xms20M", "-Xmx20M");
+        output = TestCommon.exec(appJar, use_whitebox_jar,
+                                 "-Xlog:cds=info",
+                                 "-Xms20M", "-Xmx20M",
+                                 "-XX:+UnlockDiagnosticVMOptions",
+                                 "-XX:+WhiteBoxAPI","GCStressApp");
+        TestCommon.checkExec(output);
+    }
+}
diff --git a/test/hotspot/jtreg/runtime/appcds/cacheObject/InstrumentationAgent.mf b/test/hotspot/jtreg/runtime/appcds/cacheObject/InstrumentationAgent.mf
new file mode 100644
index 00000000000..58dcf797de6
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/cacheObject/InstrumentationAgent.mf
@@ -0,0 +1,5 @@
+Manifest-Version: 1.0
+Premain-Class: InstrumentationRegisterClassFileTransformer
+Agent-Class: InstrumentationRegisterClassFileTransformer
+Can-Retransform-Classes: true
+Can-Redefine-Classes: true
diff --git a/src/hotspot/share/classfile/vmSymbols_ext.hpp b/test/hotspot/jtreg/runtime/appcds/cacheObject/MyException.java
similarity index 79%
rename from src/hotspot/share/classfile/vmSymbols_ext.hpp
rename to test/hotspot/jtreg/runtime/appcds/cacheObject/MyException.java
index 4b68d8f234b..39a464b4f00 100644
--- a/src/hotspot/share/classfile/vmSymbols_ext.hpp
+++ b/test/hotspot/jtreg/runtime/appcds/cacheObject/MyException.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2017, 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
@@ -21,11 +21,8 @@
  * questions.
  *
  */
-
-#ifndef SHARE_VM_CLASSFILE_VMSYMBOLS_EXT_HPP
-#define SHARE_VM_CLASSFILE_VMSYMBOLS_EXT_HPP
-
-#define VM_SYMBOLS_DO_EXT(template, do_alias)
-
-#endif // SHARE_VM_CLASSFILE_VMSYMBOLS_EXT_HPP
-
+public class MyException extends Exception {
+    public MyException(String msg) {
+        super(msg);
+    }
+}
diff --git a/test/hotspot/jtreg/runtime/appcds/cacheObject/MyOuter.java b/test/hotspot/jtreg/runtime/appcds/cacheObject/MyOuter.java
new file mode 100644
index 00000000000..8773772ac68
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/cacheObject/MyOuter.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2017, 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.
+ *
+ */
+public class MyOuter {
+   public void exp() throws MyException {
+       throw new MyException("MyOuter exception");
+   }
+
+   public void test() throws Exception {
+       System.out.println("MyOuter");
+       try {
+          exp();
+       } catch (MyException e) {
+       }
+   }
+
+   public static final class MyInner extends MyOuter {
+       static String myString = "shared_string_from_MyInner";
+       public void test() {
+           System.out.println("MyInner");
+       }
+   }
+}
diff --git a/test/hotspot/jtreg/runtime/appcds/cacheObject/OpenArchiveRegion.java b/test/hotspot/jtreg/runtime/appcds/cacheObject/OpenArchiveRegion.java
new file mode 100644
index 00000000000..df1b348913f
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/cacheObject/OpenArchiveRegion.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+/*
+ * @test
+ * @summary Test open archive heap regions
+ * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true)
+ * @requires (sun.arch.data.model != "32") & (os.family != "windows")
+ * @requires (vm.gc=="null")
+ * @library /test/lib /test/hotspot/jtreg/runtime/appcds
+ * @modules java.base/jdk.internal.misc
+ * @modules java.management
+ *          jdk.jartool/sun.tools.jar
+ * @compile ../test-classes/Hello.java
+ * @run main OpenArchiveRegion
+ */
+
+import jdk.test.lib.process.OutputAnalyzer;
+
+public class OpenArchiveRegion {
+    public static void main(String[] args) throws Exception {
+        JarBuilder.getOrCreateHelloJar();
+        String appJar = TestCommon.getTestJar("hello.jar");
+        String appClasses[] = TestCommon.list("Hello");
+
+        // Dump with open archive heap region, requires G1 GC
+        OutputAnalyzer output = TestCommon.dump(appJar, appClasses);
+        TestCommon.checkDump(output, "oa0 space:");
+        output.shouldNotContain("oa0 space:         0 [");
+        output = TestCommon.exec(appJar, "Hello");
+        TestCommon.checkExec(output, "Hello World");
+        output = TestCommon.exec(appJar, "-XX:+UseSerialGC", "Hello");
+        TestCommon.checkExec(output, "Hello World");
+
+        // Dump with open archive heap region disabled when G1 GC is not in use
+        output = TestCommon.dump(appJar, appClasses, "-XX:+UseParallelGC");
+        TestCommon.checkDump(output);
+        output.shouldNotContain("oa0 space:");
+        output = TestCommon.exec(appJar, "Hello");
+        TestCommon.checkExec(output, "Hello World");
+    }
+}
diff --git a/test/hotspot/jtreg/runtime/appcds/cacheObject/RangeNotWithinHeap.java b/test/hotspot/jtreg/runtime/appcds/cacheObject/RangeNotWithinHeap.java
new file mode 100644
index 00000000000..13242f72f0b
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/cacheObject/RangeNotWithinHeap.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+/*
+ * @test
+ * @summary Shared classes can still be used when archived heap regions cannot be
+ *          mapped due to out of range, and -Xshare:on should not fail. Test on
+ *          linux 64-bit only since the HeapBaseMinAddress value is platform specific.
+ *          The value used in the test may cause different behavior on other platforms.
+ * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true)
+ * @requires (os.family == "linux") & (os.arch == "amd64") & (sun.arch.data.model == "64")
+ * @requires (vm.gc=="null")
+ * @library /test/lib /test/hotspot/jtreg/runtime/appcds
+ * @modules java.base/jdk.internal.misc
+ * @modules java.management
+ *          jdk.jartool/sun.tools.jar
+ * @compile ../test-classes/Hello.java
+ * @run main RangeNotWithinHeap
+ */
+
+import jdk.test.lib.process.OutputAnalyzer;
+
+public class RangeNotWithinHeap {
+    public static void main(String[] args) throws Exception {
+        JarBuilder.getOrCreateHelloJar();
+        String appJar = TestCommon.getTestJar("hello.jar");
+        String appClasses[] = TestCommon.list("Hello");
+
+        OutputAnalyzer output = TestCommon.dump(appJar, appClasses,
+                    "-XX:HeapBaseMinAddress=0x600000000", "-Xmx6G", "-Xlog:gc+heap=trace");
+        TestCommon.checkDump(output, "oa0 space:");
+
+        // Force archive region out of runtime java heap
+        output = TestCommon.exec(appJar, "Hello");
+        TestCommon.checkExec(output, "Hello World");
+        output = TestCommon.exec(appJar,
+                    "-XX:HeapBaseMinAddress=0x600000000", "-Xmx2G", "-Xlog:gc+heap=trace,cds", "Hello");
+        TestCommon.checkExec(output, "Hello World");
+        try {
+            output.shouldContain(
+                "UseSharedSpaces: Unable to allocate region, range is not within java heap.");
+        } catch (Exception e) {
+            // In rare case the heap data is not used.
+            if (output.getOutput().contains("Cached heap data from the CDS archive is being ignored")) {
+                return;
+            }
+            // Check for common shared class data mapping failures.
+            TestCommon.checkCommonExecExceptions(output, e);
+        }
+    }
+}
diff --git a/test/hotspot/jtreg/runtime/appcds/cacheObject/RedefineClassApp.java b/test/hotspot/jtreg/runtime/appcds/cacheObject/RedefineClassApp.java
new file mode 100644
index 00000000000..fa482e70a5e
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/cacheObject/RedefineClassApp.java
@@ -0,0 +1,149 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+import java.lang.instrument.ClassDefinition;
+import java.lang.instrument.Instrumentation;
+import java.lang.instrument.UnmodifiableClassException;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.io.File;
+import java.security.CodeSigner;
+import java.security.CodeSource;
+import java.security.ProtectionDomain;
+import sun.hotspot.WhiteBox;
+
+public class RedefineClassApp {
+    static WhiteBox wb = WhiteBox.getWhiteBox();
+
+    public static interface Intf {            // Loaded from Boot class loader (-Xbootclasspath/a).
+        public String get();
+    }
+    public static class Bar implements Intf { // Loaded from Boot class loader.
+        public String get() {
+            return "buzz";
+        }
+    }
+    public static class Foo implements Intf { // Loaded from AppClassLoader
+        public String get() {
+            return "buzz";
+        }
+    }
+
+    static int numTests = 0;
+    static int failed = 0;
+    static Instrumentation instrumentation;
+
+    public static void main(String args[]) throws Throwable {
+        if (wb.areSharedStringsIgnored()) {
+          System.out.println("Shared strings are ignored.");
+          return;
+        }
+
+        File bootJar = new File(args[0]);
+        File appJar  = new File(args[1]);
+
+        instrumentation = InstrumentationRegisterClassFileTransformer.getInstrumentation();
+        System.out.println("INFO: instrumentation = " + instrumentation);
+
+        testBootstrapCDS("Bootstrap Loader", bootJar);
+        testAppCDSv1("Application Loader", appJar);
+
+        if (failed > 0) {
+            throw new RuntimeException("FINAL RESULT: " + failed + " out of " + numTests + " test case(s) have failed");
+        } else {
+            System.out.println("FINAL RESULT: All " + numTests + " test case(s) have passed!");
+        }
+
+        // Full GC. The cached objects in adjustable archive heap regions are
+        // scanned. The archive regions are verified. No error should be
+        // reported.
+        wb.fullGC();
+    }
+
+    static void testBootstrapCDS(String group, File jar) throws Throwable {
+        doTest(group, new Bar(), jar);
+    }
+
+    static void testAppCDSv1(String group, File jar) throws Throwable {
+        doTest(group, new Foo(), jar);
+    }
+
+    static void doTest(String group, Intf object, File jar) throws Throwable {
+        numTests ++;
+
+        Class klass = object.getClass();
+        System.out.println();
+        System.out.println("++++++++++++++++++++++++++");
+        System.out.println("Test group: " + group);
+        System.out.println("Testing with classloader = " + klass.getClassLoader());
+        System.out.println("Testing with class       = " + klass);
+        System.out.println("Test is shared           = " + wb.isSharedClass(klass));
+        System.out.println("++++++++++++++++++++++++++");
+
+        // Call get() before redefine. All strings in archived classes are shared.
+        String res = object.get();
+        System.out.println("get() returns " + res);
+        if (res.equals("buzz") && wb.isShared(res)) {
+            System.out.println("get() returns " + res + ", string is shared");
+        } else {
+            if (!res.equals("buzz")) {
+                System.out.println("FAILED. buzz is expected but got " + res);
+            } else {
+                System.out.println("FAILED. " + res + " is not shared");
+            }
+            failed ++;
+            return;
+        }
+        res = null; // release the local reference to the string
+
+        // Run GC
+        System.gc();
+        System.gc();
+        System.gc();
+
+        // Redefine the shared class
+        byte[] buff = Util.getClassFileFromJar(jar, klass.getName());
+        Util.replace(buff, "buzz", "huzz");
+        String f = "(failed)";
+        try {
+            instrumentation.redefineClasses(new ClassDefinition(klass, buff));
+            f = object.get();
+        } catch (UnmodifiableClassException|UnsupportedOperationException e) {
+            e.printStackTrace();
+        }
+        if (f.equals("huzz")) {
+            System.out.println("PASSED: object.get() after redefinition returns " + f);
+        } else {
+            System.out.println("FAILED: object.get() after redefinition returns " + f);
+            failed ++;
+        }
+
+        // Run GC. Should not crash.
+        System.gc();
+        System.gc();
+        System.gc();
+
+        System.out.println("++++++++++++++++++++++++++++++++++++++++++++++++ (done)\n\n");
+    }
+}
diff --git a/test/hotspot/jtreg/runtime/appcds/cacheObject/RedefineClassTest.java b/test/hotspot/jtreg/runtime/appcds/cacheObject/RedefineClassTest.java
new file mode 100644
index 00000000000..bfcd82f5742
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/cacheObject/RedefineClassTest.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+/*
+ * @test
+ * @summary Redefine shared class. GC should not cause crash with cached resolved_references.
+ * @library /test/lib /test/hotspot/jtreg/runtime/appcds /test/hotspot/jtreg/runtime/appcds/test-classes /test/hotspot/jtreg/runtime/appcds/jvmti
+ * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true)
+ * @requires vm.gc.G1
+ * @requires vm.flavor != "minimal"
+ * @requires (sun.arch.data.model != "32") & (os.family != "windows")
+ * @modules java.base/jdk.internal.misc
+ *          jdk.jartool/sun.tools.jar
+ *          java.management
+ * @build sun.hotspot.WhiteBox
+ *        RedefineClassApp
+ *        InstrumentationClassFileTransformer
+ *        InstrumentationRegisterClassFileTransformer
+ * @run main/othervm RedefineClassTest
+ */
+
+import com.sun.tools.attach.VirtualMachine;
+import com.sun.tools.attach.VirtualMachineDescriptor;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.util.List;
+import jdk.test.lib.Asserts;
+import jdk.test.lib.cds.CDSOptions;
+import jdk.test.lib.process.OutputAnalyzer;
+import jdk.test.lib.process.ProcessTools;
+
+public class RedefineClassTest {
+    public static String bootClasses[] = {
+        "RedefineClassApp$Intf",
+        "RedefineClassApp$Bar",
+        "sun.hotspot.WhiteBox",
+    };
+    public static String appClasses[] = {
+        "RedefineClassApp",
+        "RedefineClassApp$Foo",
+    };
+    public static String sharedClasses[] = TestCommon.concat(bootClasses, appClasses);
+
+    public static String agentClasses[] = {
+        "InstrumentationClassFileTransformer",
+        "InstrumentationRegisterClassFileTransformer",
+        "Util",
+    };
+
+    public static void main(String[] args) throws Throwable {
+        runTest();
+    }
+
+    public static void runTest() throws Throwable {
+        String bootJar =
+            ClassFileInstaller.writeJar("RedefineClassBoot.jar", bootClasses);
+        String appJar =
+            ClassFileInstaller.writeJar("RedefineClassApp.jar", appClasses);
+        String agentJar =
+            ClassFileInstaller.writeJar("InstrumentationAgent.jar",
+                                        ClassFileInstaller.Manifest.fromSourceFile("InstrumentationAgent.mf"),
+                                        agentClasses);
+
+        String bootCP = "-Xbootclasspath/a:" + bootJar;
+
+        String agentCmdArg;
+        agentCmdArg = "-javaagent:" + agentJar;
+
+        TestCommon.testDump(appJar, sharedClasses, bootCP, "-Xlog:gc+region=trace");
+
+        OutputAnalyzer out = TestCommon.execAuto("-cp", appJar,
+                bootCP,
+                "-XX:+UnlockDiagnosticVMOptions",
+                "-XX:+WhiteBoxAPI",
+                "-Xlog:gc+region=trace,cds=info",
+                agentCmdArg,
+               "RedefineClassApp", bootJar, appJar);
+        out.reportDiagnosticSummary();
+
+        CDSOptions opts = (new CDSOptions()).setXShareMode("auto");
+        TestCommon.checkExec(out, opts);
+    }
+}
+
diff --git a/test/hotspot/jtreg/runtime/appcds/customLoader/ClassListFormatA.java b/test/hotspot/jtreg/runtime/appcds/customLoader/ClassListFormatA.java
new file mode 100644
index 00000000000..65c4d846499
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/customLoader/ClassListFormatA.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+/*
+ * @test
+ * @summary Tests the format checking of class list format.
+ *
+ * (NOTE: AppCDS does not support uncompressed oops)
+ * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true)
+ * @requires vm.cds.custom.loaders
+ * @library /test/lib /test/hotspot/jtreg/runtime/appcds
+ * @modules java.base/jdk.internal.misc
+ *          java.management
+ *          jdk.jartool/sun.tools.jar
+ * @compile test-classes/Hello.java test-classes/CustomLoadee.java test-classes/CustomLoadee2.java
+ *          test-classes/CustomInterface2_ia.java test-classes/CustomInterface2_ib.java
+ * @run main ClassListFormatA
+ */
+
+public class ClassListFormatA extends ClassListFormatBase {
+    static {
+        // Uncomment the following line to run only one of the test cases
+        // ClassListFormatBase.RUN_ONLY_TEST = "TESTCASE A1";
+    }
+
+    public static void main(String[] args) throws Throwable {
+        String appJar = JarBuilder.getOrCreateHelloJar();
+        String customJarPath = JarBuilder.build("ClassListFormatA", "CustomLoadee",
+                                            "CustomLoadee2", "CustomInterface2_ia", "CustomInterface2_ib");
+        //----------------------------------------------------------------------
+        // TESTGROUP A: general bad input
+        //----------------------------------------------------------------------
+        dumpShouldFail(
+            "TESTCASE A1: bad input - interface: instead of interfaces:",
+            appJar, classlist(
+                "Hello",
+                "java/lang/Object id: 1",
+                "CustomLoadee interface: 1"
+            ),
+            "Unknown input:");
+
+        dumpShouldFail(
+            "TESTCASE A2: bad input - negative IDs not allowed",
+            appJar, classlist(
+                "Hello",
+                "java/lang/Object id: -1"
+            ),
+            "Error: negative integers not allowed");
+
+        dumpShouldFail(
+            "TESTCASE A3: bad input - bad ID (not an integer)",
+            appJar, classlist(
+                "Hello",
+                "java/lang/Object id: xyz"
+            ),
+            "Error: expected integer");
+
+        if (false) {
+              // FIXME - classFileParser.cpp needs fixing.
+            dumpShouldFail(
+                "TESTCASE A4: bad input - bad ID (integer too big)",
+                appJar, classlist(
+                    "Hello",
+                    "java/lang/Object id: 2147483648" // <- this is 0x80000000
+                ),
+                "Error: expected integer");
+
+              // FIXME
+            dumpShouldFail(
+                "TESTCASE A5: bad input - bad ID (integer too big)",
+                appJar, classlist(
+                    "Hello",
+                    "java/lang/Object id: 21474836489" // bigger than 32-bit!
+                ),
+                "Error: expected integer");
+        }
+
+        // Good input:
+        dumpShouldPass(
+            "TESTCASE A6: extraneous spaces, tab characters and trailing new line characters",
+            appJar, classlist(
+                "Hello   ",                   // trailing spaces
+                "java/lang/Object\tid:\t1",   // \t instead of ' '
+                "CustomLoadee id: 2 super: 1 source: " + customJarPath,
+                "CustomInterface2_ia id: 3 super: 1 source: " + customJarPath + " ",
+                "CustomInterface2_ib id: 4 super: 1 source: " + customJarPath + "\t\t\r" ,
+                "CustomLoadee2 id: 5 super: 1 interfaces: 3 4 source: " + customJarPath      // preceding spaces
+            ));
+
+        int _max_allowed_line = 4096; // Must match ClassListParser::_max_allowed_line in C code.
+        int _line_buf_extra = 10;     // Must match ClassListParser::_line_buf_extra in C code.
+        StringBuffer sbuf = new StringBuffer();
+        for (int i=0; i<_max_allowed_line+1; i++) {
+          sbuf.append("x");
+        }
+
+        dumpShouldFail(
+            "TESTCASE A7: bad input - line too long",
+            appJar, classlist(
+                sbuf.toString()
+            ),
+            "input line too long (must be no longer than " + _max_allowed_line + " chars");
+
+        for (int i=0; i<_line_buf_extra + 1000; i++) {
+          sbuf.append("X");
+        }
+
+        dumpShouldFail(
+            "TESTCASE A8: bad input - line too long: try to overflow C buffer",
+            appJar, classlist(
+                sbuf.toString()
+            ),
+            "input line too long (must be no longer than " + _max_allowed_line + " chars");
+    }
+}
+
diff --git a/test/hotspot/jtreg/runtime/appcds/customLoader/ClassListFormatB.java b/test/hotspot/jtreg/runtime/appcds/customLoader/ClassListFormatB.java
new file mode 100644
index 00000000000..6ae1488d125
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/customLoader/ClassListFormatB.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+/*
+ * @test
+ * @summary Tests the format checking of hotspot/src/closed/share/vm/classfile/classListParser.cpp.
+ *
+ * (NOTE: AppCDS does not support uncompressed oops)
+ * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true)
+ * @requires vm.cds.custom.loaders
+ * @library /test/lib /test/hotspot/jtreg/runtime/appcds
+ * @modules java.base/jdk.internal.misc
+ *          java.management
+ *          jdk.jartool/sun.tools.jar
+ * @compile test-classes/Hello.java test-classes/CustomLoadee.java test-classes/CustomLoadee2.java
+ *          test-classes/CustomInterface2_ia.java test-classes/CustomInterface2_ib.java
+ * @run main ClassListFormatB
+ */
+
+public class ClassListFormatB extends ClassListFormatBase {
+    static {
+        // Uncomment the following line to run only one of the test cases
+        // ClassListFormatBase.RUN_ONLY_TEST = "TESTCASE B1";
+    }
+
+    public static void main(String[] args) throws Throwable {
+        String appJar = JarBuilder.getOrCreateHelloJar();
+        String customJarPath = JarBuilder.build("ClassListFormatB", "CustomLoadee",
+                                            "CustomLoadee2", "CustomInterface2_ia", "CustomInterface2_ib");
+        //----------------------------------------------------------------------
+        // TESTGROUP B if source IS specified
+        //----------------------------------------------------------------------
+        dumpShouldFail(
+            "TESTCASE B1: if source: is specified, must specify super:",
+            appJar, classlist(
+                "Hello",
+                "java/lang/Object id: 1",
+                "CustomLoadee id: 2 source: " + customJarPath
+            ),
+            "If source location is specified, super class must be also specified");
+
+        dumpShouldFail(
+            "TESTCASE B2: if source: is specified, must specify id:",
+            appJar, classlist(
+                "Hello",
+                "java/lang/Object id: 1",
+                "CustomLoadee super: 1 source: " + customJarPath
+            ),
+            "If source location is specified, id must be also specified");
+    }
+}
+
diff --git a/test/hotspot/jtreg/runtime/appcds/customLoader/ClassListFormatBase.java b/test/hotspot/jtreg/runtime/appcds/customLoader/ClassListFormatBase.java
new file mode 100644
index 00000000000..7b1301c0137
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/customLoader/ClassListFormatBase.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+import jdk.test.lib.process.OutputAnalyzer;
+
+/**
+ * Base class for ClassListFormat[A,B,C...].java
+ */
+public class ClassListFormatBase {
+    protected static String RUN_ONLY_TEST = null;
+
+    static void dumpShouldFail(String caseHelp, String appJar, String[] appClasses,
+                               String... expected_errors) throws Throwable {
+        if (RUN_ONLY_TEST != null && !caseHelp.startsWith(RUN_ONLY_TEST)) {
+            System.out.println("Skipped via RUN_ONLY_TEST: " + caseHelp);
+            return;
+        }
+        System.out.println("------------------------------");
+        System.out.println(caseHelp);
+        System.out.println("------------------------------");
+
+        try {
+            OutputAnalyzer output = TestCommon.dump(appJar, appClasses);
+            output.shouldHaveExitValue(1);
+            for (String s : expected_errors) {
+                output.shouldContain(s);
+            }
+        } catch (Throwable t) {
+            System.out.println("FAILED CASE: " + caseHelp);
+            throw t;
+        }
+    }
+
+    static void dumpShouldPass(String caseHelp, String appJar, String[] appClasses,
+                               String... expected_msgs) throws Throwable {
+        if (RUN_ONLY_TEST != null && !caseHelp.startsWith(RUN_ONLY_TEST)) {
+            System.out.println("Skipped via RUN_ONLY_TEST: " + caseHelp);
+            return;
+        }
+        System.out.println("------------------------------");
+        System.out.println(caseHelp);
+        System.out.println("------------------------------");
+
+        try {
+            OutputAnalyzer output = TestCommon.dump(appJar, appClasses);
+            output.shouldHaveExitValue(0);
+            output.shouldContain("Dumping");
+            for (String s : expected_msgs) {
+                output.shouldContain(s);
+            }
+        } catch (Throwable t) {
+            System.out.println("FAILED CASE: " + caseHelp);
+            throw t;
+        }
+    }
+
+    static String[] classlist(String... args) {
+        return TestCommon.list(args);
+    }
+}
+
diff --git a/test/hotspot/jtreg/runtime/appcds/customLoader/ClassListFormatC.java b/test/hotspot/jtreg/runtime/appcds/customLoader/ClassListFormatC.java
new file mode 100644
index 00000000000..ff4dd11eb1e
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/customLoader/ClassListFormatC.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+/*
+ * @test
+ * @summary Tests the format checking of hotspot/src/closed/share/vm/classfile/classListParser.cpp.
+ *
+ * (NOTE: AppCDS does not support uncompressed oops)
+ * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true)
+ * @requires vm.cds.custom.loaders
+ * @library /test/lib /test/hotspot/jtreg/runtime/appcds
+ * @modules java.base/jdk.internal.misc
+ *          java.management
+ *          jdk.jartool/sun.tools.jar
+ * @compile test-classes/Hello.java test-classes/CustomLoadee.java test-classes/CustomLoadee2.java
+ *          test-classes/CustomInterface2_ia.java test-classes/CustomInterface2_ib.java
+ * @run main ClassListFormatC
+ */
+
+public class ClassListFormatC extends ClassListFormatBase {
+    static {
+        // Uncomment the following line to run only one of the test cases
+        // ClassListFormatBase.RUN_ONLY_TEST = "TESTCASE C1";
+    }
+
+    public static void main(String[] args) throws Throwable {
+        String appJar = JarBuilder.getOrCreateHelloJar();
+        String customJarPath = JarBuilder.build("ClassListFormatC", "CustomLoadee",
+                                                "CustomLoadee2", "CustomInterface2_ia",
+                                                "CustomInterface2_ib");
+
+        //----------------------------------------------------------------------
+        // TESTGROUP C: if source IS NOT specified
+        //----------------------------------------------------------------------
+        dumpShouldFail(
+            "TESTCASE C1: if source: is NOT specified, must NOT specify super:",
+            appJar, classlist(
+                "Hello",
+                "java/lang/Object id: 1",
+                "CustomLoadee super: 1"
+            ),
+            "If source location is not specified, super class must not be specified");
+
+        dumpShouldFail(
+            "TESTCASE C2: if source: is NOT specified, must NOT specify interface:",
+            appJar, classlist(
+                "Hello",
+                "java/lang/Object id: 1",
+                "CustomLoadee interfaces: 1"
+            ),
+            "If source location is not specified, interface(s) must not be specified");
+    }
+}
+
diff --git a/test/hotspot/jtreg/runtime/appcds/customLoader/ClassListFormatD.java b/test/hotspot/jtreg/runtime/appcds/customLoader/ClassListFormatD.java
new file mode 100644
index 00000000000..0a911d6750e
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/customLoader/ClassListFormatD.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+/*
+ * @test
+ * @summary Tests the format checking of hotspot/src/closed/share/vm/classfile/classListParser.cpp.
+ *
+ * (NOTE: AppCDS does not support uncompressed oops)
+ * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true)
+ * @requires vm.cds.custom.loaders
+ * @library /test/lib /test/hotspot/jtreg/runtime/appcds
+ * @modules java.base/jdk.internal.misc
+ *          java.management
+ *          jdk.jartool/sun.tools.jar
+ * @compile test-classes/Hello.java test-classes/CustomLoadee.java test-classes/CustomLoadee2.java
+ *          test-classes/CustomInterface2_ia.java test-classes/CustomInterface2_ib.java
+ * @run main ClassListFormatD
+ */
+
+public class ClassListFormatD extends ClassListFormatBase {
+    static {
+        // Uncomment the following line to run only one of the test cases
+        // ClassListFormatBase.RUN_ONLY_TEST = "TESTCASE D1";
+    }
+
+    public static void main(String[] args) throws Throwable {
+        String appJar = JarBuilder.getOrCreateHelloJar();
+        String customJarPath = JarBuilder.build("ClassListFormatD", "CustomLoadee",
+                                                "CustomLoadee2", "CustomInterface2_ia",
+                                                "CustomInterface2_ib");
+
+        //----------------------------------------------------------------------
+        // TESTGROUP D: bad use of IDs
+        //----------------------------------------------------------------------
+        dumpShouldFail(
+            "TESTCASE D1: duplicated id:",
+            appJar, classlist(
+                "Hello",
+                "java/lang/Object id: 1",
+                "CustomLoadee id: 1 super: 1 source: " + customJarPath
+            ),
+            "Duplicated ID 1 for class CustomLoadee");
+
+        dumpShouldFail(
+            "TESTCASE D2: bad ID for super:",
+            appJar, classlist(
+                "Hello",
+                "java/lang/Object id: 1",
+                "CustomLoadee id: 2 super: 2 source: " + customJarPath
+            ),
+            "Super class id 2 is not yet loaded");
+
+        dumpShouldFail(
+            "TESTCASE D3: bad ID in interfaces:",
+            appJar, classlist(
+                "Hello",
+                "java/lang/Object id: 1",
+                "CustomLoadee id: 2 super: 1 interfaces: 2 source: " + customJarPath
+            ),
+            "Interface id 2 is not yet loaded");
+    }
+}
+
diff --git a/test/hotspot/jtreg/runtime/appcds/customLoader/ClassListFormatE.java b/test/hotspot/jtreg/runtime/appcds/customLoader/ClassListFormatE.java
new file mode 100644
index 00000000000..b61e08459b6
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/customLoader/ClassListFormatE.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+/*
+ * @test
+ * @summary Tests the format checking of hotspot/src/closed/share/vm/classfile/classListParser.cpp.
+ *
+ * (NOTE: AppCDS does not support uncompressed oops)
+ * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true)
+ * @requires vm.cds.custom.loaders
+ * @library /test/lib /test/hotspot/jtreg/runtime/appcds
+ * @modules java.base/jdk.internal.misc
+ *          java.management
+ *          jdk.jartool/sun.tools.jar
+ * @compile test-classes/Hello.java test-classes/CustomLoadee.java test-classes/CustomLoadee2.java
+ *          test-classes/CustomInterface2_ia.java test-classes/CustomInterface2_ib.java
+ * @run main ClassListFormatE
+ */
+
+public class ClassListFormatE extends ClassListFormatBase {
+    static {
+        // Uncomment the following line to run only one of the test cases
+        // ClassListFormatBase.RUN_ONLY_TEST = "TESTCASE E1";
+    }
+
+    public static void main(String[] args) throws Throwable {
+        String appJar = JarBuilder.getOrCreateHelloJar();
+        String customJarPath = JarBuilder.build("ClassListFormatE", "CustomLoadee",
+                                                "CustomLoadee2", "CustomInterface2_ia",
+                                                "CustomInterface2_ib");
+
+        //----------------------------------------------------------------------
+        // TESTGROUP E: super class and interfaces
+        //----------------------------------------------------------------------
+        dumpShouldFail(
+            "TESTCASE E1: missing interfaces: keyword",
+            appJar, classlist(
+                "Hello",
+                "java/lang/Object id: 1",
+                "CustomLoadee2 id: 1 super: 1 source: " + customJarPath
+            ),
+            "Class CustomLoadee2 implements the interface CustomInterface2_ia, but no interface has been specified in the input line");
+
+        dumpShouldFail(
+            "TESTCASE E2: missing one interface",
+            appJar, classlist(
+                "Hello",
+                "java/lang/Object id: 1",
+                "CustomInterface2_ia id: 2 super: 1 source: " + customJarPath,
+                "CustomInterface2_ib id: 3 super: 1 source: " + customJarPath,
+                "CustomLoadee2 id: 4 super: 1 interfaces: 2 source: " + customJarPath
+            ),
+            "The interface CustomInterface2_ib implemented by class CustomLoadee2 does not match any of the specified interface IDs");
+
+        dumpShouldFail(
+            "TESTCASE E3: specifying an interface that's not implemented by the class",
+            appJar, classlist(
+                "Hello",
+                "java/lang/Object id: 1",
+                "CustomInterface2_ia id: 2 super: 1 source: " + customJarPath,
+                "CustomLoadee id: 2 super: 1 interfaces: 2 source: " + customJarPath
+            ),
+            "The number of interfaces (1) specified in class list does not match the class file (0)");
+
+        dumpShouldFail(
+            "TESTCASE E4: repeating an ID in the interfaces: keyword",
+            appJar, classlist(
+                "Hello",
+                "java/lang/Object id: 1",
+                "CustomInterface2_ia id: 2 super: 1 source: " + customJarPath,
+                "CustomInterface2_ib id: 3 super: 1 source: " + customJarPath,
+                "CustomLoadee2 id: 4 super: 1 interfaces: 2 2 3 source: " + customJarPath
+            ),
+            "The number of interfaces (3) specified in class list does not match the class file (2)");
+
+        dumpShouldFail(
+            "TESTCASE E5: wrong super class",
+            appJar, classlist(
+                "Hello",
+                "java/lang/Object id: 1",
+                "CustomInterface2_ia id: 2 super: 1 source: " + customJarPath,
+                "CustomInterface2_ib id: 3 super: 1 source: " + customJarPath,
+                "CustomLoadee id: 4 super: 1 source: " + customJarPath,
+                "CustomLoadee2 id: 5 super: 4 interfaces: 2 3 source: " + customJarPath
+            ),
+            "The specified super class CustomLoadee (id 4) does not match actual super class java.lang.Object");
+    }
+}
+
diff --git a/test/hotspot/jtreg/runtime/appcds/customLoader/CustomLoaderApp.java b/test/hotspot/jtreg/runtime/appcds/customLoader/CustomLoaderApp.java
new file mode 100644
index 00000000000..0075529e8f5
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/customLoader/CustomLoaderApp.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright (c) 2016, 2017, 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.
+ *
+ */
+
+// This is a utlitity test class for loading classes-under-test
+// by means of custom class loader.
+// See AppCDS/jvmti/transformRelatedClasses/TransformRelatedClasses.java
+// for an example.
+// Use this test app in conjunction with other tests
+// to load and exercise classes using custom class loader(s).
+// This class is intended to be called by the "main test driver"
+// inside a child process, normally with sharing enabled.
+//
+// Arguments: customJarPath, loaderType, testClass
+//     customJarPath - a path to jar file containing classes for
+//         loading via this custom class loader, including the
+//         testClass
+//     loaderType - Currently only "unregistered"
+//         (Fingerprint verification method) is allowed
+//     testClass - the class to be loader; the test method with
+//         signature 'public static void test()' will be called
+//         on this class, so class must contain such method
+
+
+import java.io.File;
+import java.lang.reflect.Method;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.logging.Logger;
+
+public class CustomLoaderApp {
+    public static void ping() {};
+
+    private static void log(String msg) {
+        System.out.println("CustomLoaderApp: " + msg);
+    }
+
+    public static void main(String[] args) throws Exception {
+        String path = args[0];
+        URL url = new File(path).toURI().toURL();
+        URL[] urls = new URL[] {url};
+
+        String loaderType = args[1];
+        log("loaderType = " + loaderType);
+
+        String testClass = args[2];
+        log("testClass = " + testClass);
+
+        switch(loaderType) {
+        case "unregistered":
+            loadAndUseWithUnregisteredLoader(urls, testClass);
+            break;
+        default:
+            throw new IllegalArgumentException("loader type is wrong: " + loaderType);
+        }
+    }
+
+
+    // Load the test classes using unregistered loader
+    // (i.e. loader that is not using AppCDS API)
+    private static void loadAndUseWithUnregisteredLoader(URL[] urls, String testClass)
+        throws Exception {
+        URLClassLoader urlClassLoader = new URLClassLoader(urls);
+        callTestMethod(loadAndCheck(urlClassLoader, testClass));
+    }
+
+    private static Class loadAndCheck(ClassLoader loader, String className)
+        throws ClassNotFoundException {
+        Class c = loader.loadClass(className);
+        log("class =" + c);
+        log("loader = " + c.getClassLoader());
+
+        // Check that c is defined by the correct loader
+        if (c.getClassLoader() != loader) {
+            String msg = String.format("c.getClassLoader() equals to <%s>, expected <%s>",
+                                       c.getClassLoader(), loader);
+            throw new RuntimeException(msg);
+        }
+        return c;
+    }
+
+    private static void callTestMethod(Class c) throws Exception {
+        Method[] methods = c.getDeclaredMethods();
+        for (Method m : methods) {
+            log("method = " + m.getName());
+            if (m.getName().equals("test"))
+                m.invoke(null);
+        }
+    }
+}
diff --git a/test/hotspot/jtreg/runtime/appcds/customLoader/HelloCustom.java b/test/hotspot/jtreg/runtime/appcds/customLoader/HelloCustom.java
new file mode 100644
index 00000000000..750a251f178
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/customLoader/HelloCustom.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+/*
+ * @test
+ * @summary Hello World test for AppCDS custom loader support
+ * (NOTE: AppCDS does not support uncompressed oops)
+ * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true)
+ * @requires vm.cds.custom.loaders
+ * @library /test/lib /test/hotspot/jtreg/runtime/appcds
+ * @modules java.base/jdk.internal.misc
+ *          java.management
+ * @compile test-classes/Hello.java test-classes/CustomLoadee.java
+ * @build sun.hotspot.WhiteBox
+ * @run main ClassFileInstaller -jar hello.jar Hello
+ * @run main ClassFileInstaller -jar hello_custom.jar CustomLoadee
+ * @run main ClassFileInstaller -jar WhiteBox.jar sun.hotspot.WhiteBox
+ * @run main HelloCustom
+ */
+
+import jdk.test.lib.process.OutputAnalyzer;
+import sun.hotspot.WhiteBox;
+
+public class HelloCustom {
+    public static void main(String[] args) throws Exception {
+        String wbJar = ClassFileInstaller.getJarPath("WhiteBox.jar");
+        String use_whitebox_jar = "-Xbootclasspath/a:" + wbJar;
+
+        String appJar = ClassFileInstaller.getJarPath("hello.jar");
+        String customJarPath = ClassFileInstaller.getJarPath("hello_custom.jar");
+
+        // Dump the archive
+        String classlist[] = new String[] {
+            "Hello",
+            "java/lang/Object id: 1",
+            "CustomLoadee id: 2 super: 1 source: " + customJarPath
+        };
+
+        OutputAnalyzer output;
+        TestCommon.testDump(appJar, classlist,
+                            // command-line arguments ...
+                            use_whitebox_jar);
+
+        output = TestCommon.exec(appJar,
+                                 // command-line arguments ...
+                                 use_whitebox_jar,
+                                 "-XX:+UnlockDiagnosticVMOptions",
+                                 "-XX:+WhiteBoxAPI",
+                                 "Hello", customJarPath);
+        TestCommon.checkExec(output);
+    }
+}
+
diff --git a/test/hotspot/jtreg/runtime/appcds/customLoader/LoaderSegregationTest.java b/test/hotspot/jtreg/runtime/appcds/customLoader/LoaderSegregationTest.java
new file mode 100644
index 00000000000..f7d6b7bdcf0
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/customLoader/LoaderSegregationTest.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+/*
+ * @test
+ * @summary Check that during dumping, the classes for BOOT/EXT/APP loaders are segregated from the
+ *          custom loader classes.
+ * (NOTE: AppCDS does not support uncompressed oops)
+ * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true)
+ * @requires vm.cds.custom.loaders
+ * @library /test/lib /test/hotspot/jtreg/runtime/appcds
+ * @modules java.base/jdk.internal.misc
+ *          java.management
+ *          jdk.jartool/sun.tools.jar
+ * @compile test-classes/LoaderSegregation.java
+ *          test-classes/CustomLoadee.java test-classes/CustomLoadee2.java
+ *          test-classes/CustomInterface2_ia.java test-classes/CustomInterface2_ib.java
+ *          test-classes/CustomLoadee3.java test-classes/CustomLoadee3Child.java
+ *          test-classes/OnlyBuiltin.java
+ *          test-classes/OnlyUnregistered.java
+ *          ../test-classes/Util.java
+ * @build sun.hotspot.WhiteBox
+ * @run main ClassFileInstaller sun.hotspot.WhiteBox
+ * @run main LoaderSegregationTest
+ */
+
+import jdk.test.lib.process.OutputAnalyzer;
+import sun.hotspot.WhiteBox;
+
+/**
+ * See "Handling of the classes in the AppCDS archive" at the top of
+ * systemDicrionatyShared.hpp.
+ *
+ * This test ensure that the 2 types of archived classes (BUILTIN and UNREGISTERED)
+ * are segregated at both dump-time and run time:
+ *
+ * [A] An archived BUILTIN class cannot be a subclass of a non-BUILTIN class.
+ * [B] An archived BUILTIN class cannot implement a non-BUILTIN interface.
+ * [C] BUILTIN and UNREGISTERED classes can be loaded only by their corresponding
+ *     type of loaders.
+ *
+ */
+public class LoaderSegregationTest {
+    public static void main(String[] args) throws Exception {
+        String wbJar = JarBuilder.build(true, "WhiteBox", "sun/hotspot/WhiteBox");
+        String use_whitebox_jar = "-Xbootclasspath/a:" + wbJar;
+
+        String appJar = JarBuilder.build("LoaderSegregation_app", "LoaderSegregation",
+                                         "CustomLoadee", "CustomLoadee2", "CustomLoadee3Child", "CustomInterface2_ia",
+                                         "OnlyBuiltin", "Util");
+
+        String app2Jar = JarBuilder.build("LoaderSegregation_app2", "CustomLoadee3", "CustomInterface2_ib");
+
+        String customJarPath = JarBuilder.build("LoaderSegregation_custom", "CustomLoadee",
+                                                "CustomLoadee2", "CustomInterface2_ia", "CustomInterface2_ib",
+                                                "CustomLoadee3", "CustomLoadee3Child",
+                                                "OnlyBuiltin", "OnlyUnregistered");
+
+        // Dump the archive
+        String classlist[] = new String[] {
+            "LoaderSegregation",
+            "java/lang/Object id: 1",
+
+            // These are the UNREGISTERED classes: they have "source:"
+            // but they don't have "loader:".
+            "CustomLoadee id: 2 super: 1 source: " + customJarPath,
+
+            "CustomInterface2_ia id: 3 super: 1 source: " + customJarPath,
+            "CustomInterface2_ib id: 4 super: 1 source: " + customJarPath,
+            "CustomLoadee2 id: 5 super: 1 interfaces: 3 4 source: " + customJarPath,
+
+            "CustomLoadee3 id: 6 super: 1 source: " + customJarPath,
+            "CustomLoadee3Child id: 7 super: 6 source: " + customJarPath,
+
+            // At dump time, the following BUILTIN classes are loaded after the UNREGISTERED
+            // classes from above. However, at dump time, they cannot use the UNREGISTERED classes are their
+            // super or interface.
+            "CustomLoadee",          // can be loaded at dump time
+            "CustomLoadee2",         // cannot be loaded at dump time (interface missing)
+            "CustomLoadee3Child",    // cannot be loaded at dump time (super missing)
+
+            // Check that BUILTIN and UNREGISTERED classes can be loaded only by their
+            // corresponding type of loaders.
+            "OnlyBuiltin",
+            "OnlyUnregistered id: 9 super: 1 source: " + customJarPath,
+        };
+
+        OutputAnalyzer output;
+        TestCommon.testDump(appJar, classlist,
+                            // command-line arguments ...
+                            use_whitebox_jar);
+
+        output = TestCommon.exec(TestCommon.concatPaths(appJar, app2Jar),
+                                 // command-line arguments ...
+                                 "--add-opens=java.base/java.lang=ALL-UNNAMED",
+                                 "--add-opens=java.base/java.security=ALL-UNNAMED",
+                                 use_whitebox_jar,
+                                 "-XX:+UnlockDiagnosticVMOptions",
+                                 "-XX:+WhiteBoxAPI",
+                                 "LoaderSegregation", customJarPath);
+        TestCommon.checkExec(output);
+    }
+}
diff --git a/test/hotspot/jtreg/runtime/appcds/customLoader/ParallelTestBase.java b/test/hotspot/jtreg/runtime/appcds/customLoader/ParallelTestBase.java
new file mode 100644
index 00000000000..a536d831a27
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/customLoader/ParallelTestBase.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+import jdk.test.lib.process.OutputAnalyzer;
+
+/*
+ * This is a base class for the following test cases:
+ *   ParallelTestMultiFP.java
+ *   ParallelTestSingleFP.java
+ */
+public class ParallelTestBase {
+    public static final int MAX_CLASSES = 40; // must match ../test-classes/ParallelLoad.java
+    public static int NUM_THREADS = 4;        // must match ../test-classes/ParallelLoad.java
+
+    public static final int SINGLE_CUSTOM_LOADER = 1;
+    public static final int MULTI_CUSTOM_LOADER  = 2;
+
+    public static final int FINGERPRINT_MODE = 1;
+
+    public static void run(String[] args, int loaderType, int mode) throws Exception {
+        String[] cust_classes = new String[MAX_CLASSES];
+        String[] cust_list;
+
+        if (mode == FINGERPRINT_MODE) {
+            cust_list = new String[MAX_CLASSES];
+        } else {
+            cust_list = new String[MAX_CLASSES * NUM_THREADS];
+        }
+
+        for (int i = 0; i<MAX_CLASSES; i++) {
+            cust_classes[i] = "ParallelClass" + i;
+        }
+        String customJarPath = JarBuilder.build("ParallelTestBase", cust_classes);
+
+        for (int i = 0, n=0; i<MAX_CLASSES; i++) {
+            int super_id = 1;
+            if (mode == FINGERPRINT_MODE) {
+                // fingerprint mode -- no need to use the "loader:" option.
+                int id = i + 2;
+                cust_list[i] = cust_classes[i] + " id: " + id + " super: " + super_id + " source: " + customJarPath;
+            } else {
+                throw new RuntimeException("Only FINGERPRINT_MODE is supported");
+            }
+        }
+
+        String app_list[];
+        String mainClass;
+        String appJar;
+
+        if (mode == FINGERPRINT_MODE) {
+            appJar = JarBuilder.build("parallel_fp",
+                                      "ParallelLoad",
+                                      "ParallelLoadThread",
+                                      "ParallelLoadWatchdog");
+            app_list = new String[] {
+                "java/lang/Object id: 1",
+                "ParallelLoad",
+                "ParallelLoadThread",
+                "ParallelLoadWatchdog"
+            };
+            mainClass = "ParallelLoad";
+        } else {
+            throw new RuntimeException("Currently only FINGERPRINT_MODE is supported");
+        }
+
+        OutputAnalyzer output;
+        TestCommon.testDump(appJar, TestCommon.concat(app_list, cust_list));
+
+        String loaderTypeArg = (loaderType == SINGLE_CUSTOM_LOADER) ? "SINGLE_CUSTOM_LOADER" : "MULTI_CUSTOM_LOADER";
+        String modeArg = "FINGERPRINT_MODE";
+
+        output = TestCommon.exec(appJar,
+                                 // command-line arguments ...
+                                 "--add-opens=java.base/java.security=ALL-UNNAMED",
+                                 mainClass, loaderTypeArg, modeArg, customJarPath);
+        TestCommon.checkExec(output);
+    }
+}
diff --git a/test/hotspot/jtreg/runtime/appcds/customLoader/ParallelTestMultiFP.java b/test/hotspot/jtreg/runtime/appcds/customLoader/ParallelTestMultiFP.java
new file mode 100644
index 00000000000..bd7f99ccccc
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/customLoader/ParallelTestMultiFP.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+/*
+ * @test
+ * @summary Load classes from CDS archive into multiple custom loader using parallel threads
+ * (NOTE: AppCDS does not support uncompressed oops)
+ * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true)
+ * @requires vm.cds.custom.loaders
+ * @library /test/lib /test/hotspot/jtreg/runtime/appcds
+ * @modules java.base/jdk.internal.misc
+ *          java.management
+ *          jdk.jartool/sun.tools.jar
+ * @compile ../test-classes/ParallelLoad.java ../test-classes/ParallelClasses.java
+ * @run main ParallelTestMultiFP
+ */
+
+public class ParallelTestMultiFP extends ParallelTestBase {
+    public static void main(String[] args) throws Exception {
+        ParallelTestBase.run(args, MULTI_CUSTOM_LOADER, FINGERPRINT_MODE);
+    }
+}
diff --git a/test/hotspot/jtreg/runtime/appcds/customLoader/ParallelTestSingleFP.java b/test/hotspot/jtreg/runtime/appcds/customLoader/ParallelTestSingleFP.java
new file mode 100644
index 00000000000..585cd67f71c
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/customLoader/ParallelTestSingleFP.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+/*
+ * @test
+ * @summary Load classes from CDS archive into a single custom loader using parallel threads (finger print)
+ * (NOTE: AppCDS does not support uncompressed oops)
+ * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true)
+ * @requires vm.cds.custom.loaders
+ * @library /test/lib /test/hotspot/jtreg/runtime/appcds
+ * @modules java.base/jdk.internal.misc
+ *          java.management
+ *          jdk.jartool/sun.tools.jar
+ * @compile ../test-classes/ParallelLoad.java ../test-classes/ParallelClasses.java
+ * @run main ParallelTestSingleFP
+ */
+
+public class ParallelTestSingleFP extends ParallelTestBase {
+    public static void main(String[] args) throws Exception {
+        ParallelTestBase.run(args, SINGLE_CUSTOM_LOADER, FINGERPRINT_MODE);
+    }
+}
diff --git a/test/hotspot/jtreg/runtime/appcds/customLoader/ProhibitedPackageNamesTest.java b/test/hotspot/jtreg/runtime/appcds/customLoader/ProhibitedPackageNamesTest.java
new file mode 100644
index 00000000000..1b0da00532f
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/customLoader/ProhibitedPackageNamesTest.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+/*
+ * @test
+ * @summary Make sure prohibited packages cannot be stored into archive for custom loaders.
+ * (NOTE: AppCDS does not support uncompressed oops)
+ * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true)
+ * @requires vm.cds.custom.loaders
+ * @library /test/lib /test/hotspot/jtreg/runtime/appcds
+ * @modules java.base/jdk.internal.misc
+ *          java.management
+ *          jdk.jartool/sun.tools.jar
+ * @compile ClassListFormatBase.java test-classes/Hello.java test-classes/InProhibitedPkg.java
+ * @run main ProhibitedPackageNamesTest
+ */
+
+public class ProhibitedPackageNamesTest extends ClassListFormatBase {
+    static {
+        // Uncomment the following line to run only one of the test cases
+        // ClassListFormatBase.RUN_ONLY_TEST = "TESTCASE PPN1";
+    }
+
+    public static void main(String[] args) throws Throwable {
+        String appJar = JarBuilder.getOrCreateHelloJar();
+        String customJarPath = JarBuilder.build("ProhibitedPackageNames_custom", "java/InProhibitedPkg");
+
+        dumpShouldPass(
+            "TESTCASE PPN1: prohibited package name without loader:",
+            appJar, classlist(
+                "Hello",
+                "java/lang/Object id: 1",
+                // Without "loader:" keyword.
+                "java/InProhibitedPkg id: 2 super: 1 source: " + customJarPath
+            ),
+            "Prohibited package for non-bootstrap classes: java/InProhibitedPkg.class");
+    }
+}
diff --git a/test/hotspot/jtreg/runtime/appcds/customLoader/ProtectionDomain.java b/test/hotspot/jtreg/runtime/appcds/customLoader/ProtectionDomain.java
new file mode 100644
index 00000000000..dad59f91ab7
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/customLoader/ProtectionDomain.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+/*
+ * @test
+ * @summary AppCDS handling of protection domain in custom loaders.
+ *
+ * AppCDS does not support uncompressed oops
+ * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true)
+ * @requires vm.cds.custom.loaders
+ *
+ * @library /test/lib /test/hotspot/jtreg/runtime/appcds
+ * @modules java.base/jdk.internal.misc
+ *          java.management
+ *          jdk.jartool/sun.tools.jar
+ * @compile test-classes/ProtDomain.java
+ * @run main ProtectionDomain
+ */
+
+public class ProtectionDomain {
+    public static void main(String[] args) throws Exception {
+        String appJar = JarBuilder.build("ProtectionDomain-app", "ProtDomain");
+
+        String customJar = JarBuilder.build("ProtectionDomain-custom",
+            "ProtDomainClassForArchive", "ProtDomainNotForArchive");
+        String[] classlist = new String[] {
+            "java/lang/Object id: 1",
+            "ProtDomain id: 2 super: 1 source: " + appJar,
+            "ProtDomainClassForArchive id: 3 super: 1 source: " + customJar
+        };
+
+        TestCommon.testDump(appJar, classlist);
+
+        // First class is loaded from CDS, second class is loaded from JAR
+        TestCommon.checkExec(TestCommon.exec(appJar, "-verbose:class", "ProtDomain", customJar),
+            "[class,load] ProtDomainClassForArchive source: shared objects file",
+            "[class,load] ProtDomainNotForArchive source: file");
+    }
+}
diff --git a/test/hotspot/jtreg/runtime/appcds/customLoader/SameNameInTwoLoadersTest.java b/test/hotspot/jtreg/runtime/appcds/customLoader/SameNameInTwoLoadersTest.java
new file mode 100644
index 00000000000..0e1df9ee222
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/customLoader/SameNameInTwoLoadersTest.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+/*
+ * @test
+ * @summary Testing the loading of a class with the same name in two different class loaders.
+ *
+ * (NOTE: AppCDS does not support uncompressed oops)
+ * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true)
+ * @requires vm.cds.custom.loaders
+ *
+ * @library /test/lib /test/hotspot/jtreg/runtime/appcds
+ * @modules java.base/jdk.internal.misc
+ *          java.management
+ *          jdk.jartool/sun.tools.jar
+ * @compile test-classes/CustomLoadee.java
+ *     test-classes/CustomLoadee3.java
+ *     test-classes/SameNameUnrelatedLoaders.java
+ * @build sun.hotspot.WhiteBox
+ * @run main ClassFileInstaller sun.hotspot.WhiteBox
+ * @run main SameNameInTwoLoadersTest
+ */
+
+import jdk.test.lib.process.OutputAnalyzer;
+import sun.hotspot.WhiteBox;
+
+
+public class SameNameInTwoLoadersTest {
+    private static String appJar;
+    private static String customJar;
+    private static String useWbParam;
+
+    public static void main(String[] args) throws Exception {
+        appJar = JarBuilder.build("SameNameInTwoLoadersTest",
+            "SameNameUnrelatedLoaders");
+
+        customJar = JarBuilder.build("SameNameInTwoLoadersTest_custom", "CustomLoadee", "CustomLoadee3");
+
+        useWbParam = "-Xbootclasspath/a:" +
+            JarBuilder.build(true, "WhiteBox", "sun/hotspot/WhiteBox");;
+
+        // ====== unrelated loaders
+        executeTestCase(getClassList_FP(),
+            "SameNameUnrelatedLoaders", "FpBoth");
+    }
+
+    private static void executeTestCase(String[] classlist,
+        String testClass, String testCaseId) throws Exception {
+        classlist[0] = testClass;
+
+        TestCommon.testDump(appJar, classlist, useWbParam);
+
+        OutputAnalyzer output = TestCommon.exec(appJar,
+                                 // command-line arguments ...
+                                 "--add-opens=java.base/java.security=ALL-UNNAMED",
+                                 useWbParam,
+                                 "-XX:+UnlockDiagnosticVMOptions",
+                                 "-XX:+WhiteBoxAPI",
+                                 testClass,
+                                 customJar, testCaseId);
+        TestCommon.checkExec(output);
+    }
+
+    // Single entry, no loader specified (FP method)
+    private static String[] getClassList_FP() {
+        return new String[] {
+            "SameNameUnrelatedLoaders",
+            "java/lang/Object id: 1",
+            "CustomLoadee id: 10 super: 1 source: " + customJar,
+        };
+    }
+}
diff --git a/test/hotspot/jtreg/runtime/appcds/customLoader/UnintendedLoadersTest.java b/test/hotspot/jtreg/runtime/appcds/customLoader/UnintendedLoadersTest.java
new file mode 100644
index 00000000000..a83d2cdd9e3
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/customLoader/UnintendedLoadersTest.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+/*
+ * @test
+ * @summary Make sure classes intended for custom loaders cannot be loaded by BOOT/EXT/APP loaders
+ * (NOTE: AppCDS does not support uncompressed oops)
+ * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true)
+ * @requires vm.cds.custom.loaders
+ * @library /test/lib /test/hotspot/jtreg/runtime/appcds
+ * @modules java.base/jdk.internal.misc
+ *          java.management
+ *          jdk.jartool/sun.tools.jar
+ * @compile test-classes/UnintendedLoaders.java test-classes/CustomLoadee.java
+ * @build sun.hotspot.WhiteBox
+ * @run main ClassFileInstaller sun.hotspot.WhiteBox
+ * @run main UnintendedLoadersTest
+ */
+
+import jdk.test.lib.process.OutputAnalyzer;
+import sun.hotspot.WhiteBox;
+
+public class UnintendedLoadersTest {
+    public static void main(String[] args) throws Exception {
+        String wbJar = JarBuilder.build(true, "WhiteBox", "sun/hotspot/WhiteBox");
+        String use_whitebox_jar = "-Xbootclasspath/a:" + wbJar;
+
+        String appJar = JarBuilder.build("UnintendedLoaders_app", "UnintendedLoaders");
+        String customJarPath = JarBuilder.build("UnintendedLoaders_custom", "CustomLoadee");
+
+        // Dump the archive
+        String classlist[] = new String[] {
+            "UnintendedLoadersTest",
+            "java/lang/Object id: 1",
+
+            // Without "loader:" keyword.
+            "CustomLoadee id: 2 super: 1 source: " + customJarPath,
+        };
+
+        OutputAnalyzer output;
+        TestCommon.testDump(appJar, classlist,
+                            // command-line arguments ...
+                            use_whitebox_jar);
+
+        output = TestCommon.exec(appJar,
+                                 // command-line arguments ...
+                                 use_whitebox_jar,
+                                 "-XX:+UnlockDiagnosticVMOptions",
+                                 "-XX:+WhiteBoxAPI",
+                                 "UnintendedLoaders");
+        TestCommon.checkExec(output);
+    }
+}
+
diff --git a/test/hotspot/jtreg/runtime/appcds/customLoader/UnloadUnregisteredLoaderTest.java b/test/hotspot/jtreg/runtime/appcds/customLoader/UnloadUnregisteredLoaderTest.java
new file mode 100644
index 00000000000..1b80ebca452
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/customLoader/UnloadUnregisteredLoaderTest.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+/*
+ * @test
+ * @summary Test the behavior when shared classes loaded by custom loaders are
+ *          unloaded.
+ * (NOTE: AppCDS does not support uncompressed oops)
+ * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true)
+ * @requires vm.cds.custom.loaders
+ * @library /test/lib /test/hotspot/jtreg/runtime/appcds /test/hotspot/jtreg/runtime/testlibrary
+ * @modules java.base/jdk.internal.misc
+ *          java.management
+ *          jdk.jartool/sun.tools.jar
+ * @build sun.hotspot.WhiteBox ClassUnloadCommon
+ * @compile test-classes/UnloadUnregisteredLoader.java test-classes/CustomLoadee.java
+ * @run main ClassFileInstaller sun.hotspot.WhiteBox
+ * @run main ClassFileInstaller ClassUnloadCommon
+ * @run main ClassFileInstaller ClassUnloadCommon$1
+ * @run main ClassFileInstaller ClassUnloadCommon$TestFailure
+ * @run main UnloadUnregisteredLoaderTest
+ */
+
+import jdk.test.lib.process.OutputAnalyzer;
+import sun.hotspot.WhiteBox;
+
+public class UnloadUnregisteredLoaderTest {
+    public static void main(String[] args) throws Exception {
+        String appJar1 = JarBuilder.build("UnloadUnregisteredLoader_app1", "UnloadUnregisteredLoader");
+        String appJar2 = JarBuilder.build(true, "UnloadUnregisteredLoader_app2",
+                                          "ClassUnloadCommon", "ClassUnloadCommon$1", "ClassUnloadCommon$TestFailure");
+        String customJarPath = JarBuilder.build("UnloadUnregisteredLoader_custom", "CustomLoadee");
+        String wbJar = JarBuilder.build(true, "WhiteBox", "sun/hotspot/WhiteBox");
+        String use_whitebox_jar = "-Xbootclasspath/a:" + wbJar;
+
+        String classpath = TestCommon.concatPaths(appJar1, appJar2);
+        String classlist[] = new String[] {
+            "UnloadUnregisteredLoader",
+            "ClassUnloadCommon",
+            "ClassUnloadCommon$1",
+            "ClassUnloadCommon$TestFailure",
+            "java/lang/Object id: 1",
+            "CustomLoadee id: 2 super: 1 source: " + customJarPath,
+        };
+
+        OutputAnalyzer output;
+        TestCommon.testDump(classpath, classlist,
+                            // command-line arguments ...
+                            use_whitebox_jar);
+
+        output = TestCommon.exec(classpath,
+                                 // command-line arguments ...
+                                 use_whitebox_jar,
+                                 "-XX:+UnlockDiagnosticVMOptions",
+                                 "-XX:+WhiteBoxAPI",
+                                 "UnloadUnregisteredLoader",
+                                 customJarPath);
+        TestCommon.checkExec(output);
+    }
+}
diff --git a/test/hotspot/jtreg/runtime/appcds/customLoader/UnsupportedPlatforms.java b/test/hotspot/jtreg/runtime/appcds/customLoader/UnsupportedPlatforms.java
new file mode 100644
index 00000000000..ade10497dab
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/customLoader/UnsupportedPlatforms.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+/*
+ * @test
+ * @summary Ensure that support for AppCDS custom class loaders are not enabled on unsupported platforms.
+ * (NOTE: AppCDS does not support uncompressed oops)
+ * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true)
+ * @requires vm.cds
+ * @library /test/lib /test/hotspot/jtreg/runtime/appcds
+ * @modules java.base/jdk.internal.misc
+ *          java.management
+ *          jdk.jartool/sun.tools.jar
+ * @compile test-classes/SimpleHello.java
+ * @run main UnsupportedPlatforms
+ */
+
+import jdk.test.lib.Platform;
+import jdk.test.lib.process.OutputAnalyzer;
+
+public class UnsupportedPlatforms {
+    public static String PLATFORM_NOT_SUPPORTED_WARNING =
+        "AppCDS custom class loaders not supported on this platform";
+
+    public static void main(String[] args) throws Exception {
+        String appJar = JarBuilder.build("UnsupportedPlatforms", "SimpleHello");
+
+        // Dump the archive
+        String classlist[] = new String[] {
+            "SimpleHello",
+            "java/lang/Object id: 1",
+            "CustomLoadee id: 2 super: 1 source: " + appJar
+        };
+
+        OutputAnalyzer out = TestCommon.dump(appJar, classlist);
+
+        if (Platform.areCustomLoadersSupportedForCDS()) {
+            out.shouldNotContain(PLATFORM_NOT_SUPPORTED_WARNING);
+            out.shouldHaveExitValue(0);
+        } else {
+            out.shouldContain(PLATFORM_NOT_SUPPORTED_WARNING);
+            out.shouldHaveExitValue(1);
+        }
+    }
+}
diff --git a/test/hotspot/jtreg/runtime/appcds/customLoader/test-classes/CustomInterface2_ia.java b/test/hotspot/jtreg/runtime/appcds/customLoader/test-classes/CustomInterface2_ia.java
new file mode 100644
index 00000000000..99da97c4c83
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/customLoader/test-classes/CustomInterface2_ia.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2015, 2017, 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.
+ *
+ */
+
+interface CustomInterface2_ia {}
diff --git a/test/hotspot/jtreg/runtime/appcds/customLoader/test-classes/CustomInterface2_ib.java b/test/hotspot/jtreg/runtime/appcds/customLoader/test-classes/CustomInterface2_ib.java
new file mode 100644
index 00000000000..5d4990725b4
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/customLoader/test-classes/CustomInterface2_ib.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2015, 2017, 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.
+ *
+ */
+
+interface CustomInterface2_ib {}
diff --git a/test/hotspot/jtreg/runtime/appcds/customLoader/test-classes/CustomLoadee.java b/test/hotspot/jtreg/runtime/appcds/customLoader/test-classes/CustomLoadee.java
new file mode 100644
index 00000000000..7848c282873
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/customLoader/test-classes/CustomLoadee.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2015, 2017, 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.
+ *
+ */
+
+public class CustomLoadee {
+    public String toString() {
+        return "this is CustomLoadee";
+    }
+}
diff --git a/test/hotspot/jtreg/runtime/appcds/customLoader/test-classes/CustomLoadee2.java b/test/hotspot/jtreg/runtime/appcds/customLoader/test-classes/CustomLoadee2.java
new file mode 100644
index 00000000000..779f1919d28
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/customLoader/test-classes/CustomLoadee2.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2015, 2017, 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.
+ *
+ */
+
+public class CustomLoadee2 implements CustomInterface2_ia, CustomInterface2_ib {
+    public String toString() {
+        return "this is CustomLoadee";
+    }
+}
diff --git a/test/hotspot/jtreg/runtime/appcds/customLoader/test-classes/CustomLoadee3.java b/test/hotspot/jtreg/runtime/appcds/customLoader/test-classes/CustomLoadee3.java
new file mode 100644
index 00000000000..7af68cf6100
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/customLoader/test-classes/CustomLoadee3.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2015, 2017, 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.
+ *
+ */
+
+public class CustomLoadee3 {
+    public String toString() {
+        return "this is CustomLoadee3";
+    }
+}
diff --git a/test/hotspot/jtreg/runtime/appcds/customLoader/test-classes/CustomLoadee3Child.java b/test/hotspot/jtreg/runtime/appcds/customLoader/test-classes/CustomLoadee3Child.java
new file mode 100644
index 00000000000..fa47bf962cf
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/customLoader/test-classes/CustomLoadee3Child.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2015, 2017, 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.
+ *
+ */
+
+public class CustomLoadee3Child extends CustomLoadee3 {
+    public String toString() {
+        return "this is CustomLoadee3Child";
+    }
+}
diff --git a/test/hotspot/jtreg/runtime/appcds/customLoader/test-classes/Hello.java b/test/hotspot/jtreg/runtime/appcds/customLoader/test-classes/Hello.java
new file mode 100644
index 00000000000..2c9c796bca1
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/customLoader/test-classes/Hello.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+import java.io.*;
+import java.net.*;
+import sun.hotspot.WhiteBox;
+
+public class Hello {
+    public static void main(String args[]) throws Exception {
+        String path = args[0];
+        URL url = new File(path).toURI().toURL();
+        URL[] urls = new URL[] {url};
+        System.out.println(path);
+        System.out.println(url);
+
+        URLClassLoader urlClassLoader = new URLClassLoader(urls);
+        Class c = urlClassLoader.loadClass("CustomLoadee");
+        System.out.println(c);
+        System.out.println(c.getClassLoader());
+
+        // [1] Check that CustomLoadee is defined by the correct loader
+        if (c.getClassLoader() != urlClassLoader) {
+            throw new RuntimeException("c.getClassLoader() == " + c.getClassLoader() +
+                                       ", expected == " + urlClassLoader);
+        }
+
+        // [2] Check that CustomLoadee is loaded from shared archive.
+        WhiteBox wb = WhiteBox.getWhiteBox();
+        if (wb.isSharedClass(Hello.class)) {
+            if (!wb.isSharedClass(c)) {
+                throw new RuntimeException("wb.isSharedClass(c) should be true");
+            }
+        }
+    }
+}
diff --git a/test/hotspot/jtreg/runtime/appcds/customLoader/test-classes/InProhibitedPkg.java b/test/hotspot/jtreg/runtime/appcds/customLoader/test-classes/InProhibitedPkg.java
new file mode 100644
index 00000000000..0bd6ad64385
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/customLoader/test-classes/InProhibitedPkg.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2015, 2017, 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 java;
+
+public class InProhibitedPkg {
+    static {
+        if (true) {
+            throw new RuntimeException("This class shouldn't be loaded by any loader other than BOOT");
+        }
+    }
+}
diff --git a/test/hotspot/jtreg/runtime/appcds/customLoader/test-classes/LoaderAPI.mf b/test/hotspot/jtreg/runtime/appcds/customLoader/test-classes/LoaderAPI.mf
new file mode 100644
index 00000000000..19f4c6217cd
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/customLoader/test-classes/LoaderAPI.mf
@@ -0,0 +1,12 @@
+Manifest-Version: 1.0
+Created-By: 1.9.0-internal (Oracle Corporation)
+Specification-Title: My Specification Title
+Specification-Version: 1.0
+Specification-Vendor: My Specification Vendor
+Implementation-Title: My Implementation Title
+Implementation-Version: 1.0
+Implementation-Vendor: My Implementation Vendor
+
+Name: pkg1/
+Implementation-Version: 2.0
+Sealed: true
diff --git a/test/hotspot/jtreg/runtime/appcds/customLoader/test-classes/LoaderSegregation.java b/test/hotspot/jtreg/runtime/appcds/customLoader/test-classes/LoaderSegregation.java
new file mode 100644
index 00000000000..4cd4ab7d640
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/customLoader/test-classes/LoaderSegregation.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+import java.io.*;
+import java.net.*;
+import sun.hotspot.WhiteBox;
+
+public class LoaderSegregation {
+    // Use these definitions instead of literal strings, to avoid typos.
+    static final String ONLY_BUILTIN      = "OnlyBuiltin";
+    static final String ONLY_UNREGISTERED = "OnlyUnregistered";
+
+    public static void main(String args[]) throws Exception {
+        WhiteBox wb = WhiteBox.getWhiteBox();
+
+        // [A] An archived BUILTIN class cannot be a subclass of a non-BUILTIN class.
+        // [B] An archived BUILTIN class cannot implement a non-BUILTIN interface.
+        if (wb.isSharedClass(LoaderSegregation.class)) {
+            // [1] check that CustomLoadee is loadable from archive
+            if (!wb.isSharedClass(CustomLoadee.class)) {
+                throw new RuntimeException("wb.isSharedClass(CustomLoadee.class) should be true");
+            }
+
+            // [2] CustomInterface2_ia should be archived, even though it was not specified in the classlist.
+            //     It was successfully dumped as a side effect of attempting to load CustomLoadee2
+            //     during dump time. Note that CustomLoadee2 failed to dump because one of its interfaces,
+            //     CustomInterface2_ib, was not loadable from the BOOT/EXT/APP classpath. during dump time.
+            if (!wb.isSharedClass(CustomInterface2_ia.class)) {
+                throw new RuntimeException("wb.isSharedClass(CustomInterface2_ia.class) should be true");
+            }
+
+            // [3] Check that the BUILTIN versions of CustomLoadee2 and CustomLoadee3Child are loadable
+            //     at run time (since we have append LoaderSegregation_app2.jar the classpath),
+            //     but these classes must be loaded from the JAR file.
+            if (wb.isSharedClass(CustomLoadee2.class)) {
+                throw new RuntimeException("wb.isSharedClass(CustomLoadee2.class) should be false");
+            }
+            if (wb.isSharedClass(CustomLoadee3.class)) {
+                throw new RuntimeException("wb.isSharedClass(CustomLoadee3.class) should be false");
+            }
+            if (wb.isSharedClass(CustomLoadee3Child.class)) {
+                throw new RuntimeException("wb.isSharedClass(CustomLoadee3Child.class) should be false");
+            }
+        }
+
+        // [C] BUILTIN and UNREGISTERED classes can be loaded only by their corresponding
+        //     type of loaders.
+
+        String path = args[0];
+        File jarFile = new File(path);
+        URL url = new File(path).toURI().toURL();
+        URL[] urls = new URL[] {url};
+        ClassLoader appLoader = LoaderSegregation.class.getClassLoader();
+
+        { // BUILTIN LOADER
+            try {
+                appLoader.loadClass(ONLY_UNREGISTERED);
+                throw new RuntimeException("BUILTIN loader cannot load archived UNREGISTERED class");
+            } catch (ClassNotFoundException expected) {}
+        }
+
+        { // UNREGISTERED LOADER
+            URLClassLoader urlClassLoader = new URLClassLoader(urls);
+            Class c2 = Util.defineClassFromJAR(urlClassLoader, jarFile, ONLY_BUILTIN);
+
+            if (c2.getClassLoader() != urlClassLoader) {
+                throw new RuntimeException("Error in test");
+            }
+
+            if (wb.isSharedClass(LoaderSegregation.class)) {
+                if (wb.isSharedClass(c2)) {
+                    throw new RuntimeException("wb.isSharedClass(c2) should be false - " +
+                                               "unregistered loader cannot load an archived BUILTIN class");
+                }
+            }
+        }
+    }
+}
diff --git a/test/hotspot/jtreg/runtime/appcds/customLoader/test-classes/OnlyBuiltin.java b/test/hotspot/jtreg/runtime/appcds/customLoader/test-classes/OnlyBuiltin.java
new file mode 100644
index 00000000000..90390f52e8b
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/customLoader/test-classes/OnlyBuiltin.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2015, 2017, 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.
+ *
+ */
+
+// See ../LoaderSegregationTest.java for details.
+//
+// This class is archived only as a BUILTIN class.
+public class OnlyBuiltin {}
diff --git a/test/hotspot/jtreg/runtime/appcds/customLoader/test-classes/OnlyUnregistered.java b/test/hotspot/jtreg/runtime/appcds/customLoader/test-classes/OnlyUnregistered.java
new file mode 100644
index 00000000000..9a24f2925ad
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/customLoader/test-classes/OnlyUnregistered.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2015, 2017, 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.
+ *
+ */
+
+// See ../LoaderSegregationTest.java for details.
+//
+// This class is archived only as a UNREGISTERED class.
+public class OnlyUnregistered {}
diff --git a/test/hotspot/jtreg/runtime/appcds/customLoader/test-classes/ProtDomain.java b/test/hotspot/jtreg/runtime/appcds/customLoader/test-classes/ProtDomain.java
new file mode 100644
index 00000000000..bdc37ca78f3
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/customLoader/test-classes/ProtDomain.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+import java.security.ProtectionDomain;
+import java.net.URLClassLoader;
+import java.net.URL;
+import java.io.File;
+
+// Intended to be called from test ProtectionDomain.java
+//
+// ProtDomainClassForArchive is     stored in CDS archive.
+// ProtDomainNotForArchive   is NOT stored in CDS archive.
+//
+// However, they should have the same ProtectionDomain instance.
+public class ProtDomain {
+    public static void main(String args[]) throws Exception {
+        String customLdrPath = args[0];
+
+        URL[] urls = new URL[] {new File(customLdrPath).toURI().toURL()};
+        URLClassLoader ldr = new URLClassLoader(urls);
+        ProtectionDomain domain1 = ldr.loadClass("ProtDomainClassForArchive").getProtectionDomain();
+        ProtectionDomain domain2 = ldr.loadClass("ProtDomainNotForArchive").getProtectionDomain();
+
+        System.out.println("domain1 = " + domain1);
+        System.out.println("domain2  = " + domain2);
+
+        if (domain1 != domain2)
+            throw new RuntimeException("Protection Domains do not match!");
+    }
+}
+
+class ProtDomainClassForArchive {}
+
+class ProtDomainNotForArchive {}
diff --git a/test/hotspot/jtreg/runtime/appcds/customLoader/test-classes/SameNameUnrelatedLoaders.java b/test/hotspot/jtreg/runtime/appcds/customLoader/test-classes/SameNameUnrelatedLoaders.java
new file mode 100644
index 00000000000..10b436563ec
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/customLoader/test-classes/SameNameUnrelatedLoaders.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+import java.io.File;
+import java.net.URL;
+import java.net.URLClassLoader;
+import sun.hotspot.WhiteBox;
+
+public class SameNameUnrelatedLoaders {
+    public static void main(String args[]) throws Exception {
+        String path = args[0];
+        String testCase = args[1];
+        URL url = new File(path).toURI().toURL();
+        URL[] urls = new URL[] {url};
+
+        ClassLoader appLoader = SameNameUnrelatedLoaders.class.getClassLoader();
+        URLClassLoader ldr01 = null;
+        URLClassLoader ldr02 = null;
+
+        switch (testCase) {
+            case "FpBoth":
+                ldr01 = new URLClassLoader(urls);
+                ldr02 = new URLClassLoader(urls);
+            break;
+
+            default:
+                throw new IllegalArgumentException("Invalid testCase ID");
+        }
+
+
+        Class class01 = ldr01.loadClass("CustomLoadee");
+        Class class02  = ldr02.loadClass("CustomLoadee");
+
+        System.out.println("class01 = " + class01);
+        System.out.println("class02 = " + class02);
+
+        if (class01.getClassLoader() != ldr01) {
+            throw new RuntimeException("class01 loaded by wrong loader");
+        }
+        if (class02.getClassLoader() != ldr02) {
+            throw new RuntimeException("class02 loaded by wrong loader");
+        }
+
+        if (true) {
+            if (class01.isAssignableFrom(class02)) {
+                throw new RuntimeException("assignable condition failed");
+            }
+
+            Object obj01 = class01.newInstance();
+            Object obj02 = class02.newInstance();
+
+            if (class01.isInstance(obj02)) {
+                throw new RuntimeException("instance relationship condition 01 failed");
+            }
+            if (class02.isInstance(obj01)) {
+                throw new RuntimeException("instance relationship condition 02 failed");
+            }
+        }
+
+        WhiteBox wb = WhiteBox.getWhiteBox();
+        if (wb.isSharedClass(SameNameUnrelatedLoaders.class)) {
+            boolean class1Shared = wb.isSharedClass(class01);
+            boolean class2Shared = wb.isSharedClass(class02);
+
+            if (testCase.equals("FpBoth")) {
+                if (!class1Shared) {
+                    throw new RuntimeException("first class is not shared");
+                }
+
+                if (class2Shared) {
+                    throw new RuntimeException("second class is shared, " +
+                    "and it should not be - first come first serve violation");
+                }
+            } else {
+                if (! (class1Shared && class2Shared) )
+                    throw new RuntimeException("both classes expected to be shared, but are not");
+            }
+        }
+    }
+}
diff --git a/test/hotspot/jtreg/runtime/appcds/customLoader/test-classes/SimpleHello.java b/test/hotspot/jtreg/runtime/appcds/customLoader/test-classes/SimpleHello.java
new file mode 100644
index 00000000000..3da0d6c8cb5
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/customLoader/test-classes/SimpleHello.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2015, 2017, 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.
+ *
+ */
+
+public class SimpleHello {
+    public static void main(String[] args) {
+        System.out.println("Simple Hello");
+    }
+}
diff --git a/test/hotspot/jtreg/runtime/appcds/customLoader/test-classes/UnintendedLoaders.java b/test/hotspot/jtreg/runtime/appcds/customLoader/test-classes/UnintendedLoaders.java
new file mode 100644
index 00000000000..c380876d3ad
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/customLoader/test-classes/UnintendedLoaders.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+import sun.hotspot.WhiteBox;
+
+public class UnintendedLoaders {
+    public static void main(String[] args) throws Exception {
+        ClassLoader loaders[] = new ClassLoader[2];
+        loaders[0] = UnintendedLoaders.class.getClassLoader(); // app loader
+        loaders[1] = loaders[0].getParent();                   // platform loader
+
+        String[] names = {
+            "CustomLoadee",
+        };
+
+
+        for (int i=0; i<3; i++) {
+            for (String s : names) {
+                try {
+                    if (i <= 1) {
+                        System.out.println(loaders[i].loadClass(s));
+                    } else {
+                        System.out.println(Class.forName(s));
+                    }
+                } catch (ClassNotFoundException e) {
+                    System.out.println("Expected exception:" + e);
+                    continue;
+                }
+                throw new RuntimeException("The class \"" + s + "\" should not be resolved by the application or platform class loader");
+            }
+        }
+    }
+}
diff --git a/test/hotspot/jtreg/runtime/appcds/customLoader/test-classes/UnloadUnregisteredLoader.java b/test/hotspot/jtreg/runtime/appcds/customLoader/test-classes/UnloadUnregisteredLoader.java
new file mode 100644
index 00000000000..47bf6d1b2e1
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/customLoader/test-classes/UnloadUnregisteredLoader.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+import java.io.File;
+import java.net.URL;
+import java.net.URLClassLoader;
+import sun.hotspot.WhiteBox;
+
+public class UnloadUnregisteredLoader {
+    public static void main(String args[]) throws Exception {
+        String path = args[0];
+        URL url = new File(path).toURI().toURL();
+        URL[] urls = new URL[] {url};
+        WhiteBox wb = WhiteBox.getWhiteBox();
+        String className = "CustomLoadee";
+
+        for (int i=0; i<5; i++) {
+            doit(urls, className, (i == 0));
+
+            ClassUnloadCommon.triggerUnloading();
+            ClassUnloadCommon.failIf(wb.isClassAlive(className), "should have been unloaded");
+        }
+    }
+
+  public static void doit(URL urls[], String className, boolean isFirstTime) throws Exception {
+        ClassLoader appLoader = UnloadUnregisteredLoader.class.getClassLoader();
+        URLClassLoader custLoader = new URLClassLoader(urls, appLoader);
+
+        Class klass = custLoader.loadClass(className);
+        WhiteBox wb = WhiteBox.getWhiteBox();
+        if (wb.isSharedClass(UnloadUnregisteredLoader.class)) {
+            if (isFirstTime) {
+                // First time: we should be able to load the class from the CDS archive
+                if (!wb.isSharedClass(klass)) {
+                    throw new RuntimeException("wb.isSharedClass(klass) should be true for first time");
+                }
+            } else {
+                // Second time:  the class in the CDS archive is not available, because it has not been cleaned
+                // up (see bug 8140287), so we must load the class dynamically.
+                //
+                // FIXME: after 8140287 is fixed, class should be shard regardless of isFirstTime.
+                if (wb.isSharedClass(klass)) {
+                    throw new RuntimeException("wb.isSharedClass(klass) should be false for second time");
+                }
+            }
+        }
+    }
+}
diff --git a/test/hotspot/jtreg/runtime/appcds/javaldr/ArrayTest.java b/test/hotspot/jtreg/runtime/appcds/javaldr/ArrayTest.java
new file mode 100644
index 00000000000..3deb59b3219
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/javaldr/ArrayTest.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+/*
+ * @test
+ * @summary test the ability to archive array classes and load them from the archive
+ * AppCDS does not support uncompressed oops
+ * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true)
+ * @library /test/lib /test/hotspot/jtreg/runtime/appcds
+ * @modules jdk.jartool/sun.tools.jar
+ * @compile ArrayTestHelper.java
+ * @build sun.hotspot.WhiteBox
+ * @run main ClassFileInstaller sun.hotspot.WhiteBox
+ * @run main ArrayTest
+ */
+
+import java.util.List;
+import java.util.ArrayList;
+import jdk.test.lib.process.OutputAnalyzer;
+
+public class ArrayTest {
+
+    static String arrayClasses[] = {
+        "ArrayTestHelper",
+        "[Ljava/lang/Comparable;",
+        "[I"
+    };
+
+    public static void main(String[] args) throws Exception {
+        JarBuilder.build("arrayTestHelper", "ArrayTestHelper");
+
+        String appJar = TestCommon.getTestJar("arrayTestHelper.jar");
+        JarBuilder.build(true, "WhiteBox", "sun/hotspot/WhiteBox");
+        String whiteBoxJar = TestCommon.getTestJar("WhiteBox.jar");
+        String bootClassPath = "-Xbootclasspath/a:" + whiteBoxJar;
+
+        // create an archive containing array classes
+        TestCommon.dump(appJar, TestCommon.list(arrayClasses), bootClassPath, "-verbose:class");
+
+        List<String> argsList = new ArrayList<String>();
+        argsList.add("-XX:+UnlockDiagnosticVMOptions");
+        argsList.add("-XX:+WhiteBoxAPI");
+        argsList.add("-cp");
+        argsList.add(appJar);
+        argsList.add(bootClassPath);
+        argsList.add("-verbose:class");
+        argsList.add("ArrayTestHelper");
+        // the following are input args to the ArrayTestHelper.
+        for (int i = 0; i < arrayClasses.length; i++) {
+            argsList.add(arrayClasses[i]);
+        }
+        String[] opts = new String[argsList.size()];
+        opts = argsList.toArray(opts);
+        OutputAnalyzer output = TestCommon.execCommon(opts);
+        TestCommon.checkExec(output);
+    }
+}
diff --git a/test/hotspot/jtreg/runtime/appcds/javaldr/ArrayTestHelper.java b/test/hotspot/jtreg/runtime/appcds/javaldr/ArrayTestHelper.java
new file mode 100644
index 00000000000..614f47e6546
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/javaldr/ArrayTestHelper.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+import sun.hotspot.WhiteBox;
+
+public class ArrayTestHelper {
+    public static void main(String[] args) throws Throwable {
+
+        // load the classes one by one and ensure each one is from
+        // the shared archive
+        for (int i = 0; i < args.length; i++) {
+
+            String cn = args[i].replace('/', '.');
+            Class cls = Class.forName(cn);
+
+            WhiteBox wb = WhiteBox.getWhiteBox();
+            if (wb.isSharedClass(cls)) {
+                System.out.println("As expected, " + args[i] + " is in shared space.");
+            } else {
+                throw new java.lang.RuntimeException(args[i] + " is not in shared space.");
+            }
+        }
+   }
+}
+
diff --git a/test/hotspot/jtreg/runtime/appcds/javaldr/CheckAnonymousClass.java b/test/hotspot/jtreg/runtime/appcds/javaldr/CheckAnonymousClass.java
new file mode 100644
index 00000000000..804c480cd74
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/javaldr/CheckAnonymousClass.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+/*
+ * @test
+ * @summary ensure no anonymous class is being dumped into the CDS archive
+ * AppCDS does not support uncompressed oops
+ * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true)
+ * @library /test/lib /test/hotspot/jtreg/runtime/appcds
+ * @modules jdk.jartool/sun.tools.jar
+ * @compile ../test-classes/Hello.java
+ * @run main CheckAnonymousClass
+ */
+
+import jdk.test.lib.process.OutputAnalyzer;
+
+public class CheckAnonymousClass {
+
+  public static void main(String[] args) throws Exception {
+    JarBuilder.build("hello", "Hello");
+
+    String appJar = TestCommon.getTestJar("hello.jar");
+
+    TestCommon.dump(appJar, TestCommon.list("Hello", "org/omg/CORBA/ORB"),
+        "--add-modules", "java.corba", "-Xlog:class+load=info");
+
+    OutputAnalyzer output = TestCommon.execCommon("-XX:+UnlockDiagnosticVMOptions",
+        "-cp", appJar, "-Xlog:class+load=info", "--add-modules", "java.corba", "Hello");
+
+    String prefix = ".class.load. ";
+    // class name pattern like the following:
+    // jdk.internal.loader.BuiltinClassLoader$$Lambda$1/1816757085
+    // java.lang.invoke.LambdaForm$MH/1585787493
+    String class_pattern = ".*Lambda([a-z0-9$]+)/([0-9]+).*";
+    String suffix = ".*source: shared objects file.*";
+    String pattern = prefix + class_pattern + suffix;
+    // during run time, anonymous classes shouldn't be loaded from the archive
+    try {
+        output.shouldNotMatch(pattern);
+    } catch (Exception e) {
+        TestCommon.checkCommonExecExceptions(output, e);
+    }
+
+    // inspect the archive and make sure no anonymous class is in there
+    output = TestCommon.execCommon("-XX:+UnlockDiagnosticVMOptions",
+        "-cp", appJar, "-Xlog:class+load=info", "-XX:+PrintSharedArchiveAndExit",
+        "-XX:+PrintSharedDictionary", "--add-modules", "java.corba", "Hello");
+    try {
+        output.shouldNotMatch(class_pattern);
+    } catch (Exception e) {
+        TestCommon.checkCommonExecExceptions(output, e);
+    }
+  }
+}
diff --git a/test/hotspot/jtreg/runtime/appcds/javaldr/GCDuringDump.java b/test/hotspot/jtreg/runtime/appcds/javaldr/GCDuringDump.java
new file mode 100644
index 00000000000..22a7dad0a6d
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/javaldr/GCDuringDump.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+/*
+ * @test
+ * @summary When dumping the CDS archive, try to cause garbage collection while classes are being loaded.
+ * @library /test/lib /test/hotspot/jtreg/runtime/appcds /test/hotspot/jtreg/runtime/appcds/test-classes
+ * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true)
+ * @requires vm.flavor != "minimal"
+ * @modules java.base/jdk.internal.misc
+ *          jdk.jartool/sun.tools.jar
+ *          java.management
+ * @build GCDuringDumpTransformer Hello
+ * @run main/othervm GCDuringDump
+ */
+
+import jdk.test.lib.cds.CDSOptions;
+import jdk.test.lib.process.OutputAnalyzer;
+import jdk.test.lib.process.ProcessTools;
+
+public class GCDuringDump {
+    public static String appClasses[] = {
+        "Hello",
+    };
+    public static String agentClasses[] = {
+        "GCDuringDumpTransformer",
+    };
+
+    public static void main(String[] args) throws Throwable {
+        String agentJar =
+            ClassFileInstaller.writeJar("GCDuringDumpTransformer.jar",
+                                        ClassFileInstaller.Manifest.fromSourceFile("GCDuringDumpTransformer.mf"),
+                                        agentClasses);
+
+        String appJar =
+            ClassFileInstaller.writeJar("GCDuringDumpApp.jar", appClasses);
+
+        String gcLog = "-Xlog:gc*=info,gc+region=trace,gc+alloc+region=debug";
+
+        for (int i=0; i<2; i++) {
+            // i = 0 -- run without agent = no extra GCs
+            // i = 1 -- run with agent = cause extra GCs
+
+            String extraArg = (i == 0) ? "-showversion" : "-javaagent:" + agentJar;
+
+            TestCommon.testDump(appJar, TestCommon.list("Hello"),
+                                extraArg, "-Xmx32m", gcLog);
+
+            OutputAnalyzer output = TestCommon.execCommon(
+                "-cp", appJar,
+                "-Xmx32m",
+                "-XX:+PrintSharedSpaces",
+                gcLog,
+                "Hello");
+            TestCommon.checkExec(output);
+        }
+    }
+}
+
diff --git a/test/hotspot/jtreg/runtime/appcds/javaldr/GCDuringDumpTransformer.java b/test/hotspot/jtreg/runtime/appcds/javaldr/GCDuringDumpTransformer.java
new file mode 100644
index 00000000000..bbde62429c4
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/javaldr/GCDuringDumpTransformer.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+import java.lang.instrument.ClassFileTransformer;
+import java.lang.instrument.Instrumentation;
+import java.lang.instrument.IllegalClassFormatException;
+import java.security.ProtectionDomain;
+
+public class GCDuringDumpTransformer implements ClassFileTransformer {
+    static int n = 0;
+    public byte[] transform(ClassLoader loader, String name, Class<?> classBeingRedefined,
+                            ProtectionDomain pd, byte[] buffer) throws IllegalClassFormatException {
+        n++;
+
+        System.out.println("dump time loading: " + name + " in loader: " + loader);
+        System.out.println("making garbage: " + n);
+        try {
+            makeGarbage();
+        } catch (Throwable t) {
+            t.printStackTrace();
+            try {
+                Thread.sleep(200); // let GC to have a chance to run
+            } catch (Throwable t2) {}
+        }
+        System.out.println("making garbage: done");
+
+        return null;
+    }
+
+    private static Instrumentation savedInstrumentation;
+
+    public static void premain(String agentArguments, Instrumentation instrumentation) {
+        System.out.println("ClassFileTransformer.premain() is called");
+        instrumentation.addTransformer(new GCDuringDumpTransformer(), /*canRetransform=*/true);
+        savedInstrumentation = instrumentation;
+    }
+
+    public static Instrumentation getInstrumentation() {
+        return savedInstrumentation;
+    }
+
+    public static void agentmain(String args, Instrumentation inst) throws Exception {
+        premain(args, inst);
+    }
+
+    public static void makeGarbage() {
+        for (int x=0; x<10; x++) {
+            Object[] a = new Object[10000];
+        }
+    }
+}
diff --git a/test/hotspot/jtreg/runtime/appcds/javaldr/GCDuringDumpTransformer.mf b/test/hotspot/jtreg/runtime/appcds/javaldr/GCDuringDumpTransformer.mf
new file mode 100644
index 00000000000..8b44e6e9801
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/javaldr/GCDuringDumpTransformer.mf
@@ -0,0 +1,5 @@
+Manifest-Version: 1.0
+Premain-Class: GCDuringDumpTransformer
+Agent-Class: GCDuringDumpTransformer
+Can-Retransform-Classes: true
+Can-Redefine-Classes: true
diff --git a/test/hotspot/jtreg/runtime/appcds/javaldr/GCSharedStringsDuringDump.java b/test/hotspot/jtreg/runtime/appcds/javaldr/GCSharedStringsDuringDump.java
new file mode 100644
index 00000000000..8faffaaf14e
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/javaldr/GCSharedStringsDuringDump.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+/*
+ * @test
+ * @summary Similar to GCDuringDumping.java, this test adds the -XX:SharedArchiveConfigFile
+ *          option for testing the interaction with GC and shared strings.
+ * @library /test/lib /test/hotspot/jtreg/runtime/appcds /test/hotspot/jtreg/runtime/appcds/test-classes
+ * @requires (sun.arch.data.model != "32") & (os.family != "windows")
+ * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true)
+ * @requires vm.flavor != "minimal"
+ * @requires vm.gc.G1
+ * @modules java.base/jdk.internal.misc
+ *          jdk.jartool/sun.tools.jar
+ *          java.management
+ * @build sun.hotspot.WhiteBox GCDuringDumpTransformer GCSharedStringsDuringDumpWb
+ * @run main ClassFileInstaller sun.hotspot.WhiteBox
+ * @run main/othervm/timeout=480 GCSharedStringsDuringDump
+ */
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import jdk.test.lib.cds.CDSOptions;
+import jdk.test.lib.process.OutputAnalyzer;
+import jdk.test.lib.process.ProcessTools;
+import sun.hotspot.WhiteBox;
+
+public class GCSharedStringsDuringDump {
+    public static String appClasses[] = {
+        "GCSharedStringsDuringDumpWb",
+    };
+    public static String agentClasses[] = {
+        "GCDuringDumpTransformer",
+    };
+
+    public static void main(String[] args) throws Throwable {
+        String agentJar =
+            ClassFileInstaller.writeJar("GCDuringDumpTransformer.jar",
+                                        ClassFileInstaller.Manifest.fromSourceFile("GCDuringDumpTransformer.mf"),
+                                        agentClasses);
+
+        String appJar =
+            ClassFileInstaller.writeJar("GCSharedStringsDuringDumpApp.jar", appClasses);
+
+        String gcLog = "-Xlog:gc*=info,gc+region=trace,gc+alloc+region=debug";
+
+        String sharedArchiveCfgFile =
+            System.getProperty("user.dir") + File.separator + "GCSharedStringDuringDump_gen.txt";
+        try (FileOutputStream fos = new FileOutputStream(sharedArchiveCfgFile)) {
+            PrintWriter out = new PrintWriter(new OutputStreamWriter(fos));
+            out.println("VERSION: 1.0");
+            out.println("@SECTION: String");
+            out.println("31: shared_test_string_unique_14325");
+            for (int i=0; i<100000; i++) {
+                String s = "generated_string " + i;
+                out.println(s.length() + ": " + s);
+            }
+            out.close();
+        }
+
+        JarBuilder.build(true, "WhiteBox", "sun/hotspot/WhiteBox");
+        String whiteBoxJar = TestCommon.getTestJar("WhiteBox.jar");
+        String bootClassPath = "-Xbootclasspath/a:" + whiteBoxJar;
+
+        for (int i=0; i<2; i++) {
+            // i = 0 -- run without agent = no extra GCs
+            // i = 1 -- run with agent = cause extra GCs
+
+            String extraArg = (i == 0) ? "-showversion" : "-javaagent:" + agentJar;
+
+            OutputAnalyzer output = TestCommon.dump(
+                                appJar, TestCommon.list("GCSharedStringsDuringDumpWb"),
+                                bootClassPath, extraArg, "-Xmx32m", gcLog,
+                                "-XX:+UseCompressedOops", "-XX:+UseG1GC",
+                                "-XX:SharedReadOnlySize=30m",
+                                "-XX:SharedArchiveConfigFile=" + sharedArchiveCfgFile);
+
+            if (output.getStdout().contains("Too many string space regions") ||
+                output.getStderr().contains("Unable to write archive heap memory regions") ||
+                output.getStdout().contains("Try increasing NewSize") ||
+                !output.getStdout().contains("oa0 space:") ||
+                output.getExitValue() != 0) {
+                // Try again with larger heap and NewSize, this should increase the
+                // G1 heap region size to 2M
+                TestCommon.testDump(
+                    appJar, TestCommon.list("GCSharedStringsDuringDumpWb"),
+                    bootClassPath, extraArg, "-Xmx8g", "-XX:NewSize=8m", gcLog,
+                    "-XX:+UseCompressedOops", "-XX:+UseG1GC",
+                    "-XX:SharedReadOnlySize=30m",
+                    "-XX:SharedArchiveConfigFile=" + sharedArchiveCfgFile);
+            }
+
+            output = TestCommon.execCommon(
+                "-cp", appJar,
+                bootClassPath,
+                "-Xmx32m",
+                "-XX:+PrintSharedSpaces",
+                "-XX:+UseCompressedOops",
+                "-XX:+UseG1GC",
+                "-XX:+UnlockDiagnosticVMOptions",
+                "-XX:+WhiteBoxAPI",
+                "-XX:SharedReadOnlySize=30m",
+                gcLog,
+                "GCSharedStringsDuringDumpWb");
+            TestCommon.checkExec(output);
+        }
+    }
+}
+
diff --git a/test/hotspot/jtreg/runtime/appcds/javaldr/GCSharedStringsDuringDumpWb.java b/test/hotspot/jtreg/runtime/appcds/javaldr/GCSharedStringsDuringDumpWb.java
new file mode 100644
index 00000000000..3fcebe5f5ed
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/javaldr/GCSharedStringsDuringDumpWb.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+import sun.hotspot.WhiteBox;
+
+public class GCSharedStringsDuringDumpWb {
+    public static void main(String[] args) throws Exception {
+        WhiteBox wb = WhiteBox.getWhiteBox();
+        String s = "shared_test_string_unique_14325";
+        s = s.intern();
+        CheckString(wb, s);
+        for (int i=0; i<100000; i++) {
+            s = "generated_string " + i;
+            s = s.intern();
+            CheckString(wb, s);
+        }
+    }
+
+    public static void CheckString(WhiteBox wb, String s) {
+        if (!wb.areSharedStringsIgnored() && !wb.isShared(s)) {
+            throw new RuntimeException("String is not shared.");
+        }
+    }
+}
diff --git a/test/hotspot/jtreg/runtime/appcds/jigsaw/CheckUnsupportedDumpingOptions.java b/test/hotspot/jtreg/runtime/appcds/jigsaw/CheckUnsupportedDumpingOptions.java
new file mode 100644
index 00000000000..99efbcbf135
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/jigsaw/CheckUnsupportedDumpingOptions.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+/*
+ * @test
+ * @summary Abort dumping if any of the new jigsaw vm options is specified.
+ * AppCDS does not support uncompressed oops
+ * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true)
+ * @library /test/lib ..
+ * @modules java.base/jdk.internal.misc
+ *          java.management
+ *          jdk.jartool/sun.tools.jar
+ *          jdk.internal.jvmstat/sun.jvmstat.monitor
+ * @compile ../test-classes/Hello.java
+ * @run main CheckUnsupportedDumpingOptions
+ */
+
+import jdk.test.lib.compiler.InMemoryJavaCompiler;
+import jdk.test.lib.process.OutputAnalyzer;
+
+public class CheckUnsupportedDumpingOptions {
+    private static final String[] jigsawOptions = {
+        "-m",
+        "--limit-modules",
+        "--module-path",
+        "--upgrade-module-path",
+        "--patch-module"
+    };
+    private static final String[] optionValues = {
+        "mymod",
+        "mymod",
+        "mydir",
+        ".",
+        "java.naming=javax.naming.spi.NamingManger"
+    };
+    private static final int infoIdx = 1;
+
+    public static void main(String[] args) throws Exception {
+        String source = "package javax.naming.spi; "                +
+                        "public class NamingManager { "             +
+                        "    static { "                             +
+                        "        System.out.println(\"I pass!\"); " +
+                        "    } "                                    +
+                        "}";
+        ClassFileInstaller.writeClassToDisk("javax/naming/spi/NamingManager",
+            InMemoryJavaCompiler.compile("javax.naming.spi.NamingManager", source, "--patch-module=java.naming"),
+            "mods/java.naming");
+
+        JarBuilder.build("hello", "Hello");
+        String appJar = TestCommon.getTestJar("hello.jar");
+        String appClasses[] = {"Hello"};
+        for (int i = 0; i < jigsawOptions.length; i++) {
+            OutputAnalyzer output;
+            if (i == 5) {
+                // --patch-module
+                output = TestCommon.dump(appJar, appClasses, "-Xlog:cds,cds+hashtables",
+                                         jigsawOptions[i] + optionValues[i] + appJar);
+            } else {
+                output = TestCommon.dump(appJar, appClasses, "-Xlog:cds,cds+hashtables",
+                                         jigsawOptions[i], optionValues[i]);
+            }
+            if (i < infoIdx) {
+                output.shouldContain("Cannot use the following option " +
+                    "when dumping the shared archive: " + jigsawOptions[i])
+                      .shouldHaveExitValue(1);
+            } else {
+                output.shouldContain("Info: the " + jigsawOptions[i] +
+                    " option is ignored when dumping the shared archive");
+                if (optionValues[i].equals("mymod")) {
+                      // java will throw FindException for a module
+                      // which cannot be found during init_phase2() of vm init
+                      output.shouldHaveExitValue(1)
+                            .shouldContain("java.lang.module.FindException: Module mymod not found");
+                } else {
+                      output.shouldHaveExitValue(0);
+                }
+            }
+        }
+    }
+}
+
diff --git a/test/hotspot/jtreg/runtime/appcds/jigsaw/JigsawOptionsCombo.java b/test/hotspot/jtreg/runtime/appcds/jigsaw/JigsawOptionsCombo.java
new file mode 100644
index 00000000000..2513d0e5a19
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/jigsaw/JigsawOptionsCombo.java
@@ -0,0 +1,216 @@
+/*
+ * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+/*
+ * @test
+ * @summary Test combinations of jigsaw options that affect the use of AppCDS
+ *
+ * AppCDS does not support uncompressed oops
+ * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true)
+ * @library /test/lib ..
+ * @modules java.base/jdk.internal.misc
+ *          java.management
+ *          jdk.jartool/sun.tools.jar
+ *          jdk.internal.jvmstat/sun.jvmstat.monitor
+ * @compile ../test-classes/Hello.java ../test-classes/HelloMore.java
+ * @run main JigsawOptionsCombo
+ */
+import jdk.test.lib.compiler.InMemoryJavaCompiler;
+import jdk.test.lib.process.OutputAnalyzer;
+import java.util.ArrayList;
+
+
+// Remaining WORK: TODO:
+// 1. test with -m initial-module; waiting for changes from Chris will provide
+//    utils to build modules
+// 2. Loading classes from Jmod files - waiting on utils
+// 3. Loading classes from exploded module dir"
+
+public class JigsawOptionsCombo {
+
+    public static void main(String[] args) throws Exception {
+        String source = "package javax.naming.spi; "                +
+                        "public class NamingManager { "             +
+                        "    static { "                             +
+                        "        System.out.println(\"I pass!\"); " +
+                        "    } "                                    +
+                        "}";
+        ClassFileInstaller.writeClassToDisk("javax/naming/spi/NamingManager",
+            InMemoryJavaCompiler.compile("javax.naming.spi.NamingManager", source, "--patch-module=java.naming"),
+            "mods/java.naming");
+
+        JarBuilder.build("hello", "Hello");
+        JarBuilder.build("hello_more", "HelloMore");
+
+        (new JigsawOptionsCombo()).runTests();
+    }
+
+
+    private ArrayList<TestCase> testCaseTable = new ArrayList<TestCase>();
+
+    public static String infoDuringDump(String option) {
+        return "Info: the " + option +
+            " option is ignored when dumping the shared archive";
+    }
+
+    public void runTests() throws Exception {
+
+        testCaseTable.add(new TestCase(
+            "basic: Basic dump and execute, to verify the test plumbing works",
+            "", "", 0,
+            "", "", 0) );
+
+        String bcpArg = "-Xbootclasspath/a:" +
+        TestCommon.getTestJar("hello_more.jar");
+
+        testCaseTable.add(new TestCase(
+            "Xbootclasspath/a: is OK for both dump and run time",
+            bcpArg, "", 0,
+            bcpArg, "", 0) );
+
+        testCaseTable.add(new TestCase(
+            "module-path-01: --module-path is ignored for dump time",
+            "--module-path mods",
+            infoDuringDump("--module-path"), 0,
+            null, null, 0) );
+
+        testCaseTable.add(new TestCase(
+            "module-path-02: --module-path is ok for run time",
+            "", "", 0,
+            "--module-path mods", "", 0) );
+
+        testCaseTable.add(new TestCase(
+            "add-modules-01: --add-modules is ok at dump time",
+            "--add-modules java.management",
+            "", 0,
+            null, null, 0) );
+
+        testCaseTable.add(new TestCase(
+            "add-modules-02: --add-modules is ok at run time",
+            "", "", 0,
+            "--add-modules java.management", "", 0) );
+
+        testCaseTable.add(new TestCase(
+            "limit-modules-01: --limit-modules is ignored at dump time",
+            "--limit-modules java.base",
+            infoDuringDump("--limit-modules"), 0,
+            null, null, 0) );
+
+        testCaseTable.add(new TestCase(
+            "limit-modules-02: --limit-modules is ok at run time",
+            "", "", 0,
+            "--limit-modules java.base", "", 0) );
+
+        testCaseTable.add(new TestCase(
+            "upgrade-module-path-01: --upgrade-module-path is ignored at dump time",
+            "--upgrade-module-path mods",
+            infoDuringDump("--upgrade-module-path"), 0,
+            null, null, 0) );
+
+        testCaseTable.add(new TestCase(
+            "-upgrade-module-path-module-path-02: --upgrade-module-path is ok at run time",
+            "", "", 0,
+            "--upgrade-module-path mods", "", 0) );
+
+        for (TestCase tc : testCaseTable) tc.execute();
+    }
+
+
+    // class representing a singe test case
+    public class TestCase {
+        String description;
+        String dumpTimeArgs;
+        String dumpTimeExpectedOutput;
+        int    dumpTimeExpectedExitValue;
+        String runTimeArgs;
+        String runTimeExpectedOutput;
+        int    runTimeExpectedExitValue;
+
+        private String appJar = TestCommon.getTestJar("hello.jar");
+        private String appClasses[] = {"Hello"};
+
+
+        public TestCase(String description,
+            String dumpTimeArgs, String dumpTimeExpectedOutput, int dumpTimeExpectedExitValue,
+            String runTimeArgs, String runTimeExpectedOutput, int runTimeExpectedExitValue) {
+
+            this.description = description;
+            this.dumpTimeArgs = dumpTimeArgs;
+            this.dumpTimeExpectedOutput = dumpTimeExpectedOutput;
+            this.dumpTimeExpectedExitValue = dumpTimeExpectedExitValue;
+            this.runTimeArgs = runTimeArgs;
+            this.runTimeExpectedOutput = runTimeExpectedOutput;
+            this.runTimeExpectedExitValue = runTimeExpectedExitValue;
+        }
+
+
+        public void execute() throws Exception {
+            System.out.println("Description: " + description);
+
+            // ===== dump step - create the archive
+            OutputAnalyzer dumpOutput = TestCommon.dump(
+                appJar, appClasses, getDumpOptions());
+
+            if (dumpTimeExpectedExitValue == 0) {
+                TestCommon.checkDump(dumpOutput, dumpTimeExpectedOutput);
+            } else {
+                dumpOutput.shouldMatch(dumpTimeExpectedOutput);
+                dumpOutput.shouldHaveExitValue(dumpTimeExpectedExitValue);
+            }
+
+            // ===== exec step - use the archive
+            if (runTimeArgs != null) {
+                OutputAnalyzer execOutput = TestCommon.exec(appJar, getRunOptions());
+
+                if (runTimeExpectedExitValue == 0) {
+                    TestCommon.checkExec(execOutput, runTimeExpectedOutput, "Hello World");
+                } else {
+                    execOutput.shouldMatch(dumpTimeExpectedOutput);
+                    execOutput.shouldHaveExitValue(dumpTimeExpectedExitValue);
+                }
+            }
+        }
+
+
+        // dump command line options can be separated by a space
+        private String[] getDumpOptions() {
+            return dumpTimeArgs.split(" ");
+        }
+
+
+        // run command line options can be separated by a space
+        private String[] getRunOptions() {
+            ArrayList<String> result = new ArrayList<>();
+
+            if (runTimeArgs != "") {
+                String splitArgs[] = runTimeArgs.split(" ");
+                for (String arg : splitArgs)
+                    result.add(arg);
+            }
+
+            result.add("Hello");
+            return result.toArray(new String[1]);
+        }
+    }
+}
diff --git a/test/hotspot/jtreg/runtime/appcds/jigsaw/PatchModule/AppClassInCP.java b/test/hotspot/jtreg/runtime/appcds/jigsaw/PatchModule/AppClassInCP.java
new file mode 100644
index 00000000000..07a09fda627
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/jigsaw/PatchModule/AppClassInCP.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+/*
+ * @test
+ * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true)
+ * @summary a test to demonstrate that an application class in the -cp
+ *          will be archived although --patch-module is specified. The class in
+ *          the -cp has no dependencies on the class in the --patch-module.
+ * @library ../..
+ * @library /test/hotspot/jtreg/testlibrary
+ * @library /test/lib
+ * @modules java.base/jdk.internal.misc
+ *          jdk.jartool/sun.tools.jar
+ * @build PatchMain
+ * @run main AppClassInCP
+ */
+
+import java.io.File;
+import jdk.test.lib.compiler.InMemoryJavaCompiler;
+import jdk.test.lib.process.OutputAnalyzer;
+
+public class AppClassInCP {
+    private static String moduleJar;
+    private static String appJar;
+
+    public static void main(String args[]) throws Throwable {
+
+        // Create a class file in the module java.naming. This class file
+        // will be put in the javanaming.jar file.
+        String source = "package javax.naming.spi; "                +
+                        "public class NamingManager { "             +
+                        "    static { "                             +
+                        "        System.out.println(\"I pass!\"); " +
+                        "    } "                                    +
+                        "}";
+
+        String classDir = System.getProperty("test.classes");
+
+        ClassFileInstaller.writeClassToDisk("javax/naming/spi/NamingManager",
+             InMemoryJavaCompiler.compile("javax.naming.spi.NamingManager", source, "--patch-module=java.naming"),
+             classDir);
+
+        // Build the jar file that will be used for the module "java.naming".
+        JarBuilder.build("javanaming", "javax/naming/spi/NamingManager");
+        moduleJar = TestCommon.getTestJar("javanaming.jar");
+
+        String source2 = "package mypackage; "                +
+                        "public class Hello { "             +
+                        "    static { "                             +
+                        "        System.out.println(\"Hello!\"); " +
+                        "    } "                                    +
+                        "}";
+        ClassFileInstaller.writeClassToDisk("mypackage/Hello",
+             InMemoryJavaCompiler.compile("mypackage.Hello", source2),
+             classDir);
+
+        JarBuilder.build("hello", "mypackage/Hello");
+        appJar = TestCommon.getTestJar("hello.jar");
+
+        System.out.println("Test dumping with --patch-module");
+        OutputAnalyzer output =
+            TestCommon.dump(appJar,
+                TestCommon.list("javax/naming/spi/NamingManager", "mypackage/Hello"),
+                "--patch-module=java.naming=" + moduleJar,
+                "-Xlog:class+load",
+                "PatchMain", "javax.naming.spi.NamingManager", "mypackage.Hello");
+        TestCommon.checkDump(output, "Loading classes to share");
+
+        String classPath = appJar + File.pathSeparator + classDir;
+        System.out.println("classPath: " + classPath);
+        output = TestCommon.execCommon(
+            "-XX:+UnlockDiagnosticVMOptions",
+            "-cp", classPath,
+            "--patch-module=java.naming=" + moduleJar,
+            "-Xlog:class+load",
+            "PatchMain", "javax.naming.spi.NamingManager", "mypackage.Hello");
+        TestCommon.checkExec(output,
+            "I pass!",
+            "Hello!",
+            "Hello source: shared objects file");
+    }
+}
diff --git a/test/hotspot/jtreg/runtime/appcds/jigsaw/PatchModule/CustomPackage.java b/test/hotspot/jtreg/runtime/appcds/jigsaw/PatchModule/CustomPackage.java
new file mode 100644
index 00000000000..9f6fbbb2ad1
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/jigsaw/PatchModule/CustomPackage.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+/*
+ * @test
+ * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true)
+ * @summary if a class is defined to a package which is not defined to any
+ *          module in the jimage, the class will not be found during dump
+ *          time but it will be used during run time.
+ * @library ../..
+ * @library /test/hotspot/jtreg/testlibrary
+ * @library /test/lib
+ * @modules java.base/jdk.internal.misc
+ *          jdk.jartool/sun.tools.jar
+ * @build PatchMain
+ * @run main CustomPackage
+ */
+
+import jdk.test.lib.compiler.InMemoryJavaCompiler;
+import jdk.test.lib.process.OutputAnalyzer;
+
+public class CustomPackage {
+    private static String moduleJar;
+
+    public static void main(String args[]) throws Throwable {
+
+        // Create a class file in the module java.naming. This class file
+        // will be put in the javanaming.jar file.
+        String source = "package javax.naming.myspi; "                +
+                        "public class NamingManager { "             +
+                        "    static { "                             +
+                        "        System.out.println(\"I pass!\"); " +
+                        "    } "                                    +
+                        "}";
+
+        ClassFileInstaller.writeClassToDisk("javax/naming/myspi/NamingManager",
+             InMemoryJavaCompiler.compile("javax.naming.myspi.NamingManager", source, "--patch-module=java.naming"),
+             System.getProperty("test.classes"));
+
+        // Build the jar file that will be used for the module "java.naming".
+        JarBuilder.build("javanaming", "javax/naming/myspi/NamingManager");
+        moduleJar = TestCommon.getTestJar("javanaming.jar");
+
+        System.out.println("Test dumping with --patch-module");
+        OutputAnalyzer output =
+            TestCommon.dump(null,
+                TestCommon.list("javax/naming/myspi/NamingManager"),
+                "--patch-module=java.naming=" + moduleJar,
+                "-Xlog:class+load",
+                "-Xlog:class+path=info",
+                "PatchMain", "javax.naming.myspi.NamingManager");
+        TestCommon.checkDump(output, "Preload Warning: Cannot find javax/naming/myspi/NamingManager");
+
+        output = TestCommon.execCommon(
+            "-XX:+UnlockDiagnosticVMOptions",
+            "--patch-module=java.naming=" + moduleJar,
+            "-Xlog:class+load",
+            "-Xlog:class+path=info",
+            "PatchMain", "javax.naming.myspi.NamingManager");
+        TestCommon.checkExec(output, "I pass!");
+    }
+}
diff --git a/test/hotspot/jtreg/runtime/appcds/jigsaw/PatchModule/MismatchedPatchModule.java b/test/hotspot/jtreg/runtime/appcds/jigsaw/PatchModule/MismatchedPatchModule.java
new file mode 100644
index 00000000000..b614f58a551
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/jigsaw/PatchModule/MismatchedPatchModule.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+/*
+ * @test
+ * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true)
+ * @summary different settings of --patch-module at dump time and runtime are
+ *          acceptable. The class found in runtime --patch-module entry should
+ *          be used.
+ * @library ../..
+ * @library /test/hotspot/jtreg/testlibrary
+ * @library /test/lib
+ * @modules java.base/jdk.internal.misc
+ *          jdk.jartool/sun.tools.jar
+ * @build PatchMain
+ * @run main MismatchedPatchModule
+ */
+
+import jdk.test.lib.compiler.InMemoryJavaCompiler;
+import jdk.test.lib.process.OutputAnalyzer;
+
+public class MismatchedPatchModule {
+    private static String moduleJar;
+
+    public static void main(String args[]) throws Throwable {
+
+        // Create a class file in the module java.naming. This class file
+        // will be put in the javanaming.jar file.
+        String source = "package javax.naming.spi; "                +
+                        "public class NamingManager { "             +
+                        "    static { "                             +
+                        "        System.out.println(\"I pass!\"); " +
+                        "    } "                                    +
+                        "}";
+
+        ClassFileInstaller.writeClassToDisk("javax/naming/spi/NamingManager",
+             InMemoryJavaCompiler.compile("javax.naming.spi.NamingManager", source, "--patch-module=java.naming"),
+             System.getProperty("test.classes"));
+
+        // Build the jar file that will be used for the module "java.naming".
+        JarBuilder.build("javanaming", "javax/naming/spi/NamingManager");
+        moduleJar = TestCommon.getTestJar("javanaming.jar");
+
+        // Case 1: --patch-module specified for dump time and run time
+        System.out.println("Case 1: --patch-module specified for dump time and run time");
+        OutputAnalyzer output =
+            TestCommon.dump(null,
+                TestCommon.list("javax/naming/spi/NamingManager"),
+                "--patch-module=java.naming=" + moduleJar,
+                "PatchMain", "javax.naming.spi.NamingManager");
+        TestCommon.checkDump(output, "Loading classes to share");
+
+        // javax.naming.spi.NamingManager is not patched at runtime
+        output = TestCommon.execCommon(
+            "-XX:+UnlockDiagnosticVMOptions",
+            "--patch-module=java.naming2=" + moduleJar,
+            "-Xlog:class+path=info",
+            "PatchMain", "javax.naming.spi.NamingManager");
+        output.shouldNotContain("I pass!");
+
+        // Case 2: --patch-module specified for dump time but not for run time
+        System.out.println("Case 2: --patch-module specified for dump time but not for run time");
+        output =
+            TestCommon.dump(null,
+                TestCommon.list("javax/naming/spi/NamingManager"),
+                "--patch-module=java.naming=" + moduleJar,
+                "PatchMain", "javax.naming.spi.NamingManager");
+        TestCommon.checkDump(output, "Loading classes to share");
+
+        // javax.naming.spi.NamingManager is not patched at runtime
+        output = TestCommon.execCommon(
+            "-XX:+UnlockDiagnosticVMOptions",
+            "-Xlog:class+path=info",
+            "PatchMain", "javax.naming.spi.NamingManager");
+        output.shouldNotContain("I pass!");
+
+        // Case 3: --patch-module specified for run time but not for dump time
+        System.out.println("Case 3: --patch-module specified for run time but not for dump time");
+        output =
+            TestCommon.dump(null,
+                TestCommon.list("javax/naming/spi/NamingManager"),
+                "PatchMain", "javax.naming.spi.NamingManager");
+        TestCommon.checkDump(output, "Loading classes to share");
+
+        // javax.naming.spi.NamingManager is patched at runtime
+        output = TestCommon.execCommon(
+            "-XX:+UnlockDiagnosticVMOptions",
+            "--patch-module=java.naming=" + moduleJar,
+            "-Xlog:class+path=info",
+            "PatchMain", "javax.naming.spi.NamingManager");
+        TestCommon.checkExec(output, "I pass!");
+
+        // Case 4: mismatched --patch-module entry counts between dump time and run time
+        System.out.println("Case 4: mismatched --patch-module entry counts between dump time and run time");
+        output =
+            TestCommon.dump(null,
+                TestCommon.list("javax/naming/spi/NamingManager"),
+                "--patch-module=java.naming=" + moduleJar,
+                "PatchMain", "javax.naming.spi.NamingManager");
+        TestCommon.checkDump(output, "Loading classes to share");
+
+        // javax.naming.spi.NamingManager is patched at runtime
+        output = TestCommon.execCommon(
+            "-XX:+UnlockDiagnosticVMOptions",
+            "--patch-module=java.naming=" + moduleJar,
+            "--patch-module=java.naming2=" + moduleJar,
+            "-Xlog:class+path=info",
+            "PatchMain", "javax.naming.spi.NamingManager");
+        TestCommon.checkExec(output, "I pass!");
+    }
+}
diff --git a/test/hotspot/jtreg/runtime/appcds/jigsaw/PatchModule/PatchDir.java b/test/hotspot/jtreg/runtime/appcds/jigsaw/PatchModule/PatchDir.java
new file mode 100644
index 00000000000..854d4419922
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/jigsaw/PatchModule/PatchDir.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+/*
+ * @test
+ * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true)
+ * @summary a simple test to ensure that a directory in the --patch-module
+ *          option does not affect dump process
+ * @library ../..
+ * @library /test/hotspot/jtreg/testlibrary
+ * @library /test/lib
+ * @modules java.base/jdk.internal.misc
+ *          jdk.jartool/sun.tools.jar
+ * @build PatchMain
+ * @run main PatchDir
+ */
+
+import java.io.File;
+import jdk.test.lib.compiler.InMemoryJavaCompiler;
+
+public class PatchDir {
+    private static String moduleJar;
+
+    public static void main(String args[]) throws Throwable {
+
+        // Create a class file in the module java.naming. This class file
+        // will be put in the javanaming.jar file.
+        String source = "package javax.naming.spi; "                +
+                        "public class NamingManager { "             +
+                        "    static { "                             +
+                        "        System.out.println(\"I pass!\"); " +
+                        "    } "                                    +
+                        "}";
+
+        String classDir = System.getProperty("test.classes");
+        ClassFileInstaller.writeClassToDisk("javax/naming/spi/NamingManager",
+             InMemoryJavaCompiler.compile("javax.naming.spi.NamingManager", source, "--patch-module=java.naming"),
+             classDir);
+
+        JarBuilder.build("javanaming", "javax/naming/spi/NamingManager");
+        moduleJar = TestCommon.getTestJar("javanaming.jar");
+
+        System.out.println("Test dumping with --patch-module");
+        TestCommon.dump(null,
+            TestCommon.list("javax/naming/spi/NamingManager"),
+            "--patch-module=java.naming=" + moduleJar,
+            "-Xlog:class+load",
+            "PatchMain", "javax.naming.spi.NamingManager")
+            .shouldContain("Loading classes to share")
+            .shouldHaveExitValue(0);
+    }
+}
diff --git a/test/hotspot/jtreg/runtime/appcds/jigsaw/PatchModule/PatchJavaBase.java b/test/hotspot/jtreg/runtime/appcds/jigsaw/PatchModule/PatchJavaBase.java
new file mode 100644
index 00000000000..726cf1b7675
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/jigsaw/PatchModule/PatchJavaBase.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+/*
+ * @test
+ * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true)
+ * @summary sharing is disabled if java.base is patch at runtime
+ * @library ../..
+ * @library /test/hotspot/jtreg/testlibrary
+ * @library /test/lib
+ * @modules java.base/jdk.internal.misc
+ *          jdk.jartool/sun.tools.jar
+ * @build PatchMain
+ * @run main PatchJavaBase
+ */
+
+import jdk.test.lib.compiler.InMemoryJavaCompiler;
+import jdk.test.lib.process.OutputAnalyzer;
+
+public class PatchJavaBase {
+    private static String moduleJar;
+
+    public static void main(String args[]) throws Throwable {
+
+        String source = "package java.lang; "                       +
+                        "public class NewClass { "                  +
+                        "    static { "                             +
+                        "        System.out.println(\"I pass!\"); " +
+                        "    } "                                    +
+                        "}";
+
+        ClassFileInstaller.writeClassToDisk("java/lang/NewClass",
+             InMemoryJavaCompiler.compile("java.lang.NewClass", source, "--patch-module=java.base"),
+             System.getProperty("test.classes"));
+
+        JarBuilder.build("javabase", "java/lang/NewClass");
+        moduleJar = TestCommon.getTestJar("javabase.jar");
+
+        System.out.println("Test dumping with --patch-module");
+        OutputAnalyzer output =
+            TestCommon.dump(null, null,
+                "--patch-module=java.base=" + moduleJar,
+                "PatchMain", "java.lang.NewClass");
+        TestCommon.checkDump(output, "Loading classes to share");
+
+        output = TestCommon.execCommon(
+            "-XX:+UnlockDiagnosticVMOptions",
+            "--patch-module=java.base=" + moduleJar,
+            "PatchMain", "java.lang.NewClass");
+        output.shouldContain("CDS is disabled when java.base module is patched");
+    }
+}
diff --git a/test/hotspot/jtreg/runtime/appcds/jigsaw/PatchModule/PatchMain.java b/test/hotspot/jtreg/runtime/appcds/jigsaw/PatchModule/PatchMain.java
new file mode 100644
index 00000000000..86430957875
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/jigsaw/PatchModule/PatchMain.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2016, 2017, 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.
+ *
+ */
+
+// This loads the class affected by the --patch-module option.  For the test to pass
+// it must load the class from the --patch-module directory, not the jimage file.
+public class PatchMain {
+    public static void main(String[] args) throws Exception {
+        for (int i = 0; i < args.length; i++) {
+            Class.forName(args[i]);
+        }
+    }
+}
diff --git a/test/hotspot/jtreg/runtime/appcds/jigsaw/PatchModule/Simple.java b/test/hotspot/jtreg/runtime/appcds/jigsaw/PatchModule/Simple.java
new file mode 100644
index 00000000000..80959541cfd
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/jigsaw/PatchModule/Simple.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+/*
+ * @test
+ * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true)
+ * @summary a simple test to ensure that class is loaded from jar file in --patch-module at runtime
+ * @library ../..
+ * @library /test/hotspot/jtreg/testlibrary
+ * @library /test/lib
+ * @modules java.base/jdk.internal.misc
+ *          jdk.jartool/sun.tools.jar
+ * @build PatchMain
+ * @run main Simple
+ */
+
+import jdk.test.lib.compiler.InMemoryJavaCompiler;
+import jdk.test.lib.process.OutputAnalyzer;
+
+public class Simple {
+    private static String moduleJar;
+
+    public static void main(String args[]) throws Throwable {
+
+        // Create a class file in the module java.naming. This class file
+        // will be put in the javanaming.jar file.
+        String source = "package javax.naming.spi; "                +
+                        "public class NamingManager { "             +
+                        "    static { "                             +
+                        "        System.out.println(\"I pass!\"); " +
+                        "    } "                                    +
+                        "}";
+
+        ClassFileInstaller.writeClassToDisk("javax/naming/spi/NamingManager",
+             InMemoryJavaCompiler.compile("javax.naming.spi.NamingManager", source, "--patch-module=java.naming"),
+             System.getProperty("test.classes"));
+
+        // Build the jar file that will be used for the module "java.naming".
+        JarBuilder.build("javanaming", "javax/naming/spi/NamingManager");
+        moduleJar = TestCommon.getTestJar("javanaming.jar");
+
+        System.out.println("Test dumping with --patch-module");
+        OutputAnalyzer output =
+            TestCommon.dump(null,
+                TestCommon.list("javax/naming/spi/NamingManager"),
+                "--patch-module=java.naming=" + moduleJar,
+                "-Xlog:class+load",
+                "-Xlog:class+path=info",
+                "PatchMain", "javax.naming.spi.NamingManager");
+        TestCommon.checkDump(output, "Loading classes to share");
+
+        output = TestCommon.execCommon(
+            "-XX:+UnlockDiagnosticVMOptions",
+            "--patch-module=java.naming=" + moduleJar,
+            "-Xlog:class+load",
+            "-Xlog:class+path=info",
+            "PatchMain", "javax.naming.spi.NamingManager");
+        TestCommon.checkExec(output, "I pass!");
+    }
+}
diff --git a/test/hotspot/jtreg/runtime/appcds/jigsaw/PatchModule/SubClassOfPatchedClass.java b/test/hotspot/jtreg/runtime/appcds/jigsaw/PatchModule/SubClassOfPatchedClass.java
new file mode 100644
index 00000000000..de460b5f2ee
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/jigsaw/PatchModule/SubClassOfPatchedClass.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+/*
+ * @test
+ * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true)
+ * @summary the class in the -cp is a subclass of the class in --patch-module. The
+ *          patched class should be used at runtime.
+ * @library ../..
+ * @library /test/hotspot/jtreg/testlibrary
+ * @library /test/lib
+ * @modules java.base/jdk.internal.misc
+ *          jdk.jartool/sun.tools.jar
+ * @build PatchMain
+ * @run main SubClassOfPatchedClass
+ */
+
+import java.io.File;
+import jdk.test.lib.compiler.InMemoryJavaCompiler;
+import jdk.test.lib.process.OutputAnalyzer;
+
+public class SubClassOfPatchedClass {
+    private static String moduleJar;
+    private static String appJar;
+
+    public static void main(String args[]) throws Throwable {
+
+        // Create a class file in the module java.naming. This class file
+        // will be put in the javanaming.jar file.
+        String source = "package javax.naming; "                +
+                        "public class Reference { "             +
+                        "    static { "                             +
+                        "        System.out.println(\"I pass!\"); " +
+                        "    } "                                    +
+                        "}";
+
+        String classDir = System.getProperty("test.classes");
+
+        ClassFileInstaller.writeClassToDisk("javax/naming/Reference",
+             InMemoryJavaCompiler.compile("javax.naming.Reference", source, "--patch-module=java.naming"),
+             classDir);
+
+        // Build the jar file that will be used for the module "java.naming".
+        JarBuilder.build("javanaming", "javax/naming/Reference");
+        moduleJar = TestCommon.getTestJar("javanaming.jar");
+
+        String source2 = "package mypackage; "                +
+                        "public class MyReference extends javax.naming.Reference { "             +
+                        "    static { "                             +
+                        "        System.out.println(\"MyReference!\"); " +
+                        "    } "                                    +
+                        "    public MyReference(String mystring) { " +
+                        "        super(mystring); " +
+                        "    } " +
+                        "}";
+        ClassFileInstaller.writeClassToDisk("mypackage/MyReference",
+             InMemoryJavaCompiler.compile("mypackage.MyReference", source2),
+             classDir);
+
+        JarBuilder.build("myjavanaming", "mypackage/MyReference");
+        appJar = TestCommon.getTestJar("myjavanaming.jar");
+
+        System.out.println("Test dumping with --patch-module");
+        OutputAnalyzer output =
+            TestCommon.dump(appJar,
+                TestCommon.list("javax/naming/Reference", "mypackage/MyReference"),
+                "--patch-module=java.naming=" + moduleJar,
+                "-Xlog:class+load",
+                "PatchMain", "javax.naming.Reference", "mypackage.MyReference");
+        TestCommon.checkDump(output, "Loading classes to share");
+
+        String classPath = appJar + File.pathSeparator + classDir;
+        System.out.println("classPath: " + classPath);
+        output = TestCommon.execCommon(
+            "-XX:+UnlockDiagnosticVMOptions",
+            "-cp", classPath,
+            "--patch-module=java.naming=" + moduleJar,
+            "-Xlog:class+load",
+            "PatchMain", "javax.naming.Reference", "mypackage.MyReference");
+        TestCommon.checkExec(output,
+            "I pass!",
+            "MyReference source: file:");
+    }
+}
diff --git a/test/hotspot/jtreg/runtime/appcds/jigsaw/PatchModule/TwoJars.java b/test/hotspot/jtreg/runtime/appcds/jigsaw/PatchModule/TwoJars.java
new file mode 100644
index 00000000000..ef8b71a2165
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/jigsaw/PatchModule/TwoJars.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+/*
+ * @test
+ * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true)
+ * @summary a patched class found in --patch-module should be used at runtime
+ * @library ../..
+ * @library /test/hotspot/jtreg/testlibrary
+ * @library /test/lib
+ * @modules java.base/jdk.internal.misc
+ *          jdk.jartool/sun.tools.jar
+ * @build PatchMain
+ * @run main TwoJars
+ */
+
+import java.io.File;
+import jdk.test.lib.compiler.InMemoryJavaCompiler;
+import jdk.test.lib.process.OutputAnalyzer;
+
+public class TwoJars {
+    private static String moduleJar;
+    private static String moduleJar2;
+
+    public static void main(String args[]) throws Throwable {
+
+        // Create a class file in the module java.naming. This class file
+        // will be put in the javanaming.jar file.
+        String source = "package javax.naming.spi; "                +
+                        "public class NamingManager { "             +
+                        "    static { "                             +
+                        "        System.out.println(\"I pass!\"); " +
+                        "    } "                                    +
+                        "}";
+
+        // Create a class file in the module java.naming. This class file
+        // will be put in the javanaming2.jar file.
+        String source2 = "package javax.naming.spi; "                +
+                        "public class DirectoryManager { "             +
+                        "    static { "                             +
+                        "        System.out.println(\"I fail!\"); " +
+                        "    } "                                    +
+                        "}";
+
+        ClassFileInstaller.writeClassToDisk("javax/naming/spi/NamingManager",
+             InMemoryJavaCompiler.compile("javax.naming.spi.NamingManager", source, "--patch-module=java.naming"),
+             System.getProperty("test.classes"));
+
+        // Build the jar file that will be used for the module "java.naming".
+        JarBuilder.build("javanaming", "javax/naming/spi/NamingManager");
+        moduleJar = TestCommon.getTestJar("javanaming.jar");
+
+        ClassFileInstaller.writeClassToDisk("javax/naming/spi/DirectoryManager",
+             InMemoryJavaCompiler.compile("javax.naming.spi.DirectoryManager", source2, "--patch-module=java.naming"),
+             System.getProperty("test.classes"));
+
+        // Build the jar file that will be used for the module "java.naming".
+        JarBuilder.build("javanaming2", "javax/naming/spi/DirectoryManager");
+        moduleJar2 = TestCommon.getTestJar("javanaming2.jar");
+
+        System.out.println("Test dumping with --patch-module");
+        OutputAnalyzer output =
+            TestCommon.dump(null,
+                TestCommon.list("javax/naming/spi/NamingManager"),
+                "--patch-module=java.naming=" + moduleJar2 + File.pathSeparator + moduleJar,
+                "-Xlog:class+load",
+                "-Xlog:class+path=info",
+                "PatchMain", "javax.naming.spi.NamingManager");
+        TestCommon.checkDump(output, "Loading classes to share");
+
+        output = TestCommon.execCommon(
+            "-XX:+UnlockDiagnosticVMOptions",
+            "--patch-module=java.naming=" + moduleJar2 + File.pathSeparator + moduleJar,
+            "-Xlog:class+load",
+            "-Xlog:class+path=info",
+            "PatchMain", "javax.naming.spi.NamingManager");
+        TestCommon.checkExec(output, "I pass");
+    }
+}
diff --git a/test/hotspot/jtreg/runtime/appcds/jigsaw/classpathtests/BootAppendTests.java b/test/hotspot/jtreg/runtime/appcds/jigsaw/classpathtests/BootAppendTests.java
new file mode 100644
index 00000000000..71392801fda
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/jigsaw/classpathtests/BootAppendTests.java
@@ -0,0 +1,256 @@
+/*
+ * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+/**
+ * @test
+ * @summary AppCDS tests for testing -Xbootclasspath/a
+ * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true)
+ * @library /test/lib /test/hotspot/jtreg/runtime/appcds
+ * @modules java.base/jdk.internal.misc
+ *          java.management
+ *          jdk.jartool/sun.tools.jar
+ *          jdk.internal.jvmstat/sun.jvmstat.monitor
+ * @compile src/jdk/test/Main.java
+ * @compile src/com/sun/tools/javac/Main2.jasm
+ * @compile src/sun/nio/cs/ext/MyClass.java
+ * @compile src/sun/nio/cs/ext1/MyClass.java
+ * @run main BootAppendTests
+ */
+
+import java.io.File;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import jdk.test.lib.cds.CDSOptions;
+import jdk.test.lib.cds.CDSTestUtils;
+import jdk.test.lib.process.ProcessTools;
+import jdk.test.lib.process.OutputAnalyzer;
+
+public class BootAppendTests {
+    private static final String TEST_SRC = System.getProperty("test.src");
+    private static final Path SRC_DIR = Paths.get(TEST_SRC, "src");
+    private static final Path CLASSES_DIR = Paths.get("classes");
+
+    private static final String MAIN_CLASS = "jdk.test.Main";
+    private static final String APP_MODULE_CLASS = "com/sun/tools/javac/Main2";
+    private static final String BOOT_APPEND_MODULE_CLASS = "sun/nio/cs/ext/MyClass";
+    private static final String BOOT_APPEND_CLASS = "sun/nio/cs/ext1/MyClass";
+    private static final String[] ARCHIVE_CLASSES =
+         {APP_MODULE_CLASS, BOOT_APPEND_MODULE_CLASS, BOOT_APPEND_CLASS};
+
+    private static String appJar;
+    private static String bootAppendJar;
+    private static String testArchiveName;
+
+    public static void main(String... args) throws Exception {
+        dumpArchive();
+
+        System.out.println("TESTCASE: 1: testBootAppendModuleClassWithoutAppCDS");
+        testBootAppendModuleClassWithoutAppCDS();
+
+        System.out.println("TESTCASE: 2" );
+        testBootAppendModuleClassWithAppCDS();
+
+        System.out.println("TESTCASE: 3" );
+        testBootAppendExcludedModuleClassWithoutAppCDS();
+
+        System.out.println("TESTCASE: 4" );
+        testBootAppendExcludedModuleClassWithAppCDS();
+
+        System.out.println("TESTCASE: 5" );
+        testBootAppendClassWithoutAppCDS();
+
+        System.out.println("TESTCASE: 6" );
+        testBootAppendClassWithAppCDS();
+
+        System.out.println("TESTCASE: 7" );
+        testBootAppendAppModuleClassWithoutAppCDS();
+
+        System.out.println("TESTCASE: 9" );
+        testBootAppendAppModuleClassWithAppCDS();
+
+        System.out.println("TESTCASE: 9" );
+        testBootAppendAppExcludeModuleClassWithoutAppCDS();
+
+        System.out.println("TESTCASE: 10" );
+        testBootAppendAppExcludeModuleClassAppCDS();
+    }
+
+    static void dumpArchive() throws Exception {
+        JarBuilder.build("classpathtests", "jdk/test/Main");
+        appJar = TestCommon.getTestJar("classpathtests.jar");
+
+        JarBuilder.build("bootAppend",
+                         APP_MODULE_CLASS, BOOT_APPEND_MODULE_CLASS, BOOT_APPEND_CLASS);
+        bootAppendJar = TestCommon.getTestJar("bootAppend.jar");
+
+        OutputAnalyzer output1  = TestCommon.dump(
+            appJar, TestCommon.list(ARCHIVE_CLASSES), "-Xbootclasspath/a:" + bootAppendJar);
+        TestCommon.checkDump(output1);
+
+        if (!TestCommon.isUnableToMap(output1)) {
+            // Make sure all the classes were successfully archived.
+            for (String archiveClass : ARCHIVE_CLASSES) {
+                output1.shouldNotContain("Preload Warning: Cannot find " + archiveClass);
+            }
+        }
+
+        testArchiveName = TestCommon.getCurrentArchiveName();
+    }
+
+    // Test #1: A class in package defined in boot module
+    //    - should not be loaded from the -Xbootclasspath/a without AppCDS
+    public static void testBootAppendModuleClassWithoutAppCDS() throws Exception {
+        CDSOptions opts = (new CDSOptions())
+            .addPrefix("-Xbootclasspath/a:" + bootAppendJar, "-cp", appJar)
+            .setArchiveName(testArchiveName)
+            .addSuffix(MAIN_CLASS, "Test #1", BOOT_APPEND_MODULE_CLASS, "false");
+
+        CDSTestUtils.runWithArchiveAndCheck(opts);
+    }
+
+    // Test #2: A shared class in package defined in boot module that's archived
+    //          from -Xbootclasspath/a
+    //     - should not be loaded by AppCDS
+    public static void testBootAppendModuleClassWithAppCDS() throws Exception {
+        OutputAnalyzer output = TestCommon.exec(
+            appJar,
+            "-Xbootclasspath/a:" + bootAppendJar,
+            MAIN_CLASS,
+            "Test #2", BOOT_APPEND_MODULE_CLASS, "false");
+        TestCommon.checkExec(output);
+    }
+
+
+    // Test #3: A class in excluded package defined in boot module
+    //     - should be loaded from the -Xbootclasspath/a by the boot classloader
+    public static void testBootAppendExcludedModuleClassWithoutAppCDS() throws Exception {
+        CDSOptions opts = (new CDSOptions())
+            .addPrefix("-Xbootclasspath/a:" + bootAppendJar, "-cp", appJar,
+                       "--limit-modules", "java.base")
+            .setArchiveName(testArchiveName)
+            .addSuffix(MAIN_CLASS, "Test #3", BOOT_APPEND_MODULE_CLASS, "true", "BOOT");
+
+        CDSTestUtils.runWithArchiveAndCheck(opts);
+    }
+
+    // Test #4: A shared class in excluded package that's archived from
+    //          -Xbootclasspath/a
+    //     - should be loaded from the archive by the bootstrap classloader
+    public static void testBootAppendExcludedModuleClassWithAppCDS() throws Exception {
+        OutputAnalyzer output = TestCommon.exec(
+            appJar,
+            "-Xbootclasspath/a:" + bootAppendJar,
+            "--limit-modules", "java.base",
+            "-XX:+TraceClassLoading",
+            MAIN_CLASS,
+            "Test #4", BOOT_APPEND_MODULE_CLASS, "true", "BOOT");
+        TestCommon.checkExec(output);
+        if (!TestCommon.isUnableToMap(output))
+            output.shouldContain("[class,load] sun.nio.cs.ext.MyClass source: shared objects file");
+    }
+
+
+    // Test #5: A class not in package defined in boot module
+    //    - should be loaded from the -Xbootclasspath/a without AppCDS
+    public static void testBootAppendClassWithoutAppCDS() throws Exception {
+        CDSOptions opts = (new CDSOptions())
+            .addPrefix("-Xbootclasspath/a:" + bootAppendJar, "-cp", appJar)
+            .setArchiveName(testArchiveName)
+            .addSuffix(MAIN_CLASS, "Test #5", BOOT_APPEND_CLASS, "true", "BOOT");
+
+        CDSTestUtils.runWithArchiveAndCheck(opts);
+    }
+
+
+    // Test #6: A shared class not in package defined in boot module that's
+    //          archived from -Xbootclasspath/a
+    //    - should be loaded from the archive by the bootstrap class loader
+    public static void testBootAppendClassWithAppCDS() throws Exception {
+        OutputAnalyzer output = TestCommon.exec(
+            appJar,
+            "-Xbootclasspath/a:" + bootAppendJar,
+            "-XX:+TraceClassLoading",
+            MAIN_CLASS,
+            "Test #6", BOOT_APPEND_CLASS, "true", "BOOT");
+        TestCommon.checkExec(output);
+        if (!TestCommon.isUnableToMap(output))
+            output.shouldContain("[class,load] sun.nio.cs.ext1.MyClass source: shared objects file");
+    }
+
+
+    // Test #7: A class in package defined in jimage app module
+    //    - should not be loaded from the -Xbootclasspath/a without AppCDS
+    public static void testBootAppendAppModuleClassWithoutAppCDS() throws Exception {
+        CDSOptions opts = (new CDSOptions())
+            .addPrefix("-Xbootclasspath/a:" + bootAppendJar, "-cp", appJar)
+            .setArchiveName(testArchiveName)
+            .addSuffix(MAIN_CLASS, "Test #7", APP_MODULE_CLASS, "false");
+
+        CDSTestUtils.runWithArchiveAndCheck(opts);
+    }
+
+
+    // Test #8: A shared class in package defined in jimage app module that's
+    //          archived from -Xbootclasspath/a
+    //    - should not be loaded from the archive
+    public static void testBootAppendAppModuleClassWithAppCDS() throws Exception {
+        OutputAnalyzer output = TestCommon.exec(
+            appJar,
+            "-Xbootclasspath/a:" + bootAppendJar,
+            MAIN_CLASS,
+            "Test #8", APP_MODULE_CLASS, "false");
+        TestCommon.checkExec(output);
+    }
+
+
+    // Test #9: A class in excluded package defined in jimage app module
+    //    - should be loaded from the -Xbootclasspath/a without AppCDS
+    public static void testBootAppendAppExcludeModuleClassWithoutAppCDS()
+        throws Exception {
+
+        CDSOptions opts = (new CDSOptions())
+            .addPrefix("-Xbootclasspath/a:" + bootAppendJar, "-cp", appJar,
+                       "--limit-modules", "java.base")
+            .setArchiveName(testArchiveName)
+            .addSuffix(MAIN_CLASS, "Test #9", APP_MODULE_CLASS, "true", "BOOT");
+
+        CDSTestUtils.runWithArchiveAndCheck(opts);
+    }
+
+    // Test #10: A shared class in excluded package defined in jimage app module
+    //    - should be loaded from the -Xbootclasspath/a with AppCDS
+    public static void testBootAppendAppExcludeModuleClassAppCDS() throws Exception {
+        OutputAnalyzer output = TestCommon.exec(
+            appJar,
+            "-Xbootclasspath/a:" + bootAppendJar,
+            "-XX:+TraceClassLoading",
+            "--limit-modules", "java.base",
+            MAIN_CLASS,
+            "Test #10", APP_MODULE_CLASS, "true", "BOOT");
+        TestCommon.checkExec(output);
+
+        if (!TestCommon.isUnableToMap(output))
+            output.shouldContain("[class,load] com.sun.tools.javac.Main2 source: shared objects file");
+    }
+}
diff --git a/test/hotspot/jtreg/runtime/appcds/jigsaw/classpathtests/ClassPathTests.java b/test/hotspot/jtreg/runtime/appcds/jigsaw/classpathtests/ClassPathTests.java
new file mode 100644
index 00000000000..6f929ab227f
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/jigsaw/classpathtests/ClassPathTests.java
@@ -0,0 +1,240 @@
+/*
+ * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+/**
+ * @test
+ * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true)
+ * @library ../..
+ * @library /test/lib
+ * @modules java.base/jdk.internal.misc
+ * @modules jdk.jartool/sun.tools.jar
+ * @compile src/jdk/test/Main.java
+ * @compile src/com/sun/tools/javac/Main.jasm
+ * @compile src/com/sun/tools/javac/Main2.jasm
+ * @compile src/javax/activation/UnsupportedDataTypeException2.jasm
+ * @run main ClassPathTests
+ * @summary AppCDS tests for testing classpath/package conflicts
+ */
+
+/*
+ * These tests will verify that AppCDS will correctly handle archived classes
+ * on the classpath that are in a package that is also exported by the jimage.
+ * These classes should fail to load unless --limit-modules is used to hide the
+ * package exported by the jimage. There are 8 variants of this test:
+ *   - With a jimage app package and with a jimage ext package
+ *   - With --limit-modules and without --limit-modules
+ *   - With AppCDS and without AppCDS (to verify behaviour is the same for both).
+ *
+ * There is also a 9th test to verify that when --limit-modules is used, a jimage
+ * class in the archive can be replaced by a classpath class with the
+ * same name and package.
+ */
+
+import java.lang.reflect.Method;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+
+import jdk.test.lib.Asserts;
+import jdk.test.lib.cds.CDSOptions;
+import jdk.test.lib.cds.CDSTestUtils;
+import jdk.test.lib.process.ProcessTools;
+import jdk.test.lib.process.OutputAnalyzer;
+
+
+public class ClassPathTests {
+    private static final String TEST_SRC = System.getProperty("test.src");
+    private static final Path SRC_DIR = Paths.get(TEST_SRC, "src");
+    private static final Path CLASSES_DIR = Paths.get("classes");
+
+    // the test module
+    private static final String MAIN_CLASS = "jdk.test.Main";
+    private static final String LIMITMODS_MAIN_CLASS = "jdk.test.LimitModsMain";
+
+    // test classes to archive. These are both in UPGRADED_MODULES
+    private static final String JIMAGE_CLASS      = "com/sun/tools/javac/Main";
+    private static final String APP_ARCHIVE_CLASS = "com/sun/tools/javac/Main2";
+    private static final String PLATFORM_ARCHIVE_CLASS = "javax/activation/UnsupportedDataTypeException2";
+    private static final String[] ARCHIVE_CLASSES = {APP_ARCHIVE_CLASS, PLATFORM_ARCHIVE_CLASS, JIMAGE_CLASS};
+    private static final int NUMBER_OF_TEST_CASES = 10;
+
+    private static String appJar;
+    private static String testArchiveName;
+
+
+    public static void main(String[] args) throws Exception {
+        ClassPathTests tests = new ClassPathTests();
+        tests.dumpArchive();
+
+        Method[] methods = tests.getClass().getDeclaredMethods();
+        int numOfTestMethodsRun = 0;
+        for (Method m : methods) {
+            if (m.getName().startsWith("test")) {
+                System.out.println("About to run test method: " + m.getName());
+                m.invoke(tests);
+                numOfTestMethodsRun++;
+            }
+        }
+
+        Asserts.assertTrue((numOfTestMethodsRun == NUMBER_OF_TEST_CASES),
+            "Expected " + NUMBER_OF_TEST_CASES + " test methods to run, actual number is "
+            + numOfTestMethodsRun);
+    }
+
+    private void dumpArchive() throws Exception {
+        // Create a jar file with all the classes related to this test.
+        JarBuilder.build( "classpathtests",
+                          APP_ARCHIVE_CLASS, PLATFORM_ARCHIVE_CLASS, JIMAGE_CLASS,
+                          "jdk/test/Main");
+        appJar = TestCommon.getTestJar("classpathtests.jar");
+
+        // dump the archive with altnernate jdk.comiler and jdk.activation classes in the class list
+        OutputAnalyzer output1  = TestCommon.dump(appJar, TestCommon.list(ARCHIVE_CLASSES));
+        TestCommon.checkDump(output1);
+        // Only a class that belongs to a module which is not defined by default
+        // can be found. In this case the PLATFORM_ARCHIVE_CLASS belongs
+        // to the java.activation which is not defined by default; it is the only
+        // class can be found during dumping.
+        for (String archiveClass : ARCHIVE_CLASSES) {
+            if (archiveClass.equals(PLATFORM_ARCHIVE_CLASS)) {
+                output1.shouldNotContain("Preload Warning: Cannot find " + archiveClass);
+            } else {
+                output1.shouldContain("Preload Warning: Cannot find " + archiveClass);
+            }
+        }
+
+        testArchiveName = TestCommon.getCurrentArchiveName();
+    }
+
+    // #1: Archived classpath class in same package as jimage app class. With AppCDS.
+    // Should fail to load.
+    public void testAppClassWithAppCDS() throws Exception {
+        OutputAnalyzer output = TestCommon.exec(
+            appJar, MAIN_CLASS,
+            "Test #1", APP_ARCHIVE_CLASS, "false"); // last 3 args passed to test
+        TestCommon.checkExec(output);
+    }
+
+    // #2: Archived classpath class in same package as jimage app class. Without AppCDS.
+    // Should fail to load.
+    public void testAppClassWithoutAppCDS() throws Exception {
+        CDSOptions opts = (new CDSOptions())
+            .addPrefix("-cp", appJar)
+            .setArchiveName(testArchiveName)
+            .addSuffix(MAIN_CLASS, "Test #2", APP_ARCHIVE_CLASS, "false");
+
+        CDSTestUtils.runWithArchiveAndCheck(opts);
+    }
+
+    // For tests #3 and #4, we need to "--add-modules java.activation" since the
+    // java.activation module won't be defined by default.
+
+    // #3: Archived classpath class in same package as jimage ext class. With AppCDS.
+    // Should fail to load.
+    public void testExtClassWithAppCDS() throws Exception {
+        OutputAnalyzer output = TestCommon.exec(
+            appJar, "--add-modules", "java.activation", MAIN_CLASS,
+            "Test #3", PLATFORM_ARCHIVE_CLASS, "false"); // last 3 args passed to test
+        TestCommon.checkExec(output);
+    }
+
+    // #4: Archived classpath class in same package as jimage ext class. Without AppCDS.
+    // Should fail to load.
+    public void testExtClassWithoutAppCDS() throws Exception {
+        CDSOptions opts = (new CDSOptions())
+            .addPrefix("-cp", appJar, "--add-modules", "java.activation")
+            .setArchiveName(testArchiveName)
+            .addSuffix(MAIN_CLASS, "Test #4", PLATFORM_ARCHIVE_CLASS, "false");
+
+        CDSTestUtils.runWithArchiveAndCheck(opts);
+    }
+
+    // #5: Archived classpath class in same package as jimage app class. With AppCDS.
+    // Should load because --limit-modules is used.
+    public void testAppClassWithLimitModsWithAppCDS() throws Exception {
+        OutputAnalyzer output = TestCommon.exec(
+            appJar,
+            "--limit-modules", "java.base",
+            MAIN_CLASS,
+            "Test #5", APP_ARCHIVE_CLASS, "true"); // last 3 args passed to test
+        TestCommon.checkExec(output);
+    }
+
+    // #6: Archived classpath class in same package as jimage app class. Without AppCDS.
+    // Should load because --limit-modules is used.
+    public void testAppClassWithLimitModsWithoutAppCDS() throws Exception {
+        CDSOptions opts = (new CDSOptions())
+            .addPrefix("-cp", appJar, "--limit-modules", "java.base")
+            .setArchiveName(testArchiveName)
+            .addSuffix(MAIN_CLASS, "Test #6", APP_ARCHIVE_CLASS, "true");
+
+        CDSTestUtils.runWithArchiveAndCheck(opts);
+    }
+
+    // #7: Archived classpath class in same package as jimage ext class. With AppCDS.
+    // Should load because --limit-modules is used.
+    public void testExtClassWithLimitModsWithAppCDS() throws Exception {
+        OutputAnalyzer output = TestCommon.exec(
+            appJar,
+            "--limit-modules", "java.base",
+            MAIN_CLASS,
+            "Test #7", PLATFORM_ARCHIVE_CLASS, "true"); // last 3 args passed to test
+        TestCommon.checkExec(output);
+    }
+
+    // #8: Archived classpath class in same package as jimage ext class. Without AppCDS.
+    // Should load because --limit-modules is used.
+    public void testExtClassWithLimitModsWithoutAppCDS() throws Exception {
+        CDSOptions opts = (new CDSOptions())
+            .addPrefix("-cp", appJar, "--limit-modules", "java.base")
+            .setArchiveName(testArchiveName)
+            .addSuffix(MAIN_CLASS, "Test #8", PLATFORM_ARCHIVE_CLASS, "true");
+
+        CDSTestUtils.runWithArchiveAndCheck(opts);
+    }
+
+    // #9: Archived classpath class with same name as jimage app class. With AppCDS.
+    // Should load because --limit-modules is used.
+    public void testReplacingJImageClassWithAppCDS() throws Exception {
+        OutputAnalyzer output = TestCommon.exec(
+            appJar,
+            "--limit-modules", "java.base", "-XX:+TraceClassLoading",
+            MAIN_CLASS,
+            "Test #9", JIMAGE_CLASS, "true"); // last 3 args passed to test
+        TestCommon.checkExec(output);
+    }
+
+    // #10: Archived classpath class with same name as jimage app class. Without AppCDS.
+    // Should load because --limit-modules is used. Note the archive will actually contain
+    // the original jimage version of the class, but AppCDS should refuse to load it
+    // since --limit-modules is used. This should result in the -cp version being used.
+    public void testReplacingJImageClassWithoutAppCDS() throws Exception {
+        CDSOptions opts = (new CDSOptions())
+            .addPrefix("-cp", appJar, "--limit-modules", "java.base")
+            .setArchiveName(testArchiveName)
+            .addSuffix(MAIN_CLASS, "Test #10", JIMAGE_CLASS, "true");
+
+        CDSTestUtils.runWithArchiveAndCheck(opts);
+    }
+
+}
diff --git a/test/hotspot/jtreg/runtime/appcds/jigsaw/classpathtests/DummyClassesInBootClassPath.java b/test/hotspot/jtreg/runtime/appcds/jigsaw/classpathtests/DummyClassesInBootClassPath.java
new file mode 100644
index 00000000000..05311e818a1
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/jigsaw/classpathtests/DummyClassesInBootClassPath.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+/*
+ * @test
+ * @summary Ensure that classes found in jimage takes precedence over classes found in -Xbootclasspath/a.
+ * AppCDS does not support uncompressed oops
+ * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true)
+ * @library /test/lib /test/hotspot/jtreg/runtime/appcds
+ * @modules java.activation
+ *          jdk.jartool/sun.tools.jar
+ * @compile ../../test-classes/DummyClassHelper.java
+ * @compile ../../test-classes/java/net/HttpCookie.jasm
+ * @compile ../../test-classes/javax/activation/MimeType.jasm
+ * @build sun.hotspot.WhiteBox
+ * @run main ClassFileInstaller sun.hotspot.WhiteBox
+ * @run main DummyClassesInBootClassPath
+ */
+
+import java.io.File;
+import java.util.List;
+import java.util.ArrayList;
+import jdk.test.lib.process.OutputAnalyzer;
+
+public class DummyClassesInBootClassPath {
+    private static final String METHOD_NAME = "thisClassIsDummy()";
+
+    public static void main(String[] args) throws Exception {
+        String classNames[] = { "java/net/HttpCookie",
+                                "javax/activation/MimeType"};
+        JarBuilder.build("dummyClasses", classNames[0], classNames[1]);
+
+        String appJar = TestCommon.getTestJar("dummyClasses.jar");
+        OutputAnalyzer dumpOutput = TestCommon.dump(
+            appJar, classNames, "-Xbootclasspath/a:" + appJar);
+
+        List<String> argsList = new ArrayList<String>();
+        for (int i = 0; i < classNames.length; i++) {
+            argsList.add(classNames[i].replace('/', '.'));
+        }
+        String[] arguments = new String[argsList.size()];
+        arguments = argsList.toArray(arguments);
+        OutputAnalyzer execOutput = TestCommon.execCommon(
+            "-cp", TestCommon.getTestDir("."), "-verbose:class",
+            "--add-modules", "java.activation",
+            "-Xbootclasspath/a:" + appJar, "DummyClassHelper",
+            arguments[0], arguments[1]);
+        for (int i = 0; i < arguments.length; i++) {
+            TestCommon.checkExec(execOutput,
+                "java.lang.NoSuchMethodException: " + arguments[i] + "." +
+                METHOD_NAME);
+        }
+
+        JarBuilder.build(true, "WhiteBox", "sun/hotspot/WhiteBox");
+        String whiteBoxJar = TestCommon.getTestJar("WhiteBox.jar");
+        String bootClassPath = "-Xbootclasspath/a:" + appJar +
+            File.pathSeparator + whiteBoxJar;
+        argsList.add("testWithWhiteBox");
+        arguments = new String[argsList.size()];
+        arguments = argsList.toArray(arguments);
+        String[] opts = {"-XX:+UnlockDiagnosticVMOptions", "-XX:+WhiteBoxAPI",
+            bootClassPath, "-XX:+TraceClassPaths", "DummyClassHelper",
+            arguments[0], arguments[1], arguments[2]};
+        OutputAnalyzer output = TestCommon.execCommon(opts);
+    }
+}
+
diff --git a/test/hotspot/jtreg/runtime/appcds/jigsaw/classpathtests/EmptyClassInBootClassPath.java b/test/hotspot/jtreg/runtime/appcds/jigsaw/classpathtests/EmptyClassInBootClassPath.java
new file mode 100644
index 00000000000..a635603473e
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/jigsaw/classpathtests/EmptyClassInBootClassPath.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+/*
+ * @test
+ * @summary Test a few scenarios if an empty class, which has the same name as the one in the jimage, is specified in the -Xbootclasspath/a
+ *     1) boot loader will always load the class from the bootclasspath
+ *     2) app loader will load the class from the jimage by default;
+ *        app loader will load the class from the bootclasspath if the
+ *        "--limit-modules java.base" option is specified
+ * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true)
+ * @library /test/lib /test/hotspot/jtreg/runtime/appcds
+ * @modules java.base/jdk.internal.misc
+ *          java.management
+ *          jdk.jartool/sun.tools.jar
+ *          jdk.internal.jvmstat/sun.jvmstat.monitor
+ * @compile ../../test-classes/EmptyClassHelper.java
+ * @compile ../../test-classes/com/sun/tools/javac/Main.jasm
+ * @run main EmptyClassInBootClassPath
+ */
+
+import java.io.File;
+import java.lang.*;
+import java.lang.reflect.*;
+import java.util.List;
+import java.util.ArrayList;
+import jdk.test.lib.process.OutputAnalyzer;
+
+public class EmptyClassInBootClassPath {
+    static final String EXPECTED_EXCEPTION =
+        "java.lang.NoSuchMethodException: com.sun.tools.javac.Main.main([Ljava.lang.String;)";
+    public static void main(String[] args) throws Exception {
+        String[] className = {"com/sun/tools/javac/Main"};
+        JarBuilder.build("emptyClass", className);
+        String appJar = TestCommon.getTestJar("emptyClass.jar");
+        JarBuilder.build("EmptyClassHelper", "EmptyClassHelper");
+        String helperJar = TestCommon.getTestJar("EmptyClassHelper.jar");
+        OutputAnalyzer dumpOutput = TestCommon.dump(
+            appJar, className, "-Xbootclasspath/a:" + appJar);
+        TestCommon.checkDump(dumpOutput);
+        dumpOutput.shouldNotContain("Preload Warning: skipping class from -Xbootclasspath/a " + className[0]);
+
+        String bootclasspath = "-Xbootclasspath/a:" + appJar;
+        String classPath = "-Djava.class.path=" + appJar + File.pathSeparator + helperJar;
+        List<String> argsList = new ArrayList<String>();
+        argsList.add(classPath);
+        argsList.add(bootclasspath);
+        argsList.add("--add-exports=java.base/jdk.internal.misc=ALL-UNNAMED");
+        argsList.add("EmptyClassHelper");
+
+        // case 1: load class in bootclasspath using app loader
+        argsList.add("useAppLoader");
+        String[] opts = new String[argsList.size()];
+        opts = argsList.toArray(opts);
+        OutputAnalyzer runOutput = TestCommon.execCommon(opts);
+        TestCommon.checkExec(runOutput, "appLoader found method main");
+
+        // case 2: load class in bootclasspath using boot loader
+        argsList.remove(argsList.size() - 1);
+        argsList.add("useBootLoader");
+        opts = new String[argsList.size()];
+        opts = argsList.toArray(opts);
+        runOutput = TestCommon.execCommon(opts);
+        TestCommon.checkExec(runOutput, EXPECTED_EXCEPTION);
+
+        // case 3: load class in bootclasspath using app loader with '--limit-modules java.base'
+        argsList.add(0, "--limit-modules");
+        argsList.add(1, "java.base");
+        argsList.remove(argsList.size() - 1);
+        argsList.add("useAppLoader");
+        opts = new String[argsList.size()];
+        opts = argsList.toArray(opts);
+        runOutput = TestCommon.execCommon(opts);
+        TestCommon.checkExec(runOutput, EXPECTED_EXCEPTION);
+
+        // case 4: load class in bootclasspath using boot loader with '--limit-modules java.base'
+        argsList.remove(argsList.size() - 1);
+        argsList.add("useBootLoader");
+        opts = new String[argsList.size()];
+        opts = argsList.toArray(opts);
+        runOutput = TestCommon.execCommon(opts);
+        TestCommon.checkExec(runOutput, EXPECTED_EXCEPTION);
+
+    }
+}
diff --git a/test/hotspot/jtreg/runtime/appcds/jigsaw/classpathtests/src/com/sun/tools/javac/Main.jasm b/test/hotspot/jtreg/runtime/appcds/jigsaw/classpathtests/src/com/sun/tools/javac/Main.jasm
new file mode 100644
index 00000000000..efe6c4a8e6e
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/jigsaw/classpathtests/src/com/sun/tools/javac/Main.jasm
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2016, 2017, 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 com/sun/tools/javac;
+
+public class Main
+    version 51:0
+{
+
+public Method "<init>":"()V"
+    stack 1 locals 1
+{
+    aload_0;
+    invokespecial   Method java/lang/Object."<init>":"()V";
+    return;
+}
+
+public Method toString:"()Ljava/lang/String;"
+    stack 1 locals 1
+{
+    ldc String "hi";
+    areturn;
+}
+
+} // end class Main
diff --git a/test/hotspot/jtreg/runtime/appcds/jigsaw/classpathtests/src/com/sun/tools/javac/Main2.jasm b/test/hotspot/jtreg/runtime/appcds/jigsaw/classpathtests/src/com/sun/tools/javac/Main2.jasm
new file mode 100644
index 00000000000..3d0f43256bf
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/jigsaw/classpathtests/src/com/sun/tools/javac/Main2.jasm
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2016, 2017, 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 com/sun/tools/javac;
+
+public class Main2
+    version 51:0
+{
+
+public Method "<init>":"()V"
+    stack 1 locals 1
+{
+    aload_0;
+    invokespecial   Method java/lang/Object."<init>":"()V";
+    return;
+}
+
+public Method toString:"()Ljava/lang/String;"
+    stack 1 locals 1
+{
+    ldc String "hi";
+    areturn;
+}
+
+} // end class Main2
diff --git a/test/hotspot/jtreg/runtime/appcds/jigsaw/classpathtests/src/javax/activation/UnsupportedDataTypeException2.jasm b/test/hotspot/jtreg/runtime/appcds/jigsaw/classpathtests/src/javax/activation/UnsupportedDataTypeException2.jasm
new file mode 100644
index 00000000000..3a30e511397
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/jigsaw/classpathtests/src/javax/activation/UnsupportedDataTypeException2.jasm
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2016, 2017, 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 javax/activation;
+
+public class UnsupportedDataTypeException2
+    version 51:0
+{
+
+public Method "<init>":"()V"
+    stack 1 locals 1
+{
+    aload_0;
+    invokespecial   Method java/lang/Object."<init>":"()V";
+    return;
+}
+
+public Method toString:"()Ljava/lang/String;"
+    stack 1 locals 1
+{
+    ldc String "hi";
+    areturn;
+}
+
+} // end class UnsupportedDataTypeException2
diff --git a/test/hotspot/jtreg/runtime/appcds/jigsaw/classpathtests/src/jdk/test/Main.java b/test/hotspot/jtreg/runtime/appcds/jigsaw/classpathtests/src/jdk/test/Main.java
new file mode 100644
index 00000000000..752fd9b7baf
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/jigsaw/classpathtests/src/jdk/test/Main.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright (c) 2015, 2017, 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.
+ *
+ */
+
+/**
+ * Tests loading an archived class that has the same class name as one in the
+ * jimage. The class should normally fail to load since a classpath class is not
+ * allowed to have the same package name as a module in the jimage. However,
+ * if --limit-modules was used then archived class should be loaded.
+ */
+
+package jdk.test;
+
+public class Main {
+    static final ClassLoader BOOT_LOADER     = null;
+    static final ClassLoader PLATFORM_LOADER = ClassLoader.getPlatformClassLoader();
+    static final ClassLoader SYS_LOADER      = ClassLoader.getSystemClassLoader();
+
+    public static void main(String[] args) throws Exception {
+        boolean shouldLoad = false;
+        ClassLoader expectedLoader = SYS_LOADER;
+
+        /*
+         * 3 Arguments are passed to this test:
+         *   1. testName: Name of the test being run.
+         *   2. className: Name of the class to load and instantiate.
+         *   3. shouldLoad: Either "true" or "false" to indicate whether the class should
+         *      successfully load ("true" indicates --limit-modules was used.)
+         * The 4th argument is optional. It specifies the classloader.
+         */
+
+        assertTrue(args.length <= 4);
+        String testName = args[0];
+        String className = args[1].replace('/', '.');
+        String shouldLoadName = args[2];  // "true" or "false"
+        String loaderName = "SYS";
+        if (args.length == 4) {
+            loaderName = args[3];
+        }
+
+        if (shouldLoadName.equals("true")) {
+            shouldLoad = true;
+        } else if (shouldLoadName.equals("false")) {
+            shouldLoad = false;
+        } else {
+            assertTrue(false);
+        }
+
+        if (loaderName.equals("SYS")) {
+            expectedLoader = SYS_LOADER;
+        } else if (loaderName.equals("EXT")) {
+            expectedLoader = PLATFORM_LOADER;
+        } else if (loaderName.equals("BOOT")) {
+            expectedLoader = BOOT_LOADER;
+        }
+
+        System.out.println(testName + ": class=" + className + " shouldLoad=" +
+                           shouldLoadName + " by loader:" + expectedLoader);
+
+        // Try to load the specified class with the default ClassLoader.
+        Class<?> clazz = null;
+        try {
+            clazz = Class.forName(className);
+        } catch (ClassNotFoundException e) {
+            System.out.println(e);
+        }
+
+        if (clazz != null) {
+            // class loaded
+            if (shouldLoad) {
+                // Make sure we got the expected defining ClassLoader
+                ClassLoader actualLoader = clazz.getClassLoader();
+                if (actualLoader != expectedLoader) {
+                    throw new RuntimeException(testName + " FAILED: " + clazz + " loaded by " + actualLoader +
+                                               ", expected " + expectedLoader);
+                }
+                // Make sure we got the right version of the class. toString() of an instance
+                // of the overridden version of the class should return "hi".
+                String s = clazz.newInstance().toString();
+                if (!s.equals("hi")) {
+                    throw new RuntimeException(testName + " FAILED: toString() returned \"" + s
+                                               + "\" instead of \"hi\"" );
+                }
+                System.out.println(testName + " PASSED: class loaded as expected.");
+            } else {
+                throw new RuntimeException(testName + " FAILED: class loaded, but should have failed to load.");
+            }
+        } else {
+            // class did not load
+            if (shouldLoad) {
+                throw new RuntimeException(testName + " FAILED: class failed to load.");
+            } else {
+                System.out.println(testName + " PASSED: ClassNotFoundException thrown as expected");
+            }
+        }
+    }
+
+    static void assertTrue(boolean expr) {
+        if (!expr)
+            throw new RuntimeException("assertion failed");
+    }
+}
diff --git a/test/hotspot/jtreg/runtime/appcds/jigsaw/classpathtests/src/sun/nio/cs/ext/MyClass.java b/test/hotspot/jtreg/runtime/appcds/jigsaw/classpathtests/src/sun/nio/cs/ext/MyClass.java
new file mode 100644
index 00000000000..92e9204dce2
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/jigsaw/classpathtests/src/sun/nio/cs/ext/MyClass.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2015, 2017, 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.nio.cs.ext;
+
+public class MyClass {
+    public String toString() {
+        return "hi";
+    }
+}
diff --git a/test/hotspot/jtreg/runtime/appcds/jigsaw/classpathtests/src/sun/nio/cs/ext1/MyClass.java b/test/hotspot/jtreg/runtime/appcds/jigsaw/classpathtests/src/sun/nio/cs/ext1/MyClass.java
new file mode 100644
index 00000000000..43b24f598ee
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/jigsaw/classpathtests/src/sun/nio/cs/ext1/MyClass.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2015, 2017, 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.nio.cs.ext1;
+
+public class MyClass {
+    public String toString() {
+        return "hi";
+    }
+}
diff --git a/test/hotspot/jtreg/runtime/appcds/jigsaw/limitmods/LimitModsHelper.java b/test/hotspot/jtreg/runtime/appcds/jigsaw/limitmods/LimitModsHelper.java
new file mode 100644
index 00000000000..cb30412bae4
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/jigsaw/limitmods/LimitModsHelper.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (c) 2015, 2017, 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.
+ *
+ */
+
+/**
+ * Used with -p or --upgrade-module-path to exercise the replacement
+ * of classes in modules that are linked into the runtime image.
+ */
+
+import java.lang.*;
+import java.lang.reflect.*;
+import sun.hotspot.WhiteBox;
+
+
+public class LimitModsHelper {
+    static final ClassLoader PLATFORM_LOADER = ClassLoader.getPlatformClassLoader();
+    static final ClassLoader SYS_LOADER      = ClassLoader.getSystemClassLoader();
+
+    public static void main(String[] args) throws Exception {
+        assertTrue(args.length == 4);
+        String[] classNames = new String[3];
+        for (int i = 0; i < 3; i++) {
+            classNames[i] = args[i].replace('/', '.');
+        }
+        int excludeModIdx = Integer.parseInt(args[3]);
+
+        ClassLoader expectedLoaders[] = {null, PLATFORM_LOADER, SYS_LOADER};
+
+        WhiteBox wb = WhiteBox.getWhiteBox();
+
+        Class<?> clazz = null;
+        for (int i = 0; i < 3; i++) {
+            try {
+                // Load the class with the default ClassLoader.
+                clazz = Class.forName(classNames[i]);
+            } catch (Exception e) {
+                if (i == excludeModIdx) {
+                    System.out.println(classNames[i] + " not found as expected because the module isn't in the --limit-modules - PASSED");
+                } else {
+                    throw(e);
+                }
+            }
+
+            if (clazz != null && i != excludeModIdx) {
+                // Make sure we got the expected defining ClassLoader
+                testLoader(clazz, expectedLoaders[i]);
+
+                // Make sure the class is in the shared space
+                if (!wb.isSharedClass(clazz)) {
+                    throw new RuntimeException(clazz.getName() +
+                        ".class should be in the shared space. " +
+                         "loader=" + clazz.getClassLoader() + " module=" + clazz.getModule().getName());
+                }
+            }
+            clazz = null;
+        }
+    }
+
+    /**
+     * Asserts that given class has the expected defining loader.
+     */
+    static void testLoader(Class<?> clazz, ClassLoader expected) {
+        ClassLoader loader = clazz.getClassLoader();
+        if (loader != expected) {
+            throw new RuntimeException(clazz + " loaded by " + loader + ", expected " + expected);
+        }
+    }
+
+    static void assertTrue(boolean expr) {
+        if (!expr)
+            throw new RuntimeException("assertion failed");
+    }
+}
diff --git a/test/hotspot/jtreg/runtime/appcds/jigsaw/limitmods/LimitModsTests.java b/test/hotspot/jtreg/runtime/appcds/jigsaw/limitmods/LimitModsTests.java
new file mode 100644
index 00000000000..434ddfcfae6
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/jigsaw/limitmods/LimitModsTests.java
@@ -0,0 +1,164 @@
+/*
+ * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+/**
+ * @test
+ * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true)
+ * @library ../..
+ * @library /test/lib
+ * @modules java.base/jdk.internal.misc
+ * @modules jdk.jartool/sun.tools.jar
+ *          jdk.internal.jvmstat/sun.jvmstat.monitor
+ * @compile LimitModsHelper.java
+ * @compile ../../test-classes/java/net/HttpCookie.jasm
+ * @compile ../../test-classes/jdk/dynalink/DynamicLinker.jasm
+ * @compile ../../test-classes/com/sun/tools/javac/Main.jasm
+ * @build sun.hotspot.WhiteBox
+ * @run main ClassFileInstaller sun.hotspot.WhiteBox
+ * @run main LimitModsTests
+ * @summary AppCDS tests for excluding class in module by using --limit-modules.
+ */
+
+/**
+ * This is for testing the --limit-modules option with AppCDS.
+ * This test assumes the following defining class loader, module, class relations:
+ * class loader    module            class
+ * -----------------------------------------------------
+ * boot            java.base         java/net/HttpCookie
+ * platform        jdk.dynalink      jdk/dynalink/DynamicLinker
+ * app             jdk.compiler      com/sun/tools/javac/Main
+ *
+ * This test dumps the above 3 classes into a shared archive.
+ * Then it will run the following 4 -limit-modules scenarios:
+ * 1. without --limit-modules
+ *    All 3 classes should be loaded successfully.
+ *    All 3 classes should be loaded by the appropriate class loader.
+ *    All 3 classes should be found in the shared archive.
+ * 2. --limit-modules java.base,jdk.dynalink
+ *    The loading of the com/sun/tools/javac/Main class should fail.
+ *    The other 2 classes should be loaded successfully and by the appropriate class loader.
+ *    The other 2 classes should be found in the shared archive.
+ * 3. --limit-modules java.base,jdk.compiler
+ *    The loading of the jdk/nio/dynalink/DynamicLinker class should fail.
+ *    The other 2 classes should be loaded successfully and by the appropriate class loader.
+ *    The other 2 classes should be found in the shared archive.
+ * 4. --limit-modules jdk.dynalink,jdk.compiler
+ *    The java.base module can't be excluded.
+ *    The results for this case is the same as for case #1.
+ */
+
+import java.io.File;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+
+import jdk.test.lib.process.ProcessTools;
+import jdk.test.lib.process.OutputAnalyzer;
+
+
+public class LimitModsTests {
+
+    // the module that is limited
+    private static final String[] LIMIT_MODULES = {"java.base", "jdk.dynalink", "jdk.compiler"};
+
+    // test classes to archive.
+    private static final String BOOT_ARCHIVE_CLASS = "java/net/HttpCookie";
+    private static final String PLATFORM_ARCHIVE_CLASS = "jdk/dynalink/DynamicLinker";
+    private static final String APP_ARCHIVE_CLASS = "com/sun/tools/javac/Main";
+    private static final String[] ARCHIVE_CLASSES = {
+        BOOT_ARCHIVE_CLASS, PLATFORM_ARCHIVE_CLASS, APP_ARCHIVE_CLASS};
+    private String bootClassPath = null;
+    private String whiteBoxJar = null;
+    private String helperJar = null;
+    private String appJar = null;
+    private OutputAnalyzer output = null;
+
+    public static void main(String[] args) throws Exception {
+        LimitModsTests tests = new LimitModsTests();
+        tests.dumpArchive();
+        tests.runTestNoLimitMods();
+        tests.runTestLimitMods();
+    }
+
+    void dumpArchive() throws Exception {
+        JarBuilder.build("limitModsTest", BOOT_ARCHIVE_CLASS, PLATFORM_ARCHIVE_CLASS, APP_ARCHIVE_CLASS);
+        JarBuilder.build(true, "WhiteBox", "sun/hotspot/WhiteBox");
+        JarBuilder.build("limitModsHelper", "LimitModsHelper");
+
+        appJar = TestCommon.getTestJar("limitModsTest.jar");
+        whiteBoxJar = TestCommon.getTestJar("WhiteBox.jar");
+        helperJar = TestCommon.getTestJar("limitModsHelper.jar");
+        bootClassPath = "-Xbootclasspath/a:" + whiteBoxJar;
+        // Dump the test classes into the archive
+        OutputAnalyzer output1  = TestCommon.dump(appJar, TestCommon.list(ARCHIVE_CLASSES), bootClassPath);
+        TestCommon.checkDump(output1);
+        // Make sure all the classes where successfully archived.
+        for (String archiveClass : ARCHIVE_CLASSES) {
+            output1.shouldNotContain("Preload Warning: Cannot find " + archiveClass);
+        }
+    }
+
+    // run the test without --limit-modules
+    public void runTestNoLimitMods() throws Exception {
+        output = TestCommon.exec(
+            appJar + File.pathSeparator + helperJar,
+            "-XX:+UnlockDiagnosticVMOptions", "-XX:+WhiteBoxAPI", bootClassPath,
+            "LimitModsHelper",
+            BOOT_ARCHIVE_CLASS, PLATFORM_ARCHIVE_CLASS, APP_ARCHIVE_CLASS, "-1"); // last 4 args passed to test
+        TestCommon.checkExec(output);
+    }
+
+    // run the test with --limit-modules
+    //
+    // --limit-modules jdk.dynalink,jdk.compiler
+    // It seems we can't exclude the java.base module. For this case,
+    // although the java.base module isn't in --limit-modules, the class
+    // in the java.base module (java.net.HttpCookie) can also be found.
+    //
+    // --limit-modules java.base,jdk.dynalink
+    // --limit-modules java.base,jdk.compiler
+    public void runTestLimitMods() throws Exception {
+        String limitMods = null;
+        for (int excludeModIdx = 0; excludeModIdx < 3; excludeModIdx++) {
+            for (int includeModIdx = 0; includeModIdx < 3; includeModIdx++) {
+                if (includeModIdx != excludeModIdx) {
+                    if (limitMods != null) {
+                        limitMods += ",";
+                        limitMods += LIMIT_MODULES[includeModIdx];
+                    } else {
+                        limitMods = LIMIT_MODULES[includeModIdx];
+                    }
+                }
+            }
+            output = TestCommon.exec(
+                appJar + File.pathSeparator + helperJar,
+                "-XX:+UnlockDiagnosticVMOptions", "-XX:+WhiteBoxAPI", bootClassPath,
+                "--limit-modules", limitMods,
+                "LimitModsHelper",
+                BOOT_ARCHIVE_CLASS, PLATFORM_ARCHIVE_CLASS, APP_ARCHIVE_CLASS,
+                Integer.toString(excludeModIdx)); // last 4 args passed to test
+            TestCommon.checkExec(output);
+            limitMods = null;
+        }
+    }
+}
diff --git a/test/hotspot/jtreg/runtime/appcds/jigsaw/overridetests/OverrideTests.java b/test/hotspot/jtreg/runtime/appcds/jigsaw/overridetests/OverrideTests.java
new file mode 100644
index 00000000000..42355b73ec2
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/jigsaw/overridetests/OverrideTests.java
@@ -0,0 +1,238 @@
+/*
+ * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+/**
+ * @test
+ * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true)
+ * @modules java.base/jdk.internal.misc
+ * @library ../..
+ * @library /test/lib
+ * @run main OverrideTests
+ * @summary AppCDS tests for overriding archived classes with -p and --upgrade-module-path
+ */
+
+/*
+ * This test consists of 4 tests:
+ *   1. Archive PLATFORM class and override with --upgrade-module-path.
+ *   2. Archive PLATFORM class and override with -p.
+ *   3. Archive APP class and override with --upgrade-module-path.
+ *   4. Archive App class and override with -p.
+ * For all 4 tests, the class is instantiatied and toString() is called
+ * to check whether the archived version or the override version was instantiatied.
+ * For tests 1 and 3, the overridden version should be instantiatied.
+ * For tests 2 and 4, the archived version should be instantiated.
+ *
+ * This test uses the same test helper class in all 4 cases. It is located in
+ * src/test/jdk/test/Main.java. It will be invoked once for each test cases,
+ * with parameters to the test determining how it is run and what the
+ * expected result is. See Main.java for a description of these 3 arguments.
+ */
+
+import java.io.File;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+
+import jdk.test.lib.Asserts;
+import jdk.test.lib.cds.CDSOptions;
+import jdk.test.lib.cds.CDSTestUtils;
+import jdk.test.lib.process.OutputAnalyzer;
+import jdk.test.lib.process.ProcessTools;
+
+
+public class OverrideTests {
+    private static final String TEST_SRC = System.getProperty("test.src");
+    private static final Path SRC_DIR = Paths.get(TEST_SRC, "src");
+    private static final Path MODS_DIR = Paths.get("mods");
+
+    // the module that is upgraded
+    private static final String[] UPGRADED_MODULES = {"jdk.compiler", "java.activation"};
+    private static final Path[] UPGRADEDMODS_DIR = {Paths.get("upgradedmod1"), Paths.get("upgradedmod2")};
+
+    // the test module
+    private static final String TEST_MODULE = "test";
+    private static final String MAIN_CLASS = "jdk.test.Main";
+
+    // test classes to archive. These are both in UPGRADED_MODULES
+    private static final String APP_ARCHIVE_CLASS = "com/sun/tools/javac/Main";
+    private static final String PLATFORM_ARCHIVE_CLASS = "javax/activation/UnsupportedDataTypeException";
+    private static final String[] ARCHIVE_CLASSES = {APP_ARCHIVE_CLASS, PLATFORM_ARCHIVE_CLASS};
+    private static String testArchiveName;
+
+
+    public static void main(String[] args) throws Exception {
+        OverrideTests tests = new OverrideTests();
+        tests.compileModulesAndDumpArchive();
+        tests.testAppClassOverriding();
+        tests.testPlatformClassOverriding();
+    }
+
+    void compileModulesAndDumpArchive() throws Exception {
+        boolean compiled;
+        // javac -d upgradedmods/$upgradedMod src/$upgradedMod/**
+        int i = 0;
+        for (String upgradedMod : UPGRADED_MODULES) {
+            compiled = CompilerUtils.compile(
+                SRC_DIR.resolve(upgradedMod),
+                UPGRADEDMODS_DIR[i].resolve(upgradedMod)
+            );
+            Asserts.assertTrue(compiled, upgradedMod + " did not compile");
+            i++;
+        }
+
+        // javac -d mods/test --upgrade-module-path upgradedmods ...
+        compiled = CompilerUtils.compile(
+            SRC_DIR.resolve(TEST_MODULE),
+            MODS_DIR.resolve(TEST_MODULE),
+            "--upgrade-module-path", UPGRADEDMODS_DIR[0].toString() +
+             System.getProperty("path.separator") + UPGRADEDMODS_DIR[1].toString()
+        );
+        Asserts.assertTrue(compiled, TEST_MODULE + " did not compile");
+
+        // the java.activation module is not defined by default; --add-modules is required.
+        // dumping without "--add-modules java.activation"
+        // the class in the javax.activation package cannot be found
+        OutputAnalyzer output1  = TestCommon.dump(null /* appJar*/, TestCommon.list(ARCHIVE_CLASSES));
+        TestCommon.checkDump(output1);
+        output1.shouldContain(
+            "Preload Warning: Cannot find javax/activation/UnsupportedDataTypeException");
+
+        // dump the archive with jdk.comiler and java.activation classes in the class list
+        // with "--add-modules java.activation"
+        output1  = TestCommon.dump(null /* appJar*/, TestCommon.list(ARCHIVE_CLASSES),
+            "--add-modules", "java.activation");
+        TestCommon.checkDump(output1);
+        // Make sure all the classes where successfully archived.
+        for (String archiveClass : ARCHIVE_CLASSES) {
+            output1.shouldNotContain("Preload Warning: Cannot find " + archiveClass);
+        }
+
+        testArchiveName = TestCommon.getCurrentArchiveName();
+    }
+
+    /**
+     * APP Class Overriding Tests
+     *
+     * Archive APP class com.sun.tools.javac.Main from module jdk.compiler.
+     *  -At run time, upgrade module jdk.compiler using --upgrade-module-path.
+     *   Class.forname(Main) MUST NOT load the archived Main.
+     *  -At run time, module jdk.compiler also exists in --module-path.
+     *   Class.forname(Main) MUST load the archived Main.
+     */
+    public void testAppClassOverriding() throws Exception {
+        testClassOverriding(APP_ARCHIVE_CLASS, "app");
+    }
+
+    /**
+     * PLATFORM Class Overriding Tests
+     *
+     * Archive PLATFORM class javax.activation.UnsupportedDataTypeException from module jdk.activation.
+     *  -At run time, upgrade module jdk.activation using --upgrade-module-path.
+     *   Class.forname(UnsupportedDataTypeException) MUST NOT load the archived UnsupportedDataTypeException.
+     *  -At run time, module jdk.activation also exists in --module-path.
+     *   Class.forname(UnsupportedDataTypeException) MUST load the archived UnsupportedDataTypeException.
+     */
+    public void testPlatformClassOverriding() throws Exception {
+        testClassOverriding(PLATFORM_ARCHIVE_CLASS, "platform");
+    }
+
+    /**
+     * Run the test twice. Once with upgrade module on --upgrade-module-path and once with it on -p.
+     * Only modules defined to the PlatformClassLoader are upgradeable.
+     * Modules defined to the AppClassLoader are not upgradeble; we expect the
+     * FindException to be thrown.
+     */
+    void testClassOverriding(String archiveClass, String loaderName) throws Exception {
+        String mid = TEST_MODULE + "/" + MAIN_CLASS;
+        OutputAnalyzer output;
+        boolean isAppLoader = loaderName.equals("app");
+        int upgradeModIdx = isAppLoader ? 0 : 1;
+        String expectedException = "java.lang.module.FindException: Unable to compute the hash";
+        String prefix[] = new String[4];
+        prefix[0] = "-cp";
+        prefix[1] = "\"\"";
+        prefix[2] = "--add-modules";
+        prefix[3] = "java.activation";
+
+        // Run the test with --upgrade-module-path set to alternate location of archiveClass
+        // The alternate version of archiveClass SHOULD be found.
+        output = TestCommon.execModule(
+            prefix,
+            UPGRADEDMODS_DIR[upgradeModIdx].toString(),
+            MODS_DIR.toString(),
+            mid,
+            archiveClass, loaderName, "true"); // last 3 args passed to test
+        if (isAppLoader) {
+            try {
+                output.shouldContain(expectedException);
+            } catch (Exception e) {
+                TestCommon.checkCommonExecExceptions(output, e);
+            }
+        } else {
+            TestCommon.checkExec(output);
+        }
+
+        // Now run this same test again, but this time without AppCDS. Behavior should be the same.
+        CDSOptions opts = (new CDSOptions())
+            .addPrefix(prefix)
+            .setArchiveName(testArchiveName).setUseVersion(false)
+            .addSuffix("--upgrade-module-path", UPGRADEDMODS_DIR[upgradeModIdx].toString(),
+                       "-p", MODS_DIR.toString(), "-m", mid)
+            .addSuffix(archiveClass, loaderName, "true");
+
+        output = CDSTestUtils.runWithArchive(opts);
+
+        if (isAppLoader) {
+            try {
+                output.shouldContain(expectedException);
+            } catch (Exception e) {
+                TestCommon.checkCommonExecExceptions(output, e);
+            }
+        } else {
+            if (!CDSTestUtils.isUnableToMap(output))
+                output.shouldHaveExitValue(0);
+        }
+
+        // Run the test with -p set to alternate location of archiveClass.
+        // The alternate version of archiveClass SHOULD NOT be found.
+        output = TestCommon.execModule(
+            prefix,
+            null,
+            UPGRADEDMODS_DIR[upgradeModIdx].toString() + java.io.File.pathSeparator + MODS_DIR.toString(),
+            mid,
+            archiveClass, loaderName, "false"); // last 3 args passed to test
+        TestCommon.checkExec(output);
+
+        // Now  run this same test again, but this time without AppCDS. Behavior should be the same.
+        opts = (new CDSOptions())
+            .addPrefix(prefix)
+            .setArchiveName(testArchiveName).setUseVersion(false)
+            .addSuffix("-p", MODS_DIR.toString(), "-m", mid)
+            .addSuffix(archiveClass, loaderName, "false"); // params to the test class
+
+        OutputAnalyzer out = CDSTestUtils.runWithArchive(opts);
+        if (!CDSTestUtils.isUnableToMap(out))
+            out.shouldHaveExitValue(0);
+    }
+}
diff --git a/test/hotspot/jtreg/runtime/appcds/jigsaw/overridetests/src/java.activation/javax/activation/UnsupportedDataTypeException.java b/test/hotspot/jtreg/runtime/appcds/jigsaw/overridetests/src/java.activation/javax/activation/UnsupportedDataTypeException.java
new file mode 100644
index 00000000000..3016bb41a10
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/jigsaw/overridetests/src/java.activation/javax/activation/UnsupportedDataTypeException.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2015, 2017, 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 javax.activation;
+
+import java.io.IOException;
+
+public class UnsupportedDataTypeException extends IOException {
+    public UnsupportedDataTypeException() {
+    }
+
+    public String toString() {
+        return "hi";
+    }
+}
diff --git a/test/hotspot/jtreg/runtime/appcds/jigsaw/overridetests/src/java.activation/module-info.java b/test/hotspot/jtreg/runtime/appcds/jigsaw/overridetests/src/java.activation/module-info.java
new file mode 100644
index 00000000000..23c707a46f7
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/jigsaw/overridetests/src/java.activation/module-info.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2015, 2017, 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.
+ *
+ */
+
+module java.activation {
+    exports javax.activation;
+}
+
diff --git a/test/hotspot/jtreg/runtime/appcds/jigsaw/overridetests/src/jdk.compiler/com/sun/tools/javac/Main.java b/test/hotspot/jtreg/runtime/appcds/jigsaw/overridetests/src/jdk.compiler/com/sun/tools/javac/Main.java
new file mode 100644
index 00000000000..f535ccd034a
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/jigsaw/overridetests/src/jdk.compiler/com/sun/tools/javac/Main.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2015, 2017, 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 com.sun.tools.javac;
+
+public class Main {
+    public String toString() {
+        return "hi";
+    }
+}
diff --git a/test/hotspot/jtreg/runtime/appcds/jigsaw/overridetests/src/jdk.compiler/module-info.java b/test/hotspot/jtreg/runtime/appcds/jigsaw/overridetests/src/jdk.compiler/module-info.java
new file mode 100644
index 00000000000..a9c236d7bac
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/jigsaw/overridetests/src/jdk.compiler/module-info.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2015, 2017, 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.
+ *
+ */
+
+module jdk.compiler {
+    exports com.sun.tools.javac;
+}
+
diff --git a/test/hotspot/jtreg/runtime/appcds/jigsaw/overridetests/src/test/jdk/test/Main.java b/test/hotspot/jtreg/runtime/appcds/jigsaw/overridetests/src/test/jdk/test/Main.java
new file mode 100644
index 00000000000..e752f4d3da5
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/jigsaw/overridetests/src/test/jdk/test/Main.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (c) 2015, 2017, 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.
+ *
+ */
+
+/**
+ * Used with -p or --upgrade-module-path to exercise the replacement
+ * of classes in modules that are linked into the runtime image.
+ */
+
+package jdk.test;
+
+public class Main {
+    static final ClassLoader PLATFORM_LOADER = ClassLoader.getPlatformClassLoader();
+    static final ClassLoader SYS_LOADER      = ClassLoader.getSystemClassLoader();
+
+    public static void main(String[] args) throws Exception {
+        ClassLoader loader = null;
+        boolean shouldOverride = false;
+
+        /*
+         * 3 Arguments are passed to this test:
+         *   1. className: Name of the class to load.
+         *   2. loaderName: Either "platform" or "app", which specifies which ClassLoader is expected
+         *      to be the defining ClassLoader once the class is loaded. The initiating
+         *      ClassLoader is always the default ClassLoader (which should be the
+         *      app (system) ClassLoader.
+         *   3. shouldOverride: Either "true" or "false" to indicate whether the loaded class
+         *      should be the one we are attempting to override with (not the archived version).
+         */
+
+        assertTrue(args.length == 3, "Unexpected number of arguments: expected 3, actual " + args.length);
+        String className = args[0].replace('/', '.');
+        String loaderName = args[1]; // "platform" or "app"
+        String shouldOverrideName = args[2];  // "true" or "false"
+
+        if (loaderName.equals("app")) {
+            loader = SYS_LOADER;
+        } else if (loaderName.equals("platform")) {
+            loader = PLATFORM_LOADER;
+        } else {
+            assertTrue(false);
+        }
+
+        if (shouldOverrideName.equals("true")) {
+            shouldOverride = true;
+        } else if (shouldOverrideName.equals("false")) {
+            shouldOverride = false;
+        } else {
+            assertTrue(false);
+        }
+
+        // Load the class with the default ClassLoader.
+        Class<?> clazz = Class.forName(className, true, loader);
+        // Make sure we got the expected defining ClassLoader
+        testLoader(clazz, loader);
+        // Create an instance and see what toString() returns
+        String s = clazz.newInstance().toString();
+        // The overridden version of the class should return "hi". Make sure
+        // it does only if we are expecting to have loaded the overridden version.
+        assertTrue(s.equals("hi") == shouldOverride);
+    }
+
+    /**
+     * Asserts that given class has the expected defining loader.
+     */
+    static void testLoader(Class<?> clazz, ClassLoader expected) {
+        ClassLoader loader = clazz.getClassLoader();
+        if (loader != expected) {
+            throw new RuntimeException(clazz + " loaded by " + loader + ", expected " + expected);
+        }
+    }
+
+    static void assertTrue(boolean expr) {
+        assertTrue(expr, "");
+    }
+
+    static void assertTrue(boolean expr, String msg) {
+        if (!expr)
+            throw new RuntimeException("assertion failed: " + msg);
+    }
+}
diff --git a/test/hotspot/jtreg/runtime/appcds/jigsaw/overridetests/src/test/module-info.java b/test/hotspot/jtreg/runtime/appcds/jigsaw/overridetests/src/test/module-info.java
new file mode 100644
index 00000000000..8ca8942555b
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/jigsaw/overridetests/src/test/module-info.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2015, 2017, 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.
+ *
+ */
+
+module test {
+    requires jdk.compiler;
+    requires java.activation;
+}
diff --git a/test/hotspot/jtreg/runtime/appcds/jvmti/ClassFileLoadHook.java b/test/hotspot/jtreg/runtime/appcds/jvmti/ClassFileLoadHook.java
new file mode 100644
index 00000000000..7d564caa8d1
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/jvmti/ClassFileLoadHook.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+import sun.hotspot.WhiteBox;
+
+class LoadMe {
+    static String getValue() {
+        return "beforeHook";
+    }
+    static String getOtherValue() {
+        return "abc-beforeHook-xyz";
+    }
+}
+
+public class ClassFileLoadHook {
+    public enum TestCaseId {
+        SHARING_OFF_CFLH_ON,   // test case to establish a baseline
+        SHARING_ON_CFLH_OFF,
+        SHARING_AUTO_CFLH_ON,
+        SHARING_ON_CFLH_ON
+    }
+
+    public static void main(String args[]) {
+        TestCaseId testCase = TestCaseId.valueOf(args[0]);
+        WhiteBox wb = WhiteBox.getWhiteBox();
+
+        System.out.println("====== ClassFileLoadHook.main():testCase = " + testCase);
+        System.out.println("getValue():" + LoadMe.getValue());
+        System.out.println("getOtherValue():" + LoadMe.getOtherValue());
+
+        switch (testCase) {
+            case SHARING_OFF_CFLH_ON:
+                assertTrue("after_Hook".equals(LoadMe.getValue()) &&
+                           "abc-after_Hook-xyz".equals(LoadMe.getOtherValue()),
+                           "Not sharing, this test should replace beforeHook " +
+                           "with after_Hook");
+            break;
+
+            case SHARING_ON_CFLH_OFF:
+                assertTrue(wb.isSharedClass(LoadMe.class),
+                    "LoadMe should be shared, but is not");
+                assertTrue("beforeHook".equals(LoadMe.getValue()) &&
+                           "abc-beforeHook-xyz".equals(LoadMe.getOtherValue()),
+                           "CFLH off, bug values are redefined");
+            break;
+
+            case SHARING_AUTO_CFLH_ON:
+            case SHARING_ON_CFLH_ON:
+                // LoadMe is rewritten on CFLH
+                assertFalse(wb.isSharedClass(LoadMe.class),
+                    "LoadMe should not be shared if CFLH has modified the class");
+                assertFalse("beforeHook".equals(LoadMe.getValue()) &&
+                           "abc-beforeHook-xyz".equals(LoadMe.getOtherValue()),
+                           "Class contents should be changed if CFLH is enabled");
+             break;
+
+             default:
+                 throw new RuntimeException("Invalid testcase");
+
+        }
+    }
+
+    private static void assertTrue(boolean expr, String msg) {
+        if (!expr)
+            throw new RuntimeException(msg);
+    }
+
+    private static void assertFalse(boolean expr, String msg) {
+        if (expr)
+            throw new RuntimeException(msg);
+    }
+}
diff --git a/test/hotspot/jtreg/runtime/appcds/jvmti/ClassFileLoadHookTest.java b/test/hotspot/jtreg/runtime/appcds/jvmti/ClassFileLoadHookTest.java
new file mode 100644
index 00000000000..e62013ba8a3
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/jvmti/ClassFileLoadHookTest.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+/*
+ * @test
+ * @summary Test jvmti class file loader hook interaction with AppCDS
+ * @library /test/lib /test/hotspot/jtreg/runtime/appcds
+ * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true)
+ * @modules java.base/jdk.internal.misc
+ *          jdk.jartool/sun.tools.jar
+ *          java.management
+ * @build ClassFileLoadHook
+ * @run main/othervm/native ClassFileLoadHookTest
+ */
+
+
+import jdk.test.lib.Asserts;
+import jdk.test.lib.cds.CDSOptions;
+import jdk.test.lib.process.OutputAnalyzer;
+import jdk.test.lib.process.ProcessTools;
+
+
+public class ClassFileLoadHookTest {
+    public static String sharedClasses[] = {
+        "ClassFileLoadHook",
+        "ClassFileLoadHook$TestCaseId",
+        "ClassFileLoadHook$1",
+        "LoadMe"
+    };
+
+    public static void main(String[] args) throws Exception {
+        String wbJar =
+            ClassFileInstaller.writeJar("WhiteBox.jar", "sun.hotspot.WhiteBox");
+        String appJar =
+            ClassFileInstaller.writeJar("ClassFileLoadHook.jar", sharedClasses);
+        String useWb = "-Xbootclasspath/a:" + wbJar;
+
+        // First, run the test class directly, w/o sharing, as a baseline reference
+        ProcessBuilder pb = ProcessTools.createJavaProcessBuilder(
+                "-XX:+UnlockDiagnosticVMOptions",
+                "-XX:+WhiteBoxAPI",
+                useWb,
+                "-agentlib:SimpleClassFileLoadHook=LoadMe,beforeHook,after_Hook",
+                "ClassFileLoadHook",
+                "" + ClassFileLoadHook.TestCaseId.SHARING_OFF_CFLH_ON);
+        TestCommon.executeAndLog(pb, "no-sharing").shouldHaveExitValue(0);
+
+        // Run with AppCDS, but w/o CFLH - second baseline
+        TestCommon.testDump(appJar, sharedClasses, useWb);
+        OutputAnalyzer out = TestCommon.exec(appJar,
+                "-XX:+UnlockDiagnosticVMOptions",
+                "-XX:+WhiteBoxAPI", useWb,
+                "ClassFileLoadHook",
+                "" + ClassFileLoadHook.TestCaseId.SHARING_ON_CFLH_OFF);
+
+        TestCommon.checkExec(out);
+
+
+        // Now, run with AppCDS with -Xshare:auto and CFLH
+        out = TestCommon.execAuto("-cp", appJar,
+                "-XX:+UnlockDiagnosticVMOptions",
+                "-XX:+WhiteBoxAPI", useWb,
+                "-agentlib:SimpleClassFileLoadHook=LoadMe,beforeHook,after_Hook",
+                "ClassFileLoadHook",
+                "" + ClassFileLoadHook.TestCaseId.SHARING_AUTO_CFLH_ON);
+
+        CDSOptions opts = (new CDSOptions()).setXShareMode("auto");
+        TestCommon.checkExec(out, opts);
+
+        // Now, run with AppCDS -Xshare:on and CFLH
+        out = TestCommon.exec(appJar,
+                "-XX:+UnlockDiagnosticVMOptions",
+                "-XX:+WhiteBoxAPI", useWb,
+                "-agentlib:SimpleClassFileLoadHook=LoadMe,beforeHook,after_Hook",
+                "ClassFileLoadHook",
+                "" + ClassFileLoadHook.TestCaseId.SHARING_ON_CFLH_ON);
+        TestCommon.checkExec(out);
+    }
+}
diff --git a/test/hotspot/jtreg/runtime/appcds/jvmti/InstrumentationAgent.mf b/test/hotspot/jtreg/runtime/appcds/jvmti/InstrumentationAgent.mf
new file mode 100644
index 00000000000..58dcf797de6
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/jvmti/InstrumentationAgent.mf
@@ -0,0 +1,5 @@
+Manifest-Version: 1.0
+Premain-Class: InstrumentationRegisterClassFileTransformer
+Agent-Class: InstrumentationRegisterClassFileTransformer
+Can-Retransform-Classes: true
+Can-Redefine-Classes: true
diff --git a/test/hotspot/jtreg/runtime/appcds/jvmti/InstrumentationApp.java b/test/hotspot/jtreg/runtime/appcds/jvmti/InstrumentationApp.java
new file mode 100644
index 00000000000..1328f583031
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/jvmti/InstrumentationApp.java
@@ -0,0 +1,220 @@
+/*
+ * Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+import java.lang.instrument.ClassDefinition;
+import java.lang.instrument.Instrumentation;
+import java.lang.instrument.UnmodifiableClassException;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.io.File;
+import java.security.CodeSigner;
+import java.security.CodeSource;
+import java.security.ProtectionDomain;
+import sun.hotspot.WhiteBox;
+
+public class InstrumentationApp {
+    static WhiteBox wb = WhiteBox.getWhiteBox();
+
+    public static final String COO_CLASS_NAME = "InstrumentationApp$Coo";
+
+    public static interface Intf {            // Loaded from Boot class loader (-Xbootclasspath/a).
+        public String get();
+    }
+    public static class Bar implements Intf { // Loaded from Boot class loader.
+        public String get() {
+            // The initial transform:
+            //      change "buzz" -> "fuzz"
+            // The re-transform:
+            //      change "buzz" -> "guzz"
+            return "buzz";
+        }
+    }
+    public static class Foo implements Intf { // Loaded from AppClassLoader, or from a custom loader
+        public String get() {
+            // The initial transform:
+            //      change "buzz" -> "fuzz"
+            // The re-transform:
+            //      change "buzz" -> "guzz"
+            return "buzz";
+        }
+    }
+    public static class Coo implements Intf { // Loaded from custom class loader.
+        public String get() {
+            // The initial transform:
+            //      change "buzz" -> "fuzz"
+            // The re-transform:
+            //      change "buzz" -> "guzz"
+            return "buzz";
+        }
+    }
+
+    // This class file should be archived if AppCDSv2 is enabled on this platform. See
+    // the comments around the call to TestCommon.dump in InstrumentationTest.java.
+    public static class ArchivedIfAppCDSv2Enabled {}
+
+    public static boolean isAppCDSV2Enabled() {
+        return wb.isSharedClass(ArchivedIfAppCDSv2Enabled.class);
+    }
+
+    public static class MyLoader extends URLClassLoader {
+        public MyLoader(URL[] urls, ClassLoader parent, File jar) {
+            super(urls, parent);
+            this.jar = jar;
+        }
+        File jar;
+
+        @Override
+        protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
+            synchronized (getClassLoadingLock(name)) {
+                // First, check if the class has already been loaded
+                Class<?> clz = findLoadedClass(name);
+                if (clz != null) {
+                    return clz;
+                }
+
+                if (name.equals(COO_CLASS_NAME)) {
+                    try {
+                        byte[] buff = Util.getClassFileFromJar(jar, name);
+                        return defineClass(name, buff, 0, buff.length);
+                    } catch (Throwable t) {
+                        t.printStackTrace();
+                        throw new RuntimeException("Unexpected", t);
+                    }
+                }
+            }
+            return super.loadClass(name, resolve);
+        }
+    }
+
+    static int numTests = 0;
+    static int failed = 0;
+    static boolean isAttachingAgent = false;
+    static Instrumentation instrumentation;
+
+    public static void main(String args[]) throws Throwable {
+        System.out.println("INFO: AppCDSv1 " + (wb.isSharedClass(InstrumentationApp.class) ? "enabled" :"disabled"));
+        System.out.println("INFO: AppCDSv2 " + (isAppCDSV2Enabled()                        ? "enabled" : "disabled"));
+
+        File bootJar = new File(args[0]);
+        File appJar  = new File(args[1]);
+        File custJar = new File(args[2]);
+        String flagFile = args[3];
+        waitAttach(flagFile);
+
+        instrumentation = InstrumentationRegisterClassFileTransformer.getInstrumentation();
+        System.out.println("INFO: instrumentation = " + instrumentation);
+
+        testBootstrapCDS("Bootstrap Loader", bootJar);
+        testAppCDSv1("Application Loader", appJar);
+
+        if (isAppCDSV2Enabled()) {
+          testAppCDSv2("Custom Loader (unregistered)", custJar);
+        }
+
+        if (failed > 0) {
+            throw new RuntimeException("FINAL RESULT: " + failed + " out of " + numTests + " test case(s) have failed");
+        } else {
+            System.out.println("FINAL RESULT: All " + numTests + " test case(s) have passed!");
+        }
+    }
+
+    static void waitAttach(String flagFile) throws Throwable {
+        if (!flagFile.equals("noattach")) {
+            File f = new File(flagFile);
+            long start = System.currentTimeMillis();
+            while (f.exists()) {
+                long elapsed = System.currentTimeMillis() - start;
+                System.out.println(".... (" + elapsed + ") waiting for deletion of " + f);
+                Thread.sleep(1000);
+            }
+            System.out.println("Attach succeeded (child)");
+            isAttachingAgent = true;
+        }
+    }
+
+    static void testBootstrapCDS(String group, File jar) throws Throwable {
+        doTest(group, new Bar(), jar);
+    }
+
+    static void testAppCDSv1(String group, File jar) throws Throwable {
+        doTest(group, new Foo(), jar);
+    }
+
+    static void testAppCDSv2(String group, File jar) throws Throwable {
+        URL[] urls = new URL[] {jar.toURI().toURL()};
+        MyLoader loader = new MyLoader(urls, InstrumentationApp.class.getClassLoader(), jar);
+        Class klass = loader.loadClass(COO_CLASS_NAME);
+        doTest(group, (Intf)klass.newInstance(), jar);
+    }
+
+    static void doTest(String group, Intf object, File jar) throws Throwable {
+        Class klass = object.getClass();
+        System.out.println();
+        System.out.println("++++++++++++++++++++++++++");
+        System.out.println("Test group: " + group);
+        System.out.println("Testing with classloader = " + klass.getClassLoader());
+        System.out.println("Testing with class       = " + klass);
+        System.out.println("++++++++++++++++++++++++++");
+
+        // Initial transform
+        String f = object.get();
+        assertTrue(f.equals("fuzz"), "object.get(): Initial transform should give 'fuzz'", f);
+
+        // Retransform
+        f = "(failed)";
+        try {
+            instrumentation.retransformClasses(klass);
+            f = object.get();
+        } catch (UnmodifiableClassException|UnsupportedOperationException e) {
+            e.printStackTrace();
+        }
+        assertTrue(f.equals("guzz"), "object.get(): retransformation should give 'guzz'", f);
+
+        // Redefine
+        byte[] buff = Util.getClassFileFromJar(jar, klass.getName());
+        Util.replace(buff, "buzz", "huzz");
+        f = "(failed)";
+        try {
+            instrumentation.redefineClasses(new ClassDefinition(klass, buff));
+            f = object.get();
+        } catch (UnmodifiableClassException|UnsupportedOperationException e) {
+            e.printStackTrace();
+        }
+        assertTrue(f.equals("quzz"), "object.get(): redefinition should give 'quzz'", f);
+
+        System.out.println("++++++++++++++++++++++++++++++++++++++++++++++++ (done)\n\n");
+    }
+
+    private static void assertTrue(boolean expr, String msg, String string) {
+        numTests ++;
+        System.out.printf("Test case %2d ", numTests);
+
+        if (expr) {
+            System.out.println("PASSED: " + msg + " and we got '" + string + "'");
+        } else {
+            failed ++;
+            System.out.println("FAILED: " + msg + " but we got '" + string + "'");
+        }
+    }
+}
diff --git a/test/hotspot/jtreg/runtime/appcds/jvmti/InstrumentationClassFileTransformer.java b/test/hotspot/jtreg/runtime/appcds/jvmti/InstrumentationClassFileTransformer.java
new file mode 100644
index 00000000000..64eabbaf003
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/jvmti/InstrumentationClassFileTransformer.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+import java.lang.instrument.ClassFileTransformer;
+import java.lang.instrument.IllegalClassFormatException;
+import java.security.ProtectionDomain;
+
+// Note: Util is from /test/hotspot/jtreg/runtime/appcds/test-classes/TestCommon.java
+
+public class InstrumentationClassFileTransformer implements ClassFileTransformer {
+    public byte[] transform(ClassLoader loader, String name, Class<?> classBeingRedefined,
+                            ProtectionDomain pd, byte[] buffer) throws IllegalClassFormatException {
+
+        if (name.startsWith("InstrumentationApp$") && !name.equals("InstrumentationApp$NotTransformed")) {
+            System.out.println("Transforming: " + name + " class = " + classBeingRedefined);
+            try {
+                if (classBeingRedefined == null) {
+                    // Initial transform
+                    replace(buffer, "buzz", "fuzz");
+                } else {
+                    replace(buffer, "buzz", "guzz"); // Retransform
+                    replace(buffer, "huzz", "quzz"); // Redefine
+                }
+            } catch (Throwable t) {
+                t.printStackTrace();
+            }
+            return buffer;
+        }
+        return null;
+    }
+
+    static void replace(byte[] buffer, String from, String to) {
+        int n = Util.replace(buffer, from, to);
+        System.out.println("..... replaced " + n + " occurrence(s) of '" + from + "' to '" + to + "'");
+    }
+}
diff --git a/test/hotspot/jtreg/runtime/appcds/jvmti/InstrumentationRegisterClassFileTransformer.java b/test/hotspot/jtreg/runtime/appcds/jvmti/InstrumentationRegisterClassFileTransformer.java
new file mode 100644
index 00000000000..2d2aec74269
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/jvmti/InstrumentationRegisterClassFileTransformer.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+import java.lang.instrument.ClassFileTransformer;
+import java.lang.instrument.Instrumentation;
+
+// This class is available on the classpath so it can be accessed by InstrumentationApp
+public class InstrumentationRegisterClassFileTransformer {
+    private static Instrumentation savedInstrumentation;
+
+    public static void premain(String agentArguments, Instrumentation instrumentation) {
+        System.out.println("InstrumentationRegisterClassFileTransformer.premain() is called");
+        instrumentation.addTransformer(new InstrumentationClassFileTransformer(), /*canRetransform=*/true);
+        savedInstrumentation = instrumentation;
+    }
+
+    public static Instrumentation getInstrumentation() {
+        return savedInstrumentation;
+    }
+
+    public static void agentmain(String args, Instrumentation inst) throws Exception {
+        premain(args, inst);
+    }
+}
diff --git a/test/hotspot/jtreg/runtime/appcds/jvmti/InstrumentationTest.java b/test/hotspot/jtreg/runtime/appcds/jvmti/InstrumentationTest.java
new file mode 100644
index 00000000000..1e869c28929
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/jvmti/InstrumentationTest.java
@@ -0,0 +1,278 @@
+/*
+ * Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+/*
+ * @test
+ * @summary Exercise the java.lang.instrument.Instrumentation APIs on classes archived
+ *          using CDS/AppCDSv1/AppCDSv2.
+ * @library /test/lib /test/hotspot/jtreg/runtime/appcds /test/hotspot/jtreg/runtime/appcds/test-classes
+ * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true)
+ * @requires vm.flavor != "minimal"
+ * @modules java.base/jdk.internal.misc
+ *          jdk.jartool/sun.tools.jar
+ *          java.management
+ * @build sun.hotspot.WhiteBox
+ *        InstrumentationApp
+ *        InstrumentationClassFileTransformer
+ *        InstrumentationRegisterClassFileTransformer
+ * @run main/othervm InstrumentationTest
+ */
+
+// Note: TestCommon is from /test/hotspot/jtreg/runtime/appcds/TestCommon.java
+// Note: Util       is from /test/hotspot/jtreg/runtime/appcds/test-classes/TestCommon.java
+
+import com.sun.tools.attach.VirtualMachine;
+import com.sun.tools.attach.VirtualMachineDescriptor;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.util.List;
+import jdk.test.lib.Asserts;
+import jdk.test.lib.cds.CDSOptions;
+import jdk.test.lib.process.OutputAnalyzer;
+import jdk.test.lib.process.ProcessTools;
+
+public class InstrumentationTest {
+    public static String bootClasses[] = {
+        "InstrumentationApp$Intf",
+        "InstrumentationApp$Bar",
+        "sun.hotspot.WhiteBox",
+    };
+    public static String appClasses[] = {
+        "InstrumentationApp",
+        "InstrumentationApp$Foo",
+        "InstrumentationApp$MyLoader",
+    };
+    public static String custClasses[] = {
+        "InstrumentationApp$Coo",
+    };
+    public static String sharedClasses[] = TestCommon.concat(bootClasses, appClasses);
+
+    public static String agentClasses[] = {
+        "InstrumentationClassFileTransformer",
+        "InstrumentationRegisterClassFileTransformer",
+        "Util",
+    };
+
+    public static void main(String[] args) throws Throwable {
+        runTest(false);
+        runTest(true);
+    }
+
+    public static void runTest(boolean attachAgent) throws Throwable {
+        String bootJar =
+            ClassFileInstaller.writeJar("InstrumentationBoot.jar", bootClasses);
+        String appJar =
+            ClassFileInstaller.writeJar("InstrumentationApp.jar",
+                                        TestCommon.concat(appClasses,
+                                                          "InstrumentationApp$ArchivedIfAppCDSv2Enabled"));
+        String custJar =
+            ClassFileInstaller.writeJar("InstrumentationCust.jar", custClasses);
+        String agentJar =
+            ClassFileInstaller.writeJar("InstrumentationAgent.jar",
+                                        ClassFileInstaller.Manifest.fromSourceFile("InstrumentationAgent.mf"),
+                                        agentClasses);
+
+        String bootCP = "-Xbootclasspath/a:" + bootJar;
+
+        System.out.println("");
+        System.out.println("============================================================");
+        System.out.println("CDS: NO, attachAgent: " + (attachAgent ? "YES" : "NO"));
+        System.out.println("============================================================");
+        System.out.println("");
+
+        String agentCmdArg, flagFile;
+        if (attachAgent) {
+            // we will attach the agent, so don't specify -javaagent in the command line. We'll use
+            // something harmless like -showversion to make it easier to construct the command line
+            agentCmdArg = "-showversion";
+        } else {
+            agentCmdArg = "-javaagent:" + agentJar;
+        }
+
+        // First, run the test class directly, w/o sharing, as a baseline reference
+        flagFile = getFlagFile(attachAgent);
+        AgentAttachThread t = doAttach(attachAgent, flagFile, agentJar);
+        ProcessBuilder pb = ProcessTools.createJavaProcessBuilder(
+                bootCP,
+                "-cp", appJar,
+                "-XX:+UnlockDiagnosticVMOptions",
+                "-XX:+WhiteBoxAPI",
+                "-Xshare:off",
+                agentCmdArg,
+                "InstrumentationApp", bootJar, appJar, custJar, flagFile);
+        TestCommon.executeAndLog(pb, "no-sharing").shouldHaveExitValue(0);
+        checkAttach(t);
+
+        // Dump the AppCDS archive. On some platforms AppCDSv2 may not be enabled, so we
+        // first try the v2 classlist, and if that fails, revert to the v1 classlist.
+        // Note that the InstrumentationApp$ArchivedIfAppCDSv2Enabled class is archived
+        // only if V2 is enabled. This is tested by InstrumentationApp.isAppCDSV2Enabled().
+        String[] v2Classes = {
+            "InstrumentationApp$ArchivedIfAppCDSv2Enabled",
+            "java/lang/Object id: 0",
+            "InstrumentationApp$Intf id: 1",
+            "InstrumentationApp$Coo  id: 2 super: 0 interfaces: 1 source: " + custJar,
+        };
+        String[] sharedClassesWithV2 = TestCommon.concat(v2Classes, sharedClasses);
+        OutputAnalyzer out = TestCommon.dump(appJar, sharedClassesWithV2, bootCP);
+        if (out.getExitValue() != 0) {
+            System.out.println("Redumping with AppCDSv2 disabled");
+                TestCommon.testDump(appJar, sharedClasses, bootCP);
+        }
+
+        // Run with AppCDS.
+        System.out.println("");
+        System.out.println("============================================================");
+        System.out.println("CDS: YES, attachAgent: " + (attachAgent ? "YES" : "NO"));
+        System.out.println("============================================================");
+        System.out.println("");
+
+        flagFile = getFlagFile(attachAgent);
+        t = doAttach(attachAgent, flagFile, agentJar);
+        out = TestCommon.execAuto("-cp", appJar,
+                bootCP,
+                "-XX:+UnlockDiagnosticVMOptions",
+                "-XX:+WhiteBoxAPI",
+                agentCmdArg,
+               "InstrumentationApp", bootJar, appJar, custJar, flagFile);
+
+        CDSOptions opts = (new CDSOptions()).setXShareMode("auto");
+        TestCommon.checkExec(out, opts);
+        checkAttach(t);
+    }
+
+    static int flagFileSerial = 1;
+    static private String getFlagFile(boolean attachAgent) {
+        if (attachAgent) {
+            // Do not reuse the same file name as Windows may fail to
+            // delete the file.
+            return "attach.flag." + ProcessHandle.current().pid() +
+                    "." + (flagFileSerial++) + "." + System.currentTimeMillis();
+        } else {
+            return "noattach";
+        }
+    }
+
+    static AgentAttachThread doAttach(boolean attachAgent, String flagFile, String agentJar) throws Throwable {
+        if (!attachAgent) {
+            return null;
+        }
+
+        // We use the flagFile to prevent the child process to make progress, until we have
+        // attached to it.
+        File f = new File(flagFile);
+        FileOutputStream o = new FileOutputStream(f);
+        o.write(1);
+        o.close();
+        if (!f.exists()) {
+            throw new RuntimeException("Failed to create " + f);
+        }
+
+        // At this point, the child process is not yet launched. Note that
+        // TestCommon.exec() and OutputAnalyzer.OutputAnalyzer() both block
+        // until the child process has finished.
+        //
+        // So, we will launch a AgentAttachThread which will poll the system
+        // until the child process is launched, and then do the attachment.
+        // The child process is uniquely identified by having flagFile in its
+        // command-line -- see AgentAttachThread.getPid().
+        AgentAttachThread t = new AgentAttachThread(flagFile, agentJar);
+        t.start();
+        return t;
+    }
+
+    static void checkAttach(AgentAttachThread thread) throws Throwable {
+        if (thread != null) {
+            thread.check();
+        }
+    }
+
+    static class AgentAttachThread extends Thread {
+        String flagFile;
+        String agentJar;
+        volatile boolean succeeded;
+
+        AgentAttachThread(String flagFile, String agentJar) {
+            this.flagFile = flagFile;
+            this.agentJar = agentJar;
+            this.succeeded = false;
+        }
+
+        static String getPid(String flagFile) throws Throwable {
+            while (true) {
+                // Keep polling until the child process has been launched. If for some
+                // reason the child process fails to launch, this test will be terminated
+                // by JTREG's time-out mechanism.
+                Thread.sleep(100);
+                List<VirtualMachineDescriptor> vmds = VirtualMachine.list();
+                for (VirtualMachineDescriptor vmd : vmds) {
+                    if (vmd.displayName().contains(flagFile) && vmd.displayName().contains("InstrumentationApp")) {
+                        // We use flagFile (which has the PID of this process) as a unique identifier
+                        // to ident the child process, which we want to attach to.
+                        System.out.println("Process found: " + vmd.id() + " " + vmd.displayName());
+                        return vmd.id();
+                    }
+                }
+            }
+        }
+
+        public void run() {
+            try {
+                String pid = getPid(flagFile);
+                VirtualMachine vm = VirtualMachine.attach(pid);
+                System.out.println(agentJar);
+                vm.loadAgent(agentJar);
+            } catch (Throwable t) {
+                t.printStackTrace();
+                throw new RuntimeException(t);
+            }
+
+            // Delete the flagFile to indicate to the child process that we
+            // have attached to it, so it should proceed.
+            File f = new File(flagFile);
+            for (int i=0; i<5; i++) {
+                // The detele may fail on Windows if the child JVM is checking
+                // f.exists() at exactly the same time?? Let's do a little
+                // dance.
+                f.delete();
+                try {
+                    Thread.sleep(10);
+                } catch (Throwable t) {;}
+            }
+            if (f.exists()) {
+                throw new RuntimeException("Failed to delete " + f);
+            }
+            System.out.println("Attach succeeded (parent)");
+            succeeded = true;
+        }
+
+        void check() throws Throwable {
+            super.join();
+            if (!succeeded) {
+                throw new RuntimeException("Attaching agent to child VM failed");
+            }
+        }
+    }
+}
+
diff --git a/test/hotspot/jtreg/runtime/appcds/jvmti/parallelLoad/ParallelClassesTransform.java b/test/hotspot/jtreg/runtime/appcds/jvmti/parallelLoad/ParallelClassesTransform.java
new file mode 100644
index 00000000000..b7cfe22d5fc
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/jvmti/parallelLoad/ParallelClassesTransform.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2016, 2017, 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.
+ *
+ */
+
+class ParallelClassTr0  { public static String testString = ParallelClassesTransform.BEFORE_PATTERN; }
+class ParallelClassTr1  { public static String testString = ParallelClassesTransform.BEFORE_PATTERN; }
+class ParallelClassTr2  { public static String testString = ParallelClassesTransform.BEFORE_PATTERN; }
+class ParallelClassTr3  { public static String testString = ParallelClassesTransform.BEFORE_PATTERN; }
+class ParallelClassTr4  { public static String testString = ParallelClassesTransform.BEFORE_PATTERN; }
+class ParallelClassTr5  { public static String testString = ParallelClassesTransform.BEFORE_PATTERN; }
+class ParallelClassTr6  { public static String testString = ParallelClassesTransform.BEFORE_PATTERN; }
+class ParallelClassTr7  { public static String testString = ParallelClassesTransform.BEFORE_PATTERN; }
+class ParallelClassTr8  { public static String testString = ParallelClassesTransform.BEFORE_PATTERN; }
+class ParallelClassTr9  { public static String testString = ParallelClassesTransform.BEFORE_PATTERN; }
+class ParallelClassTr10 { public static String testString = ParallelClassesTransform.BEFORE_PATTERN; }
+class ParallelClassTr11 { public static String testString = ParallelClassesTransform.BEFORE_PATTERN; }
+class ParallelClassTr12 { public static String testString = ParallelClassesTransform.BEFORE_PATTERN; }
+class ParallelClassTr13 { public static String testString = ParallelClassesTransform.BEFORE_PATTERN; }
+class ParallelClassTr14 { public static String testString = ParallelClassesTransform.BEFORE_PATTERN; }
+class ParallelClassTr15 { public static String testString = ParallelClassesTransform.BEFORE_PATTERN; }
+class ParallelClassTr16 { public static String testString = ParallelClassesTransform.BEFORE_PATTERN; }
+class ParallelClassTr17 { public static String testString = ParallelClassesTransform.BEFORE_PATTERN; }
+class ParallelClassTr18 { public static String testString = ParallelClassesTransform.BEFORE_PATTERN; }
+class ParallelClassTr19 { public static String testString = ParallelClassesTransform.BEFORE_PATTERN; }
+class ParallelClassTr20 { public static String testString = ParallelClassesTransform.BEFORE_PATTERN; }
+class ParallelClassTr21 { public static String testString = ParallelClassesTransform.BEFORE_PATTERN; }
+class ParallelClassTr22 { public static String testString = ParallelClassesTransform.BEFORE_PATTERN; }
+class ParallelClassTr23 { public static String testString = ParallelClassesTransform.BEFORE_PATTERN; }
+class ParallelClassTr24 { public static String testString = ParallelClassesTransform.BEFORE_PATTERN; }
+class ParallelClassTr25 { public static String testString = ParallelClassesTransform.BEFORE_PATTERN; }
+class ParallelClassTr26 { public static String testString = ParallelClassesTransform.BEFORE_PATTERN; }
+class ParallelClassTr27 { public static String testString = ParallelClassesTransform.BEFORE_PATTERN; }
+class ParallelClassTr28 { public static String testString = ParallelClassesTransform.BEFORE_PATTERN; }
+class ParallelClassTr29 { public static String testString = ParallelClassesTransform.BEFORE_PATTERN; }
+class ParallelClassTr30 { public static String testString = ParallelClassesTransform.BEFORE_PATTERN; }
+class ParallelClassTr31 { public static String testString = ParallelClassesTransform.BEFORE_PATTERN; }
+class ParallelClassTr32 { public static String testString = ParallelClassesTransform.BEFORE_PATTERN; }
+class ParallelClassTr33 { public static String testString = ParallelClassesTransform.BEFORE_PATTERN; }
+class ParallelClassTr34 { public static String testString = ParallelClassesTransform.BEFORE_PATTERN; }
+class ParallelClassTr35 { public static String testString = ParallelClassesTransform.BEFORE_PATTERN; }
+class ParallelClassTr36 { public static String testString = ParallelClassesTransform.BEFORE_PATTERN; }
+class ParallelClassTr37 { public static String testString = ParallelClassesTransform.BEFORE_PATTERN; }
+class ParallelClassTr38 { public static String testString = ParallelClassesTransform.BEFORE_PATTERN; }
+class ParallelClassTr39 { public static String testString = ParallelClassesTransform.BEFORE_PATTERN; }
+
+class ParallelClassesTransform {
+    public static final int NUMBER_OF_CLASSES = 40;
+    public static final String BEFORE_PATTERN = "class-transform-check: this-should-be-transformed";
+    public static final String AFTER_PATTERN =  "class-transform-check: this-has-been--transformed";
+}
diff --git a/test/hotspot/jtreg/runtime/appcds/jvmti/parallelLoad/ParallelLoadAndTransformTest.java b/test/hotspot/jtreg/runtime/appcds/jvmti/parallelLoad/ParallelLoadAndTransformTest.java
new file mode 100644
index 00000000000..6356e2d4a6d
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/jvmti/parallelLoad/ParallelLoadAndTransformTest.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+/*
+ * @test
+ * @summary Load app classes from CDS archive in parallel threads,
+ * use initial transformation (CFLH)
+ * @library /test/lib /test/hotspot/jtreg/runtime/appcds
+ *     /test/hotspot/jtreg/runtime/appcds/test-classes /test/hotspot/jtreg/runtime/appcds/jvmti
+ *     /test/hotspot/jtreg/testlibrary/jvmti
+ * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true)
+ * @modules java.base/jdk.internal.misc
+ *          java.management
+ *          jdk.jartool/sun.tools.jar
+ *          java.instrument
+ * @build TransformUtil TransformerAgent ParallelLoad
+ * @run main ParallelLoadAndTransformTest
+ */
+import java.util.List;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
+
+public class ParallelLoadAndTransformTest {
+
+    public static void main(String[] args) throws Exception {
+        String prop = "-Dappcds.parallel.transform.mode=cflh";
+        String appJar = ClassFileInstaller.writeJar("parallel_load.jar",
+                            getClassList(true));
+        String agentJar = prepareAgent();
+
+        TestCommon.test(appJar, getClassList(false),
+                        "-javaagent:" + agentJar + "=ParallelClassTr.*",
+                        prop, "ParallelLoad");
+    }
+
+
+    private static String[] getClassList(boolean includeWatchdog) {
+        List<String> classList =
+            IntStream.range(0, ParallelClassesTransform.NUMBER_OF_CLASSES)
+            .mapToObj(i -> "ParallelClassTr" + i)
+            .collect(Collectors.toList());
+
+        classList.add("ParallelLoad");
+        classList.add("ParallelLoadThread");
+        if (includeWatchdog)
+            classList.add("ParallelLoadWatchdog");
+
+        return classList.toArray(new String[0]);
+    }
+
+
+    // Agent is the same for all test cases
+    private static String prepareAgent() throws Exception {
+        String agentClasses[] = {
+            "TransformerAgent",
+            "TransformerAgent$SimpleTransformer",
+            "TransformUtil"
+        };
+
+        String manifest = "../../../../testlibrary/jvmti/TransformerAgent.mf";
+
+        return ClassFileInstaller.writeJar("TransformerAgent.jar",
+            ClassFileInstaller.Manifest.fromSourceFile(manifest),
+                                        agentClasses);
+    }
+
+}
diff --git a/test/hotspot/jtreg/runtime/appcds/jvmti/transformRelatedClasses/TransformInterfaceImplementorAppCDS.java b/test/hotspot/jtreg/runtime/appcds/jvmti/transformRelatedClasses/TransformInterfaceImplementorAppCDS.java
new file mode 100644
index 00000000000..4addd099854
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/jvmti/transformRelatedClasses/TransformInterfaceImplementorAppCDS.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+/*
+ * @test
+ * @summary Exercise initial transformation (class file loader hook)
+ *  with CDS/AppCDS with Interface/Implementor pair
+ * @library /test/lib /test/hotspot/jtreg/runtime/appcds /test/hotspot/jtreg/runtime/appcds/test-classes
+ *     /test/hotspot/jtreg/runtime/appcds/jvmti /test/hotspot/jtreg/runtime/SharedArchiveFile/serviceability
+ *     /test/hotspot/jtreg/runtime/SharedArchiveFile/serviceability/transformRelatedClasses
+ *     /test/hotspot/jtreg/runtime/SharedArchiveFile /test/hotspot/jtreg/testlibrary/jvmti
+ *     /test/hotspot/jtreg/runtime/appcds/customLoader
+ *     /test/hotspot/jtreg/runtime/appcds/customLoader/test-classes
+ * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true)
+ * @requires vm.flavor != "minimal"
+ * @modules java.base/jdk.internal.misc
+ *          jdk.jartool/sun.tools.jar
+ *          java.management
+ *          java.instrument
+ * @build TransformUtil TransformerAgent Interface Implementor
+ * @run main/othervm TransformRelatedClassesAppCDS Interface Implementor
+ */
diff --git a/test/hotspot/jtreg/runtime/appcds/jvmti/transformRelatedClasses/TransformRelatedClassesAppCDS.java b/test/hotspot/jtreg/runtime/appcds/jvmti/transformRelatedClasses/TransformRelatedClassesAppCDS.java
new file mode 100644
index 00000000000..0ad374471a1
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/jvmti/transformRelatedClasses/TransformRelatedClassesAppCDS.java
@@ -0,0 +1,205 @@
+/*
+ * Copyright (c) 2016, 2017, 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.
+ *
+ */
+
+// Structure of the test:
+// TransformRelatedClassesAppCDS -- common main test driver
+// Invoked from test driver classes:
+//     TransformInterfaceAndImplementor, TransformSuperAndSubClasses.java
+//     prepares test artifacts, launches tests, checks results
+// SuperClazz, SubClass -- classes under test
+// Interface, Implementor -- classes under test
+// TransformerAgent -- an agent that is used when JVM-under-test is executed
+//     to transform specific strings inside specified classes
+// TransformerAgent.mf - accompanies transformer agent
+// CustomLoaderApp -- a test "application" that is used to load
+//     classes-under-test (Parent, Child) via custom class loader, using
+//     AppCDS-v2 mechanism (unregistered custom loaders, aka FP)
+//     This "app" is launched in a child process by this driver with sharing on.
+
+import java.io.File;
+import java.util.ArrayList;
+import jdk.test.lib.Platform;
+import jdk.test.lib.process.OutputAnalyzer;
+
+// This class is intended to test 2 parent-child relationships:
+// 1. Base Class (parent) and Derived Class (child)
+// 2. Interface (parent) and Implementor (child)
+//    Parameters to main(): parent, child
+
+public class TransformRelatedClassesAppCDS extends TransformRelatedClasses {
+    private static void log(String msg, Object... args) {
+        String msg0 = String.format(msg, args);
+        System.out.println("TransformRelatedClassesAppCDS: " + msg0);
+    }
+
+    // Initial Test Matrix:
+    // (ParentTransformed = true/false, ChildTransformed = true/false) x
+    // (BootCDS - see open tests, AppCDS-v1, AppCDS-v2-unregistered)
+    // Total cases: 2 x 4 = 8
+    public static void main(String args[]) throws Exception {
+        TransformRelatedClassesAppCDS test =
+            new TransformRelatedClassesAppCDS(args[0], args[1]);
+
+        test.prepareAgent(agentClasses);
+
+        // Test Table
+        // testCaseId |  transformParent | tranformChild | isParentExpectedShared | isChildExpectedShared
+        ArrayList<TestEntry> testTable = new ArrayList<>();
+
+        // base case - no tranformation - all expected to be shared
+        testTable.add(new TestEntry(0, false, false, true, true));
+
+        // transform parent only - both parent and child should not be shared
+        testTable.add(new TestEntry(1, true, false, false, false));
+
+        // transform parent and child - both parent and child should not be shared
+        testTable.add(new TestEntry(2, true, true, false, false));
+
+        // transform child only - parent should still be shared, but not child
+        testTable.add(new TestEntry(3, false, true, true, false));
+
+        // run the tests
+        test.runWithAppLoader(testTable);
+        test.runWithCustomLoader(testTable);
+    }
+
+
+    public TransformRelatedClassesAppCDS(String parent, String child) {
+        super(parent, child);
+
+        // a trick to get it compiled by jtreg
+        CustomLoaderApp.ping();
+    }
+
+
+    private void prepareAgent(String[] agentClasses) throws Exception {
+        String manifest = "../../../../testlibrary/jvmti/TransformerAgent.mf";
+        agentJar = ClassFileInstaller.writeJar("TransformerAgent.jar",
+                   ClassFileInstaller.Manifest.fromSourceFile(manifest),
+                                           agentClasses);
+    }
+
+
+    private void runWithAppLoader(ArrayList<TestEntry> testTable) throws Exception {
+        String appJar = writeJar("app", testClasses);
+
+        // create an archive
+        OutputAnalyzer out = TestCommon.dump(appJar, testClasses);
+        TestCommon.checkDump(out);
+
+        // execute with archive
+        for (TestEntry entry : testTable) {
+            log("runTestWithAppLoader(): testCaseId = %d", entry.testCaseId);
+            String params = TransformTestCommon.getAgentParams(entry, parent, child);
+            String agentParam = String.format("-javaagent:%s=%s", agentJar, params);
+            out = TestCommon.execCommon("-Xlog:class+load=info", "-cp", appJar,
+                                        agentParam, child);
+
+            TransformTestCommon.checkResults(entry, out, parent, child);
+        }
+    }
+
+
+    private String[] getCustomClassList(String loaderType, String customJar) {
+        String type = child + "-" + loaderType;
+
+        switch (type) {
+
+        case "SubClass-unregistered":
+            return new String[] {
+                "CustomLoaderApp",
+                "java/lang/Object id: 0",
+                parent + " id: 1 super: 0 source: " + customJar,
+                child +  " id: 2 super: 1 source: " + customJar,
+            };
+
+        case "Implementor-unregistered":
+            return new String[] {
+                "CustomLoaderApp",
+                "java/lang/Object id: 0",
+                parent + " id: 1 super: 0 source: " + customJar,
+                child +  " id: 2 super: 0 interfaces: 1 source: " + customJar,
+            };
+
+        default:
+            throw new IllegalArgumentException("getCustomClassList - wrong type: " + type);
+        }
+    }
+
+
+    private void runWithCustomLoader(ArrayList<TestEntry> testTable) throws Exception {
+        if (!Platform.areCustomLoadersSupportedForCDS()) {
+            log("custom loader not supported for this platform" +
+                " - skipping test case for custom loader");
+            return;
+        }
+
+        String appClasses[] = {
+            "CustomLoaderApp",
+        };
+
+        String customClasses[] = { parent, child };
+
+        // create jar files: appJar, customJar (for custom loaders to load classes from)
+        String appJar = writeJar("custldr-app", appClasses);
+        String customJar = writeJar("custldr-custom", customClasses);
+
+        for (TestEntry entry : testTable) {
+            log("runTestWithCustomLoader(): testCaseId = %d", entry.testCaseId);
+            // unregistered (aka FP) case
+            String[] classList = getCustomClassList("unregistered",customJar);
+            execAndCheckWithCustomLoader(entry, "unregistered", classList,
+                                         appJar, agentJar, customJar);
+        }
+    }
+
+
+    private void
+        execAndCheckWithCustomLoader(TestEntry entry, String loaderType,
+                                     String[] classList, String appJar,
+                                     String agentJar, String customJar)
+        throws Exception {
+
+        OutputAnalyzer out = TestCommon.dump(appJar, classList);
+        TestCommon.checkDump(out);
+
+        String agentParam = "-javaagent:" + agentJar + "=" +
+            TransformTestCommon.getAgentParams(entry, parent, child);
+
+        out = TestCommon.execCommon("-Xlog:class+load=info",
+                                    "-cp", appJar,
+                                    "--add-opens=java.base/java.security=ALL-UNNAMED",
+                                    agentParam,
+                                    "CustomLoaderApp",
+                                    customJar, loaderType, child);
+        TransformTestCommon.checkResults(entry, out, parent, child);
+    }
+
+
+    private String writeJar(String type, String[] classes)
+        throws Exception {
+        String jarName = String.format("%s-%s.jar", child, type);
+        return ClassFileInstaller.writeJar(jarName, classes);
+    }
+}
diff --git a/test/hotspot/jtreg/runtime/appcds/jvmti/transformRelatedClasses/TransformSuperSubAppCDS.java b/test/hotspot/jtreg/runtime/appcds/jvmti/transformRelatedClasses/TransformSuperSubAppCDS.java
new file mode 100644
index 00000000000..2c631b916e4
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/jvmti/transformRelatedClasses/TransformSuperSubAppCDS.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+/*
+ * @test
+ * @summary Exercise initial transformation (class file loader hook)
+ *  with CDS/AppCDS with SubClass and SuperClass
+ * @library /test/lib /test/hotspot/jtreg/runtime/appcds /test/hotspot/jtreg/runtime/appcds/test-classes
+ *     /test/hotspot/jtreg/runtime/appcds/jvmti /test/hotspot/jtreg/runtime/SharedArchiveFile/serviceability
+ *     /test/hotspot/jtreg/runtime/SharedArchiveFile/serviceability/transformRelatedClasses
+ *     /test/hotspot/jtreg/runtime/SharedArchiveFile /test/hotspot/jtreg/testlibrary/jvmti
+ *     /test/hotspot/jtreg/runtime/appcds/customLoader
+ *     /test/hotspot/jtreg/runtime/appcds/customLoader/test-classes
+ * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true)
+ * @requires vm.flavor != "minimal"
+ * @modules java.base/jdk.internal.misc
+ *          jdk.jartool/sun.tools.jar
+ *          java.management
+ *          java.instrument
+ * @build TransformUtil TransformerAgent SubClass SuperClazz
+ * @run main/othervm TransformRelatedClassesAppCDS SuperClazz SubClass
+ */
diff --git a/test/hotspot/jtreg/runtime/appcds/redefineClass/RedefineBasic.java b/test/hotspot/jtreg/runtime/appcds/redefineClass/RedefineBasic.java
new file mode 100644
index 00000000000..943a5257557
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/redefineClass/RedefineBasic.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+import sun.hotspot.WhiteBox;
+
+public class RedefineBasic {
+
+    public static String newB =
+        " class RedefineBasic$B { " +
+        " public static void okToCallBeforeRedefine() { " +
+        "    throw new RuntimeException(\"newB: okToCallBeforeRedefine is " +
+        "    called after redefinition, test failed\"); }" +
+        " public static void okToCallAfterRedefine() { " +
+        "     System.out.println(\"newB: okToCallAfterRedefine\"); } " +
+        " } ";
+
+
+    static class B {
+        public static void okToCallBeforeRedefine() {
+            System.out.println("okToCallBeforeRedefine");
+        }
+        public static void okToCallAfterRedefine() {
+            throw new RuntimeException(
+            "okToCallAfterRedefine is called before redefinition, test failed");
+        }
+    }
+
+    static class SubclassOfB extends B {
+        public static void testAfterRedefine() {
+            B.okToCallAfterRedefine();
+        }
+    }
+
+    class Subclass2OfB extends B {
+        public void testAfterRedefine() {
+            super.okToCallAfterRedefine();
+        }
+    }
+
+    // verify that a given class is shared, report error if necessary
+    public static void
+    verifyClassIsShared(WhiteBox wb, Class c) throws Exception {
+        if (!wb.isSharedClass(c)) {
+            throw new RuntimeException(
+            "This class should be shared but isn't: " + c.getName());
+        } else {
+            System.out.println("The class is shared as expected: " +
+                c.getName());
+        }
+    }
+
+    public static void main(String[] args) throws Exception {
+        WhiteBox wb = WhiteBox.getWhiteBox();
+
+        verifyClassIsShared(wb, RedefineBasic.class);
+        verifyClassIsShared(wb, B.class);
+        verifyClassIsShared(wb, SubclassOfB.class);
+        verifyClassIsShared(wb, Subclass2OfB.class);
+
+        // (1) Test case: verify that original B works as expected
+        // and that redefined B is shared and works as expected,
+        // with new behavior
+        B.okToCallBeforeRedefine();
+        RedefineClassHelper.redefineClass(B.class, newB);
+        verifyClassIsShared(wb, B.class);
+        B.okToCallAfterRedefine();
+
+        // Static subclass of the super:
+        // 1. Make sure it is still shared
+        // 2. and it calls the correct super (the redefined one)
+        verifyClassIsShared(wb, SubclassOfB.class);
+        SubclassOfB.testAfterRedefine();
+
+        // Same as above, but for non-static class
+        verifyClassIsShared(wb, Subclass2OfB.class);
+        RedefineBasic thisTest = new RedefineBasic();
+        thisTest.testSubclass2OfB();
+    }
+
+    public void testSubclass2OfB() {
+        Subclass2OfB sub = new Subclass2OfB();
+        sub.testAfterRedefine();
+    }
+}
diff --git a/test/hotspot/jtreg/runtime/appcds/redefineClass/RedefineBasicTest.java b/test/hotspot/jtreg/runtime/appcds/redefineClass/RedefineBasicTest.java
new file mode 100644
index 00000000000..396e2cba703
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/redefineClass/RedefineBasicTest.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+/*
+ * @test
+ * @summary Run /runtime/RedefineTests/RedefineRunningMethods in AppCDS mode to
+ *          make sure class redefinition works with CDS.
+ *          (Note: AppCDS does not support uncompressed oops)
+ * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true)
+ * @library /test/lib /test/hotspot/jtreg/runtime/RedefineTests /test/hotspot/jtreg/runtime/appcds
+ * @modules java.compiler
+ *          java.instrument
+ *          jdk.jartool/sun.tools.jar
+ *          java.base/jdk.internal.misc
+ *          java.management
+ * @run main RedefineClassHelper
+ * @build sun.hotspot.WhiteBox RedefineBasic
+ * @run main RedefineBasicTest
+ */
+
+import jdk.test.lib.process.OutputAnalyzer;
+
+public class RedefineBasicTest {
+    public static String sharedClasses[] = {
+        "RedefineBasic",
+        "RedefineBasic$B",
+        "RedefineBasic$SubclassOfB",
+        "RedefineBasic$Subclass2OfB",
+        "RedefineClassHelper",
+        "jdk/test/lib/compiler/InMemoryJavaCompiler",
+        "jdk/test/lib/compiler/InMemoryJavaCompiler$FileManagerWrapper",
+        "jdk/test/lib/compiler/InMemoryJavaCompiler$FileManagerWrapper$1",
+        "jdk/test/lib/compiler/InMemoryJavaCompiler$MemoryJavaFileObject"
+    };
+
+    public static void main(String[] args) throws Exception {
+        String wbJar =
+            ClassFileInstaller.writeJar("WhiteBox.jar", "sun.hotspot.WhiteBox");
+        String appJar =
+            ClassFileInstaller.writeJar("RedefineBasic.jar", sharedClasses);
+        String useWb = "-Xbootclasspath/a:" + wbJar;
+
+        OutputAnalyzer output;
+        TestCommon.testDump(appJar, sharedClasses, useWb);
+
+        // redefineagent.jar is created by executing "@run main RedefineClassHelper"
+        // which should be called before executing RedefineBasicTest
+        output = TestCommon.exec(appJar, useWb,
+                                 "-XX:+UnlockDiagnosticVMOptions",
+                                 "-XX:+WhiteBoxAPI",
+                                 "-javaagent:redefineagent.jar",
+                                 "RedefineBasic");
+        TestCommon.checkExec(output);
+    }
+}
diff --git a/test/hotspot/jtreg/runtime/appcds/redefineClass/RedefineRunningMethods_Shared.java b/test/hotspot/jtreg/runtime/appcds/redefineClass/RedefineRunningMethods_Shared.java
new file mode 100644
index 00000000000..2c2782873a6
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/redefineClass/RedefineRunningMethods_Shared.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+/*
+ * @test
+ * @summary Run /runtime/RedefineTests/RedefineRunningMethods in AppCDS mode to
+ *          make sure class redefinition works with CDS.
+ *          (Note: AppCDS does not support uncompressed oops)
+ * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true)
+ * @library /test/lib /test/hotspot/jtreg/runtime/RedefineTests /test/hotspot/jtreg/runtime/appcds
+ * @modules java.base/jdk.internal.misc
+ * @modules java.compiler
+ *          java.instrument
+ *          jdk.jartool/sun.tools.jar
+ * @run main RedefineClassHelper
+ * @build sun.hotspot.WhiteBox RedefineRunningMethods_SharedHelper
+ * @run main RedefineRunningMethods_Shared
+ */
+
+import jdk.test.lib.process.OutputAnalyzer;
+
+public class RedefineRunningMethods_Shared {
+    public static String shared_classes[] = {
+        "RedefineRunningMethods_Shared",
+        "RedefineRunningMethods_SharedHelper",
+        "RedefineRunningMethods",
+        "RedefineRunningMethods$1",
+        "RedefineRunningMethods$2",
+        "RedefineRunningMethods$3",
+        "RedefineRunningMethods$B",
+        "RedefineClassHelper",
+        "jdk/test/lib/compiler/InMemoryJavaCompiler",
+        "jdk/test/lib/compiler/InMemoryJavaCompiler$FileManagerWrapper",
+        "jdk/test/lib/compiler/InMemoryJavaCompiler$FileManagerWrapper$1",
+        "jdk/test/lib/compiler/InMemoryJavaCompiler$MemoryJavaFileObject"
+    };
+
+    public static void main(String[] args) throws Exception {
+        String wbJar = ClassFileInstaller.writeJar("WhiteBox.jar", "sun.hotspot.WhiteBox");
+        String appJar = ClassFileInstaller.writeJar("RedefineRunningMethods_Shared.jar", shared_classes);
+        String use_whitebox_jar = "-Xbootclasspath/a:" + wbJar;
+
+        OutputAnalyzer output;
+        TestCommon.testDump(appJar, shared_classes,
+                            // command-line arguments ...
+                            use_whitebox_jar);
+
+        // RedefineRunningMethods.java contained this:
+        // @run main/othervm -javaagent:redefineagent.jar -Xlog:redefine+class+iklass+add=trace,redefine+class+iklass+purge=trace RedefineRunningMethods
+        output = TestCommon.exec(appJar,
+                                 // command-line arguments ...
+                                 use_whitebox_jar,
+                                 "-XX:+UnlockDiagnosticVMOptions",
+                                 "-XX:+WhiteBoxAPI",
+                                 // These arguments are expected by RedefineRunningMethods
+                                 "-javaagent:redefineagent.jar",
+                                 "-Xlog:redefine+class+iklass+add=trace,redefine+class+iklass+purge=trace",
+                                 "RedefineRunningMethods_SharedHelper");
+        TestCommon.checkExec(output);
+    }
+}
diff --git a/test/hotspot/jtreg/runtime/appcds/redefineClass/RedefineRunningMethods_SharedHelper.java b/test/hotspot/jtreg/runtime/appcds/redefineClass/RedefineRunningMethods_SharedHelper.java
new file mode 100644
index 00000000000..ff1ffada27b
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/redefineClass/RedefineRunningMethods_SharedHelper.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+import sun.hotspot.WhiteBox;
+
+/**
+ * This class is executed by RedefineRunningMethods_Shared.java in
+ * a sub-process.
+ */
+public class RedefineRunningMethods_SharedHelper {
+    public static void main(String[] args) throws Exception {
+        // (1) Validate that all classes used by RedefineRunningMethods are all shared.
+        WhiteBox wb = WhiteBox.getWhiteBox();
+        for (String name : RedefineRunningMethods_Shared.shared_classes) {
+            name = name.replace('/', '.');
+            Class c = Class.forName(name);
+            if (!wb.isSharedClass(c)) {
+                throw new RuntimeException("Test set-up problem. " +
+                           "This class should be shared but isn't: " + name);
+            } else {
+                System.out.println("The class is shared as expected: " + name);
+            }
+        }
+
+        // (2) Run the class redefinition test.
+        RedefineRunningMethods.main(args);
+    }
+}
diff --git a/test/hotspot/jtreg/runtime/appcds/sharedStrings/ExerciseGC.java b/test/hotspot/jtreg/runtime/appcds/sharedStrings/ExerciseGC.java
new file mode 100644
index 00000000000..7d70f463b4e
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/sharedStrings/ExerciseGC.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+/*
+ * @test
+ * @summary Exercise GC with shared strings
+ * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true)
+ * @requires vm.gc.G1
+ * @requires (sun.arch.data.model != "32") & (os.family != "windows")
+ * @library /test/hotspot/jtreg/runtime/appcds /test/lib
+ * @modules java.base/jdk.internal.misc
+ * @modules java.management
+ *          jdk.jartool/sun.tools.jar
+ * @build HelloStringGC sun.hotspot.WhiteBox
+ * @run main ClassFileInstaller sun.hotspot.WhiteBox
+ * @run main ExerciseGC
+ */
+public class ExerciseGC {
+    public static void main(String[] args) throws Exception {
+        SharedStringsUtils.buildJarAndWhiteBox("HelloStringGC");
+
+        SharedStringsUtils.dumpWithWhiteBox(TestCommon.list("HelloStringGC"),
+            "SharedStringsBasic.txt");
+
+        SharedStringsUtils.runWithArchiveAndWhiteBox("HelloStringGC",
+            "-XX:+UnlockDiagnosticVMOptions", "-XX:+VerifyBeforeGC");
+    }
+}
diff --git a/test/hotspot/jtreg/runtime/appcds/sharedStrings/ExtraSharedInput.txt b/test/hotspot/jtreg/runtime/appcds/sharedStrings/ExtraSharedInput.txt
new file mode 100644
index 00000000000..5b9257d07e9
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/sharedStrings/ExtraSharedInput.txt
@@ -0,0 +1,7 @@
+VERSION: 1.0
+@SECTION: Symbol
+0 -1: 
+41 -1: (Ljava/util/Set<TE;>;Ljava/lang/Object;)V
+11 -1: linkMethod 
+18 -1: type can't be null
+20 -1: isAlphaNumericString
diff --git a/test/hotspot/jtreg/runtime/appcds/sharedStrings/FlagCombo.java b/test/hotspot/jtreg/runtime/appcds/sharedStrings/FlagCombo.java
new file mode 100644
index 00000000000..951f2ec6447
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/sharedStrings/FlagCombo.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+/*
+ * @test
+ * @summary Test relevant combinations of command line flags with shared strings
+ * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true)
+ * @requires (sun.arch.data.model != "32") & (os.family != "windows")
+ * @requires (vm.gc=="null")
+ * @library /test/lib /test/hotspot/jtreg/runtime/appcds
+ * @modules java.base/jdk.internal.misc
+ * @modules java.management
+ *          jdk.jartool/sun.tools.jar
+ * @build HelloString
+ * @run main FlagCombo
+ */
+
+import jdk.test.lib.BuildHelper;
+
+public class FlagCombo {
+    public static void main(String[] args) throws Exception {
+        SharedStringsUtils.buildJar("HelloString");
+
+        SharedStringsUtils.dump(TestCommon.list("HelloString"),
+            "SharedStringsBasic.txt");
+
+        SharedStringsUtils.runWithArchive("HelloString", "-XX:+UseG1GC");
+
+        if (BuildHelper.isCommercialBuild()) {
+            SharedStringsUtils.runWithArchiveAuto("HelloString", "-XX:+UnlockCommercialFeatures",
+                "-XX:StartFlightRecording=dumponexit=true");
+        }
+
+        SharedStringsUtils.runWithArchive("HelloString", "-XX:+UnlockDiagnosticVMOptions",
+           "-XX:NativeMemoryTracking=detail", "-XX:+PrintNMTStatistics");
+    }
+}
diff --git a/test/hotspot/jtreg/runtime/appcds/sharedStrings/HelloString.java b/test/hotspot/jtreg/runtime/appcds/sharedStrings/HelloString.java
new file mode 100644
index 00000000000..3c1cbbe4361
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/sharedStrings/HelloString.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2015, 2017, 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.
+ *
+ */
+
+public class HelloString {
+    public static void main(String args[]) {
+        // Let's reference the string that is in the archive
+        // Make sure the string below is in the shared string data file (string list)
+        String testString = "shared_test_string_unique_14325";
+        System.out.println("Hello String: " + testString);
+    }
+}
diff --git a/test/hotspot/jtreg/runtime/appcds/sharedStrings/HelloStringGC.java b/test/hotspot/jtreg/runtime/appcds/sharedStrings/HelloStringGC.java
new file mode 100644
index 00000000000..14732626348
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/sharedStrings/HelloStringGC.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+import sun.hotspot.WhiteBox;
+
+public class HelloStringGC {
+    public static String[] array01 = new String[1000];
+    public static String[] array02 = new String[1000];
+
+    public static void main(String args[]) throws RuntimeException {
+        String testString1 = "shared_test_string_unique_14325";
+        String testString2 = "test123";
+
+        WhiteBox wb = WhiteBox.getWhiteBox();
+        if (!wb.isShared(testString1) && !wb.areSharedStringsIgnored()) {
+            throw new RuntimeException("testString1 is not shared");
+        }
+
+        for (int i=0; i<5; i++) {
+            allocSomeStrings(testString1, testString2);
+            array01 = null;
+            array02 = null;
+            System.gc();
+            sleep(300);
+            array01 = new String[1000];
+            array02 = new String[1000];
+        }
+
+        wb.fullGC();
+
+        System.out.println("HelloStringGC: PASS");
+    }
+
+    private static void allocSomeStrings(String s1, String s2) {
+        for (int i = 0; i < 1000; i ++) {
+            array01[i] = new String(s1);
+            array02[i] = new String(s2);
+        }
+    }
+
+    private static void sleep(int ms) {
+        try {
+            Thread.sleep(ms);
+        } catch (InterruptedException e) {
+        }
+    }
+
+}
diff --git a/test/hotspot/jtreg/runtime/appcds/sharedStrings/HelloStringPlus.java b/test/hotspot/jtreg/runtime/appcds/sharedStrings/HelloStringPlus.java
new file mode 100644
index 00000000000..5129a10ba80
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/sharedStrings/HelloStringPlus.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2015, 2017, 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.
+ *
+ */
+
+// A test class to be launched in AppCDS mode, has basic+
+// coverage of string operations
+
+import sun.hotspot.WhiteBox;
+
+public class HelloStringPlus {
+    public static void main(String args[]) {
+        // Let's reference the string that is in archive
+        String testString1 = "shared_test_string_unique_14325";
+        System.out.println("Hello String: " + testString1);
+
+        WhiteBox wb = WhiteBox.getWhiteBox();
+        if (!wb.isShared(testString1) && !wb.areSharedStringsIgnored()) {
+            throw new RuntimeException("testString1 is not shared");
+        }
+
+        // Check other basic string operations
+        // Interning and equality
+        String[] testArray = new String[] {"shared_", "test_", "string_", "intern_", "12345"};
+        String toBeInterned = "";
+
+        StringBuilder sb = new StringBuilder();
+        for (String s : testArray) {
+            sb.append(s);
+        }
+        toBeInterned = sb.toString();
+
+        System.out.println("About to intern a string: " + toBeInterned);
+        toBeInterned.intern();
+
+        // check equality
+        if (testString1.equals(toBeInterned))
+            throw new RuntimeException("Equality test 1 failed");
+
+        if (!testString1.equals("shared_test_string" + '_' + "unique_14325"))
+            throw new RuntimeException("Equality test 2 failed");
+
+        // Chech the hash code functionality; no special assertions, just make sure
+        // no crashe or exception occurs
+        System.out.println("testString1.hashCode() = " + testString1.hashCode());
+
+        // Check intern() method for "" string
+        String empty = "";
+        String empty_interned = empty.intern();
+        if (wb.isShared(empty)) {
+           throw new RuntimeException("Empty string should not be shared");
+        }
+        if (empty_interned != empty) {
+            throw new RuntimeException("Different string is returned from intern() for empty string");
+        }
+    }
+}
diff --git a/test/hotspot/jtreg/runtime/appcds/sharedStrings/IncompatibleOptions.java b/test/hotspot/jtreg/runtime/appcds/sharedStrings/IncompatibleOptions.java
new file mode 100644
index 00000000000..6e71c88dcea
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/sharedStrings/IncompatibleOptions.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+/*
+ * @test
+ * @summary Test options that are incompatible with use of shared strings
+ *          Also test mismatch in oops encoding between dump time and run time
+ * Feature support: G1GC only, compressed oops/kptrs, 64-bit os, not on windows
+ * @requires (sun.arch.data.model != "32") & (os.family != "windows")
+ * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true)
+ * @requires (vm.gc=="null")
+ * @library /test/lib /test/hotspot/jtreg/runtime/appcds
+ * @modules java.base/jdk.internal.misc
+ * @modules java.management
+ *          jdk.jartool/sun.tools.jar
+ * @build HelloString
+ * @run main IncompatibleOptions
+ */
+
+import jdk.test.lib.Asserts;
+import jdk.test.lib.process.OutputAnalyzer;
+
+public class IncompatibleOptions {
+    static final String COOPS_DUMP_WARNING =
+        "Cannot dump shared archive when UseCompressedOops or UseCompressedClassPointers is off";
+    static final String COOPS_EXEC_WARNING =
+        "UseCompressedOops and UseCompressedClassPointers must be on for UseSharedSpaces";
+    static final String GC_WARNING =
+        "Archived java heap is not supported";
+    static final String OBJ_ALIGNMENT_MISMATCH =
+        "The shared archive file's ObjectAlignmentInBytes of .* does not equal the current ObjectAlignmentInBytes of";
+    static final String COMPACT_STRING_MISMATCH =
+        "The shared archive file's CompactStrings setting .* does not equal the current CompactStrings setting";
+
+    static String appJar;
+
+    public static void main(String[] args) throws Exception {
+        appJar = JarBuilder.build("IncompatibleOptions", "HelloString");
+
+        // Uncompressed OOPs
+        testDump(1, "-XX:+UseG1GC", "-XX:-UseCompressedOops", COOPS_DUMP_WARNING, true);
+
+        // incompatible GCs
+        testDump(2, "-XX:+UseParallelGC", "", GC_WARNING, false);
+        testDump(3, "-XX:+UseSerialGC", "", GC_WARNING, false);
+        testDump(4, "-XX:+UseConcMarkSweepGC", "", GC_WARNING, false);
+
+        // ======= archive with compressed oops, run w/o
+        testDump(5, "-XX:+UseG1GC", "-XX:+UseCompressedOops", null, false);
+        testExec(5, "-XX:+UseG1GC", "-XX:-UseCompressedOops",
+                 COOPS_EXEC_WARNING, true);
+
+        // NOTE: No warning is displayed, by design
+        // Still run, to ensure no crash or exception
+        testExec(6, "-XX:+UseParallelGC", "", "", false);
+        testExec(7, "-XX:+UseSerialGC", "", "", false);
+        testExec(8, "-XX:+UseConcMarkSweepGC", "", "", false);
+
+        // Test various oops encodings, by varying ObjectAlignmentInBytes and heap sizes
+        testDump(9, "-XX:+UseG1GC", "-XX:ObjectAlignmentInBytes=8", null, false);
+        testExec(9, "-XX:+UseG1GC", "-XX:ObjectAlignmentInBytes=16",
+                 OBJ_ALIGNMENT_MISMATCH, true);
+
+        // See JDK-8081416 - Oops encoding mismatch with shared strings
+        // produces unclear or incorrect warning
+        // Correct the test case once the above is fixed
+        // @ignore JDK-8081416 - for tracking purposes
+        // for now, run test as is until the proper behavior is determined
+        testDump(10, "-XX:+UseG1GC", "-Xmx1g", null, false);
+        testExec(10, "-XX:+UseG1GC", "-Xmx32g", null, true);
+
+        // CompactStrings must match between dump time and run time
+        testDump(11, "-XX:+UseG1GC", "-XX:-CompactStrings", null, false);
+        testExec(11, "-XX:+UseG1GC", "-XX:+CompactStrings",
+                 COMPACT_STRING_MISMATCH, true);
+        testDump(12, "-XX:+UseG1GC", "-XX:+CompactStrings", null, false);
+        testExec(12, "-XX:+UseG1GC", "-XX:-CompactStrings",
+                 COMPACT_STRING_MISMATCH, true);
+    }
+
+    static void testDump(int testCaseNr, String collectorOption, String extraOption,
+        String expectedWarning, boolean expectedToFail) throws Exception {
+
+        System.out.println("Testcase: " + testCaseNr);
+        OutputAnalyzer output = TestCommon.dump(appJar, TestCommon.list("Hello"),
+            "-XX:+UseCompressedOops",
+            collectorOption,
+            "-XX:SharedArchiveConfigFile=" + TestCommon.getSourceFile("SharedStringsBasic.txt"),
+            extraOption);
+
+        if (expectedWarning != null)
+            output.shouldContain(expectedWarning);
+
+        if (expectedToFail) {
+            Asserts.assertNE(output.getExitValue(), 0,
+            "JVM is expected to fail, but did not");
+        }
+    }
+
+    static void testExec(int testCaseNr, String collectorOption, String extraOption,
+        String expectedWarning, boolean expectedToFail) throws Exception {
+
+        OutputAnalyzer output;
+        System.out.println("Testcase: " + testCaseNr);
+
+        // needed, otherwise system considers empty extra option as a
+        // main class param, and fails with "Could not find or load main class"
+        if (!extraOption.isEmpty()) {
+            output = TestCommon.exec(appJar, "-XX:+UseCompressedOops",
+                collectorOption, extraOption, "HelloString");
+        } else {
+            output = TestCommon.exec(appJar, "-XX:+UseCompressedOops",
+                collectorOption, "HelloString");
+        }
+
+        if (expectedWarning != null)
+            output.shouldMatch(expectedWarning);
+
+        if (expectedToFail)
+            Asserts.assertNE(output.getExitValue(), 0);
+        else
+            SharedStringsUtils.checkExec(output);
+    }
+}
diff --git a/test/hotspot/jtreg/runtime/appcds/sharedStrings/InternSharedString.java b/test/hotspot/jtreg/runtime/appcds/sharedStrings/InternSharedString.java
new file mode 100644
index 00000000000..d64643257d0
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/sharedStrings/InternSharedString.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+/*
+ * @test
+ * @summary Test shared strings together with string intern operation
+ * Feature support: G1GC only, compressed oops/kptrs, 64-bit os, not on windows
+ * @requires (sun.arch.data.model != "32") & (os.family != "windows")
+ * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true)
+ * @requires vm.gc.G1
+ * @library /test/hotspot/jtreg/runtime/appcds /test/lib
+ * @modules java.base/jdk.internal.misc
+ * @modules java.management
+ *          jdk.jartool/sun.tools.jar
+ * @compile InternStringTest.java
+ * @build sun.hotspot.WhiteBox
+ * @run main ClassFileInstaller sun.hotspot.WhiteBox
+ * @run main InternSharedString
+ */
+
+public class InternSharedString {
+    public static void main(String[] args) throws Exception {
+        SharedStringsUtils.buildJarAndWhiteBox("InternStringTest");
+
+        SharedStringsUtils.dumpWithWhiteBox(TestCommon.list("InternStringTest"),
+            "ExtraSharedInput.txt");
+
+        String[] extraMatches = new String[]   {
+            InternStringTest.passed_output1,
+            InternStringTest.passed_output2,
+            InternStringTest.passed_output3  };
+
+        SharedStringsUtils.runWithArchiveAndWhiteBox(extraMatches, "InternStringTest");
+    }
+}
diff --git a/test/hotspot/jtreg/runtime/appcds/sharedStrings/InternStringTest.java b/test/hotspot/jtreg/runtime/appcds/sharedStrings/InternStringTest.java
new file mode 100644
index 00000000000..cc95cdf6cbe
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/sharedStrings/InternStringTest.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+import sun.hotspot.WhiteBox;
+
+public class InternStringTest {
+    public static String passed_output1 = "Found shared string.";
+    public static String passed_output2 = "Shared strings are equal.";
+    public static String passed_output3 = "Found shared string containing latin1 supplement chars.";
+    public static String passed_output4 = "Found shared string containing non-western chars.";
+    public static final String latin1Sup  = "XXXX \u00a3 YYYY"; // \u00a3 = The pound sign
+    public static final String nonWestern = "XXXX \u5678 YYYY"; // \u5678 = Unicode Han Character 'ton (metric or English)'
+
+    public static void main(String[] args) throws Exception {
+        WhiteBox wb = WhiteBox.getWhiteBox();
+
+        // All string literals are shared.
+        String shared1 = "LiveOak";
+        String interned1 = shared1.intern();
+        if (wb.areSharedStringsIgnored() || wb.isShared(interned1)) {
+            System.out.println(passed_output1);
+        } else {
+            throw new RuntimeException("Failed: String is not shared.");
+        }
+
+        // Test 2: shared_string1.intern() == shared_string2.intern()
+        String shared2 = "LiveOak";
+        String interned2 = shared2.intern();
+        if (interned1 == interned2) {
+            System.out.println(passed_output2);
+        } else {
+            throw new RuntimeException("Not equal!");
+        }
+
+        // Test 3: interned strings with a char in latin1 supplement block [\u0080-\u00ff]
+        {
+            String a = "X" + latin1Sup.substring(1);
+            String b = a.intern();
+
+            if (wb.areSharedStringsIgnored() || wb.isShared(b)) {
+                System.out.println(passed_output3);
+            } else {
+                throw new RuntimeException("Failed: expected shared string with latin1-supplement chars.");
+            }
+        }
+
+        // Test 5: interned strings with non-western characters
+        {
+            String a = "X" + nonWestern.substring(1);
+            String b = a.intern();
+            if (wb.areSharedStringsIgnored() || wb.isShared(b)) {
+                System.out.println(passed_output4);
+            } else {
+                throw new RuntimeException("Failed: expected shared string with non-western chars.");
+            }
+        }
+    }
+}
diff --git a/test/hotspot/jtreg/runtime/appcds/sharedStrings/InvalidFileFormat.java b/test/hotspot/jtreg/runtime/appcds/sharedStrings/InvalidFileFormat.java
new file mode 100644
index 00000000000..b781f345eed
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/sharedStrings/InvalidFileFormat.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+/*
+ * @test
+ * @summary Check most common errors in file format
+ * Feature support: G1GC only, compressed oops/kptrs, 64-bit os, not on windows
+ * @requires (sun.arch.data.model != "32") & (os.family != "windows")
+ * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true)
+ * @requires vm.gc.G1
+ * @library /test/lib /test/hotspot/jtreg/runtime/appcds
+ * @modules java.base/jdk.internal.misc
+ * @modules java.management
+ *          jdk.jartool/sun.tools.jar
+ * @build HelloString
+ * @run main InvalidFileFormat
+ */
+
+import jdk.test.lib.process.OutputAnalyzer;
+import java.io.File;
+
+// Checking most common error use cases
+// This file is not an exhastive test of various shared data file corruption
+// Note on usability intent: the shared data file is created and handled by
+// the previledge person in the server environment.
+public class InvalidFileFormat {
+    public static void main(String[] args) throws Exception {
+        SharedStringsUtils.buildJar("HelloString");
+
+        test("NonExistentFile.txt", "Unable to get hashtable dump file size");
+        test("InvalidHeader.txt", "wrong version of hashtable dump file");
+        test("InvalidVersion.txt", "wrong version of hashtable dump file");
+        test("CorruptDataLine.txt", "Unknown data type. Corrupted at line 2");
+        test("InvalidSymbol.txt", "Unexpected character. Corrupted at line 2");
+        test("InvalidSymbolFormat.txt", "Unrecognized format. Corrupted at line 9");
+        test("OverflowPrefix.txt", "Num overflow. Corrupted at line 4");
+        test("UnrecognizedPrefix.txt", "Unrecognized format. Corrupted at line 5");
+        test("TruncatedString.txt", "Truncated. Corrupted at line 3");
+    }
+
+    private static void
+        test(String dataFileName, String expectedWarning) throws Exception {
+        System.out.println("Filename for testcase: " + dataFileName);
+
+        OutputAnalyzer out = SharedStringsUtils.dumpWithoutChecks(TestCommon.list("HelloString"),
+                                 "invalidFormat" + File.separator + dataFileName);
+
+        if (!TestCommon.isUnableToMap(out))
+            out.shouldContain(expectedWarning).shouldHaveExitValue(1);
+    }
+
+}
diff --git a/test/hotspot/jtreg/runtime/appcds/sharedStrings/LargePages.java b/test/hotspot/jtreg/runtime/appcds/sharedStrings/LargePages.java
new file mode 100644
index 00000000000..a234da4a9df
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/sharedStrings/LargePages.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+/*
+ * @test
+ * @summary Basic shared string test with large pages
+ * Feature support: G1GC only, compressed oops/kptrs, 64-bit os, not on windows
+ * @requires (sun.arch.data.model != "32") & (os.family != "windows")
+ * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true)
+ * @requires vm.gc.G1
+ * @library /test/lib /test/hotspot/jtreg/runtime/appcds
+ * @modules java.base/jdk.internal.misc
+ * @modules java.management
+ *          jdk.jartool/sun.tools.jar
+ * @build HelloString
+ * @run main LargePages
+ */
+public class LargePages {
+    public static void main(String[] args) throws Exception {
+        SharedStringsUtils.buildJar("HelloString");
+
+        SharedStringsUtils.dump(TestCommon.list("HelloString"),
+            "SharedStringsBasic.txt", "-XX:+UseLargePages");
+        SharedStringsUtils.runWithArchive("HelloString", "-XX:+UseLargePages");
+
+        SharedStringsUtils.dump(TestCommon.list("HelloString"),
+            "SharedStringsBasic.txt",
+            "-XX:+UseLargePages", "-XX:+UseLargePagesInMetaspace");
+        SharedStringsUtils.runWithArchive("HelloString",
+            "-XX:+UseLargePages", "-XX:+UseLargePagesInMetaspace");
+    }
+}
diff --git a/test/hotspot/jtreg/runtime/appcds/sharedStrings/LockSharedStrings.java b/test/hotspot/jtreg/runtime/appcds/sharedStrings/LockSharedStrings.java
new file mode 100644
index 00000000000..921361ec96a
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/sharedStrings/LockSharedStrings.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+/*
+ * @test
+ * @summary Test locking on shared strings
+ * Feature support: G1GC only, compressed oops/kptrs, 64-bit os, not on windows
+ * @requires (sun.arch.data.model != "32") & (os.family != "windows")
+ * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true)
+ * @requires vm.gc.G1
+ * @library /test/hotspot/jtreg/runtime/appcds /test/lib
+ * @modules java.base/jdk.internal.misc
+ * @modules java.management
+ *          jdk.jartool/sun.tools.jar
+ * @compile LockStringTest.java LockStringValueTest.java
+ * @build sun.hotspot.WhiteBox
+ * @run main ClassFileInstaller sun.hotspot.WhiteBox
+ * @run main LockSharedStrings
+ */
+
+public class LockSharedStrings {
+    public static void main(String[] args) throws Exception {
+        SharedStringsUtils.buildJarAndWhiteBox("LockStringTest", "LockStringValueTest");
+
+        SharedStringsUtils.dumpWithWhiteBox(
+            TestCommon.list("LockStringTest", "LockStringValueTest"),
+            "ExtraSharedInput.txt");
+
+        String[] extraMatch = new String[] {"LockStringTest: PASS"};
+        SharedStringsUtils.runWithArchiveAndWhiteBox(extraMatch, "LockStringTest");
+
+        extraMatch = new String[] {"LockStringValueTest: PASS"};
+        SharedStringsUtils.runWithArchiveAndWhiteBox(extraMatch, "LockStringValueTest",
+            "--add-opens=java.base/java.lang=ALL-UNNAMED");
+    }
+}
diff --git a/test/hotspot/jtreg/runtime/appcds/sharedStrings/LockStringTest.java b/test/hotspot/jtreg/runtime/appcds/sharedStrings/LockStringTest.java
new file mode 100644
index 00000000000..daecd0a93f8
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/sharedStrings/LockStringTest.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+import sun.hotspot.WhiteBox;
+
+public class LockStringTest extends Thread {
+    static String lock = "StringLock";
+    static boolean done = false;
+
+    public static void main(String[] args) throws Exception {
+        WhiteBox wb = WhiteBox.getWhiteBox();
+        if (wb.areSharedStringsIgnored()) {
+            System.out.println("The shared strings are ignored");
+            System.out.println("LockStringTest: PASS");
+            return;
+        }
+
+        if (!wb.isShared(lock)) {
+            throw new RuntimeException("Failed: String is not shared.");
+        }
+
+        new LockStringTest().start();
+
+        synchronized(lock) {
+            while (!done) {
+                lock.wait();
+            }
+        }
+        System.gc();
+        System.out.println("LockStringTest: PASS");
+    }
+
+    public void run() {
+        String shared = "LiveOak";
+        synchronized (lock) {
+            for (int i = 0; i < 100; i++) {
+                new String(shared);
+                System.gc();
+                try {
+                    sleep(5);
+                } catch (InterruptedException e) {}
+            }
+            done = true;
+            lock.notify();
+        }
+    }
+}
diff --git a/test/hotspot/jtreg/runtime/appcds/sharedStrings/LockStringValueTest.java b/test/hotspot/jtreg/runtime/appcds/sharedStrings/LockStringValueTest.java
new file mode 100644
index 00000000000..4d1e2c1a808
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/sharedStrings/LockStringValueTest.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+import java.lang.reflect.*;
+import sun.hotspot.WhiteBox;
+
+/*
+ * Lock the 'value' field of a known shared string, java.lang.Object
+ */
+public class LockStringValueTest {
+    public static void main(String args[]) {
+        String s = "LiveOak";
+        WhiteBox wb = WhiteBox.getWhiteBox();
+
+        if (wb.areSharedStringsIgnored()) {
+            System.out.println("The shared strings are ignored");
+            System.out.println("LockStringValueTest: PASS");
+            return;
+        }
+
+        if (!wb.isShared(s)) {
+            throw new RuntimeException("LockStringValueTest Failed: String is not shared.");
+        }
+
+        Class c = s.getClass();
+        try {
+            Field f = c.getDeclaredField("value");
+            f.setAccessible(true);
+            Object v = f.get(s);
+            lock(v);
+        } catch (NoSuchFieldException nfe) {
+        } catch (IllegalAccessException iae) {}
+    }
+
+    public static void lock(Object o) {
+        synchronized (o) {
+            System.out.println("LockStringValueTest: PASS");
+        }
+    }
+}
diff --git a/test/hotspot/jtreg/runtime/appcds/sharedStrings/SharedStringsBasic.java b/test/hotspot/jtreg/runtime/appcds/sharedStrings/SharedStringsBasic.java
new file mode 100644
index 00000000000..7d9623aa4b7
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/sharedStrings/SharedStringsBasic.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+/*
+ * @test
+ * @summary Basic test for shared strings
+ * Feature support: G1GC only, compressed oops/kptrs, 64-bit os, not on windows
+ * @requires (sun.arch.data.model != "32") & (os.family != "windows")
+ * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true)
+ * @requires vm.gc.G1
+ * @library /test/hotspot/jtreg/runtime/appcds /test/lib
+ * @modules java.base/jdk.internal.misc
+ * @modules java.management
+ *          jdk.jartool/sun.tools.jar
+ * @build HelloString
+ * @run main SharedStringsBasic
+ */
+import jdk.test.lib.process.OutputAnalyzer;
+import jdk.test.lib.process.ProcessTools;
+
+// This test does not use SharedStringsUtils intentionally:
+// - in order to demonstrate the basic use of the functionality
+// - to provide sanity check and catch potential problems in the utils
+public class SharedStringsBasic {
+    public static void main(String[] args) throws Exception {
+        String appJar = JarBuilder.build("SharedStringsBasic", "HelloString");
+
+        String sharedArchiveConfigFile =
+            TestCommon.getSourceFile("SharedStringsBasic.txt").toString();
+
+        ProcessBuilder dumpPb = ProcessTools.createJavaProcessBuilder(true,
+            "-XX:+UseAppCDS",
+            "-XX:+UseCompressedOops",
+            "-XX:+UseG1GC",
+            "-cp", appJar,
+            "-XX:SharedArchiveConfigFile=" + sharedArchiveConfigFile,
+            "-XX:SharedArchiveFile=./SharedStringsBasic.jsa",
+            "-Xshare:dump",
+            "-Xlog:cds,cds+hashtables");
+
+        TestCommon.executeAndLog(dumpPb, "dump")
+            .shouldContain("Shared string table stats")
+            .shouldHaveExitValue(0);
+
+        ProcessBuilder runPb = ProcessTools.createJavaProcessBuilder(true,
+            "-XX:+UseAppCDS",
+            "-XX:+UseCompressedOops",
+            "-XX:+UseG1GC",
+            "-cp", appJar,
+            "-XX:SharedArchiveFile=./SharedStringsBasic.jsa",
+            "-Xshare:auto",
+            "-showversion",
+            "HelloString");
+
+        TestCommon.executeAndLog(runPb, "run").shouldHaveExitValue(0);
+    }
+}
diff --git a/test/hotspot/jtreg/runtime/appcds/sharedStrings/SharedStringsBasic.txt b/test/hotspot/jtreg/runtime/appcds/sharedStrings/SharedStringsBasic.txt
new file mode 100644
index 00000000000..a43dabaaea9
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/sharedStrings/SharedStringsBasic.txt
@@ -0,0 +1,60 @@
+VERSION: 1.0
+@SECTION: String
+0: 
+5: cp819
+31: shared_test_string_unique_14325
+31: shared_test_string_intern_12345
+7: test123
+1: *
+1: -
+1: .
+1: /
+1: :
+1: C
+1: I
+1: J
+1: U
+1: Z
+1: _
+8: segments
+1: |
+5: cp850
+5: cp852
+5: cp855
+5: cp857
+5: cp858
+5: cp862
+5: cp866
+11: ISO_8859_13
+11: ISO_8859_15
+5: cp874
+47: java.lang.invoke.MethodHandle.TRACE_INTERPRETER
+7: CHECKED
+3: zip
+10: waitStatus
+33: java.lang.invoke.MethodHandleImpl
+7: .jimage
+5: cp912
+5: cp914
+5: cp915
+5: cp920
+5: cp923
+5: cp936
+5: euccn
+5: eucjp
+11: permissions
+5: euckr
+6: SIGNAL
+5: cp737
+17: java.library.path
+5: cp775
+13: classValueMap
+4: utf8
+9: PROPAGATE
+9: baseCount
+7: cskoi8r
+8: cyrillic
+@SECTION: Symbol
+41 -1: (Ljava/util/Set<TE;>;Ljava/lang/Object;)V
+10 -1: linkMethod
+20 -1: isAlphaNumericString
diff --git a/test/hotspot/jtreg/runtime/appcds/sharedStrings/SharedStringsBasicPlus.java b/test/hotspot/jtreg/runtime/appcds/sharedStrings/SharedStringsBasicPlus.java
new file mode 100644
index 00000000000..93209142709
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/sharedStrings/SharedStringsBasicPlus.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+/*
+ * @test
+ * @summary Basic plus test for shared strings
+ * Feature support: G1GC only, compressed oops/kptrs, 64-bit os, not on windows
+ * @requires (sun.arch.data.model != "32") & (os.family != "windows")
+ * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true)
+ * @requires vm.gc.G1
+ * @library /test/hotspot/jtreg/runtime/appcds /test/lib
+ * @modules java.base/jdk.internal.misc
+ * @modules java.management
+ *          jdk.jartool/sun.tools.jar
+ * @build HelloStringPlus sun.hotspot.WhiteBox
+ * @run main ClassFileInstaller sun.hotspot.WhiteBox
+ * @run main SharedStringsBasicPlus
+ */
+
+public class SharedStringsBasicPlus {
+    public static void main(String[] args) throws Exception {
+        SharedStringsUtils.buildJarAndWhiteBox("HelloStringPlus");
+
+        SharedStringsUtils.dumpWithWhiteBox( TestCommon.list("HelloStringPlus"),
+            "SharedStringsBasic.txt");
+
+        SharedStringsUtils.runWithArchiveAndWhiteBox("HelloStringPlus");
+    }
+}
diff --git a/test/hotspot/jtreg/runtime/appcds/sharedStrings/SharedStringsStress.java b/test/hotspot/jtreg/runtime/appcds/sharedStrings/SharedStringsStress.java
new file mode 100644
index 00000000000..db531617a88
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/sharedStrings/SharedStringsStress.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+/*
+ * @test
+ * @summary Write a lots of shared strings.
+ * Feature support: G1GC only, compressed oops/kptrs, 64-bit os, not on windows
+ * @requires (sun.arch.data.model != "32") & (os.family != "windows")
+ * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true)
+ * @requires vm.gc.G1
+ * @library /test/hotspot/jtreg/runtime/appcds /test/lib
+ * @modules jdk.jartool/sun.tools.jar
+ * @build HelloString
+ * @run main SharedStringsStress
+ */
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import jdk.test.lib.process.OutputAnalyzer;
+import jdk.test.lib.process.ProcessTools;
+
+public class SharedStringsStress {
+    public static void main(String[] args) throws Exception {
+        String appJar = JarBuilder.build("SharedStringsStress", "HelloString");
+
+        String sharedArchiveConfigFile = System.getProperty("user.dir") + File.separator + "SharedStringsStress_gen.txt";
+        try (FileOutputStream fos = new FileOutputStream(sharedArchiveConfigFile)) {
+            PrintWriter out = new PrintWriter(new OutputStreamWriter(fos));
+            out.println("VERSION: 1.0");
+            out.println("@SECTION: String");
+            out.println("31: shared_test_string_unique_14325");
+            for (int i=0; i<100000; i++) {
+                String s = "generated_string " + i;
+                out.println(s.length() + ": " + s);
+            }
+            out.close();
+        }
+
+        // Set NewSize to 8m due to dumping could fail in hs-tier6 testing with
+        // the vm options: -XX:+UnlockCommercialFeatures -XX:+UseDeterministicG1GC
+        // resulting in vm initialization error:
+        // "GC triggered before VM initialization completed. Try increasing NewSize, current value 1331K."
+        OutputAnalyzer dumpOutput = TestCommon.dump(appJar, TestCommon.list("HelloString"), "-XX:NewSize=8m",
+                                                    "-XX:SharedArchiveConfigFile=" + sharedArchiveConfigFile);
+        TestCommon.checkDump(dumpOutput);
+        OutputAnalyzer execOutput = TestCommon.exec(appJar, "HelloString");
+        TestCommon.checkExec(execOutput);
+    }
+}
diff --git a/test/hotspot/jtreg/runtime/appcds/sharedStrings/SharedStringsUtils.java b/test/hotspot/jtreg/runtime/appcds/sharedStrings/SharedStringsUtils.java
new file mode 100644
index 00000000000..5121ae5924e
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/sharedStrings/SharedStringsUtils.java
@@ -0,0 +1,143 @@
+/*
+ * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+import jdk.test.lib.cds.CDSOptions;
+import jdk.test.lib.process.OutputAnalyzer;
+
+// A helper/utility class for testing shared strings
+public class SharedStringsUtils {
+    public static final String TEST_JAR_NAME =      "test";
+    public static final String TEST_JAR_NAME_FULL = "test.jar";
+    public static final String WHITEBOX_JAR_NAME =  "whitebox";
+
+    public static String getWbParam() {
+        return "-Xbootclasspath/a:" + TestCommon.getTestJar(WHITEBOX_JAR_NAME + ".jar");
+    }
+
+    // build the test jar
+    public static void buildJar(String... classes) throws Exception {
+        JarBuilder.build(TEST_JAR_NAME, classes);
+    }
+
+    // build the test jar and a whitebox jar
+    public static void buildJarAndWhiteBox(String... classes) throws Exception {
+        JarBuilder.build(true, WHITEBOX_JAR_NAME, "sun/hotspot/WhiteBox");
+        buildJar(classes);
+    }
+
+    // execute the "dump" operation, but do not check the output
+    public static OutputAnalyzer dumpWithoutChecks(String appClasses[],
+        String sharedDataFile, String... extraOptions) throws Exception {
+
+        String appJar = TestCommon.getTestJar(TEST_JAR_NAME_FULL);
+        String[] args =
+            TestCommon.concat(extraOptions, "-XX:+UseCompressedOops", "-XX:+UseG1GC",
+            "-XX:SharedArchiveConfigFile=" +
+            TestCommon.getSourceFile(sharedDataFile));
+
+        return TestCommon.dump(appJar, appClasses, args);
+    }
+
+    // execute the dump operation and check the output
+    public static OutputAnalyzer dump(String appClasses[],
+        String sharedDataFile, String... extraOptions) throws Exception {
+        OutputAnalyzer output = dumpWithoutChecks(appClasses, sharedDataFile, extraOptions);
+        checkDump(output);
+        return output;
+    }
+
+    public static OutputAnalyzer dumpWithWhiteBox(String appClasses[],
+        String sharedDataFile, String... extraOptions) throws Exception {
+        return dump(appClasses, sharedDataFile,
+            TestCommon.concat(extraOptions, getWbParam()) );
+    }
+
+    // execute/run test with shared archive
+    public static OutputAnalyzer runWithArchiveAuto(String className,
+        String... extraOptions) throws Exception {
+
+        String appJar = TestCommon.getTestJar(TEST_JAR_NAME_FULL);
+        String[] args = TestCommon.concat(extraOptions,
+            "-cp", appJar, "-XX:+UseCompressedOops", "-XX:+UseG1GC", className);
+
+        OutputAnalyzer output = TestCommon.execAuto(args);
+        checkExecAuto(output);
+        return output;
+    }
+
+    public static OutputAnalyzer runWithArchive(String className,
+        String... extraOptions) throws Exception {
+
+        return runWithArchive(new String[0], className, extraOptions);
+    }
+
+    public static OutputAnalyzer runWithArchive(String[] extraMatches,
+        String className, String... extraOptions) throws Exception {
+
+        String appJar = TestCommon.getTestJar(TEST_JAR_NAME_FULL);
+        String[] args = TestCommon.concat(extraOptions,
+            "-XX:+UseCompressedOops", "-XX:+UseG1GC", className);
+
+        OutputAnalyzer output = TestCommon.exec(appJar, args);
+        checkExec(output, extraMatches);
+        return output;
+    }
+
+
+    // execute/run test with shared archive and white box
+    public static OutputAnalyzer runWithArchiveAndWhiteBox(String className,
+        String... extraOptions) throws Exception {
+
+        return runWithArchive(className,
+            TestCommon.concat(extraOptions, getWbParam(),
+            "-XX:+UnlockDiagnosticVMOptions", "-XX:+WhiteBoxAPI") );
+    }
+
+    public static OutputAnalyzer runWithArchiveAndWhiteBox(String[] extraMatches,
+        String className, String... extraOptions) throws Exception {
+
+        return runWithArchive(extraMatches, className,
+            TestCommon.concat(extraOptions, getWbParam(),
+            "-XX:+UnlockDiagnosticVMOptions", "-XX:+WhiteBoxAPI") );
+    }
+
+
+    public static void checkDump(OutputAnalyzer output) throws Exception {
+        output.shouldContain("Shared string table stats");
+        TestCommon.checkDump(output);
+    }
+
+    public static void checkExec(OutputAnalyzer output) throws Exception {
+        TestCommon.checkExec(output, new String[0]);
+    }
+
+    public static void checkExecAuto(OutputAnalyzer output) throws Exception {
+        CDSOptions opts = (new CDSOptions()).setXShareMode("auto");
+        TestCommon.checkExec(output, opts);
+    }
+
+    public static void checkExec(OutputAnalyzer output, String[] extraMatches) throws Exception {
+        TestCommon.checkExec(output, extraMatches);
+    }
+}
diff --git a/test/hotspot/jtreg/runtime/appcds/sharedStrings/SharedStringsWb.java b/test/hotspot/jtreg/runtime/appcds/sharedStrings/SharedStringsWb.java
new file mode 100644
index 00000000000..3bd8eeb6152
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/sharedStrings/SharedStringsWb.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+import sun.hotspot.WhiteBox;
+
+public class SharedStringsWb {
+    public static void main(String[] args) throws Exception {
+        WhiteBox wb = WhiteBox.getWhiteBox();
+        String s = "shared_test_string_unique_14325";
+        s = s.intern();
+        if (wb.areSharedStringsIgnored() || wb.isShared(s)) {
+            System.out.println("Found shared string.");
+        } else {
+            throw new RuntimeException("String is not shared.");
+        }
+    }
+}
+
+
diff --git a/test/hotspot/jtreg/runtime/appcds/sharedStrings/SharedStringsWbTest.java b/test/hotspot/jtreg/runtime/appcds/sharedStrings/SharedStringsWbTest.java
new file mode 100644
index 00000000000..fe3f05350a7
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/sharedStrings/SharedStringsWbTest.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+/*
+ * @test
+ * @summary White box test for shared strings
+ * Feature support: G1GC only, compressed oops/kptrs, 64-bit os, not on windows
+ * @requires (sun.arch.data.model != "32") & (os.family != "windows")
+ * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true)
+ * @requires vm.gc.G1
+ * @library /test/lib /test/hotspot/jtreg/runtime/appcds
+ * @modules java.base/jdk.internal.misc
+ * @modules java.management
+ *          jdk.jartool/sun.tools.jar
+ * @build sun.hotspot.WhiteBox SharedStringsWb
+ * @run main ClassFileInstaller sun.hotspot.WhiteBox
+ * @run main SharedStringsWbTest
+ */
+
+import java.io.*;
+import sun.hotspot.WhiteBox;
+
+public class SharedStringsWbTest {
+    public static void main(String[] args) throws Exception {
+        SharedStringsUtils.buildJarAndWhiteBox("SharedStringsWb");
+
+        SharedStringsUtils.dumpWithWhiteBox(TestCommon.list("SharedStringsWb"),
+            "SharedStringsBasic.txt");
+
+        SharedStringsUtils.runWithArchiveAndWhiteBox("SharedStringsWb");
+    }
+}
diff --git a/test/hotspot/jtreg/runtime/appcds/sharedStrings/SysDictCrash.java b/test/hotspot/jtreg/runtime/appcds/sharedStrings/SysDictCrash.java
new file mode 100644
index 00000000000..c96a75da7e1
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/sharedStrings/SysDictCrash.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+/*
+ * @test
+ * @summary Regression test for JDK-8098821
+ * @bug 8098821
+ * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true)
+ * @requires vm.gc.G1
+ * @library /test/lib /test/hotspot/jtreg/runtime/appcds
+ * @modules java.base/jdk.internal.misc
+ * @modules java.management
+ * @run main SysDictCrash
+ */
+
+import jdk.test.lib.process.OutputAnalyzer;
+import jdk.test.lib.process.ProcessTools;
+
+public class SysDictCrash {
+    public static void main(String[] args) throws Exception {
+        // SharedBaseAddress=0 puts the archive at a very high address on solaris,
+        // which provokes the crash.
+        ProcessBuilder dumpPb = ProcessTools.createJavaProcessBuilder(true,
+            "-XX:+UseG1GC", "-XX:MaxRAMPercentage=12.5",
+            "-XX:+UseAppCDS",
+            "-cp", ".",
+            "-XX:SharedBaseAddress=0", "-XX:SharedArchiveFile=./SysDictCrash.jsa",
+            "-Xshare:dump",
+            "-showversion", "-Xlog:cds,cds+hashtables");
+
+        TestCommon.checkDump(TestCommon.executeAndLog(dumpPb, "dump"));
+
+        ProcessBuilder runPb = ProcessTools.createJavaProcessBuilder(true,
+            "-XX:+UseG1GC", "-XX:MaxRAMPercentage=12.5",
+            "-XX:+UseAppCDS",
+            "-XX:SharedArchiveFile=./SysDictCrash.jsa",
+            "-Xshare:on",
+            "-version");
+
+        TestCommon.checkExec(TestCommon.executeAndLog(runPb, "exec"));
+    }
+}
diff --git a/test/hotspot/jtreg/runtime/appcds/sharedStrings/invalidFormat/CorruptDataLine.txt b/test/hotspot/jtreg/runtime/appcds/sharedStrings/invalidFormat/CorruptDataLine.txt
new file mode 100644
index 00000000000..fe5fb328c1e
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/sharedStrings/invalidFormat/CorruptDataLine.txt
@@ -0,0 +1,60 @@
+VERSION: 1.0
+SECTION: String
+0: 
+5: cp819
+31: shared_test_string_unique_14325
+31: shared_test_string_intern_12345
+7: test123
+1: *
+1: -
+1: .
+1: /
+1: :
+1: C
+1: I
+1: J
+1: U
+1: Z
+1: _
+8: segments
+1: |
+5: cp850
+5: cp852
+5: cp855
+5: cp857
+5: cp858
+5: cp862
+5: cp866
+11: ISO_8859_13
+11: ISO_8859_15
+5: cp874
+47: java.lang.invoke.MethodHandle.TRACE_INTERPRETER
+7: CHECKED
+3: zip
+10: waitStatus
+33: java.lang.invoke.MethodHandleImpl
+7: .jimage
+5: cp912
+5: cp914
+5: cp915
+5: cp920
+5: cp923
+5: cp936
+5: euccn
+5: eucjp
+11: permissions
+5: euckr
+6: SIGNAL
+5: cp737
+17: java.library.path
+5: cp775
+13: classValueMap
+4: utf8
+9: PROPAGATE
+9: baseCount
+7: cskoi8r
+8: cyrillic
+#DATATYPE: Symbol
+41 -1: (Ljava/util/Set<TE;>;Ljava/lang/Object;)V
+10 -1: linkMethod
+20 -1: isAlphaNumericString
diff --git a/test/hotspot/jtreg/runtime/appcds/sharedStrings/invalidFormat/InvalidDataType.txt b/test/hotspot/jtreg/runtime/appcds/sharedStrings/invalidFormat/InvalidDataType.txt
new file mode 100644
index 00000000000..d19db9e4999
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/sharedStrings/invalidFormat/InvalidDataType.txt
@@ -0,0 +1,60 @@
+VERSION: 1.0
+@SECTION: String
+0: 
+5: cp819
+31: shared_test_string_unique_14325
+31: shared_test_string_intern_12345
+7: test123
+1: *
+1: -
+1: .
+1: /
+1: :
+1: C
+1: I
+1: J
+1: U
+1: Z
+1: _
+8: segments
+1: |
+5: cp850
+5: cp852
+5: cp855
+5: cp857
+5: cp858
+5: cp862
+5: cp866
+11: ISO_8859_13
+11: ISO_8859_15
+5: cp874
+47: java.lang.invoke.MethodHandle.TRACE_INTERPRETER
+7: CHECKED
+3: zip
+10: waitStatus
+33: java.lang.invoke.MethodHandleImpl
+7: .jimage
+5: cp912
+5: cp914
+5: cp915
+5: cp920
+5: cp923
+5: cp936
+5: euccn
+5: eucjp
+11: permissions
+5: euckr
+6: SIGNAL
+5: cp737
+17: java.library.path
+5: cp775
+13: classValueMap
+4: utf8
+9: PROPAGATE
+9: baseCount
+7: cskoi8r
+8: cyrillic
+#DATATYPE: Symbol
+41 -1: (Ljava/util/Set<TE;>;Ljava/lang/Object;)V
+10 -1: linkMethod
+20 -1: isAlphaNumericString
diff --git a/test/hotspot/jtreg/runtime/appcds/sharedStrings/invalidFormat/InvalidHeader.txt b/test/hotspot/jtreg/runtime/appcds/sharedStrings/invalidFormat/InvalidHeader.txt
new file mode 100644
index 00000000000..02ff35b7246
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/sharedStrings/invalidFormat/InvalidHeader.txt
@@ -0,0 +1,60 @@
+Garbage Header x0935#%0sl
+@SECTION: String
+0: 
+5: cp819
+31: shared_test_string_unique_14325
+31: shared_test_string_intern_12345
+7: test123
+1: *
+1: -
+1: .
+1: /
+1: :
+1: C
+1: I
+1: J
+1: U
+1: Z
+1: _
+8: segments
+1: |
+5: cp850
+5: cp852
+5: cp855
+5: cp857
+5: cp858
+5: cp862
+5: cp866
+11: ISO_8859_13
+11: ISO_8859_15
+5: cp874
+47: java.lang.invoke.MethodHandle.TRACE_INTERPRETER
+7: CHECKED
+3: zip
+10: waitStatus
+33: java.lang.invoke.MethodHandleImpl
+7: .jimage
+5: cp912
+5: cp914
+5: cp915
+5: cp920
+5: cp923
+5: cp936
+5: euccn
+5: eucjp
+11: permissions
+5: euckr
+6: SIGNAL
+5: cp737
+17: java.library.path
+5: cp775
+13: classValueMap
+4: utf8
+9: PROPAGATE
+9: baseCount
+7: cskoi8r
+8: cyrillic
+#DATATYPE: Symbol
+41 -1: (Ljava/util/Set<TE;>;Ljava/lang/Object;)V
+10 -1: linkMethod
+20 -1: isAlphaNumericString
diff --git a/test/hotspot/jtreg/runtime/appcds/sharedStrings/invalidFormat/InvalidString.txt b/test/hotspot/jtreg/runtime/appcds/sharedStrings/invalidFormat/InvalidString.txt
new file mode 100644
index 00000000000..104785cc934
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/sharedStrings/invalidFormat/InvalidString.txt
@@ -0,0 +1,6 @@
+VERSION: 1.0
+@SECTION: String
+31: shred_test_string_unique_14325
+31: shared_test_string_intern_12345
+7: test123
+
diff --git a/test/hotspot/jtreg/runtime/appcds/sharedStrings/invalidFormat/InvalidStringFormat.txt b/test/hotspot/jtreg/runtime/appcds/sharedStrings/invalidFormat/InvalidStringFormat.txt
new file mode 100644
index 00000000000..bf4fe475ad7
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/sharedStrings/invalidFormat/InvalidStringFormat.txt
@@ -0,0 +1,60 @@
+VERSION: 1.0
+@SECTION: String
+0: 
+5:: cp819
+31: shared_test_string_unique_14325
+31: shared_test_string_intern_12345
+7: test123
+1: *
+1: -
+1: .
+1: /
+1: :
+1: C
+1: I
+1: J
+1: U
+1: Z
+1: _
+8: segments
+1: |
+5: cp850
+5: cp852
+5: cp855
+5: cp857
+5: cp858
+5: cp862
+5: cp866
+11: ISO_8859_13
+11: ISO_8859_15
+5: cp874
+47: java.lang.invoke.MethodHandle.TRACE_INTERPRETER
+7: CHECKED
+3: zip
+10: waitStatus
+33: java.lang.invoke.MethodHandleImpl
+7: .jimage
+5: cp912
+5: cp914
+5: cp915
+5: cp920
+5: cp923
+5: cp936
+5: euccn
+5: eucjp
+11: permissions
+5: euckr
+6: SIGNAL
+5: cp737
+17: java.library.path
+5: cp775
+13: classValueMap
+4: utf8
+9: PROPAGATE
+9: baseCount
+7: cskoi8r
+8: cyrillic
+#DATATYPE: Symbol
+41 -1: (Ljava/util/Set<TE;>;Ljava/lang/Object;)V
+10 -1: linkMethod
+20 -1: isAlphaNumericString
diff --git a/test/hotspot/jtreg/runtime/appcds/sharedStrings/invalidFormat/InvalidSymbol.txt b/test/hotspot/jtreg/runtime/appcds/sharedStrings/invalidFormat/InvalidSymbol.txt
new file mode 100644
index 00000000000..7da06b825eb
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/sharedStrings/invalidFormat/InvalidSymbol.txt
@@ -0,0 +1,12 @@
+VERSION: 1.0
+@SECTION:  String
+0: 
+5: cp819
+31: shared_test_string_unique_14325
+31: shared_test_string_intern_12345
+7: test123
+8: cyrillic
+@SECTION:  Symbol
+41 -1: (Ljava/util/Set<TE;>;Ljava/lang/Object;)V
+10 -1: linkMet%%%hod
+20 -1: isAlphaNumericString
diff --git a/test/hotspot/jtreg/runtime/appcds/sharedStrings/invalidFormat/InvalidSymbolFormat.txt b/test/hotspot/jtreg/runtime/appcds/sharedStrings/invalidFormat/InvalidSymbolFormat.txt
new file mode 100644
index 00000000000..affa466d4e9
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/sharedStrings/invalidFormat/InvalidSymbolFormat.txt
@@ -0,0 +1,11 @@
+VERSION: 1.0
+@SECTION: String
+0: 
+5: cp819
+31: shared_test_string_unique_14325
+31: shared_test_string_intern_12345
+7: test123
+@SECTION: Symbol
+41: (Ljava/util/Set<TE;>;Ljava/lang/Object;)V
+10 -1: linkMethod
+20 -1: isAlphaNumericString
diff --git a/test/hotspot/jtreg/runtime/appcds/sharedStrings/invalidFormat/InvalidVersion.txt b/test/hotspot/jtreg/runtime/appcds/sharedStrings/invalidFormat/InvalidVersion.txt
new file mode 100644
index 00000000000..2d917344c6e
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/sharedStrings/invalidFormat/InvalidVersion.txt
@@ -0,0 +1,60 @@
+VERSION: 0.0
+@SECTION: String
+0: 
+5: cp819
+31: shared_test_string_unique_14325
+31: shared_test_string_intern_12345
+7: test123
+1: *
+1: -
+1: .
+1: /
+1: :
+1: C
+1: I
+1: J
+1: U
+1: Z
+1: _
+8: segments
+1: |
+5: cp850
+5: cp852
+5: cp855
+5: cp857
+5: cp858
+5: cp862
+5: cp866
+11: ISO_8859_13
+11: ISO_8859_15
+5: cp874
+47: java.lang.invoke.MethodHandle.TRACE_INTERPRETER
+7: CHECKED
+3: zip
+10: waitStatus
+33: java.lang.invoke.MethodHandleImpl
+7: .jimage
+5: cp912
+5: cp914
+5: cp915
+5: cp920
+5: cp923
+5: cp936
+5: euccn
+5: eucjp
+11: permissions
+5: euckr
+6: SIGNAL
+5: cp737
+17: java.library.path
+5: cp775
+13: classValueMap
+4: utf8
+9: PROPAGATE
+9: baseCount
+7: cskoi8r
+8: cyrillic
+#DATATYPE: Symbol
+41 -1: (Ljava/util/Set<TE;>;Ljava/lang/Object;)V
+10 -1: linkMethod
+20 -1: isAlphaNumericString
diff --git a/test/hotspot/jtreg/runtime/appcds/sharedStrings/invalidFormat/OverflowPrefix.txt b/test/hotspot/jtreg/runtime/appcds/sharedStrings/invalidFormat/OverflowPrefix.txt
new file mode 100644
index 00000000000..8da872dca23
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/sharedStrings/invalidFormat/OverflowPrefix.txt
@@ -0,0 +1,11 @@
+VERSION: 1.0
+@SECTION: String
+0: 
+2147483648: cp819
+31: shared_test_string_unique_14325
+31: shared_test_string_intern_12345
+7: test123
+@SECTION: Symbol
+41 -1: (Ljava/util/Set<TE;>;Ljava/lang/Object;)V
+10 -1: linkMethod
+20 -1: isAlphaNumericString
diff --git a/test/hotspot/jtreg/runtime/appcds/sharedStrings/invalidFormat/TruncatedString.txt b/test/hotspot/jtreg/runtime/appcds/sharedStrings/invalidFormat/TruncatedString.txt
new file mode 100644
index 00000000000..849f8b5ddfd
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/sharedStrings/invalidFormat/TruncatedString.txt
@@ -0,0 +1,10 @@
+VERSION: 1.0
+@SECTION: String
+2147483647: s
+5: cp819
+31: shared_test_string_intern_12345
+7: test123
+@SECTION: Symbol
+41 -1: (Ljava/util/Set<TE;>;Ljava/lang/Object;)V
+10 -1: linkMethod
+20 -1: isAlphaNumericString
diff --git a/test/hotspot/jtreg/runtime/appcds/sharedStrings/invalidFormat/UnrecognizedPrefix.txt b/test/hotspot/jtreg/runtime/appcds/sharedStrings/invalidFormat/UnrecognizedPrefix.txt
new file mode 100644
index 00000000000..e979c3d8910
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/sharedStrings/invalidFormat/UnrecognizedPrefix.txt
@@ -0,0 +1,11 @@
+VERSION: 1.0
+@SECTION: String
+0: 
+5: cp819
+3E: shared_test_string_unique_14325
+31: shared_test_string_intern_12345
+7: test123
+@SECTION: Symbol
+41 -1: (Ljava/util/Set<TE;>;Ljava/lang/Object;)V
+10 -1: linkMethod
+20 -1: isAlphaNumericString
diff --git a/test/hotspot/jtreg/runtime/appcds/test-classes/ArrayListTest.java b/test/hotspot/jtreg/runtime/appcds/test-classes/ArrayListTest.java
new file mode 100644
index 00000000000..d6ac26e960a
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/test-classes/ArrayListTest.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+import java.util.*;
+
+// This is a test case executed by DumpClassList.java to load classes
+// from various places to ensure that they are not written to the class list.
+public class ArrayListTest {
+    public static void main(String args[]) throws Exception {
+        // The following lambda usage should generate various classes like
+        // java.lang.invoke.LambdaForm$MH/1146743572. All of them should be excluded from
+        // the class list.
+        List<String> a = new ArrayList<>();
+        a.add("hello world.");
+        a.forEach(str -> System.out.println(str));
+
+        System.out.println(Class.forName("java.lang.NewClass")); // should be excluded from the class list.
+        System.out.println(Class.forName("boot.append.Foo"));    // should be excluded from the class list.
+    }
+}
diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/linux/ia64/LinuxIA64ThreadContext.java b/test/hotspot/jtreg/runtime/appcds/test-classes/BootClassPathAppendHelper.java
similarity index 59%
rename from src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/linux/ia64/LinuxIA64ThreadContext.java
rename to test/hotspot/jtreg/runtime/appcds/test-classes/BootClassPathAppendHelper.java
index 749cd92a619..79cd2805ee3 100644
--- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/linux/ia64/LinuxIA64ThreadContext.java
+++ b/test/hotspot/jtreg/runtime/appcds/test-classes/BootClassPathAppendHelper.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2003, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2017, 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
@@ -22,25 +22,21 @@
  *
  */
 
-package sun.jvm.hotspot.debugger.linux.ia64;
+import sun.hotspot.WhiteBox;
 
-import sun.jvm.hotspot.debugger.*;
-import sun.jvm.hotspot.debugger.ia64.*;
-import sun.jvm.hotspot.debugger.linux.*;
+public class BootClassPathAppendHelper {
+    public static void main(String[] args) throws ClassNotFoundException {
+        Class cls = Class.forName("Hello");
 
-public class LinuxIA64ThreadContext extends IA64ThreadContext {
-  private LinuxDebugger debugger;
+        if (cls == null) {
+            throw new java.lang.RuntimeException("Cannot find Hello.class");
+        }
 
-  public LinuxIA64ThreadContext(LinuxDebugger debugger) {
-    super();
-    this.debugger = debugger;
-  }
-
-  public void setRegisterAsAddress(int index, Address value) {
-    setRegister(index, debugger.getAddressValue(value));
-  }
-
-  public Address getRegisterAsAddress(int index) {
-    return debugger.newAddress(getRegister(index));
-  }
+        WhiteBox wb = WhiteBox.getWhiteBox();
+        if (!wb.isSharedClass(cls)) {
+            System.out.println("Hello.class is not in shared space as expected.");
+        } else {
+            throw new java.lang.RuntimeException("Hello.class shouldn't be in shared space.");
+        }
+    }
 }
diff --git a/test/hotspot/jtreg/runtime/appcds/test-classes/C1.java b/test/hotspot/jtreg/runtime/appcds/test-classes/C1.java
new file mode 100644
index 00000000000..86201cd4e34
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/test-classes/C1.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2014, 2017, 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 sealed.pkg;
+
+public class C1 {
+}
diff --git a/test/hotspot/jtreg/runtime/appcds/test-classes/C2.java b/test/hotspot/jtreg/runtime/appcds/test-classes/C2.java
new file mode 100644
index 00000000000..ad0026fbc53
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/test-classes/C2.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2014, 2017, 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 pkg;
+
+public class C2 {
+}
diff --git a/test/hotspot/jtreg/runtime/appcds/test-classes/CheckIfShared.java b/test/hotspot/jtreg/runtime/appcds/test-classes/CheckIfShared.java
new file mode 100644
index 00000000000..6e919ad5bce
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/test-classes/CheckIfShared.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+import sun.hotspot.WhiteBox;
+
+public class CheckIfShared {
+  public static void main(String args[]) throws Exception {
+    WhiteBox wb = WhiteBox.getWhiteBox();
+    if ("true".equals(args[0])) {
+      if (!wb.isSharedClass(CheckIfShared.class)) {
+        throw new RuntimeException("wb.isSharedClass(CheckIfShared.class) should be true");
+      }
+    } else {
+      if (wb.isSharedClass(CheckIfShared.class)) {
+        throw new RuntimeException("wb.isSharedClass(CheckIfShared.class) should be false");
+      }
+    }
+  }
+}
diff --git a/test/hotspot/jtreg/runtime/appcds/test-classes/Child.java b/test/hotspot/jtreg/runtime/appcds/test-classes/Child.java
new file mode 100644
index 00000000000..8a3684e15a6
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/test-classes/Child.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2015, 2017, 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.
+ *
+ */
+
+public class Child extends Super {}
diff --git a/test/hotspot/jtreg/runtime/appcds/test-classes/CpAttr1.java b/test/hotspot/jtreg/runtime/appcds/test-classes/CpAttr1.java
new file mode 100644
index 00000000000..5006870cd6d
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/test-classes/CpAttr1.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2014, 2017, 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.
+ *
+ */
+
+public class CpAttr1 {
+  public static void main(String args[]) {
+    System.out.println("2"); CpAttr2.doit(); // Only the version of this class defined in CpAttr2.java will not throw exception.
+    System.out.println("3"); CpAttr3.doit(); // Only the version of this class defined in CpAttr3.java will not throw exception.
+    System.out.println("4"); CpAttr4.doit(); // Only the version of this class defined in CpAttr4.java will not throw exception.
+    System.out.println("5"); CpAttr5.doit(); // Only the version of this class defined in CpAttr5.java will not throw exception.
+    System.out.println("Test passed");
+  }
+}
+
+class CpAttr2 { static void doit() {throw new RuntimeException("");} }
+class CpAttr3 { static void doit() {throw new RuntimeException("");} }
+class CpAttr4 { static void doit() {throw new RuntimeException("");} }
+class CpAttr5 { static void doit() {throw new RuntimeException("");} }
diff --git a/test/hotspot/jtreg/runtime/appcds/test-classes/CpAttr2.java b/test/hotspot/jtreg/runtime/appcds/test-classes/CpAttr2.java
new file mode 100644
index 00000000000..4777e1caf9e
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/test-classes/CpAttr2.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2014, 2017, 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.
+ *
+ */
+
+class CpAttr2 { static void doit() {} }
diff --git a/test/hotspot/jtreg/runtime/appcds/test-classes/CpAttr3.java b/test/hotspot/jtreg/runtime/appcds/test-classes/CpAttr3.java
new file mode 100644
index 00000000000..96b19d0424c
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/test-classes/CpAttr3.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2014, 2017, 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.
+ *
+ */
+
+class CpAttr2 { static void doit() {throw new RuntimeException("");} }
+class CpAttr3 { static void doit() {} }
diff --git a/test/hotspot/jtreg/runtime/appcds/test-classes/CpAttr4.java b/test/hotspot/jtreg/runtime/appcds/test-classes/CpAttr4.java
new file mode 100644
index 00000000000..9711148f877
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/test-classes/CpAttr4.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2014, 2017, 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.
+ *
+ */
+
+class CpAttr2 { static void doit() {throw new RuntimeException("");} }
+class CpAttr3 { static void doit() {throw new RuntimeException("");} }
+class CpAttr4 { static void doit() {} }
+class CpAttr5 { static void doit() {throw new RuntimeException("");} }
diff --git a/test/hotspot/jtreg/runtime/appcds/test-classes/CpAttr5.java b/test/hotspot/jtreg/runtime/appcds/test-classes/CpAttr5.java
new file mode 100644
index 00000000000..94812653c48
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/test-classes/CpAttr5.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2014, 2017, 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.
+ *
+ */
+
+class CpAttr5 { static void doit() {} }
diff --git a/test/hotspot/jtreg/runtime/appcds/test-classes/DummyClassHelper.java b/test/hotspot/jtreg/runtime/appcds/test-classes/DummyClassHelper.java
new file mode 100644
index 00000000000..56ffa75bd29
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/test-classes/DummyClassHelper.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+import java.lang.*;
+import java.lang.reflect.*;
+import sun.hotspot.WhiteBox;
+
+public class DummyClassHelper {
+    public static void main(String[] args) throws Exception {
+        String[] classNames = {args[0], args[1]};
+        Class cls = null;
+        if (args.length == 2) {
+            for (int i = 0; i < classNames.length; i++) {
+                Method m = null;
+                cls = Class.forName(classNames[i]);
+                try {
+                    m = cls.getMethod("thisClassIsDummy");
+                    throw new java.lang.RuntimeException(classNames[i] +
+                        " should be loaded from the jimage and should not have the thisClassIsDummy() method.");
+                } catch(NoSuchMethodException ex) {
+                    System.out.println(ex.toString());
+                }
+            }
+        } else {
+            WhiteBox wb = WhiteBox.getWhiteBox();
+            for (int i = 0; i < classNames.length; i++) {
+                cls = Class.forName(classNames[i]);
+                if (!wb.isSharedClass(cls)) {
+                    System.out.println(classNames[i] + ".class" + " is not in shared space as expected.");
+                } else {
+                    throw new java.lang.RuntimeException(classNames[i] +
+                        ".class shouldn't be in shared space.");
+                }
+            }
+        }
+    }
+}
diff --git a/test/hotspot/jtreg/runtime/appcds/test-classes/EmptyClassHelper.java b/test/hotspot/jtreg/runtime/appcds/test-classes/EmptyClassHelper.java
new file mode 100644
index 00000000000..86805214617
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/test-classes/EmptyClassHelper.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+import java.lang.*;
+import java.lang.reflect.*;
+import jdk.internal.misc.JavaLangAccess;
+import jdk.internal.misc.SharedSecrets;
+
+class EmptyClassHelper {
+    static final JavaLangAccess jla = SharedSecrets.getJavaLangAccess();
+    static final String USE_APP = "useAppLoader";
+    public static void main(String[] args) throws Exception {
+        Class cls = null;
+        Method m = null;
+        ClassLoader appLoader = ClassLoader.getSystemClassLoader();
+        String className = "com.sun.tools.javac.Main";
+        if (args[0].equals(USE_APP)) {
+            cls = appLoader.loadClass(className);
+            System.out.println("appLoader loaded class");
+            try {
+                m = cls.getMethod("main", String[].class);
+                System.out.println("appLoader found method main");
+            } catch(NoSuchMethodException ex) {
+                System.out.println(ex.toString());
+            }
+        } else {
+            cls = jla.findBootstrapClassOrNull(appLoader, className);
+            System.out.println("bootLoader loaded class");
+            System.out.println("cls = " + cls);
+            try {
+                m = cls.getMethod("main", String[].class);
+                System.out.println("bootLoader found method main");
+            } catch(NoSuchMethodException ex) {
+                System.out.println(ex.toString());
+            }
+        }
+    }
+}
diff --git a/test/hotspot/jtreg/runtime/appcds/test-classes/FieldAnnotationsApp.java b/test/hotspot/jtreg/runtime/appcds/test-classes/FieldAnnotationsApp.java
new file mode 100644
index 00000000000..70808d8af64
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/test-classes/FieldAnnotationsApp.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Field;
+
+public class FieldAnnotationsApp {
+    @MyAnnotation(name="myField1",  value="myValue1")
+    public String myField1 = null;
+
+    @MyAnnotation(name="myField2",  value="myValue2")
+    public String myField2 = null;
+
+    public static void main(String args[]) throws Exception {
+        for (int i=1; i<=2; i++) {
+            Field field = FieldAnnotationsApp.class.getField("myField" + i);
+            Annotation[] annotations = field.getDeclaredAnnotations();
+
+            for (Annotation anno : annotations){
+                if (anno instanceof MyAnnotation){
+                    MyAnnotation myAnno = (MyAnnotation) anno;
+                    String name = myAnno.name();
+                    String value = myAnno.value();
+
+                    System.out.println("Field         : " + field.getName());
+                    System.out.println("  myAnno.name : " + name);
+                    System.out.println("  myAnno.value: " + value);
+
+                    if (!(name.equals("myField" + i) && value.equals("myValue" + i))) {
+                        throw new Exception("Unexpected annotation values: " + i + " = " + value);
+                    }
+                }
+            }
+        }
+        System.out.println("Field annotations are OK.");
+    }
+}
diff --git a/test/hotspot/jtreg/runtime/appcds/test-classes/ForNameTest.java b/test/hotspot/jtreg/runtime/appcds/test-classes/ForNameTest.java
new file mode 100644
index 00000000000..b313af1ccc7
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/test-classes/ForNameTest.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+import sun.hotspot.WhiteBox;
+
+public class ForNameTest {
+    public static void main(String[] args) throws Throwable {
+        // Hello is on the bootclasspath. The defining classloader is
+        // the NULL classloader. See AppCDSClassLoaderTest.
+        Class c = Class.forName("Hello");
+        ClassLoader cl = c.getClassLoader();
+        if (cl != null) {
+            throw new RuntimeException(
+                "Test Failed. Wrong classloader is used. Expect the NULL classloader.");
+        }
+
+        WhiteBox wb = WhiteBox.getWhiteBox();
+        if (!wb.isSharedClass(c)) {
+            System.out.println("As expected, Hello.class is not in shared space.");
+        } else {
+            throw new java.lang.RuntimeException("Hello.class shouldn't be in shared space.");
+        }
+    }
+}
diff --git a/test/hotspot/jtreg/runtime/appcds/test-classes/Greet.java b/test/hotspot/jtreg/runtime/appcds/test-classes/Greet.java
new file mode 100644
index 00000000000..174e97ac205
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/test-classes/Greet.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2014, 2017, 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.
+ *
+ */
+
+public class Greet {
+
+    public String Greeting() {
+        return new String(", how are you?");
+    }
+}
diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/MachineDescriptionIA64.java b/test/hotspot/jtreg/runtime/appcds/test-classes/Hello.java
similarity index 73%
rename from src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/MachineDescriptionIA64.java
rename to test/hotspot/jtreg/runtime/appcds/test-classes/Hello.java
index a113cbe4447..dc134771ba5 100644
--- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/MachineDescriptionIA64.java
+++ b/test/hotspot/jtreg/runtime/appcds/test-classes/Hello.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2003, 2008, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2014, 2017, 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
@@ -22,18 +22,8 @@
  *
  */
 
-package sun.jvm.hotspot.debugger;
-
-public class MachineDescriptionIA64 extends MachineDescriptionTwosComplement implements MachineDescription {
-  public long getAddressSize() {
-    return 8;
-  }
-
-  public boolean isLP64() {
-    return true;
-  }
-
-  public boolean isBigEndian() {
-    return false;
+public class Hello {
+  public static void main(String args[]) {
+    System.out.println("Hello World");
   }
 }
diff --git a/test/hotspot/jtreg/runtime/appcds/test-classes/HelloExt.java b/test/hotspot/jtreg/runtime/appcds/test-classes/HelloExt.java
new file mode 100644
index 00000000000..bd11763271a
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/test-classes/HelloExt.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+import sun.hotspot.WhiteBox;
+
+public class HelloExt {
+   public static void main(String[] args) throws Throwable {
+
+       String className = "org.omg.CORBA.ORB";
+       Class cls = Class.forName(className);
+
+       ClassLoader loader = cls.getClassLoader();
+       if (loader != ClassLoader.getPlatformClassLoader()) {
+           throw new java.lang.RuntimeException(className + " should be load by PlatformClassLoader but it is loaded by " + loader);
+       }
+
+       WhiteBox wb = WhiteBox.getWhiteBox();
+       if (wb.isSharedClass(cls)) {
+           System.out.println("As expected, " + className + " is in shared space.");
+       } else {
+           throw new java.lang.RuntimeException(className + " is not in shared space.");
+       }
+
+       className = "[Ljava.lang.Comparable;";
+       cls = Class.forName(className);
+       loader = cls.getClassLoader();
+       if (loader != null) {
+           throw new java.lang.RuntimeException(className + " should be load by the NULL class loader but it is loaded by " + loader);
+       }
+
+       if (wb.isSharedClass(cls)) {
+           System.out.println("As expected, " + className + " is in shared space.");
+       } else {
+           throw new java.lang.RuntimeException(className + " is not in shared space.");
+       }
+   }
+}
+
diff --git a/test/hotspot/jtreg/runtime/appcds/test-classes/HelloExtApp.java b/test/hotspot/jtreg/runtime/appcds/test-classes/HelloExtApp.java
new file mode 100644
index 00000000000..a4dd73f390b
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/test-classes/HelloExtApp.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2014, 2017, 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.
+ *
+ */
+
+public class HelloExtApp {
+  public static void main(String args[]) {
+    System.out.println("Hello World Ext: " + HelloExtExt.class.getProtectionDomain());
+  }
+}
diff --git a/test/hotspot/jtreg/runtime/appcds/test-classes/HelloExtExt.java b/test/hotspot/jtreg/runtime/appcds/test-classes/HelloExtExt.java
new file mode 100644
index 00000000000..d74901c782a
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/test-classes/HelloExtExt.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2014, 2017, 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.
+ *
+ */
+
+public class HelloExtExt {
+
+}
diff --git a/test/hotspot/jtreg/runtime/appcds/test-classes/HelloMore.java b/test/hotspot/jtreg/runtime/appcds/test-classes/HelloMore.java
new file mode 100644
index 00000000000..dee1b239177
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/test-classes/HelloMore.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2014, 2017, 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.
+ *
+ */
+
+public class HelloMore {
+  public static void main(String args[]) {
+      Hello.main(args);
+      System.out.println("Hello World ... More");
+  }
+}
diff --git a/test/hotspot/jtreg/runtime/appcds/test-classes/HelloWB.java b/test/hotspot/jtreg/runtime/appcds/test-classes/HelloWB.java
new file mode 100644
index 00000000000..92c0d9e02fa
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/test-classes/HelloWB.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+import sun.hotspot.WhiteBox;
+
+public class HelloWB {
+    public static void main(String[] args) throws Throwable {
+
+        WhiteBox wb = WhiteBox.getWhiteBox();
+        if (wb.isSharedClass(HelloWB.class)) {
+            System.out.println("As expected, HelloWB.class is in shared space.");
+        } else {
+            throw new java.lang.RuntimeException("HelloWB.class should be in shared space.");
+        }
+    }
+}
diff --git a/test/hotspot/jtreg/runtime/appcds/test-classes/Hi.java b/test/hotspot/jtreg/runtime/appcds/test-classes/Hi.java
new file mode 100644
index 00000000000..8250c323a8b
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/test-classes/Hi.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2014, 2017, 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.
+ *
+ */
+
+public class Hi extends Greet {
+    public static void main(String args[]) {
+        Greet g = new Greet();
+        MyClass.doit(g.Greeting());
+    }
+    public static class MyClass {
+        public static void doit(String greeting) {
+            System.out.println("Hi" + greeting);
+        }
+    }
+}
diff --git a/test/hotspot/jtreg/runtime/appcds/test-classes/Iloadw.jasm b/test/hotspot/jtreg/runtime/appcds/test-classes/Iloadw.jasm
new file mode 100644
index 00000000000..90d31f9fbf6
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/test-classes/Iloadw.jasm
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2014, 2017, 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.
+ *
+ */
+
+public class Iloadw 
+    version 51: 0
+{
+    public static Method run:"()I"
+        stack 1 locals 400
+    {
+        iconst_0;
+        istore_w 300;
+        iinc_w 300,1;
+        iload_w 300;
+        ireturn;
+    }
+}
diff --git a/test/hotspot/jtreg/runtime/appcds/test-classes/IloadwMain.java b/test/hotspot/jtreg/runtime/appcds/test-classes/IloadwMain.java
new file mode 100644
index 00000000000..315e00a2b38
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/test-classes/IloadwMain.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2016, 2017, 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.
+ *
+ */
+
+public class IloadwMain {
+    public static void main(String args[]) {
+        int result = Iloadw.run();
+        if (result != 1) {
+            throw new RuntimeException(
+                "Failed. Result is " + result + ", expect 1.");
+        } else {
+            System.out.println("Passed.");
+        }
+    }
+}
diff --git a/test/hotspot/jtreg/runtime/appcds/test-classes/JimageClassPackage.java b/test/hotspot/jtreg/runtime/appcds/test-classes/JimageClassPackage.java
new file mode 100644
index 00000000000..f9358a1e28f
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/test-classes/JimageClassPackage.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (c) 2015, 2017, 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.
+ *
+ */
+
+public class JimageClassPackage {
+    public static void main(String args[]) throws Throwable {
+        // Test Package for boot/app/ext module classes from the "modules" jimage.
+        // The following classes are archived. See runtime/AppCDS/Package.java.
+        //     java.util.Dictionary (testcase 0),
+        //     sun.tools.javac.Main (testcase 1),
+        //     jdk.nio.zipfs.ZipInfo (testcase 2),
+        //     java.net.URL (testcase 3),
+        //     sun.rmi.rmic.Main (testcase 4),
+        //     com.sun.jndi.dns.DnsName (testcase 5)
+        String testcases[][] =
+            {{"Loading shared boot module class first", "java.util",
+              "java.util.Dictionary", "java.util.ServiceConfigurationError"},
+
+             {"Loading shared app module class first", "sun.tools.javac",
+              "sun.tools.javac.Main", "sun.tools.javac.BatchParser"},
+
+             {"Loading shared ext module class first", "jdk.nio.zipfs",
+              "jdk.nio.zipfs.ZipInfo", "jdk.nio.zipfs.ZipPath"},
+
+             {"Loading non-shared boot module class first", "java.net",
+              "java.net.HttpCookie", "java.net.URL"},
+
+             {"Loading non-shared app module class first", "sun.rmi.rmic",
+              "sun.rmi.rmic.RMIGenerator", "sun.rmi.rmic.Main"},
+
+             {"Loading non-shared ext module class first", "com.sun.jndi.dns",
+              "com.sun.jndi.dns.Resolver", "com.sun.jndi.dns.DnsName"}};
+
+        JimageClassPackage test = new JimageClassPackage();
+        for (int i = 0; i < testcases.length; i++) {
+            System.out.println("Testcase " + i + ": " + testcases[i][0]);
+            test.testPackage(testcases[i][1], testcases[i][2], testcases[i][3]);
+        }
+    }
+
+    private void testPackage (String pkg,
+                              String shared,
+                              String nonShared) throws Throwable {
+        Class c1 = Class.forName(shared);
+        ClassLoader cl = c1.getClassLoader();
+        Package pkg_from_loader;
+        if (cl != null) {
+            pkg_from_loader = cl.getDefinedPackage(pkg);
+        } else {
+            pkg_from_loader = Package.getPackage(pkg);
+        }
+
+        Package pkg_from_shared_class = c1.getPackage();
+
+        Class c2 = Class.forName(nonShared);
+        Package pkg_from_nonshared_class = c2.getPackage();
+
+        if (pkg_from_loader != null &&
+            pkg_from_shared_class != null &&
+            pkg_from_loader == pkg_from_shared_class &&
+            pkg_from_shared_class == pkg_from_nonshared_class &&
+            pkg_from_shared_class.getName().equals(pkg)) {
+            System.out.println("Expected package: " + pkg_from_shared_class.toString());
+        } else {
+            System.out.println("Unexpected package" + pkg_from_shared_class);
+            System.exit(1);
+        }
+        if (pkg_from_shared_class.isSealed()) {
+            System.out.println("Package is sealed");
+        } else {
+            System.out.println("Package is not sealed");
+            System.exit(1);
+        }
+    }
+}
diff --git a/test/hotspot/jtreg/runtime/appcds/test-classes/JimageClassProtDomain.java b/test/hotspot/jtreg/runtime/appcds/test-classes/JimageClassProtDomain.java
new file mode 100644
index 00000000000..e47aaaa8a36
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/test-classes/JimageClassProtDomain.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2015, 2017, 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.
+ *
+ */
+
+public class JimageClassProtDomain {
+    public static void main(String args[]) throws Throwable {
+        // Test ProtectionDomain for boot/app/ext module classes from the "modules" jimage.
+        // The following classes are archived. See runtime/AppCDS/ProtectionDomain.java.
+        //     java.util.Dictionary (testcase 0),
+        //     sun.tools.javac.Main (testcase 1),
+        //     jdk.nio.zipfs.ZipInfo (testcase 2),
+        //     java.net.URL (testcase 3),
+        //     sun.rmi.rmic.Main (testcase 4),
+        //     com.sun.jndi.dns.DnsName (testcase 5)
+        String testcases[][] =
+            {{"Loading shared boot module class first",
+              "java.util.Dictionary", "java.util.ServiceConfigurationError"},
+
+             {"Loading shared app module class first",
+              "sun.tools.javac.Main", "sun.tools.javac.BatchParser"},
+
+             {"Loading shared ext module class first",
+              "jdk.nio.zipfs.ZipInfo", "jdk.nio.zipfs.ZipPath"},
+
+             {"Loading non-shared boot module class first",
+              "java.net.HttpCookie", "java.net.URL"},
+
+             {"Loading non-shared app module class first",
+              "sun.rmi.rmic.RMIGenerator", "sun.rmi.rmic.Main"},
+
+             {"Loading non-shared ext module class first",
+              "com.sun.jndi.dns.Resolver", "com.sun.jndi.dns.DnsName"}};
+        for (int i = 0; i < testcases.length; i++) {
+            System.out.println("Testcase " + i + ": " + testcases[i][0]);
+            JimageClassProtDomain.testProtectionDomain(testcases[i][1], testcases[i][2]);
+        }
+    }
+
+    private static void testProtectionDomain(String shared, String nonShared)
+              throws Throwable {
+        Class c1 = Class.forName(shared);
+        Class c2 = Class.forName(nonShared);
+        if (c1.getProtectionDomain() != c2.getProtectionDomain()) {
+            System.out.println("Failed: Protection Domains do not match!");
+            System.out.println(c1.getProtectionDomain());
+            System.out.println(c1.getProtectionDomain().getCodeSource());
+            System.out.println(c2.getProtectionDomain());
+            System.out.println(c2.getProtectionDomain().getCodeSource());
+            System.exit(1);
+        } else {
+            System.out.println("Passed: Protection Domains match.");
+        }
+    }
+}
diff --git a/test/hotspot/jtreg/runtime/appcds/test-classes/JvmtiApp.java b/test/hotspot/jtreg/runtime/appcds/test-classes/JvmtiApp.java
new file mode 100644
index 00000000000..e0c0ea55fb6
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/test-classes/JvmtiApp.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+import sun.hotspot.WhiteBox;
+
+public class JvmtiApp {
+    static Class forname() {
+        try {
+            return Class.forName("Hello");
+        } catch (Throwable t) {
+            return null;
+        }
+    }
+
+    static void failed(String msg) {
+        System.out.println("TEST FAILED: " + msg);
+        System.exit(1);
+    }
+
+    // See ../JvmtiAddPath.java for how the classpaths are configured.
+    public static void main(String args[]) {
+        if (args[0].equals("noadd")) {
+            if (forname() != null) {
+                failed("Hello class was loaded unexpectedly");
+            }
+            // We use -verbose:class to verify that Extra.class IS loaded by AppCDS if
+            // the boot classpath HAS NOT been appended.
+            ExtraClass.doit();
+            System.exit(0);
+        }
+
+        WhiteBox wb = WhiteBox.getWhiteBox();
+
+        if (args[0].equals("bootonly")) {
+            wb.addToBootstrapClassLoaderSearch(args[1]);
+            Class cls = forname();
+            if (cls == null) {
+                failed("Cannot find Hello class");
+            }
+            if (cls.getClassLoader() != null) {
+                failed("Hello class not loaded by boot classloader");
+            }
+        } else if (args[0].equals("apponly")) {
+            wb.addToSystemClassLoaderSearch(args[1]);
+            Class cls = forname();
+            if (cls == null) {
+                failed("Cannot find Hello class");
+            }
+            if (cls.getClassLoader() != JvmtiApp.class.getClassLoader()) {
+                failed("Hello class not loaded by app classloader");
+            }
+        } else if (args[0].equals("noadd-appcds")) {
+            Class cls = forname();
+            if (cls == null) {
+                failed("Cannot find Hello class");
+            }
+            if (cls.getClassLoader() != JvmtiApp.class.getClassLoader()) {
+                failed("Hello class not loaded by app classloader");
+            }
+        } else if (args[0].equals("appandboot")) {
+            wb.addToBootstrapClassLoaderSearch(args[1]);
+            wb.addToSystemClassLoaderSearch(args[2]);
+            Class cls = forname();
+            if (cls == null) {
+                failed("Cannot find Hello class");
+            }
+            if (cls.getClassLoader() != null) {
+                failed("Hello class not loaded by boot classloader");
+            }
+        } else {
+            failed("unknown option " + args[0]);
+        }
+
+        // We use -verbose:class to verify that Extra.class IS NOT loaded by AppCDS if
+        // the boot classpath HAS been appended.
+        ExtraClass.doit();
+
+        System.out.println("Test passed: " + args[0]);
+    }
+}
+
+class ExtraClass {
+    static void doit() {}
+}
\ No newline at end of file
diff --git a/test/hotspot/jtreg/runtime/appcds/test-classes/MethodNoReturn.jasm b/test/hotspot/jtreg/runtime/appcds/test-classes/MethodNoReturn.jasm
new file mode 100644
index 00000000000..c9c17a50473
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/test-classes/MethodNoReturn.jasm
@@ -0,0 +1,93 @@
+/*
+ * Copyright (c) 2017, 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.
+ *
+ */
+
+/*
+WAS:
+
+class MethodNoReturn {
+  void badMethod() {}
+}
+*/
+
+super class MethodNoReturn
+	version 52:0
+{
+
+
+Method "<init>":"()V"
+	stack 1 locals 1
+{
+		aload_0;
+		invokespecial	Method java/lang/Object."<init>":"()V";
+		return;
+}
+
+Method badMethod:"()V"
+	stack 0 locals 1
+{
+  /*
+    should be:
+		return;
+  */
+
+		iconst_1;
+		pop;
+		iconst_1;
+		pop;
+		iconst_1;
+		pop;
+		iconst_1;
+		pop;
+		iconst_1;
+		pop;
+		iconst_1;
+		pop;
+		iconst_1;
+		pop;
+		iconst_1;
+		pop;
+		iconst_1;
+		pop;
+		iconst_1;
+		pop;
+		iconst_1;
+		iconst_1;
+		iconst_1;
+		iconst_1;
+		iconst_1;
+		iconst_1;
+		iconst_1;
+		iconst_1;
+		pop;
+		pop;
+		pop;
+		pop;
+		pop;
+		pop;
+		pop;
+		pop;
+  // no return here -- so this class will fail verification
+}
+
+} // end Class MethodNoReturn
diff --git a/test/hotspot/jtreg/runtime/appcds/test-classes/MissingSuper.java b/test/hotspot/jtreg/runtime/appcds/test-classes/MissingSuper.java
new file mode 100644
index 00000000000..ef47a7cb9e4
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/test-classes/MissingSuper.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2014, 2017, 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.
+ *
+ */
+
+public class MissingSuper {
+    public static void main(String args[]) {
+        try {
+            new MissingSuperSub();
+        } catch (NoClassDefFoundError e) {
+            System.out.println("Expected NoClassDefFoundError:");
+            e.printStackTrace(System.out);
+        }
+
+        try {
+            new MissingSuperImpl();
+        } catch (NoClassDefFoundError e) {
+            System.out.println("Expected NoClassDefFoundError:");
+            e.printStackTrace(System.out);
+        }
+    }
+}
+
+class MissingSuperSup {} // This class will be deleted from missing_super.jar before dumping
+
+class MissingSuperSub extends MissingSuperSup {}
+
+interface MissingSuperIntf {} // This interface will be deleted from missing_super.jar before dumping
+
+class MissingSuperImpl implements MissingSuperIntf {}
diff --git a/test/hotspot/jtreg/runtime/appcds/test-classes/MultiProcClass.java b/test/hotspot/jtreg/runtime/appcds/test-classes/MultiProcClass.java
new file mode 100644
index 00000000000..cf67318a24e
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/test-classes/MultiProcClass.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+import sun.hotspot.WhiteBox;
+
+// This class should be loaded from a shared archive.
+public class MultiProcClass {
+    private static String instanceLabel;
+
+    public static void main(String args[]) throws Exception {
+        instanceLabel = args[0];
+        String checkPmap = args[1];
+
+        long pid = ProcessHandle.current().pid();
+        System.out.println(inst("========================== Starting MultiProcClass"));
+        System.out.println(inst("My PID: " + pid ));
+        System.out.println(inst("checkPmap = <" + checkPmap + ">" ));
+
+        if ("true".equals(checkPmap)) {
+            if (runPmap(pid, true) != 0)
+                System.out.println("MultiProcClass: Pmap failed");
+        }
+
+        WhiteBox wb = WhiteBox.getWhiteBox();
+        if (!wb.isSharedClass(MultiProcClass.class)) {
+            throw new RuntimeException(inst("MultiProcClass should be shared but is not."));
+        }
+
+        System.out.println(inst("========================== Leaving MultiProcClass"));
+    }
+
+    // A convenience method to append process instance label
+    private static String inst(String msg) {
+        return "process-" + instanceLabel + " : " + msg;
+    }
+
+    // Use on Linux-only; requires jdk-9 for Process.pid()
+    public static int runPmap(long pid, boolean inheritIO) throws Exception {
+        ProcessBuilder pmapPb = new ProcessBuilder("pmap", "" + pid);
+        if (inheritIO)
+            pmapPb.inheritIO();
+
+        return pmapPb.start().waitFor();
+    }
+}
diff --git a/test/hotspot/jtreg/runtime/appcds/test-classes/MyAnnotation.java b/test/hotspot/jtreg/runtime/appcds/test-classes/MyAnnotation.java
new file mode 100644
index 00000000000..cbec72aecf6
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/test-classes/MyAnnotation.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+import java.lang.annotation.Target;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.FIELD)
+
+public @interface MyAnnotation {
+    public String name();
+    public String value();
+}
diff --git a/test/hotspot/jtreg/runtime/appcds/test-classes/PackageSealingTest.java b/test/hotspot/jtreg/runtime/appcds/test-classes/PackageSealingTest.java
new file mode 100644
index 00000000000..a1e8ea0a234
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/test-classes/PackageSealingTest.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+import java.lang.Package;
+
+public class PackageSealingTest {
+    public static void main(String args[]) {
+        try {
+            Class c1 = PackageSealingTest.class.forName("sealed.pkg.C1");
+            Class c2 = PackageSealingTest.class.forName("pkg.C2");
+            Package p1 = c1.getPackage();
+            System.out.println("Package 1: " + p1.toString());
+            Package p2 = c2.getPackage();
+            System.out.println("Package 2: " + p2.toString());
+
+            if (!p1.isSealed()) {
+                System.out.println("Failed: sealed.pkg is not sealed.");
+                System.exit(0);
+            }
+
+            if (p2.isSealed()) {
+                System.out.println("Failed: pkg is sealed.");
+                System.exit(0);
+            }
+
+            System.out.println("OK");
+        } catch (Exception e) {
+            System.out.println(e.getMessage());
+        }
+    }
+}
diff --git a/test/hotspot/jtreg/runtime/appcds/test-classes/PackageTest.java b/test/hotspot/jtreg/runtime/appcds/test-classes/PackageTest.java
new file mode 100644
index 00000000000..f5f1f15014b
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/test-classes/PackageTest.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2014, 2017, 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 p;
+
+public class PackageTest {
+    public static void main(String args[]) {
+        (new PackageTest()).test();
+    }
+
+    private void test() {
+        ClassLoader cl = PackageTest.class.getClassLoader();
+        Package pkg_from_loader;
+        if (cl != null) {
+            pkg_from_loader = cl.getDefinedPackage("p");
+        } else {
+            pkg_from_loader = Package.getPackage("p");
+        }
+
+        Package pkg = PackageTest.class.getPackage();
+        if (pkg_from_loader != null && pkg == pkg_from_loader &&
+            pkg.getName().equals("p")) {
+            System.out.println("Expected package: " + pkg);
+        } else {
+            System.out.println("Unexpected package: " + pkg);
+            System.exit(1);
+        }
+        if (pkg.isSealed()) {
+            System.out.println("Package is sealed");
+            System.exit(1);
+        } else {
+            System.out.println("Package is not sealed");
+        }
+    }
+}
diff --git a/test/hotspot/jtreg/runtime/appcds/test-classes/ParallelClasses.java b/test/hotspot/jtreg/runtime/appcds/test-classes/ParallelClasses.java
new file mode 100644
index 00000000000..a4d0520f235
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/test-classes/ParallelClasses.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2017, 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.
+ *
+ */
+
+class ParallelClass0 {}
+class ParallelClass1 {}
+class ParallelClass2 {}
+class ParallelClass3 {}
+class ParallelClass4 {}
+class ParallelClass5 {}
+class ParallelClass6 {}
+class ParallelClass7 {}
+class ParallelClass8 {}
+class ParallelClass9 {}
+class ParallelClass10 {}
+class ParallelClass11 {}
+class ParallelClass12 {}
+class ParallelClass13 {}
+class ParallelClass14 {}
+class ParallelClass15 {}
+class ParallelClass16 {}
+class ParallelClass17 {}
+class ParallelClass18 {}
+class ParallelClass19 {}
+class ParallelClass20 {}
+class ParallelClass21 {}
+class ParallelClass22 {}
+class ParallelClass23 {}
+class ParallelClass24 {}
+class ParallelClass25 {}
+class ParallelClass26 {}
+class ParallelClass27 {}
+class ParallelClass28 {}
+class ParallelClass29 {}
+class ParallelClass30 {}
+class ParallelClass31 {}
+class ParallelClass32 {}
+class ParallelClass33 {}
+class ParallelClass34 {}
+class ParallelClass35 {}
+class ParallelClass36 {}
+class ParallelClass37 {}
+class ParallelClass38 {}
+class ParallelClass39 {}
diff --git a/test/hotspot/jtreg/runtime/appcds/test-classes/ParallelLoad.java b/test/hotspot/jtreg/runtime/appcds/test-classes/ParallelLoad.java
new file mode 100644
index 00000000000..d47c3343845
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/test-classes/ParallelLoad.java
@@ -0,0 +1,220 @@
+/*
+ * Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+import java.io.*;
+import java.net.*;
+import java.lang.reflect.Field;
+
+
+// This test helper is parameterized by:
+// - class transformation mode: property "appcds.parallel.transform.mode"
+// - class loader test types
+//
+// In the case of transformMode == "cflh", the transformation is performed
+// by AppCDS/jvmti/TransformerAgent.java. The classes to be transformed, such as
+// ParallelClassTr0, are defined in ./jvmti/parallelLoad/ParallelClasses.java
+
+public class ParallelLoad {
+    public static int MAX_CLASSES = 40;
+    public static int NUM_THREADS = 4;
+
+    public final static int SYSTEM_LOADER = 0;
+    public final static int SINGLE_CUSTOM_LOADER = 1;
+    public final static int MULTI_CUSTOM_LOADER = 2;
+
+    public static final int FINGERPRINT_MODE = 1;
+    public static final int API_MODE         = 2;
+
+    public static int loaderType = SYSTEM_LOADER;
+    public static ClassLoader classLoaders[];
+    public static int mode = FINGERPRINT_MODE;
+
+    public static float timeoutFactor =
+        Float.parseFloat(System.getProperty("test.timeout.factor", "1.0"));
+
+    public static void main(String args[]) throws Throwable {
+        run(args, null);
+    }
+    public static void run(String args[], ClassLoader loaders[]) throws Throwable {
+        String customJar = null;
+        System.out.println("ParallelLoad: timeoutFactor = " + timeoutFactor);
+
+        if (args.length >= 1) {
+            if ("SINGLE_CUSTOM_LOADER".equals(args[0])) {
+                loaderType = SINGLE_CUSTOM_LOADER;
+                customJar = args[2];
+            } else if ("MULTI_CUSTOM_LOADER".equals(args[0])) {
+                loaderType = MULTI_CUSTOM_LOADER;
+                customJar = args[2];
+            } else if ("SYSTEM_LOADER".equals(args[0])) {
+                loaderType = SYSTEM_LOADER;
+            } else {
+                throw new RuntimeException("Unexpected loaderType" + args[0]);
+            }
+        }
+
+        if (customJar != null) {
+            if ("FINGERPRINT_MODE".equals(args[1])) {
+                mode = FINGERPRINT_MODE;
+                classLoaders = new ClassLoader[NUM_THREADS];
+                for (int i=0; i<NUM_THREADS; i++) {
+                    URL url = new File(customJar).toURI().toURL();
+                    URL[] urls = new URL[] {url};
+                    classLoaders[i] = new URLClassLoader(urls);
+                }
+            } else {
+                // Loaders must be supplied by caller of the run() method
+                mode = API_MODE;
+                classLoaders = loaders;
+            }
+        }
+
+        System.out.println("Start Parallel Load ...");
+
+        Thread thread[] = new Thread[NUM_THREADS];
+        for (int i=0; i<NUM_THREADS; i++) {
+            Thread t = new ParallelLoadThread(i);
+            t.start();
+            thread[i] = t;
+        }
+
+        Thread watchdog = new ParallelLoadWatchdog();
+        watchdog.setDaemon(true);
+        watchdog.start();
+
+        for (int i=0; i<NUM_THREADS; i++) {
+            thread[i].join();
+        }
+        System.out.println("Parallel Load ... done");
+        System.exit(0);
+    }
+}
+
+
+class ParallelLoadWatchdog extends Thread {
+    public void run() {
+        try {
+            long timeout = (long) (20 * 1000 * ParallelLoad.timeoutFactor);
+            Thread.sleep(timeout);
+            System.out.println("ParallelLoadWatchdog: Timeout reached: timeout(ms) = " + timeout);
+            System.exit(1);
+        } catch (Throwable t) {
+            t.printStackTrace();
+            System.exit(1);
+        }
+    }
+};
+
+
+class ParallelLoadThread extends Thread {
+    static int num_ready[] = new int[ParallelLoad.MAX_CLASSES];
+    static Object lock = new Object();
+    static String transformMode =
+        System.getProperty("appcds.parallel.transform.mode", "none");
+
+    int thread_id;
+    ParallelLoadThread(int thread_id) {
+        this.thread_id = thread_id;
+    }
+
+    public void run() {
+        try {
+            run0();
+        } catch (Throwable t) {
+            t.printStackTrace();
+            System.exit(1);
+        }
+    }
+
+    private static void log(String msg, Object... args) {
+        String msg0 = "ParallelLoadThread: " + String.format(msg, args);
+        System.out.println(msg0);
+    }
+
+    private void run0() throws Throwable {
+        for (int i=0; i<ParallelLoad.MAX_CLASSES; i++) {
+            synchronized(lock) {
+                num_ready[i] ++;
+                while (num_ready[i] < ParallelLoad.NUM_THREADS) {
+                    lock.wait();
+                }
+                lock.notifyAll();
+            }
+            log("this = %s %d", this, i);
+            String className = "ParallelClass" + i;
+            if (transformMode.equals("cflh"))
+                className = "ParallelClassTr" + i;
+
+            Class clazz = null;
+
+            switch (ParallelLoad.loaderType) {
+            case ParallelLoad.SYSTEM_LOADER:
+                clazz = Class.forName(className);
+                break;
+            case ParallelLoad.SINGLE_CUSTOM_LOADER:
+                clazz = ParallelLoad.classLoaders[0].loadClass(className);
+                break;
+            case ParallelLoad.MULTI_CUSTOM_LOADER:
+                clazz = ParallelLoad.classLoaders[thread_id].loadClass(className);
+                break;
+            }
+
+            log("clazz = %s", clazz);
+            testTransformation(clazz);
+        }
+    }
+
+    private void testTransformation(Class c) throws Exception {
+        if (transformMode.equals("none"))
+            return;
+
+        // currently only cflh transform mode is supported
+        if (!transformMode.equals("cflh")) {
+            String msg = "wrong transform mode: " + transformMode;
+            throw new IllegalArgumentException(msg);
+        }
+
+        Field[] fields = c.getFields();
+        boolean fieldFound = false;
+        for (Field f : fields) {
+            if (f.getName().equals("testString")) {
+                checkTransformationString(c, (String) f.get(null));
+                fieldFound = true;
+            }
+        }
+
+        if (!fieldFound)
+            throw new RuntimeException ("Expected field 'testString' not found");
+    }
+
+    private void checkTransformationString(Class c, String actual) throws Exception {
+        String expected = "class-transform-check: this-has-been--transformed";
+        if (!actual.equals(expected)) {
+            String msg1 = "Transformation failed for class" + c.getName();
+            String msg2 = String.format("Expected: %s, actual: %s", expected, actual);
+            throw new RuntimeException(msg1 + "\n" + msg2);
+        }
+    }
+}
+
diff --git a/test/hotspot/jtreg/runtime/appcds/test-classes/Prohibited.jasm b/test/hotspot/jtreg/runtime/appcds/test-classes/Prohibited.jasm
new file mode 100644
index 00000000000..175462b46a7
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/test-classes/Prohibited.jasm
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2016, 2017, 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 java/lang;
+
+public class Prohibited
+    version 51:0
+{
+} // end class Prohibited
diff --git a/test/hotspot/jtreg/runtime/appcds/test-classes/ProhibitedHelper.java b/test/hotspot/jtreg/runtime/appcds/test-classes/ProhibitedHelper.java
new file mode 100644
index 00000000000..ea22b2041ed
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/test-classes/ProhibitedHelper.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2017, 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.
+ *
+ */
+
+public class ProhibitedHelper {
+    public static void main(String[] args) throws Throwable {
+
+        String className = "java.lang.Prohibited";
+        ClassLoader sysLoader = ClassLoader.getSystemClassLoader();
+        try {
+            Module unnamedModule = sysLoader.getUnnamedModule();
+            Class cls = Class.forName(unnamedModule, className);
+            System.out.println("cls " + cls);
+            throw new java.lang.RuntimeException(className +
+                "in a prohibited package shouldn't be loaded");
+        } catch (Exception e) {
+            e.printStackTrace();
+            if (!(e instanceof java.lang.SecurityException)) {
+                throw new java.lang.RuntimeException(
+                    "SecurityException is expected to be thrown while loading " + className);
+            }
+        }
+
+        try {
+            Class cls = Class.forName(className, false, sysLoader);
+            System.out.println("cls " + cls);
+            throw new java.lang.RuntimeException(className +
+                "in a prohibited package shouldn't be loaded");
+        } catch (Exception e) {
+            e.printStackTrace();
+            if (!(e instanceof java.lang.ClassNotFoundException)) {
+                throw new java.lang.RuntimeException(
+                    "ClassNotFoundException is expected to be thrown while loading " + className);
+            }
+        }
+    }
+}
diff --git a/test/hotspot/jtreg/runtime/appcds/test-classes/ProtDomain.java b/test/hotspot/jtreg/runtime/appcds/test-classes/ProtDomain.java
new file mode 100644
index 00000000000..fe88bfb1bb0
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/test-classes/ProtDomain.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+import java.security.ProtectionDomain;
+
+// See ../AppCDSProtectionDomain.java
+//
+// ProtDomain      is     stored in CDS archive.
+// ProtDomainOther is NOT stored in CDS archive.
+//
+// However, they should have the same ProtectionDomain instance.
+public class ProtDomain {
+  public static void main(String args[]) {
+    System.out.println("Testing ProtDomain");
+    ProtectionDomain mine = ProtDomain.class.getProtectionDomain();
+    ProtectionDomain his  = ProtDomainOther.class.getProtectionDomain();
+
+    System.out.println("mine = " + mine);
+    System.out.println("his  = " + his);
+
+    if (mine == his) {
+      System.out.println("Protection Domains match");
+    } else {
+      System.out.println("Protection Domains do not match!");
+      System.exit(1);
+    }
+  }
+}
+
+class ProtDomainOther {
+
+}
diff --git a/test/hotspot/jtreg/runtime/appcds/test-classes/ProtDomainB.java b/test/hotspot/jtreg/runtime/appcds/test-classes/ProtDomainB.java
new file mode 100644
index 00000000000..01a36ccf706
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/test-classes/ProtDomainB.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+import java.security.ProtectionDomain;
+
+// See ../AppCDSProtectionDomain.java
+//
+// ProtDomainB      is NOT stored in CDS archive.
+// ProtDomainBOther is     stored in CDS archive.
+//
+// However, they should have the same ProtectionDomain instance.
+public class ProtDomainB {
+  public static void main(String args[]) {
+    System.out.println("Testing ProtDomainB");
+    ProtectionDomain mine = ProtDomainB.class.getProtectionDomain();
+    ProtectionDomain his  = ProtDomainBOther.class.getProtectionDomain();
+
+    System.out.println("mine = " + mine);
+    System.out.println("his  = " + his);
+
+    if (mine == his) {
+      System.out.println("Protection Domains match");
+    } else {
+      System.out.println("Protection Domains do not match!");
+      System.exit(1);
+    }
+  }
+}
+
+class ProtDomainBOther {
+
+}
diff --git a/test/hotspot/jtreg/runtime/appcds/test-classes/ReportMyLoader.java b/test/hotspot/jtreg/runtime/appcds/test-classes/ReportMyLoader.java
new file mode 100644
index 00000000000..2b1b0892a39
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/test-classes/ReportMyLoader.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2014, 2017, 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.
+ *
+ */
+
+public class ReportMyLoader {
+    public static void main(String args[]) {
+        System.out.println("ReportMyLoader's loader = " + ReportMyLoader.class.getClassLoader());
+        System.out.println("TestClassLoader.called = " + TestClassLoader.called);
+    }
+}
+
diff --git a/test/hotspot/jtreg/runtime/appcds/test-classes/RewriteBytecodes.java b/test/hotspot/jtreg/runtime/appcds/test-classes/RewriteBytecodes.java
new file mode 100644
index 00000000000..787b51fa93c
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/test-classes/RewriteBytecodes.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+import java.io.File;
+import sun.hotspot.WhiteBox;
+
+public class RewriteBytecodes {
+  public static void main(String args[]) throws Throwable {
+    String from = "___xxx___";
+    String to   = "___yyy___";
+    File clsFile = new File(args[0]);
+    Class superClass = Util.defineModifiedClass(RewriteBytecodes.class.getClassLoader(), clsFile, from, to);
+
+    Child child = new Child();
+
+    if (child.getClass().getSuperclass() != superClass) {
+      throw new RuntimeException("Mismatched super class");
+    }
+    // Even if the Super class is not loaded from the CDS archive, make sure the Child class
+    // can still be loaded successfully, and properly inherits from the rewritten version
+    // of Super.
+    if (!child.toString().equals(to)) {
+      throw new RuntimeException("Wrong output, expected: " + to + ", but got: " + child.toString());
+    }
+
+    WhiteBox wb = WhiteBox.getWhiteBox();
+    if (wb.isSharedClass(superClass)) {
+      throw new RuntimeException("wb.isSharedClass(superClass) should be false");
+    }
+    if (wb.isSharedClass(child.getClass())) {
+      throw new RuntimeException("wb.isSharedClass(child.getClass()) should be false");
+    }
+  }
+}
diff --git a/test/hotspot/jtreg/runtime/appcds/test-classes/Super.java b/test/hotspot/jtreg/runtime/appcds/test-classes/Super.java
new file mode 100644
index 00000000000..ba698c616bf
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/test-classes/Super.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2015, 2017, 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.
+ *
+ */
+
+public class Super {
+  public String toString() {
+    return "___xxx___";
+  }
+}
diff --git a/test/hotspot/jtreg/runtime/appcds/test-classes/TestClassLoader.java b/test/hotspot/jtreg/runtime/appcds/test-classes/TestClassLoader.java
new file mode 100644
index 00000000000..29507956a27
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/test-classes/TestClassLoader.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2014, 2017, 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.
+ *
+ */
+
+// This is a class loader that simply delegates all calls to its parent loader.
+
+public class TestClassLoader extends ClassLoader {
+    static boolean called = false;
+    ClassLoader parent;
+    public TestClassLoader(ClassLoader parent) {
+        super(parent);
+        this.parent = parent;
+    }
+
+    public Class<?> loadClass(String name, boolean resolve)
+        throws ClassNotFoundException
+    {
+        called = true;
+        System.out.println("TestClassLoader: loadClass(\"" + name + "\", " + resolve + ")");
+        return (super.loadClass(name, resolve));
+    }
+}
diff --git a/test/hotspot/jtreg/runtime/appcds/test-classes/TrySwitchMyLoader.java b/test/hotspot/jtreg/runtime/appcds/test-classes/TrySwitchMyLoader.java
new file mode 100644
index 00000000000..48a455debd0
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/test-classes/TrySwitchMyLoader.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2014, 2017, 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.
+ *
+ */
+
+public class TrySwitchMyLoader {
+    public static void main(String args[]) {
+        System.out.println("TrySwitchMyLoader's loader = " + ReportMyLoader.class.getClassLoader());
+        System.setProperty("java.system.class.loader", "TestClassLoader");
+
+        // This should still report the same loader as TrySwitchMyLoader.class.getClassLoader(),
+        // as setting the java.system.class.loader after main method has been executed
+        // has no effect.
+        ReportMyLoader.main(args);
+    }
+}
+
diff --git a/test/hotspot/jtreg/runtime/appcds/test-classes/Util.java b/test/hotspot/jtreg/runtime/appcds/test-classes/Util.java
new file mode 100644
index 00000000000..4289c765257
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/test-classes/Util.java
@@ -0,0 +1,156 @@
+/*
+ * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+import java.io.*;
+import java.lang.reflect.*;
+import java.util.jar.*;
+
+public class Util {
+    /**
+     * Invoke the loader.defineClass() class method to define the class stored in clsFile,
+     * with the following modification:
+     * <ul>
+     *  <li> All ASCII strings in the class file bytes that matches fromString will be replaced with toString.
+     *       NOTE: the two strings must be the exact same length.
+     * </ul>
+     */
+    public static Class defineModifiedClass(ClassLoader loader, File clsFile, String fromString, String toString)
+        throws FileNotFoundException, IOException, NoSuchMethodException, IllegalAccessException,
+               InvocationTargetException
+    {
+        DataInputStream dis = new DataInputStream(new FileInputStream(clsFile));
+        byte[] buff = new byte[(int)clsFile.length()];
+        dis.readFully(buff);
+        replace(buff, fromString, toString);
+
+        System.out.println("Loading from: " + clsFile + " (" + buff.length + " bytes)");
+
+        Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass",
+                                                                 buff.getClass(), int.class, int.class);
+        defineClass.setAccessible(true);
+
+        // We directly call into ClassLoader.defineClass() to define the "Super" class. Also,
+        // rewrite its classfile so that it returns ___yyy___ instead of ___xxx___. Changing the
+        // classfile will guarantee that this class will NOT be loaded from the CDS archive.
+        Class cls = (Class)defineClass.invoke(loader, buff, new Integer(0), new Integer(buff.length));
+        System.out.println("Loaded : " + cls);
+
+        return cls;
+    }
+
+    /**
+     * @return the number of occurrences of the <code>from</code> string that
+     * have been replaced.
+     */
+    public static int replace(byte buff[], String from, String to) {
+        if (to.length() != from.length()) {
+            throw new RuntimeException("bad strings");
+        }
+        byte f[] = asciibytes(from);
+        byte t[] = asciibytes(to);
+        byte f0 = f[0];
+
+        int numReplaced = 0;
+        int max = buff.length - f.length;
+        for (int i=0; i<max; ) {
+            if (buff[i] == f0 && replace(buff, f, t, i)) {
+                i += f.length;
+                numReplaced ++;
+            } else {
+                i++;
+            }
+        }
+        return numReplaced;
+    }
+
+    public static boolean replace(byte buff[], byte f[], byte t[], int i) {
+        for (int x=0; x<f.length; x++) {
+            if (buff[x+i] != f[x]) {
+                return false;
+            }
+        }
+        for (int x=0; x<f.length; x++) {
+            buff[x+i] = t[x];
+        }
+        return true;
+    }
+
+    static byte[] asciibytes(String s) {
+        byte b[] = new byte[s.length()];
+        for (int i=0; i<b.length; i++) {
+            b[i] = (byte)s.charAt(i);
+        }
+        return b;
+    }
+
+    public static Class defineClassFromJAR(ClassLoader loader, File jarFile, String className)
+        throws FileNotFoundException, IOException, NoSuchMethodException, IllegalAccessException,
+               InvocationTargetException {
+        return defineClassFromJAR(loader, jarFile, className, null, null);
+    }
+
+    /**
+     * Invoke the loader.defineClass() class method to define the named class stored in a JAR file.
+     *
+     * If a class exists both in the classpath, as well as in the list of URLs of a URLClassLoader,
+     * by default, the URLClassLoader will not define the class, and instead will delegate to the
+     * app loader. This method is an easy way to force the class to be defined by the URLClassLoader.
+     *
+     * Optionally, you can modify the contents of the classfile buffer. See comments in
+     * defineModifiedClass.
+     */
+    public static Class defineClassFromJAR(ClassLoader loader, File jarFile, String className,
+                                           String fromString, String toString)
+        throws FileNotFoundException, IOException, NoSuchMethodException, IllegalAccessException,
+               InvocationTargetException
+    {
+        byte[] buff = getClassFileFromJar(jarFile, className);
+
+        if (fromString != null) {
+            replace(buff, fromString, toString);
+        }
+
+        //System.out.println("Loading from: " + ent + " (" + buff.length + " bytes)");
+
+        Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass",
+                                                                 buff.getClass(), int.class, int.class);
+        defineClass.setAccessible(true);
+        Class cls = (Class)defineClass.invoke(loader, buff, new Integer(0), new Integer(buff.length));
+
+        //System.out.println("Loaded : " + cls);
+        return cls;
+    }
+
+    public static byte[] getClassFileFromJar(File jarFile, String className) throws FileNotFoundException, IOException {
+        JarFile jf = new JarFile(jarFile);
+        JarEntry ent = jf.getJarEntry(className.replace('.', '/') + ".class");
+
+        DataInputStream dis = new DataInputStream(jf.getInputStream(ent));
+        byte[] buff = new byte[(int)ent.getSize()];
+        dis.readFully(buff);
+        dis.close();
+
+        return buff;
+    }
+}
diff --git a/test/hotspot/jtreg/runtime/appcds/test-classes/VerifierTest0.java b/test/hotspot/jtreg/runtime/appcds/test-classes/VerifierTest0.java
new file mode 100644
index 00000000000..ec60232a229
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/test-classes/VerifierTest0.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 2014, 2017, 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.
+ *
+ */
+
+/*
+ * Note: verifier_test_tmp.jar will be processed by ../AppCDSVerifierTest.java to
+ * create verifier_test.jar, which contains invalid versions of UnverifiableBase
+ * and UnverifiableIntf.
+ */
+
+public class VerifierTest0 {
+  public static void main(String args[]) {
+    boolean good = true;
+    good &= mustBeInvalid("VerifierTestA");
+    good &= mustBeInvalid("VerifierTestB");
+    good &= mustBeInvalid("VerifierTestC");
+    good &= mustBeInvalid("VerifierTestD");
+    good &= mustBeInvalid("VerifierTestE");
+    if (!good) {
+      System.out.println("VerifierTest0 failed");
+      System.exit(1);
+    }
+  }
+
+  /** @return false means error */
+  static boolean mustBeInvalid(String className) {
+    System.out.println("Testing: " + className);
+    try {
+      Class.forName(className);
+      System.out.println("ERROR: class " + className + " was loaded unexpectedly.");
+      return false;
+    } catch (Throwable t) {
+      System.out.println("Expected exception:");
+      t.printStackTrace();
+      return true;
+    }
+  }
+}
+
+class UnverifiableBase {
+  static final VerifierTest0 x = new VerifierTest0(); // <- this static initializer will be made unverifiable by type mismatch
+}
+
+interface UnverifiableIntf {
+  static final VerifierTest0 x = new VerifierTest0(); // <- this static initializer will be made unverifiable by type mismatch
+}
+
+interface UnverifiableIntfSub extends UnverifiableIntf {}
+
+class VerifierTestA extends    UnverifiableBase {}
+class VerifierTestB extends    VerifierTestA {}
+class VerifierTestC implements UnverifiableIntf {}
+class VerifierTestD extends    VerifierTestC {}
+class VerifierTestE implements UnverifiableIntfSub {}
+
diff --git a/test/hotspot/jtreg/runtime/appcds/test-classes/com/sun/tools/javac/Main.jasm b/test/hotspot/jtreg/runtime/appcds/test-classes/com/sun/tools/javac/Main.jasm
new file mode 100644
index 00000000000..aa70583fd4b
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/test-classes/com/sun/tools/javac/Main.jasm
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2016, 2017, 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 com/sun/tools/javac;
+
+public class Main
+    version 51:0
+{
+} // end class Main
diff --git a/test/hotspot/jtreg/runtime/appcds/test-classes/cpattr1.mf b/test/hotspot/jtreg/runtime/appcds/test-classes/cpattr1.mf
new file mode 100644
index 00000000000..79b4c2dd5ab
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/test-classes/cpattr1.mf
@@ -0,0 +1,3 @@
+Manifest-Version: 1.0
+Class-Path: cpattr2.jar
+Created-By: 1.9.0-internal (Oracle Corporation)
diff --git a/test/hotspot/jtreg/runtime/appcds/test-classes/cpattr1_long.mf b/test/hotspot/jtreg/runtime/appcds/test-classes/cpattr1_long.mf
new file mode 100644
index 00000000000..923535d8ab4
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/test-classes/cpattr1_long.mf
@@ -0,0 +1,18 @@
+Manifest-Version: 1.0
+Class-Path: zzzzzzz.jar zzzzzzz.jar zzzzzzz.jar zzzzzzz.jar zzzzzzz.ja
+ r zzzzzzz.jar zzzzzzz.jar zzzzzzz.jar zzzzzzz.jar zzzzzzz.jar zzzzzzz
+ .jar xx.jar zzzzzzz.jar zzzzzzz.jar zzzzzzz.jar zzzzzzz.jar zzzzzzz.j
+ ar zzzzzzz.jar xx.jar zzzzzzz.jar zzzzzzz.jar zzzzzzz.jar zzzzzzz.jar
+  zzzzzzz.jar zzzzzzz.jar xx.jar zzzzzzz.jar zzzzzzz.jar zzzzzzz.jar z
+ zzzzzz.jar zzzzzzz.jar zzzzzzz.jar xx.jar zzzzzzz.jar zzzzzzz.jar zzz
+ zzzz.jar zzzzzzz.jar zzzzzzz.jar zzzzzzz.jar xx.jar zzzzzzz.jar zzzzz
+ zz.jar zzzzzzz.jar zzzzzzz.jar zzzzzzz.jar zzzzzzz.jar xx.jar zzzzzzz
+ .jar zzzzzzz.jar zzzzzzz.jar zzzzzzz.jar zzzzzzz.jar zzzzzzz.jar xx.j
+ ar zzzzzzz.jar zzzzzzz.jar zzzzzzz.jar zzzzzzz.jar zzzzzzz.jar zzzzzz
+ z.jar xx.jar zzzzzzz.jar zzzzzzz.jar zzzzzzz.jar zzzzzzz.jar zzzzzzz.
+ jar zzzzzzz.jar xx.jar zzzzzzz.jar zzzzzzz.jar zzzzzzz.jar zzzzzzz.ja
+ r zzzzzzz.jar zzzzzzz.jar xx.jar zzzzzzz.jar zzzzzzz.jar zzzzzzz.jar 
+ zzzzzzz.jar zzzzzzz.jar zzzzzzz.jar xx.jar cpattr2.jar cpattr1_long.j
+ ar
+Created-By: 1.9.0-internal (Oracle Corporation)
+
diff --git a/test/hotspot/jtreg/runtime/appcds/test-classes/cpattr2.mf b/test/hotspot/jtreg/runtime/appcds/test-classes/cpattr2.mf
new file mode 100644
index 00000000000..e638d1cec10
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/test-classes/cpattr2.mf
@@ -0,0 +1,4 @@
+Manifest-Version: 1.0
+Class-Path: cpattr3.jar cpattr5_123456789_223456789_323456789_42345678
+ 9_523456789_623456789.jar
+Created-By: 1.9.0-internal (Oracle Corporation)
diff --git a/test/hotspot/jtreg/runtime/appcds/test-classes/cpattr3.mf b/test/hotspot/jtreg/runtime/appcds/test-classes/cpattr3.mf
new file mode 100644
index 00000000000..8cf6b52433f
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/test-classes/cpattr3.mf
@@ -0,0 +1,2 @@
+Manifest-Version: 1.0
+Created-By: 1.9.0-internal (Oracle Corporation)
diff --git a/test/hotspot/jtreg/runtime/appcds/test-classes/cpattr4.mf b/test/hotspot/jtreg/runtime/appcds/test-classes/cpattr4.mf
new file mode 100644
index 00000000000..8cf6b52433f
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/test-classes/cpattr4.mf
@@ -0,0 +1,2 @@
+Manifest-Version: 1.0
+Created-By: 1.9.0-internal (Oracle Corporation)
diff --git a/test/hotspot/jtreg/runtime/appcds/test-classes/cpattr5_extra_long.mf b/test/hotspot/jtreg/runtime/appcds/test-classes/cpattr5_extra_long.mf
new file mode 100644
index 00000000000..30d6d40f684
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/test-classes/cpattr5_extra_long.mf
@@ -0,0 +1,3 @@
+Manifest-Version: 1.0
+Created-By: 1.9.0-internal (Oracle Corporation)
+
diff --git a/test/hotspot/jtreg/runtime/appcds/test-classes/java/net/HttpCookie.jasm b/test/hotspot/jtreg/runtime/appcds/test-classes/java/net/HttpCookie.jasm
new file mode 100644
index 00000000000..6ef9b92f41f
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/test-classes/java/net/HttpCookie.jasm
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2016, 2017, 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 java/net;
+
+public class HttpCookie
+    version 51:0
+{
+
+public Method thisClassIsDummy:"()V"
+    stack 0 locals 0
+{
+    return;
+}
+
+} // end class HttpCookie
diff --git a/test/hotspot/jtreg/runtime/appcds/test-classes/javax/activation/MimeType.jasm b/test/hotspot/jtreg/runtime/appcds/test-classes/javax/activation/MimeType.jasm
new file mode 100644
index 00000000000..1436fe9b4ab
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/test-classes/javax/activation/MimeType.jasm
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2016, 2017, 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 javax/activation;
+
+public class MimeType
+    version 51:0
+{
+
+public Method thisClassIsDummy:"()V"
+    stack 0 locals 0
+{
+    return;
+}
+
+} // end class MimeType
diff --git a/test/hotspot/jtreg/runtime/appcds/test-classes/javax/transaction/InvalidTransactionException.jasm b/test/hotspot/jtreg/runtime/appcds/test-classes/javax/transaction/InvalidTransactionException.jasm
new file mode 100644
index 00000000000..5f2683903da
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/test-classes/javax/transaction/InvalidTransactionException.jasm
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2016, 2017, 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 javax/transaction;
+
+public class InvalidTransactionException
+    version 51:0
+{
+
+public Method thisClassIsDummy:"()V"
+    stack 0 locals 0
+{
+    return;
+}
+
+} // end class InvalidTransactionException
diff --git a/test/hotspot/jtreg/runtime/appcds/test-classes/jdk/dynalink/DynamicLinker.jasm b/test/hotspot/jtreg/runtime/appcds/test-classes/jdk/dynalink/DynamicLinker.jasm
new file mode 100644
index 00000000000..88544dc74de
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/test-classes/jdk/dynalink/DynamicLinker.jasm
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2016, 2017, 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 jdk/dynalink;
+
+public class DynamicLinker
+    version 51:0
+{
+
+public Method thisClassIsDummy:"()V"
+    stack 0 locals 2
+{
+    return;
+}
+
+} // end class DynamicLinker
diff --git a/test/hotspot/jtreg/runtime/appcds/test-classes/package_seal.mf b/test/hotspot/jtreg/runtime/appcds/test-classes/package_seal.mf
new file mode 100644
index 00000000000..6e43a3f801b
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/appcds/test-classes/package_seal.mf
@@ -0,0 +1,6 @@
+Manifest-Version: 1.0
+Created-By: 1.9.0-internal (Oracle Corporation)
+
+Name: sealed/pkg/
+Sealed: true
+
diff --git a/test/hotspot/jtreg/runtime/classFileParserBug/Class54.jasm b/test/hotspot/jtreg/runtime/classFileParserBug/Class54.jasm
new file mode 100644
index 00000000000..3150d5dddf4
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/classFileParserBug/Class54.jasm
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug 8188870
+ * @summary Check that the JVM accepts class files with version 54
+ * @run main Class54
+ */
+
+super public class Class54 version 54:0 {
+
+    public Method "<init>":"()V" stack 1 locals 1 {
+        aload_0;
+        invokespecial    Method java/lang/Object."<init>":"()V";
+        return;
+    }
+
+    public static Method main:"([Ljava/lang/String;)V" stack 0 locals 1 {
+        return;
+    }
+
+} // end Class Class54
diff --git a/test/hotspot/jtreg/runtime/containers/docker/TestCPUAwareness.java b/test/hotspot/jtreg/runtime/containers/docker/TestCPUAwareness.java
index 66768266aa7..3a4e2bc183f 100644
--- a/test/hotspot/jtreg/runtime/containers/docker/TestCPUAwareness.java
+++ b/test/hotspot/jtreg/runtime/containers/docker/TestCPUAwareness.java
@@ -33,28 +33,28 @@
  * @build Common
  * @run driver TestCPUAwareness
  */
+import java.util.List;
 import jdk.test.lib.containers.docker.DockerRunOptions;
 import jdk.test.lib.containers.docker.DockerTestUtils;
 
 
 public class TestCPUAwareness {
     private static final String imageName = Common.imageName("cpu");
+    private static final int availableCPUs = Runtime.getRuntime().availableProcessors();
 
     public static void main(String[] args) throws Exception {
         if (!DockerTestUtils.canTestDocker()) {
             return;
         }
 
-        int availableCPUs = Runtime.getRuntime().availableProcessors();
         System.out.println("Test Environment: detected availableCPUs = " + availableCPUs);
         DockerTestUtils.buildJdkDockerImage(imageName, "Dockerfile-BasicTest", "jdk-docker");
 
         try {
             // cpuset, period, shares, expected Active Processor Count
-            testAPCCombo("0", 200*1000, 100*1000,   4*1024, 1);
-            testAPCCombo("0,1", 200*1000, 100*1000, 4*1024, 2);
-            testAPCCombo("0,1", 200*1000, 100*1000, 1*1024, 2);
+            testComboWithCpuSets();
 
+            // cpu shares - it should be safe to use CPU shares exceeding available CPUs
             testCpuShares(256, 1);
             testCpuShares(2048, 2);
             testCpuShares(4096, 4);
@@ -70,9 +70,11 @@ public class TestCPUAwareness {
             testActiveProcessorCount(1, 1);
             testActiveProcessorCount(2, 2);
 
+            // cpu quota and period
             testCpuQuotaAndPeriod(50*1000, 100*1000);
             testCpuQuotaAndPeriod(100*1000, 100*1000);
             testCpuQuotaAndPeriod(150*1000, 100*1000);
+            testCpuQuotaAndPeriod(400*1000, 100*1000);
 
         } finally {
             DockerTestUtils.removeDockerImage(imageName);
@@ -80,6 +82,31 @@ public class TestCPUAwareness {
     }
 
 
+    private static void testComboWithCpuSets() throws Exception {
+        String cpuSetStr = CPUSetsReader.readFromProcStatus("Cpus_allowed_list");
+        System.out.println("cpuSetStr = " + cpuSetStr);
+
+        if (cpuSetStr == null) {
+            System.out.printf("The cpuset test cases are skipped");
+        } else {
+            List<Integer> cpuSet = CPUSetsReader.parseCpuSet(cpuSetStr);
+
+            // Test subset of cpuset with one element
+            if (cpuSet.size() >= 1) {
+                String testCpuSet = CPUSetsReader.listToString(cpuSet, 1);
+                testAPCCombo(testCpuSet, 200*1000, 100*1000,   4*1024, 1);
+            }
+
+            // Test subset of cpuset with two elements
+            if (cpuSet.size() >= 2) {
+                String testCpuSet = CPUSetsReader.listToString(cpuSet, 2);
+                testAPCCombo(testCpuSet, 200*1000, 100*1000, 4*1024, 2);
+                testAPCCombo(testCpuSet, 200*1000, 100*1000, 1*1024, 2);
+            }
+        }
+    }
+
+
     private static void testActiveProcessorCount(int valueToSet, int expectedValue) throws Exception {
         Common.logNewTestCase("Test ActiveProcessorCount: valueToSet = " + valueToSet);
 
@@ -99,6 +126,16 @@ public class TestCPUAwareness {
     }
 
 
+    // Expected active processor count can not exceed available CPU count
+    private static int adjustExpectedAPCForAvailableCPUs(int expectedAPC) {
+        if (expectedAPC > availableCPUs) {
+            expectedAPC = availableCPUs;
+            System.out.println("Adjusted expectedAPC = " + expectedAPC);
+        }
+        return expectedAPC;
+    }
+
+
     private static void testCpuQuotaAndPeriod(int quota, int period)
         throws Exception {
         Common.logNewTestCase("test cpu quota and period: ");
@@ -107,6 +144,7 @@ public class TestCPUAwareness {
 
         int expectedAPC = (int) Math.ceil((float) quota / (float) period);
         System.out.println("expectedAPC = " + expectedAPC);
+        expectedAPC = adjustExpectedAPCForAvailableCPUs(expectedAPC);
 
         DockerRunOptions opts = Common.newOpts(imageName)
             .addDockerOpts("--cpu-period=" + period)
@@ -129,6 +167,8 @@ public class TestCPUAwareness {
         System.out.println("shares = " + period);
         System.out.println("expectedAPC = " + expectedAPC);
 
+        expectedAPC = adjustExpectedAPCForAvailableCPUs(expectedAPC);
+
         DockerRunOptions opts = Common.newOpts(imageName)
             .addDockerOpts("--cpuset-cpus", "" + cpuset)
             .addDockerOpts("--cpu-period=" + period)
@@ -141,6 +181,10 @@ public class TestCPUAwareness {
 
     private static void testCpuShares(int shares, int expectedAPC) throws Exception {
         Common.logNewTestCase("test cpu shares, shares = " + shares);
+        System.out.println("expectedAPC = " + expectedAPC);
+
+        expectedAPC = adjustExpectedAPCForAvailableCPUs(expectedAPC);
+
         DockerRunOptions opts = Common.newOpts(imageName)
             .addDockerOpts("--cpu-shares=" + shares);
         Common.run(opts)
diff --git a/test/hotspot/jtreg/runtime/handshake/HandshakeTransitionTest.java b/test/hotspot/jtreg/runtime/handshake/HandshakeTransitionTest.java
index 4c9194e2504..3bcac9bbdf1 100644
--- a/test/hotspot/jtreg/runtime/handshake/HandshakeTransitionTest.java
+++ b/test/hotspot/jtreg/runtime/handshake/HandshakeTransitionTest.java
@@ -29,13 +29,17 @@ import java.time.Duration;
 import jdk.test.lib.process.ProcessTools;
 import jdk.test.lib.process.OutputAnalyzer;
 
+import sun.hotspot.WhiteBox;
+
 /*
  * @test HandshakeTransitionTest
  * @summary This does a sanity test of the poll in the native wrapper.
  * @requires vm.debug
  * @library /testlibrary /test/lib
  * @build HandshakeTransitionTest
- * @run main/native HandshakeTransitionTest
+ * @run main ClassFileInstaller sun.hotspot.WhiteBox
+ *                              sun.hotspot.WhiteBox$WhiteBoxPermission
+ * @run main/othervm/native -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI HandshakeTransitionTest
  */
 
 public class HandshakeTransitionTest {
@@ -44,6 +48,7 @@ public class HandshakeTransitionTest {
 
     public static void main(String[] args) throws Exception {
         String lib = System.getProperty("test.nativepath");
+        WhiteBox wb = WhiteBox.getWhiteBox();
         ProcessBuilder pb =
             ProcessTools.createJavaProcessBuilder(
                     true,
@@ -54,6 +59,8 @@ public class HandshakeTransitionTest {
                     "-XX:ParallelGCThreads=1",
                     "-XX:ConcGCThreads=1",
                     "-XX:CICompilerCount=2",
+                    "-XX:+UnlockExperimentalVMOptions",
+                    (wb.getBooleanVMFlag("UseJVMCICompiler") ?  "-XX:+UseJVMCICompiler" : "-XX:-UseJVMCICompiler"),
                     "HandshakeTransitionTest$Test");
 
 
diff --git a/test/hotspot/jtreg/runtime/handshake/HandshakeWalkExitTest.java b/test/hotspot/jtreg/runtime/handshake/HandshakeWalkExitTest.java
index 1da94483cf8..6a6b16b9e91 100644
--- a/test/hotspot/jtreg/runtime/handshake/HandshakeWalkExitTest.java
+++ b/test/hotspot/jtreg/runtime/handshake/HandshakeWalkExitTest.java
@@ -42,22 +42,18 @@ public class HandshakeWalkExitTest  implements Runnable {
     }
 
     static volatile boolean exit_now = false;
-    static Thread[] threads;
 
     public static void main(String... args) throws Exception {
-        int testRuns = 100;
-        int testThreads = 500;
+        int testRuns = 20;
+        int testThreads = 128;
 
         HandshakeWalkExitTest test = new HandshakeWalkExitTest();
 
-        threads = new Thread[64];
-
         Runnable hser = new Runnable(){
             public void run(){
                 WhiteBox wb = WhiteBox.getWhiteBox();
                 while(!exit_now) {
                     wb.handshakeWalkStack(null, true);
-                    try { Thread.sleep(1); } catch(Exception e) {}
                 }
             }
         };
diff --git a/test/hotspot/jtreg/runtime/handshake/HandshakeWalkOneExitTest.java b/test/hotspot/jtreg/runtime/handshake/HandshakeWalkOneExitTest.java
new file mode 100644
index 00000000000..45af7e0b95f
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/handshake/HandshakeWalkOneExitTest.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+/*
+ * @test HandshakeWalkOneExitTest
+ * @summary This test tries to stress the handshakes with new and exiting threads
+ * @library /testlibrary /test/lib
+ * @build HandshakeWalkOneExitTest
+ * @run main ClassFileInstaller sun.hotspot.WhiteBox
+ *                              sun.hotspot.WhiteBox$WhiteBoxPermission
+ * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI HandshakeWalkOneExitTest
+ */
+
+import jdk.test.lib.Asserts;
+import sun.hotspot.WhiteBox;
+
+public class HandshakeWalkOneExitTest  implements Runnable {
+
+    @Override
+    public void run() {
+    }
+
+    static volatile boolean exit_now = false;
+    static Thread[] threads;
+
+    public static void main(String... args) throws Exception {
+        int testRuns = 20;
+        int testThreads = 128;
+
+        HandshakeWalkOneExitTest test = new HandshakeWalkOneExitTest();
+
+        Runnable hser = new Runnable(){
+            public void run(){
+                WhiteBox wb = WhiteBox.getWhiteBox();
+                while(!exit_now) {
+                    Thread[] t = threads;
+                    for (int i = 0; i<t.length ; i++) {
+                        wb.handshakeWalkStack(t[i], false);
+                    }
+                }
+            }
+        };
+        Thread hst = new Thread(hser);
+        for (int k = 0; k<testRuns ; k++) {
+            threads = new Thread[testThreads];
+            for (int i = 0; i<threads.length ; i++) {
+                threads[i] = new Thread(test);
+                threads[i].start();
+            }
+            if (k == 0) {
+                hst.start();
+            }
+        }
+        exit_now = true;
+        hst.join();
+    }
+}
diff --git a/test/hotspot/jtreg/serviceability/dcmd/jvmti/AttachFailed/AttachFailedTestBase.java b/test/hotspot/jtreg/serviceability/dcmd/jvmti/AttachFailed/AttachFailedTestBase.java
new file mode 100644
index 00000000000..017c7654cfd
--- /dev/null
+++ b/test/hotspot/jtreg/serviceability/dcmd/jvmti/AttachFailed/AttachFailedTestBase.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+import java.nio.file.Paths;
+import jdk.test.lib.dcmd.*;
+import jdk.test.lib.Platform;
+import org.testng.annotations.Test;
+
+public abstract class AttachFailedTestBase {
+
+    public abstract void run(CommandExecutor executor);
+
+    /**
+     * Build path to shared object according to platform rules
+     */
+    public static String getSharedObjectPath(String name) {
+        String libname;
+        if (Platform.isWindows()) {
+            libname = name + ".dll";
+        } else if (Platform.isOSX()) {
+            libname = "lib" + name + ".dylib";
+        } else {
+            libname = "lib" + name + ".so";
+        }
+
+        return Paths.get(System.getProperty("test.nativepath"), libname)
+                    .toAbsolutePath()
+                    .toString();
+    }
+
+    @Test
+    public void jmx() throws Throwable {
+        run(new JMXExecutor());
+    }
+
+    @Test
+    public void cli() throws Throwable {
+        run(new PidJcmdExecutor());
+    }
+}
diff --git a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/windbg/ia64/WindbgIA64ThreadContext.java b/test/hotspot/jtreg/serviceability/dcmd/jvmti/AttachFailed/AttachIncorrectLibrary.java
similarity index 59%
rename from src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/windbg/ia64/WindbgIA64ThreadContext.java
rename to test/hotspot/jtreg/serviceability/dcmd/jvmti/AttachFailed/AttachIncorrectLibrary.java
index ba739808002..bc83dece0f2 100644
--- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/debugger/windbg/ia64/WindbgIA64ThreadContext.java
+++ b/test/hotspot/jtreg/serviceability/dcmd/jvmti/AttachFailed/AttachIncorrectLibrary.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2003, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2017, 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
@@ -19,28 +19,25 @@
  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  * or visit www.oracle.com if you need additional information or have any
  * questions.
- *
  */
+import jdk.test.lib.dcmd.*;
+import jdk.test.lib.process.OutputAnalyzer;
 
-package sun.jvm.hotspot.debugger.windbg.ia64;
-
-import sun.jvm.hotspot.debugger.*;
-import sun.jvm.hotspot.debugger.ia64.*;
-import sun.jvm.hotspot.debugger.windbg.*;
-
-class WindbgIA64ThreadContext extends IA64ThreadContext {
-  private WindbgDebugger debugger;
-
-  public WindbgIA64ThreadContext(WindbgDebugger debugger) {
-    super();
-    this.debugger = debugger;
-  }
-
-  public void setRegisterAsAddress(int index, Address value) {
-    setRegister(index, debugger.getAddressValue(value));
-  }
-
-  public Address getRegisterAsAddress(int index) {
-    return debugger.newAddress(getRegister(index));
-  }
+/*
+ * @test
+ * @bug 8165736
+ * @library /test/lib
+ * @run testng AttachIncorrectLibrary
+ */
+public class AttachIncorrectLibrary extends AttachFailedTestBase {
+    @Override
+    public void run(CommandExecutor executor)  {
+        try {
+            OutputAnalyzer output = executor.execute("JVMTI.agent_load " +
+                                           getSharedObjectPath("SilverBullet"));
+            output.shouldContain(" was not loaded");
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
 }
diff --git a/test/hotspot/jtreg/serviceability/dcmd/jvmti/AttachFailed/AttachNoEntry.java b/test/hotspot/jtreg/serviceability/dcmd/jvmti/AttachFailed/AttachNoEntry.java
new file mode 100644
index 00000000000..3920bb3691e
--- /dev/null
+++ b/test/hotspot/jtreg/serviceability/dcmd/jvmti/AttachFailed/AttachNoEntry.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+import jdk.test.lib.dcmd.*;
+import jdk.test.lib.process.OutputAnalyzer;
+
+/*
+ * @test
+ * @bug 8165736
+ * @library /test/lib
+ * @run testng AttachNoEntry
+ */
+public class AttachNoEntry extends AttachFailedTestBase {
+    @Override
+    public void run(CommandExecutor executor)  {
+        try {
+            String libpath = getSharedObjectPath("HasNoEntryPoint");
+            OutputAnalyzer output = null;
+
+            output = executor.execute("JVMTI.agent_load " + libpath);
+            output.shouldContain("Agent_OnAttach");
+            output.shouldContain("is not available");
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+}
diff --git a/test/hotspot/jtreg/serviceability/dcmd/jvmti/AttachFailed/AttachReturnError.java b/test/hotspot/jtreg/serviceability/dcmd/jvmti/AttachFailed/AttachReturnError.java
new file mode 100644
index 00000000000..ad1772de1a6
--- /dev/null
+++ b/test/hotspot/jtreg/serviceability/dcmd/jvmti/AttachFailed/AttachReturnError.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+import jdk.test.lib.dcmd.*;
+import jdk.test.lib.process.OutputAnalyzer;
+
+/*
+ * @test
+ * @bug 8165736
+ * @library /test/lib
+ * @run testng AttachReturnError
+ */
+public class AttachReturnError extends AttachFailedTestBase {
+    @Override
+    public void run(CommandExecutor executor)  {
+        try {
+            String libpath = getSharedObjectPath("ReturnError");
+            OutputAnalyzer output = null;
+
+            output = executor.execute("JVMTI.agent_load " + libpath);
+            output.shouldContain("return code: -1");
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+}
diff --git a/test/langtools/jdk/javadoc/doclet/testGroupOption/C.java b/test/hotspot/jtreg/serviceability/dcmd/jvmti/AttachFailed/libHasNoEntryPoint.c
similarity index 90%
rename from test/langtools/jdk/javadoc/doclet/testGroupOption/C.java
rename to test/hotspot/jtreg/serviceability/dcmd/jvmti/AttachFailed/libHasNoEntryPoint.c
index 38238973f8d..1a945c6ccce 100644
--- a/test/langtools/jdk/javadoc/doclet/testGroupOption/C.java
+++ b/test/hotspot/jtreg/serviceability/dcmd/jvmti/AttachFailed/libHasNoEntryPoint.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2017, 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
@@ -21,4 +21,6 @@
  * questions.
  */
 
-public class C {}
+extern int dummy() {
+  return 0;
+}
diff --git a/test/hotspot/jtreg/serviceability/dcmd/jvmti/AttachFailed/libReturnError.c b/test/hotspot/jtreg/serviceability/dcmd/jvmti/AttachFailed/libReturnError.c
new file mode 100644
index 00000000000..ce9eb216882
--- /dev/null
+++ b/test/hotspot/jtreg/serviceability/dcmd/jvmti/AttachFailed/libReturnError.c
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#include <jni.h>
+#include <jvmti.h>
+
+JNIEXPORT
+jint JNICALL Agent_OnAttach(JavaVM *vm, char *options, void *reserved) {
+  return JNI_ERR;
+}
diff --git a/test/hotspot/jtreg/serviceability/sa/ClhsdbAttach.java b/test/hotspot/jtreg/serviceability/sa/ClhsdbAttach.java
new file mode 100644
index 00000000000..32095655640
--- /dev/null
+++ b/test/hotspot/jtreg/serviceability/sa/ClhsdbAttach.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import jdk.test.lib.apps.LingeredApp;
+
+/*
+ * @test
+ * @bug 8191658
+ * @summary Test clhsdb attach, detach, reattach commands
+ * @library /test/lib
+ * @run main/othervm ClhsdbAttach
+ */
+
+public class ClhsdbAttach {
+
+    public static void main(String[] args) throws Exception {
+        System.out.println("Starting ClhsdbAttach test");
+
+        LingeredApp theApp = null;
+        try {
+            ClhsdbLauncher test = new ClhsdbLauncher();
+            theApp = LingeredApp.startApp();
+            System.out.println("Started LingeredApp with pid " + theApp.getPid());
+            String attach = "attach " + theApp.getPid();
+
+            List<String> cmds = List.of(
+                    "where",
+                    attach,
+                    "flags MaxJavaStackTraceDepth",
+                    "detach",
+                    "universe",
+                    "reattach",
+                    "longConstant markOopDesc::locked_value");
+
+            Map<String, List<String>> expStrMap = new HashMap<>();
+            expStrMap.put("where", List.of(
+                    "Command not valid until attached to a VM"));
+            expStrMap.put("flags MaxJavaStackTraceDepth", List.of(
+                    "MaxJavaStackTraceDepth = "));
+            expStrMap.put("universe", List.of(
+                    "Command not valid until attached to a VM"));
+            expStrMap.put("longConstant markOopDesc::locked_value", List.of(
+                    "longConstant markOopDesc::locked_value"));
+
+            test.run(-1, cmds, expStrMap, null);
+        } catch (Exception ex) {
+            throw new RuntimeException("Test ERROR " + ex, ex);
+        } finally {
+            LingeredApp.stopApp(theApp);
+        }
+        System.out.println("Test PASSED");
+    }
+}
diff --git a/test/hotspot/jtreg/serviceability/sa/ClhsdbField.java b/test/hotspot/jtreg/serviceability/sa/ClhsdbField.java
new file mode 100644
index 00000000000..280c0e64e4f
--- /dev/null
+++ b/test/hotspot/jtreg/serviceability/sa/ClhsdbField.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import jdk.test.lib.apps.LingeredApp;
+
+/*
+ * @test
+ * @bug 8191538
+ * @summary Test clhsdb 'field' command
+ * @library /test/lib
+ * @run main/othervm ClhsdbField
+ */
+
+public class ClhsdbField {
+
+    public static void main(String[] args) throws Exception {
+        System.out.println("Starting ClhsdbField test");
+
+        LingeredApp theApp = null;
+        try {
+            ClhsdbLauncher test = new ClhsdbLauncher();
+            theApp = LingeredApp.startApp();
+            System.out.println("Started LingeredApp with pid " + theApp.getPid());
+
+            List<String> cmds = List.of("field");
+
+            Map<String, List<String>> expStrMap = new HashMap<>();
+            expStrMap.put("field", List.of(
+                "field ConstantPool _pool_holder InstanceKlass*",
+                "field InstanceKlass _methods Array<Method*>*",
+                "field InstanceKlass _constants ConstantPool*",
+                "field Klass _name Symbol*",
+                "field JavaThread _next JavaThread*",
+                "field JavaThread _osthread OSThread*",
+                "field JVMState _bci",
+                "field TenuredGeneration _the_space ContiguousSpace*",
+                "field VirtualSpace _low_boundary char*",
+                "field MethodCounters _backedge_counter InvocationCounter",
+                "field nmethod _entry_bci int",
+                "field Universe _collectedHeap CollectedHeap"));
+            test.run(theApp.getPid(), cmds, expStrMap, null);
+        } catch (Exception ex) {
+            throw new RuntimeException("Test ERROR " + ex, ex);
+        } finally {
+            LingeredApp.stopApp(theApp);
+        }
+        System.out.println("Test PASSED");
+    }
+}
diff --git a/test/hotspot/jtreg/serviceability/sa/ClhsdbJhisto.java b/test/hotspot/jtreg/serviceability/sa/ClhsdbJhisto.java
new file mode 100644
index 00000000000..76e32a62479
--- /dev/null
+++ b/test/hotspot/jtreg/serviceability/sa/ClhsdbJhisto.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import jdk.test.lib.apps.LingeredApp;
+import jdk.test.lib.Utils;
+
+/*
+ * @test
+ * @bug 8191658
+ * @summary Test clhsdb jhisto command
+ * @library /test/lib
+ * @run main/othervm ClhsdbJhisto
+ */
+
+public class ClhsdbJhisto {
+
+    public static void main(String[] args) throws Exception {
+        System.out.println("Starting ClhsdbJhisto test");
+
+        LingeredAppWithInterface theApp = null;
+        try {
+            ClhsdbLauncher test = new ClhsdbLauncher();
+            List<String> vmArgs = new ArrayList<String>();
+            vmArgs.addAll(Utils.getVmOptions());
+
+            theApp = new LingeredAppWithInterface();
+            LingeredApp.startApp(vmArgs, theApp);
+            System.out.println("Started LingeredApp with pid " + theApp.getPid());
+
+            List<String> cmds = List.of("jhisto");
+
+            Map<String, List<String>> expStrMap = new HashMap<>();
+            expStrMap.put("jhisto", List.of(
+                    "java.lang.String",
+                    "java.util.HashMap",
+                    "java.lang.Class",
+                    "java.nio.HeapByteBuffer",
+                    "java.net.URI",
+                    "LingeredAppWithInterface",
+                    "ParselTongue",
+                    "ImmutableCollections$SetN$1"));
+
+            test.run(theApp.getPid(), cmds, expStrMap, null);
+        } catch (Exception ex) {
+            throw new RuntimeException("Test ERROR " + ex, ex);
+        } finally {
+            LingeredApp.stopApp(theApp);
+        }
+        System.out.println("Test PASSED");
+    }
+}
diff --git a/test/hotspot/jtreg/serviceability/sa/ClhsdbLauncher.java b/test/hotspot/jtreg/serviceability/sa/ClhsdbLauncher.java
index 5366be6f826..2d6ccfd3455 100644
--- a/test/hotspot/jtreg/serviceability/sa/ClhsdbLauncher.java
+++ b/test/hotspot/jtreg/serviceability/sa/ClhsdbLauncher.java
@@ -53,10 +53,12 @@ public class ClhsdbLauncher {
     private void attach(long lingeredAppPid)
         throws IOException {
 
-        System.out.println("Starting clhsdb against " + lingeredAppPid);
         JDKToolLauncher launcher = JDKToolLauncher.createUsingTestJDK("jhsdb");
         launcher.addToolArg("clhsdb");
-        launcher.addToolArg("--pid=" + Long.toString(lingeredAppPid));
+        if (lingeredAppPid != -1) {
+            launcher.addToolArg("--pid=" + Long.toString(lingeredAppPid));
+            System.out.println("Starting clhsdb against " + lingeredAppPid);
+        }
 
         ProcessBuilder processBuilder = new ProcessBuilder(launcher.getCommand());
         processBuilder.redirectError(ProcessBuilder.Redirect.INHERIT);
diff --git a/test/hotspot/jtreg/serviceability/sa/ClhsdbSymbolTable.java b/test/hotspot/jtreg/serviceability/sa/ClhsdbSymbolTable.java
new file mode 100644
index 00000000000..679d83243ac
--- /dev/null
+++ b/test/hotspot/jtreg/serviceability/sa/ClhsdbSymbolTable.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.ArrayList;
+import jdk.test.lib.apps.LingeredApp;
+
+/*
+ * @test
+ * @bug 8191538
+ * @summary Test the clhsdb 'symboltable' and 'symbol' commands
+ * @library /test/lib
+ * @run main/othervm ClhsdbSymbolTable
+ */
+
+public class ClhsdbSymbolTable {
+
+    public static void main(String[] args) throws Exception {
+        System.out.println("Starting the ClhsdbSymbolTable test");
+
+        LingeredApp theApp = null;
+        try {
+            ClhsdbLauncher test = new ClhsdbLauncher();
+
+            theApp = LingeredApp.startApp();
+            System.out.println("Started LingeredApp with pid " + theApp.getPid());
+
+            // Test the symboltable command
+            List<String> cmds = List.of(
+                "symboltable main",
+                "symboltable java/lang/Class",
+                "symboltable java/lang/Object",
+                "symboltable java/lang/String",
+                "symboltable java/util/List",
+                "symboltable jdk/test/lib/apps/LingeredApp");
+
+            Map<String, List<String>> expStrMap = new HashMap<>();
+            expStrMap.put("symboltable main", List.of(
+                "sun.jvm.hotspot.oops.Symbol@"));
+            expStrMap.put("symboltable java/lang/Class", List.of(
+                "sun.jvm.hotspot.oops.Symbol@"));
+            expStrMap.put("symboltable java/lang/Object", List.of(
+                "sun.jvm.hotspot.oops.Symbol@"));
+            expStrMap.put("symboltable java/lang/String", List.of(
+                "sun.jvm.hotspot.oops.Symbol@"));
+            expStrMap.put("symboltable java/util/List", List.of(
+                "sun.jvm.hotspot.oops.Symbol@"));
+            expStrMap.put("symboltable jdk/test/lib/apps/LingeredApp", List.of(
+                "sun.jvm.hotspot.oops.Symbol@"));
+            String consolidatedOutput =
+                test.run(theApp.getPid(), cmds, expStrMap, null);
+
+            // Test the 'symbol' command passing in the address obtained from
+            // the 'symboltable' command
+            expStrMap = new HashMap<>();
+            cmds = new ArrayList<String>();
+            int expectedStringsIdx = 0;
+            String expectedStrings[] = {"#main",
+                                        "#java/lang/Class", "#java/lang/Object",
+                                        "#java/lang/String", "#java/util/List",
+                                        "#jdk/test/lib/apps/LingeredApp"};
+            if (consolidatedOutput != null) {
+                // Output could be null due to attach permission issues
+                // and if we are skipping this.
+                String[] singleCommandOutputs = consolidatedOutput.split("hsdb>");
+
+                for (String singleCommandOutput : singleCommandOutputs) {
+                    if (singleCommandOutput.contains("@")) {
+                        String[] tokens = singleCommandOutput.split("@");
+                        String addressString = tokens[1].replace("\n","");
+
+                        // tokens[1] represents the address of the symbol
+                        String cmd = "symbol " + addressString;
+                        cmds.add(cmd);
+                        expStrMap.put(cmd, List.of
+                            (expectedStrings[expectedStringsIdx++]));
+                    }
+                }
+                test.run(theApp.getPid(), cmds, expStrMap, null);
+            }
+        } catch (Exception ex) {
+            throw new RuntimeException("Test ERROR " + ex, ex);
+        } finally {
+            LingeredApp.stopApp(theApp);
+        }
+        System.out.println("Test PASSED");
+    }
+}
diff --git a/test/hotspot/jtreg/serviceability/sa/ClhsdbVmStructsDump.java b/test/hotspot/jtreg/serviceability/sa/ClhsdbVmStructsDump.java
new file mode 100644
index 00000000000..02e832d885f
--- /dev/null
+++ b/test/hotspot/jtreg/serviceability/sa/ClhsdbVmStructsDump.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import jdk.test.lib.apps.LingeredApp;
+
+/*
+ * @test
+ * @bug 8191538
+ * @summary Test clhsdb 'vmstructsdump' command
+ * @library /test/lib
+ * @run main/othervm ClhsdbVmStructsDump
+ */
+
+public class ClhsdbVmStructsDump {
+
+    public static void main(String[] args) throws Exception {
+        System.out.println("Starting ClhsdbVmStructsDump test");
+
+        LingeredApp theApp = null;
+        try {
+            ClhsdbLauncher test = new ClhsdbLauncher();
+            theApp = LingeredApp.startApp();
+            System.out.println("Started LingeredApp with pid " + theApp.getPid());
+
+            List<String> cmds = List.of("vmstructsdump");
+
+            Map<String, List<String>> expStrMap = new HashMap<>();
+            expStrMap.put("vmstructsdump", List.of(
+                "field ConstantPool _pool_holder InstanceKlass*",
+                "field InstanceKlass _methods Array<Method*>*",
+                "field InstanceKlass _constants ConstantPool*",
+                "field Klass _name Symbol*",
+                "type ClassLoaderData* null",
+                "type DictionaryEntry KlassHashtableEntry",
+                "field JavaThread _next JavaThread*",
+                "field JavaThread _osthread OSThread*",
+                "type TenuredGeneration CardGeneration",
+                "field JVMState _bci",
+                "type Universe null",
+                "type ConstantPoolCache MetaspaceObj"));
+            test.run(theApp.getPid(), cmds, expStrMap, null);
+        } catch (Exception ex) {
+            throw new RuntimeException("Test ERROR " + ex, ex);
+        } finally {
+            LingeredApp.stopApp(theApp);
+        }
+        System.out.println("Test PASSED");
+    }
+}
diff --git a/test/hotspot/jtreg/serviceability/sa/JhsdbThreadInfoTest.java b/test/hotspot/jtreg/serviceability/sa/JhsdbThreadInfoTest.java
index 0c916001288..000e8081993 100644
--- a/test/hotspot/jtreg/serviceability/sa/JhsdbThreadInfoTest.java
+++ b/test/hotspot/jtreg/serviceability/sa/JhsdbThreadInfoTest.java
@@ -56,10 +56,10 @@ public class JhsdbThreadInfoTest {
             pb.command(jhsdbLauncher.getCommand());
             Process jhsdb = pb.start();
 
-            jhsdb.waitFor();
-
             OutputAnalyzer out = new OutputAnalyzer(jhsdb);
 
+            jhsdb.waitFor();
+
             System.out.println(out.getStdout());
             System.err.println(out.getStderr());
 
diff --git a/test/hotspot/jtreg/serviceability/sa/LingeredAppWithLock.java b/test/hotspot/jtreg/serviceability/sa/LingeredAppWithLock.java
new file mode 100644
index 00000000000..9acf9d3bbb9
--- /dev/null
+++ b/test/hotspot/jtreg/serviceability/sa/LingeredAppWithLock.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import jdk.test.lib.apps.LingeredApp;
+
+
+public class LingeredAppWithLock extends LingeredApp {
+
+    public static void lockMethod(Object lock) {
+        synchronized (lock) {
+            try {
+                Thread.sleep(300000);
+            } catch (InterruptedException e) {
+                throw new RuntimeException(e);
+            }
+        }
+    }
+
+    public static void main(String args[]) {
+        Thread classLock1 = new Thread(() -> lockMethod(LingeredAppWithLock.class));
+        Thread classLock2 = new Thread(() -> lockMethod(LingeredAppWithLock.class));
+        Thread objectLock = new Thread(() -> lockMethod(classLock1));
+        Thread primitiveLock = new Thread(() -> lockMethod(int.class));
+
+        classLock1.start();
+        classLock2.start();
+        objectLock.start();
+        primitiveLock.start();
+
+        LingeredApp.main(args);
+    }
+ }
diff --git a/test/hotspot/jtreg/serviceability/sa/TestClassDump.java b/test/hotspot/jtreg/serviceability/sa/TestClassDump.java
new file mode 100644
index 00000000000..cdaa3de6c64
--- /dev/null
+++ b/test/hotspot/jtreg/serviceability/sa/TestClassDump.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+
+import jdk.test.lib.apps.LingeredApp;
+import jdk.test.lib.Platform;
+import jdk.test.lib.process.OutputAnalyzer;
+import jdk.test.lib.process.ProcessTools;
+
+/*
+ * @test
+ * @bug 8184982
+ * @summary Test ClassDump tool
+ * @library /test/lib
+ * @run main/othervm TestClassDump
+ */
+
+public class TestClassDump {
+
+    private static void dumpClass(long lingeredAppPid)
+        throws IOException {
+
+        ProcessBuilder pb;
+        OutputAnalyzer output;
+
+        pb = ProcessTools.createJavaProcessBuilder(
+                "-Dsun.jvm.hotspot.tools.jcore.outputDir=jtreg_classes",
+                "-m", "jdk.hotspot.agent/sun.jvm.hotspot.tools.jcore.ClassDump", String.valueOf(lingeredAppPid));
+        output = new OutputAnalyzer(pb.start());
+        output.shouldHaveExitValue(0);
+        if (!Files.isDirectory(Paths.get("jtreg_classes"))) {
+            throw new RuntimeException("jtreg_classes directory not found");
+        }
+        if (Files.notExists(Paths.get("jtreg_classes", "java", "lang", "Integer.class"))) {
+            throw new RuntimeException("jtreg_classes/java/lang/Integer.class not found");
+        }
+        if (Files.notExists(Paths.get("jtreg_classes", "jdk", "test", "lib", "apps", "LingeredApp.class"))) {
+            throw new RuntimeException("jtreg_classes/jdk/test/lib/apps/LingeredApp.class not found");
+        }
+        if (Files.notExists(Paths.get("jtreg_classes", "sun", "net", "util", "URLUtil.class"))) {
+            throw new RuntimeException("jtreg_classes/sun/net/util/URLUtil.class not found");
+        }
+
+        pb = ProcessTools.createJavaProcessBuilder(
+                "-Dsun.jvm.hotspot.tools.jcore.outputDir=jtreg_classes2",
+                "-Dsun.jvm.hotspot.tools.jcore.PackageNameFilter.pkgList=jdk,sun",
+                "-m", "jdk.hotspot.agent/sun.jvm.hotspot.tools.jcore.ClassDump", String.valueOf(lingeredAppPid));
+        output = new OutputAnalyzer(pb.start());
+        output.shouldHaveExitValue(0);
+        if (Files.exists(Paths.get("jtreg_classes2", "java", "math", "BigInteger.class"))) {
+            throw new RuntimeException("jtreg_classes2/java/math/BigInteger.class not expected");
+        }
+        if (Files.notExists(Paths.get("jtreg_classes2", "sun", "util", "calendar", "BaseCalendar.class"))) {
+            throw new RuntimeException("jtreg_classes2/sun/util/calendar/BaseCalendar.class not found");
+        }
+        if (Files.notExists(Paths.get("jtreg_classes2", "jdk", "internal", "vm", "PostVMInitHook.class"))) {
+            throw new RuntimeException("jtreg_classes2/jdk/internal/vm/PostVMInitHook.class not found");
+        }
+    }
+
+    public static void main(String[] args) throws Exception {
+        if (!Platform.shouldSAAttach()) {
+            // Silently skip the test if we don't have enough permissions to attach
+            System.out.println("SA attach not expected to work - test skipped.");
+            return;
+        }
+
+        LingeredApp theApp = null;
+        try {
+            theApp = LingeredApp.startApp();
+            long pid = theApp.getPid();
+            System.out.println("Started LingeredApp with pid " + pid);
+            dumpClass(pid);
+        } catch (Exception ex) {
+            throw new RuntimeException("Test ERROR " + ex, ex);
+        } finally {
+            LingeredApp.stopApp(theApp);
+        }
+        System.out.println("Test PASSED");
+    }
+}
diff --git a/test/hotspot/jtreg/serviceability/sa/TestClhsdbJstackLock.java b/test/hotspot/jtreg/serviceability/sa/TestClhsdbJstackLock.java
new file mode 100644
index 00000000000..5f9a43f3267
--- /dev/null
+++ b/test/hotspot/jtreg/serviceability/sa/TestClhsdbJstackLock.java
@@ -0,0 +1,165 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.util.ArrayList;
+import java.util.Scanner;
+import java.util.List;
+import java.io.File;
+import java.io.IOException;
+import java.util.stream.Collectors;
+import java.io.OutputStream;
+import jdk.test.lib.apps.LingeredApp;
+import jdk.test.lib.JDKToolLauncher;
+import jdk.test.lib.Platform;
+import jdk.test.lib.process.ProcessTools;
+import jdk.test.lib.process.OutputAnalyzer;
+import jdk.test.lib.Utils;
+import jdk.test.lib.Asserts;
+
+/*
+ * @test
+ * @library /test/lib
+ * @run main/othervm TestClhsdbJstackLock
+ */
+
+public class TestClhsdbJstackLock {
+
+    private static final String JSTACK_OUT_FILE = "jstack_out.txt";
+
+    private static void verifyJStackOutput() throws Exception {
+
+        Exception unexpected = null;
+        File jstackFile = new File(JSTACK_OUT_FILE);
+        Asserts.assertTrue(jstackFile.exists() && jstackFile.isFile(),
+                           "File with jstack output not created: " +
+                           jstackFile.getAbsolutePath());
+        try {
+            Scanner scanner = new Scanner(jstackFile);
+
+            boolean classLockOwnerFound = false;
+            boolean classLockWaiterFound = false;
+            boolean objectLockOwnerFound = false;
+            boolean primitiveLockOwnerFound = false;
+
+            while (scanner.hasNextLine()) {
+                String line = scanner.nextLine();
+                System.out.println(line);
+
+                if (line.contains("missing reason for ")) {
+                    unexpected = new RuntimeException("Unexpected msg: missing reason for ");
+                    break;
+                }
+                if (line.matches("^\\s+- locked <0x[0-9a-f]+> \\(a java\\.lang\\.Class for LingeredAppWithLock\\)$")) {
+                    classLockOwnerFound = true;
+                }
+                if (line.matches("^\\s+- waiting to lock <0x[0-9a-f]+> \\(a java\\.lang\\.Class for LingeredAppWithLock\\)$")) {
+                    classLockWaiterFound = true;
+                }
+                if (line.matches("^\\s+- locked <0x[0-9a-f]+> \\(a java\\.lang\\.Thread\\)$")) {
+                    objectLockOwnerFound = true;
+                }
+                if (line.matches("^\\s+- locked <0x[0-9a-f]+> \\(a java\\.lang\\.Class for int\\)$")) {
+                    primitiveLockOwnerFound = true;
+                }
+            }
+
+            if (!classLockOwnerFound || !classLockWaiterFound ||
+                !objectLockOwnerFound || !primitiveLockOwnerFound) {
+                unexpected = new RuntimeException(
+                      "classLockOwnerFound = " + classLockOwnerFound +
+                      ", classLockWaiterFound = " + classLockWaiterFound +
+                      ", objectLockOwnerFound = " + objectLockOwnerFound +
+                      ", primitiveLockOwnerFound = " + primitiveLockOwnerFound);
+            }
+            if (unexpected != null) {
+                throw unexpected;
+            }
+        } catch (Exception ex) {
+            throw new RuntimeException("Test ERROR " + ex, ex);
+        } finally {
+            jstackFile.delete();
+        }
+    }
+
+    private static void startClhsdbForLock(long lingeredAppPid) throws Exception {
+
+        Process p;
+        JDKToolLauncher launcher = JDKToolLauncher.createUsingTestJDK("jhsdb");
+        launcher.addToolArg("clhsdb");
+        launcher.addToolArg("--pid");
+        launcher.addToolArg(Long.toString(lingeredAppPid));
+
+        ProcessBuilder pb = new ProcessBuilder();
+        pb.command(launcher.getCommand());
+        System.out.println(pb.command().stream().collect(Collectors.joining(" ")));
+
+        try {
+            p = pb.start();
+        } catch (Exception attachE) {
+            throw new Error("Couldn't start jhsdb or attach to LingeredApp : " + attachE);
+        }
+
+        // Issue the 'jstack' input at the clhsdb prompt.
+        OutputStream input = p.getOutputStream();
+        String str = "jstack > " + JSTACK_OUT_FILE + "\nquit\n";
+        try {
+            input.write(str.getBytes());
+            input.flush();
+        } catch (IOException ioe) {
+            throw new Error("Problem issuing the jstack command: " + str, ioe);
+        }
+
+        OutputAnalyzer output = new OutputAnalyzer(p);
+
+        try {
+            p.waitFor();
+        } catch (InterruptedException ie) {
+            p.destroyForcibly();
+            throw new Error("Problem awaiting the child process: " + ie, ie);
+        }
+
+        output.shouldHaveExitValue(0);
+    }
+
+    public static void main (String... args) throws Exception {
+
+        LingeredApp app = null;
+
+        if (!Platform.shouldSAAttach()) {
+            System.out.println("SA attach not expected to work - test skipped.");
+            return;
+        }
+
+        try {
+            List<String> vmArgs = new ArrayList<String>(Utils.getVmOptions());
+
+            app = new LingeredAppWithLock();
+            LingeredApp.startApp(vmArgs, app);
+            System.out.println ("Started LingeredApp with pid " + app.getPid());
+            startClhsdbForLock(app.getPid());
+            verifyJStackOutput();
+        } finally {
+            LingeredApp.stopApp(app);
+        }
+    }
+}
diff --git a/test/hotspot/jtreg/serviceability/sa/TestJhsdbJstackLock.java b/test/hotspot/jtreg/serviceability/sa/TestJhsdbJstackLock.java
new file mode 100644
index 00000000000..739ae29ba0b
--- /dev/null
+++ b/test/hotspot/jtreg/serviceability/sa/TestJhsdbJstackLock.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.List;
+import jdk.test.lib.apps.LingeredApp;
+import jdk.test.lib.Asserts;
+import jdk.test.lib.JDKToolLauncher;
+import jdk.test.lib.Platform;
+import jdk.test.lib.process.OutputAnalyzer;
+import jdk.test.lib.process.ProcessTools;
+import jdk.test.lib.Utils;
+
+/*
+ * @test
+ * @library /test/lib
+ * @run main/othervm TestJhsdbJstackLock
+ */
+
+public class TestJhsdbJstackLock {
+
+    public static void main (String... args) throws Exception {
+
+        LingeredApp app = null;
+
+        if (!Platform.shouldSAAttach()) {
+            System.out.println("SA attach not expected to work - test skipped.");
+            return;
+        }
+
+        try {
+            List<String> vmArgs = new ArrayList<String>(Utils.getVmOptions());
+
+            app = new LingeredAppWithLock();
+            LingeredApp.startApp(vmArgs, app);
+            System.out.println ("Started LingeredApp with pid " + app.getPid());
+
+            JDKToolLauncher launcher = JDKToolLauncher.createUsingTestJDK("jhsdb");
+            launcher.addToolArg("jstack");
+            launcher.addToolArg("--pid");
+            launcher.addToolArg(Long.toString(app.getPid()));
+
+            ProcessBuilder pb = new ProcessBuilder();
+            pb.command(launcher.getCommand());
+            Process jhsdb = pb.start();
+            OutputAnalyzer out = new OutputAnalyzer(jhsdb);
+
+            jhsdb.waitFor();
+
+            System.out.println(out.getStdout());
+            System.err.println(out.getStderr());
+
+            out.shouldMatch("^\\s+- locked <0x[0-9a-f]+> \\(a java\\.lang\\.Class for LingeredAppWithLock\\)$");
+            out.shouldMatch("^\\s+- waiting to lock <0x[0-9a-f]+> \\(a java\\.lang\\.Class for LingeredAppWithLock\\)$");
+            out.shouldMatch("^\\s+- locked <0x[0-9a-f]+> \\(a java\\.lang\\.Thread\\)$");
+            out.shouldMatch("^\\s+- locked <0x[0-9a-f]+> \\(a java\\.lang\\.Class for int\\)$");
+            out.stderrShouldBeEmpty();
+
+            System.out.println("Test Completed");
+        } finally {
+            LingeredApp.stopApp(app);
+        }
+    }
+}
diff --git a/test/hotspot/jtreg/serviceability/sa/TestPrintMdo.java b/test/hotspot/jtreg/serviceability/sa/TestPrintMdo.java
index 48b0b0840b8..56e18f0409a 100644
--- a/test/hotspot/jtreg/serviceability/sa/TestPrintMdo.java
+++ b/test/hotspot/jtreg/serviceability/sa/TestPrintMdo.java
@@ -132,22 +132,16 @@ public class TestPrintMdo {
             throw new Error("Problem issuing the printmdo command: " + str, ioe);
         }
 
+        OutputAnalyzer output = new OutputAnalyzer(p);
+
         try {
             p.waitFor();
         } catch (InterruptedException ie) {
+            p.destroyForcibly();
             throw new Error("Problem awaiting the child process: " + ie, ie);
         }
 
-        int exitValue = p.exitValue();
-        if (exitValue != 0) {
-            String output;
-            try {
-                output = new OutputAnalyzer(p).getOutput();
-            } catch (IOException ioe) {
-                throw new Error("Can't get failed clhsdb process output: " + ioe, ioe);
-            }
-            throw new AssertionError("clhsdb wasn't run successfully: " + output);
-        }
+        output.shouldHaveExitValue(0);
     }
 
     public static void main (String... args) throws Exception {
diff --git a/test/hotspot/jtreg/testlibrary/ctw/src/sun/hotspot/tools/ctw/CtwRunner.java b/test/hotspot/jtreg/testlibrary/ctw/src/sun/hotspot/tools/ctw/CtwRunner.java
index 453c3c0c612..cdc8efaedfb 100644
--- a/test/hotspot/jtreg/testlibrary/ctw/src/sun/hotspot/tools/ctw/CtwRunner.java
+++ b/test/hotspot/jtreg/testlibrary/ctw/src/sun/hotspot/tools/ctw/CtwRunner.java
@@ -123,14 +123,15 @@ public class CtwRunner {
                         .collect(Collectors.joining(" "));
                 String phase = phaseName(classStart);
                 Path out = Paths.get(".", phase + ".out");
+                Path err = Paths.get(".", phase + ".err");
                 System.out.printf("%s %dms START : [%s]%n" +
                         "cout/cerr are redirected to %s%n",
                         phase, TimeUnit.NANOSECONDS.toMillis(System.nanoTime()),
-                        commandLine, out);
-                int exitCode = pb.redirectErrorStream(true)
-                        .redirectOutput(out.toFile())
-                        .start()
-                        .waitFor();
+                        commandLine, phase);
+                int exitCode = pb.redirectOutput(out.toFile())
+                                 .redirectError(err.toFile())
+                                 .start()
+                                 .waitFor();
                 System.out.printf("%s %dms END : exit code = %d%n",
                         phase, TimeUnit.NANOSECONDS.toMillis(System.nanoTime()),
                         exitCode);
diff --git a/test/hotspot/jtreg/testlibrary_tests/TestMutuallyExclusivePlatformPredicates.java b/test/hotspot/jtreg/testlibrary_tests/TestMutuallyExclusivePlatformPredicates.java
index 9903b188e62..fc563ee2368 100644
--- a/test/hotspot/jtreg/testlibrary_tests/TestMutuallyExclusivePlatformPredicates.java
+++ b/test/hotspot/jtreg/testlibrary_tests/TestMutuallyExclusivePlatformPredicates.java
@@ -51,7 +51,7 @@ public class TestMutuallyExclusivePlatformPredicates {
         VM_TYPE("isClient", "isServer", "isGraal", "isMinimal", "isZero", "isEmbedded"),
         MODE("isInt", "isMixed", "isComp"),
         IGNORED("isEmulatedClient", "isDebugBuild", "isFastDebugBuild", "isSlowDebugBuild",
-                "shouldSAAttach", "isTieredSupported");
+                "shouldSAAttach", "isTieredSupported", "areCustomLoadersSupportedForCDS");
 
         public final List<String> methodNames;
 
@@ -106,7 +106,7 @@ public class TestMutuallyExclusivePlatformPredicates {
                     && m.getReturnType() == boolean.class) {
                 Asserts.assertTrue(allMethods.contains(m.getName()),
                         "All Platform's methods with signature '():Z' should "
-                                + "be tested ");
+                                + "be tested. Missing: " + m.getName());
             }
         }
     }
diff --git a/test/jdk/ProblemList.txt b/test/jdk/ProblemList.txt
index de6fed00da5..e4ec8140ed3 100644
--- a/test/jdk/ProblemList.txt
+++ b/test/jdk/ProblemList.txt
@@ -170,8 +170,6 @@ java/beans/Introspector/8132566/OverrideUserDefPropertyInfoTest.java 8132565 gen
 
 java/lang/StringCoding/CheckEncodings.sh                        7008363 generic-all
 
-jdk/internal/misc/JavaLangAccess/NewUnsafeString.java           8176188 generic-all
-
 java/lang/String/nativeEncoding/StringPlatformChars.java        8182569 windows-all,solaris-all
 
 ############################################################################
@@ -220,8 +218,6 @@ java/net/MulticastSocket/Test.java                              7145658 macosx-a
 
 java/net/DatagramSocket/SendDatagramToBadAddress.java           7143960 macosx-all
 
-java/net/httpclient/websocket/ConnectionHandover.java           8188895 windows-all
-
 ############################################################################
 
 # jdk_nio
@@ -318,6 +314,7 @@ javax/swing/JTree/DnD/LastNodeLowerHalfDrop.java 8159131 linux-all
 tools/pack200/CommandLineTests.java                             8059906 generic-all
 
 tools/launcher/FXLauncherTest.java                              8068049 linux-all,macosx-all
+tools/launcher/TestXcheckJNIWarnings.java                       8190984 solaris-all
 
 tools/jimage/JImageExtractTest.java                             8170120 generic-all
 tools/jimage/JImageListTest.java                                8170120 generic-all
diff --git a/test/jdk/TEST.ROOT b/test/jdk/TEST.ROOT
index 2f00dbb7f82..0aceb5f4710 100644
--- a/test/jdk/TEST.ROOT
+++ b/test/jdk/TEST.ROOT
@@ -39,7 +39,7 @@ requires.properties= \
     vm.cds
 
 # Minimum jtreg version
-requiredVersion=4.2 b08
+requiredVersion=4.2 b09
 
 # Path to libraries in the topmost test directory. This is needed so @library
 # does not need ../../ notation to reach them
diff --git a/test/jdk/TEST.groups b/test/jdk/TEST.groups
index b3ddcc8c25b..7eedae41735 100644
--- a/test/jdk/TEST.groups
+++ b/test/jdk/TEST.groups
@@ -89,7 +89,6 @@ jdk_util = \
 jdk_util_other = \
     java/util \
     sun/util \
-    jdk/internal/util \
     -:jdk_collections \
     -:jdk_concurrent \
     -:jdk_stream
diff --git a/test/jdk/com/sun/net/httpserver/EchoHandler.java b/test/jdk/com/sun/net/httpserver/EchoHandler.java
index 0b9de1f3d62..77033a216f4 100644
--- a/test/jdk/com/sun/net/httpserver/EchoHandler.java
+++ b/test/jdk/com/sun/net/httpserver/EchoHandler.java
@@ -66,8 +66,8 @@ public class EchoHandler implements HttpHandler {
             t.sendResponseHeaders(200, in.length);
             OutputStream os = t.getResponseBody();
             os.write(in);
-            close(os);
-            close(is);
+            close(t, os);
+            close(t, is);
         } else {
             OutputStream os = t.getResponseBody();
             byte[] buf = new byte[64 * 1024];
@@ -84,15 +84,21 @@ public class EchoHandler implements HttpHandler {
                 String s = Integer.toString(count);
                 os.write(s.getBytes());
             }
-            close(os);
-            close(is);
+            close(t, os);
+            close(t, is);
         }
     }
 
     protected void close(OutputStream os) throws IOException {
-            os.close();
+        os.close();
     }
     protected void close(InputStream is) throws IOException {
-            is.close();
+        is.close();
+    }
+    protected void close(HttpExchange t, OutputStream os) throws IOException {
+        close(os);
+    }
+    protected void close(HttpExchange t, InputStream is) throws IOException {
+        close(is);
     }
 }
diff --git a/test/jdk/com/sun/tools/attach/StartManagementAgent.java b/test/jdk/com/sun/tools/attach/StartManagementAgent.java
index 1a5234b3408..bd996ea2c63 100644
--- a/test/jdk/com/sun/tools/attach/StartManagementAgent.java
+++ b/test/jdk/com/sun/tools/attach/StartManagementAgent.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2014, 2017, 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
@@ -99,7 +99,7 @@ public class StartManagementAgent {
         } catch(AttachOperationFailedException ex) {
             // We expect parsing of "apa" above to fail, but if the file path
             // can't be read we get a different exception message
-            if (!ex.getMessage().contains("Invalid com.sun.management.jmxremote.port number")) {
+            if (!ex.getMessage().contains("NumberFormatException: For input string: \"apa\"")) {
                 throw ex;
             }
             ex.printStackTrace(System.err);
diff --git a/test/jdk/java/awt/Component/GetScreenLocTest/ComponentGetLocationOnScreenNPETest.java b/test/jdk/java/awt/Component/GetScreenLocTest/ComponentGetLocationOnScreenNPETest.java
index a5131184794..6458b308bf1 100644
--- a/test/jdk/java/awt/Component/GetScreenLocTest/ComponentGetLocationOnScreenNPETest.java
+++ b/test/jdk/java/awt/Component/GetScreenLocTest/ComponentGetLocationOnScreenNPETest.java
@@ -25,6 +25,7 @@
  * @test
  * @bug 8189204
  * @summary Possible NPE in Component::getLocationOnScreen()
+ * @key headful
  * @run main ComponentGetLocationOnScreenNPETest
  */
 
diff --git a/test/jdk/java/awt/Dialog/SiblingChildOrder/SiblingChildOrderTest.java b/test/jdk/java/awt/Dialog/SiblingChildOrder/SiblingChildOrderTest.java
index c336a0d9bc6..701d1e2deb3 100644
--- a/test/jdk/java/awt/Dialog/SiblingChildOrder/SiblingChildOrderTest.java
+++ b/test/jdk/java/awt/Dialog/SiblingChildOrder/SiblingChildOrderTest.java
@@ -21,9 +21,11 @@
  * questions.
  */
 
-/* @test
+/**
+ * @test
  * @bug 8190230
  * @summary [macosx] Order of overlapping of modal dialogs is wrong
+ * @key headful
  * @run main SiblingChildOrderTest
  */
 
diff --git a/test/jdk/java/awt/Focus/FocusTransitionTest/FocusTransitionTest.java b/test/jdk/java/awt/Focus/FocusTransitionTest/FocusTransitionTest.java
index 6610981c408..8352a2bf6fb 100644
--- a/test/jdk/java/awt/Focus/FocusTransitionTest/FocusTransitionTest.java
+++ b/test/jdk/java/awt/Focus/FocusTransitionTest/FocusTransitionTest.java
@@ -21,10 +21,12 @@
  * questions.
  */
 
-/* @test
+/**
+ * @test
  * @bug 8155197
  * @summary Tests whether the value of mostRecentFocusOwner for a window is correct, if
  *          another window is displayed during focus transition
+ * @key headful
  * @library ../../regtesthelpers
  * @build Util
  * @run main FocusTransitionTest
diff --git a/test/jdk/java/io/FileInputStream/FinalizeShdCallClose.java b/test/jdk/java/io/FileInputStream/FinalizeShdCallClose.java
deleted file mode 100644
index 6ebcbf8c8ec..00000000000
--- a/test/jdk/java/io/FileInputStream/FinalizeShdCallClose.java
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
- * Copyright (c) 2007, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-
-/**
- *
- * @test
- * @bug 6524062
- * @summary Test to ensure that FIS.finalize() invokes the close() method as per
- * the specification.
- */
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-
-public class FinalizeShdCallClose {
-
-    static final String FILE_NAME = "empty.txt";
-
-    public static class MyStream extends FileInputStream {
-        private boolean closed = false;
-
-        public MyStream(String name) throws FileNotFoundException {
-            super(name);
-        }
-
-        public void finalize() {
-            try {
-                super.finalize();
-            } catch (IOException ioe) {
-                ioe.printStackTrace();
-            }
-        }
-
-        public void close() {
-            try {
-                super.close();
-            } catch (IOException ioe) {
-                ioe.printStackTrace();
-            }
-            closed = true;
-        }
-
-        public boolean isClosed() {
-            return closed;
-        }
-    }
-
-    /* standalone interface */
-    public static void main(String argv[]) throws Exception {
-
-        File inFile= new File(System.getProperty("test.dir", "."), FILE_NAME);
-        inFile.createNewFile();
-        inFile.deleteOnExit();
-
-        String name = inFile.getPath();
-        MyStream ms = null;
-        try {
-            ms = new MyStream(name);
-        } catch (FileNotFoundException e) {
-            System.out.println("Unexpected exception " + e);
-            throw(e);
-        }
-        ms.finalize();
-        if (!ms.isClosed()) {
-            throw new Exception("MyStream.close() method is not called");
-        }
-        System.out.println("OK");
-    }
-}
diff --git a/test/jdk/java/io/FileInputStream/UnreferencedFISClosesFd.java b/test/jdk/java/io/FileInputStream/UnreferencedFISClosesFd.java
new file mode 100644
index 00000000000..7664348f329
--- /dev/null
+++ b/test/jdk/java/io/FileInputStream/UnreferencedFISClosesFd.java
@@ -0,0 +1,247 @@
+/*
+ * Copyright (c) 2007, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/**
+ *
+ * @test
+ * @modules java.base/java.io:open
+ * @bug 6524062
+ * @summary Test to ensure that FIS.finalize() invokes the close() method as per
+ * the specification.
+ * @run main/othervm UnreferencedFISClosesFd
+ */
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.lang.ref.Reference;
+import java.lang.ref.ReferenceQueue;
+import java.lang.ref.WeakReference;
+import java.lang.reflect.Field;
+import java.util.HashSet;
+import java.util.concurrent.atomic.AtomicInteger;
+
+
+/**
+ * Tests for FIS unreferenced.
+ *  - Not subclassed - cleaner cleanup
+ *  - Subclassed no finalize or close - cleaner cleanup
+ *  - Subclassed close overridden - AltFinalizer cleanup
+ *  - Subclasses finalize overridden - cleaner cleanup
+ *  - Subclasses finalize and close overridden - AltFinalizer cleanup
+ */
+public class UnreferencedFISClosesFd {
+
+    enum CleanupType {
+        CLOSE,      // Cleanup is handled via calling close
+        CLEANER}    // Cleanup is handled via Cleaner
+
+    static final String FILE_NAME = "empty.txt";
+
+    /**
+     * Subclass w/ no overrides; not finalize or close.
+     * Cleanup should be via the Cleaner (not close).
+     */
+    public static class StreamOverrides extends FileInputStream {
+
+        protected final AtomicInteger closeCounter;
+
+        public StreamOverrides(String name) throws FileNotFoundException {
+            super(name);
+            closeCounter = new AtomicInteger(0);
+        }
+
+        final AtomicInteger closeCounter() {
+            return closeCounter;
+        }
+    }
+
+    /**
+     * Subclass overrides close.
+     * Cleanup should be via AltFinalizer calling close().
+     */
+    public static class StreamOverridesClose extends StreamOverrides {
+
+        public StreamOverridesClose(String name) throws FileNotFoundException {
+            super(name);
+        }
+
+        public void close() throws IOException {
+            closeCounter.incrementAndGet();
+            super.close();
+        }
+    }
+
+    /**
+     * Subclass overrides finalize.
+     * Cleanup should be via the Cleaner (not close).
+     */
+    public static class StreamOverridesFinalize extends StreamOverrides {
+
+        public StreamOverridesFinalize(String name) throws FileNotFoundException {
+            super(name);
+        }
+
+        @SuppressWarnings({"deprecation","removal"})
+        protected void finalize() throws IOException {
+            super.finalize();
+        }
+    }
+
+    /**
+     * Subclass overrides finalize and close.
+     * Cleanup should be via AltFinalizer calling close().
+     */
+    public static class StreamOverridesFinalizeClose extends StreamOverridesClose {
+
+        public StreamOverridesFinalizeClose(String name) throws FileNotFoundException {
+            super(name);
+        }
+
+        @SuppressWarnings({"deprecation","removal"})
+        protected void finalize() throws IOException {
+            super.finalize();
+        }
+    }
+
+    /**
+     * Main runs each test case and reports number of failures.
+     */
+    public static void main(String argv[]) throws Exception {
+
+        File inFile = new File(System.getProperty("test.dir", "."), FILE_NAME);
+        inFile.createNewFile();
+        inFile.deleteOnExit();
+
+        String name = inFile.getPath();
+
+        int failCount = 0;
+        failCount += test(new FileInputStream(name), CleanupType.CLEANER);
+
+        failCount += test(new StreamOverrides(name), CleanupType.CLEANER);
+
+        failCount += test(new StreamOverridesClose(name), CleanupType.CLOSE);
+
+        failCount += test(new StreamOverridesFinalize(name), CleanupType.CLEANER);
+
+        failCount += test(new StreamOverridesFinalizeClose(name), CleanupType.CLOSE);
+
+        if (failCount > 0) {
+            throw new AssertionError("Failed test count: " + failCount);
+        }
+    }
+
+    private static int test(FileInputStream fis, CleanupType cleanType) throws Exception {
+
+        try {
+            System.out.printf("%nTesting %s%n", fis.getClass().getName());
+
+            // Prepare to wait for FIS to be reclaimed
+            ReferenceQueue<Object> queue = new ReferenceQueue<>();
+            HashSet<Reference<?>> pending = new HashSet<>();
+            WeakReference<FileInputStream> msWeak = new WeakReference<>(fis, queue);
+            pending.add(msWeak);
+
+            FileDescriptor fd = fis.getFD();
+            WeakReference<FileDescriptor> fdWeak = new WeakReference<>(fd, queue);
+            pending.add(fdWeak);
+
+            Field fdField = FileDescriptor.class.getDeclaredField("fd");
+            fdField.setAccessible(true);
+            int ffd = fdField.getInt(fd);
+
+            Field altFinalizerField = FileInputStream.class.getDeclaredField("altFinalizer");
+            altFinalizerField.setAccessible(true);
+            Object altFinalizer = altFinalizerField.get(fis);
+            if ((altFinalizer != null) ^ (cleanType == CleanupType.CLOSE)) {
+                throw new RuntimeException("Unexpected AltFinalizer: " + altFinalizer
+                + ", for " + cleanType);
+            }
+
+            Field cleanupField = FileDescriptor.class.getDeclaredField("cleanup");
+            cleanupField.setAccessible(true);
+            Object cleanup = cleanupField.get(fd);
+            System.out.printf("  cleanup: %s, alt: %s, ffd: %d, cf: %s%n",
+                    cleanup, altFinalizer, ffd, cleanupField);
+            if ((cleanup != null) ^ (cleanType == CleanupType.CLEANER)) {
+                throw new Exception("unexpected cleanup: "
+                + cleanup + ", for " + cleanType);
+            }
+            if (cleanup != null) {
+                WeakReference<Object> cleanupWeak = new WeakReference<>(cleanup, queue);
+                pending.add(cleanupWeak);
+                System.out.printf("    fdWeak: %s%n    msWeak: %s%n    cleanupWeak: %s%n",
+                        fdWeak, msWeak, cleanupWeak);
+            }
+            if (altFinalizer != null) {
+                WeakReference<Object> altFinalizerWeak = new WeakReference<>(altFinalizer, queue);
+                pending.add(altFinalizerWeak);
+                System.out.printf("    fdWeak: %s%n    msWeak: %s%n    altFinalizerWeak: %s%n",
+                        fdWeak, msWeak, altFinalizerWeak);
+            }
+
+            AtomicInteger closeCounter = fis instanceof StreamOverrides
+                    ? ((StreamOverrides)fis).closeCounter() : null;
+
+            Reference<?> r;
+            while (((r = queue.remove(1000L)) != null)
+                    || !pending.isEmpty()) {
+                System.out.printf("    r: %s, pending: %d%n",
+                        r, pending.size());
+                if (r != null) {
+                    pending.remove(r);
+                } else {
+                    fis = null;
+                    fd = null;
+                    cleanup = null;
+                    altFinalizer = null;
+                    System.gc();  // attempt to reclaim them
+                }
+            }
+            Reference.reachabilityFence(fd);
+            Reference.reachabilityFence(fis);
+            Reference.reachabilityFence(cleanup);
+            Reference.reachabilityFence(altFinalizer);
+
+            // Confirm the correct number of calls to close depending on the cleanup type
+            switch (cleanType) {
+                case CLEANER:
+                    if (closeCounter != null && closeCounter.get() > 0) {
+                        throw new RuntimeException("Close should not have been called: count: "
+                                + closeCounter);
+                    }
+                    break;
+                case CLOSE:
+                    if (closeCounter == null || closeCounter.get() == 0) {
+                        throw new RuntimeException("Close should have been called: count: 0");
+                    }
+                    break;
+            }
+        } catch (Exception ex) {
+            ex.printStackTrace(System.out);
+            return 1;
+        }
+        return 0;
+    }
+}
diff --git a/test/jdk/java/io/FileOutputStream/FinalizeShdCallClose.java b/test/jdk/java/io/FileOutputStream/FinalizeShdCallClose.java
deleted file mode 100644
index a981a5e4154..00000000000
--- a/test/jdk/java/io/FileOutputStream/FinalizeShdCallClose.java
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
- * Copyright (c) 2007, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-
-/**
- *
- * @test
- * @bug 6524062
- * @summary Test to ensure that FOS.finalize() invokes the close() method as per
- * the specification.
- */
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-
-public class FinalizeShdCallClose {
-
-    static final String FILE_NAME = "empty.txt";
-
-    public static class MyStream extends FileOutputStream {
-        private boolean closed = false;
-
-        public MyStream(String name) throws FileNotFoundException {
-            super(name);
-        }
-
-        public void finalize() {
-            try {
-                super.finalize();
-            } catch (IOException ioe) {
-                ioe.printStackTrace();
-            }
-        }
-
-        public void close() {
-            try {
-                super.close();
-            } catch (IOException ioe) {
-                ioe.printStackTrace();
-            }
-            closed = true;
-        }
-
-        public boolean isClosed() {
-            return closed;
-        }
-    }
-
-    /* standalone interface */
-    public static void main(String argv[]) throws Exception {
-
-        File inFile= new File(System.getProperty("test.dir", "."), FILE_NAME);
-        inFile.createNewFile();
-        inFile.deleteOnExit();
-
-        String name = inFile.getPath();
-        MyStream ms = null;
-        try {
-            ms = new MyStream(name);
-        } catch (FileNotFoundException e) {
-            System.out.println("Unexpected exception " + e);
-            throw(e);
-        }
-        ms.finalize();
-        if (!ms.isClosed()) {
-            throw new Exception("MyStream.close() method is not called");
-        }
-        System.out.println("OK");
-    }
-}
diff --git a/test/jdk/java/io/FileOutputStream/UnreferencedFOSClosesFd.java b/test/jdk/java/io/FileOutputStream/UnreferencedFOSClosesFd.java
new file mode 100644
index 00000000000..71d0e0008f3
--- /dev/null
+++ b/test/jdk/java/io/FileOutputStream/UnreferencedFOSClosesFd.java
@@ -0,0 +1,236 @@
+/*
+ * Copyright (c) 2007, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/**
+ *
+ * @test
+ * @modules java.base/java.io:open
+ * @bug 6524062
+ * @summary Test to ensure that FOS.finalize() invokes the close() method as per
+ * the specification.
+ * @run main/othervm UnreferencedFOSClosesFd
+ */
+import java.io.*;
+import java.lang.ref.Reference;
+import java.lang.ref.ReferenceQueue;
+import java.lang.ref.WeakReference;
+import java.lang.reflect.Field;
+import java.util.HashSet;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
+
+public class UnreferencedFOSClosesFd {
+
+    enum CleanupType {
+        CLOSE,      // Cleanup is handled via calling close
+        CLEANER}    // Cleanup is handled via Cleaner
+
+    static final String FILE_NAME = "empty.txt";
+
+    /**
+     * Subclass w/ no overrides; not finalize or close.
+     * Cleanup should be via the Cleaner (not close).
+     */
+    public static class StreamOverrides extends FileOutputStream {
+
+        protected final AtomicInteger closeCounter;
+
+        public StreamOverrides(String name) throws FileNotFoundException {
+            super(name);
+            closeCounter = new AtomicInteger(0);
+        }
+
+        final AtomicInteger closeCounter() {
+            return closeCounter;
+        }
+    }
+
+    /**
+     * Subclass overrides close.
+     * Cleanup should be via AltFinalizer calling close().
+     */
+    public static class StreamOverridesClose extends StreamOverrides {
+
+        public StreamOverridesClose(String name) throws FileNotFoundException {
+            super(name);
+        }
+
+        public void close() throws IOException {
+            closeCounter.incrementAndGet();
+            super.close();
+        }
+    }
+
+    /**
+     * Subclass overrides finalize and close.
+     * Cleanup should be via the Cleaner (not close).
+     */
+    public static class StreamOverridesFinalize extends StreamOverrides {
+
+        public StreamOverridesFinalize(String name) throws FileNotFoundException {
+            super(name);
+        }
+
+        @SuppressWarnings({"deprecation","removal"})
+        protected void finalize() throws IOException {
+            super.finalize();
+        }
+    }
+
+    /**
+     * Subclass overrides finalize and close.
+     * Cleanup should be via AltFinalizer calling close().
+     */
+    public static class StreamOverridesFinalizeClose extends StreamOverridesClose {
+
+        public StreamOverridesFinalizeClose(String name) throws FileNotFoundException {
+            super(name);
+        }
+
+        @SuppressWarnings({"deprecation","removal"})
+        protected void finalize() throws IOException {
+            super.finalize();
+        }
+    }
+
+    /**
+     * Main runs each test case and reports number of failures.
+     */
+    public static void main(String argv[]) throws Exception {
+
+        File inFile = new File(System.getProperty("test.dir", "."), FILE_NAME);
+        inFile.createNewFile();
+        inFile.deleteOnExit();
+
+        String name = inFile.getPath();
+
+        int failCount = 0;
+        failCount += test(new FileOutputStream(name), CleanupType.CLEANER);
+
+        failCount += test(new StreamOverrides(name), CleanupType.CLEANER);
+
+        failCount += test(new StreamOverridesClose(name), CleanupType.CLOSE);
+
+        failCount += test(new StreamOverridesFinalize(name), CleanupType.CLEANER);
+
+        failCount += test(new StreamOverridesFinalizeClose(name), CleanupType.CLOSE);
+
+        if (failCount > 0) {
+            throw new AssertionError("Failed test count: " + failCount);
+        }
+    }
+
+
+    private static int test(FileOutputStream fos, CleanupType cleanType) throws Exception {
+
+        try {
+            System.out.printf("%nTesting %s%n", fos.getClass().getName());
+
+            // Prepare to wait for FOS to be reclaimed
+            ReferenceQueue<Object> queue = new ReferenceQueue<>();
+            HashSet<Reference<?>> pending = new HashSet<>();
+            WeakReference<FileOutputStream> msWeak = new WeakReference<>(fos, queue);
+            pending.add(msWeak);
+
+            FileDescriptor fd = fos.getFD();
+            WeakReference<FileDescriptor> fdWeak = new WeakReference<>(fd, queue);
+            pending.add(fdWeak);
+
+            Field fdField = FileDescriptor.class.getDeclaredField("fd");
+            fdField.setAccessible(true);
+            int ffd = fdField.getInt(fd);
+
+            Field altFinalizerField = FileOutputStream.class.getDeclaredField("altFinalizer");
+            altFinalizerField.setAccessible(true);
+            Object altFinalizer = altFinalizerField.get(fos);
+            if ((altFinalizer != null) ^ (cleanType == CleanupType.CLOSE)) {
+                throw new RuntimeException("Unexpected AltFinalizer: " + altFinalizer
+                        + ", for " + cleanType);
+            }
+
+            Field cleanupField = FileDescriptor.class.getDeclaredField("cleanup");
+            cleanupField.setAccessible(true);
+            Object cleanup = cleanupField.get(fd);
+            System.out.printf("  cleanup: %s, alt: %s, ffd: %d, cf: %s%n",
+                    cleanup, altFinalizer, ffd, cleanupField);
+            if ((cleanup != null) ^ (cleanType == CleanupType.CLEANER)) {
+                throw new Exception("unexpected cleanup: "
+                        + cleanup + ", for " + cleanType);
+            }
+            if (cleanup != null) {
+                WeakReference<Object> cleanupWeak = new WeakReference<>(cleanup, queue);
+                pending.add(cleanupWeak);
+                System.out.printf("    fdWeak: %s%n    msWeak: %s%n    cleanupWeak: %s%n",
+                        fdWeak, msWeak, cleanupWeak);
+            }
+            if (altFinalizer != null) {
+                WeakReference<Object> altFinalizerWeak = new WeakReference<>(altFinalizer, queue);
+                pending.add(altFinalizerWeak);
+                System.out.printf("    fdWeak: %s%n    msWeak: %s%n    altFinalizerWeak: %s%n",
+                        fdWeak, msWeak, altFinalizerWeak);
+            }
+
+            AtomicInteger closeCounter = fos instanceof StreamOverrides
+                    ? ((StreamOverrides) fos).closeCounter() : null;
+
+            Reference<?> r;
+            while (((r = queue.remove(1000L)) != null)
+                    || !pending.isEmpty()) {
+                System.out.printf("    r: %s, pending: %d%n",
+                        r, pending.size());
+                if (r != null) {
+                    pending.remove(r);
+                } else {
+                    fos = null;
+                    fd = null;
+                    cleanup = null;
+                    altFinalizer = null;
+                    System.gc();  // attempt to reclaim them
+                }
+            }
+            Reference.reachabilityFence(fd);
+            Reference.reachabilityFence(fos);
+            Reference.reachabilityFence(cleanup);
+            Reference.reachabilityFence(altFinalizer);
+
+            // Confirm the correct number of calls to close depending on the cleanup type
+            switch (cleanType) {
+                case CLEANER:
+                    if (closeCounter != null && closeCounter.get() > 0) {
+                        throw new RuntimeException("Close should not have been called: count: "
+                                + closeCounter);
+                    }
+                    break;
+                case CLOSE:
+                    if (closeCounter == null || closeCounter.get() == 0) {
+                        throw new RuntimeException("Close should have been called: count: 0");
+                    }
+                    break;
+            }
+        } catch (Exception ex) {
+            ex.printStackTrace(System.out);
+            return 1;
+        }
+        return 0;
+    }
+}
diff --git a/test/jdk/java/io/RandomAccessFile/UnreferencedRAFClosesFd.java b/test/jdk/java/io/RandomAccessFile/UnreferencedRAFClosesFd.java
new file mode 100644
index 00000000000..b6ebff6a14b
--- /dev/null
+++ b/test/jdk/java/io/RandomAccessFile/UnreferencedRAFClosesFd.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (c) 2007, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.FileOutputStream;
+import java.io.FileNotFoundException;
+import java.io.RandomAccessFile;
+import java.lang.ref.Cleaner;
+import java.lang.ref.Reference;
+import java.lang.ref.ReferenceQueue;
+import java.lang.ref.WeakReference;
+import java.lang.reflect.Field;
+import java.util.HashSet;
+
+/**
+ * @test
+ * @bug 8080225
+ * @modules java.base/java.io:open
+ * @summary Test to ensure that an unclosed and unreferenced RandomAccessFile closes the fd
+ * @run main/othervm UnreferencedRAFClosesFd
+ */
+public class UnreferencedRAFClosesFd {
+
+    static final String FILE_NAME = "empty.txt";
+
+    /* standalone interface */
+    public static void main(String argv[]) throws Exception {
+
+        File inFile= new File(System.getProperty("test.dir", "."), FILE_NAME);
+        inFile.createNewFile();
+        inFile.deleteOnExit();
+
+        String name = inFile.getPath();
+        RandomAccessFile raf;
+        try {
+            // raf is explicitly *not* closed to allow cleaner to work
+            raf = new RandomAccessFile(name, "rw");
+        } catch (FileNotFoundException e) {
+            System.out.println("Unexpected exception " + e);
+            throw(e);
+        }
+        FileDescriptor fd = raf.getFD();
+
+        Field fdField = FileDescriptor.class.getDeclaredField("cleanup");
+        fdField.setAccessible(true);
+        Cleaner.Cleanable cleanup = (Cleaner.Cleanable)fdField.get(fd);
+
+        // Prepare to wait for FOS, FD, Cleanup to be reclaimed
+        ReferenceQueue<Object> queue = new ReferenceQueue<>();
+        HashSet<Reference<?>> pending = new HashSet<>(3);
+        pending.add(new WeakReference<>(cleanup, queue));
+        pending.add(new WeakReference<>(raf, queue));
+        pending.add(new WeakReference<>(fd, queue));
+
+        Reference<?> r;
+        while (((r = queue.remove(10L)) != null)
+                || !pending.isEmpty()) {
+            System.out.printf("r: %s, pending: %d%n", r, pending.size());
+            if (r != null) {
+                pending.remove(r);
+            } else {
+                cleanup = null;
+                raf = null;
+                fd = null;
+                System.gc();  // attempt to reclaim the RAF, cleanup, and fd
+            }
+        }
+
+        // Keep these variables in scope as gc roots
+        Reference.reachabilityFence(cleanup);
+        Reference.reachabilityFence(fd);
+        Reference.reachabilityFence(raf);
+        Reference.reachabilityFence(pending);
+    }
+}
diff --git a/test/jdk/java/io/Reader/TransferTo.java b/test/jdk/java/io/Reader/TransferTo.java
new file mode 100644
index 00000000000..9ed409443b6
--- /dev/null
+++ b/test/jdk/java/io/Reader/TransferTo.java
@@ -0,0 +1,353 @@
+/*
+ * Copyright (c) 2017, 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 source 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 source 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 Franklsource St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.io.*;
+import java.util.Arrays;
+import java.util.Random;
+
+import jdk.test.lib.RandomFactory;
+
+import static java.lang.String.format;
+
+/*
+ * @test
+ * @bug 8191706
+ * @summary tests whether java.io.Reader.transferTo conforms to its
+ *          contract defined source the javadoc
+ * @library /test/lib
+ * @build jdk.test.lib.RandomFactory
+ * @run main TransferTo
+ * @key randomness
+ * @author Patrick Reinhart
+ */
+public class TransferTo {
+
+    private static Random generator = RandomFactory.getRandom();
+
+    public static void main(String[] args) throws IOException {
+        ifOutIsNullThenNpeIsThrown();
+        ifExceptionInInputNeitherStreamIsClosed();
+        ifExceptionInOutputNeitherStreamIsClosed();
+        onReturnNeitherStreamIsClosed();
+        onReturnInputIsAtEnd();
+        contents();
+    }
+
+    private static void ifOutIsNullThenNpeIsThrown() throws IOException {
+        try (Reader in = input()) {
+            assertThrowsNPE(() -> in.transferTo(null), "out");
+        }
+
+        try (Reader in = input((char) 1)) {
+            assertThrowsNPE(() -> in.transferTo(null), "out");
+        }
+
+        try (Reader in = input((char) 1, (char) 2)) {
+            assertThrowsNPE(() -> in.transferTo(null), "out");
+        }
+
+        Reader in = null;
+        try {
+            Reader fin = in = new ThrowingReader();
+            // null check should precede everything else:
+            // Reader shouldn't be touched if Writer is null
+            assertThrowsNPE(() -> fin.transferTo(null), "out");
+        } finally {
+            if (in != null)
+                try {
+                    in.close();
+                } catch (IOException ignored) { }
+        }
+    }
+
+    private static void ifExceptionInInputNeitherStreamIsClosed()
+            throws IOException {
+        transferToThenCheckIfAnyClosed(input(0, new char[]{1, 2, 3}), output());
+        transferToThenCheckIfAnyClosed(input(1, new char[]{1, 2, 3}), output());
+        transferToThenCheckIfAnyClosed(input(2, new char[]{1, 2, 3}), output());
+    }
+
+    private static void ifExceptionInOutputNeitherStreamIsClosed()
+            throws IOException {
+        transferToThenCheckIfAnyClosed(input(new char[]{1, 2, 3}), output(0));
+        transferToThenCheckIfAnyClosed(input(new char[]{1, 2, 3}), output(1));
+        transferToThenCheckIfAnyClosed(input(new char[]{1, 2, 3}), output(2));
+    }
+
+    private static void transferToThenCheckIfAnyClosed(Reader input,
+                                                       Writer output)
+            throws IOException {
+        try (CloseLoggingReader in = new CloseLoggingReader(input);
+             CloseLoggingWriter out =
+                     new CloseLoggingWriter(output)) {
+            boolean thrown = false;
+            try {
+                in.transferTo(out);
+            } catch (IOException ignored) {
+                thrown = true;
+            }
+            if (!thrown)
+                throw new AssertionError();
+
+            if (in.wasClosed() || out.wasClosed()) {
+                throw new AssertionError();
+            }
+        }
+    }
+
+    private static void onReturnNeitherStreamIsClosed()
+            throws IOException {
+        try (CloseLoggingReader in =
+                     new CloseLoggingReader(input(new char[]{1, 2, 3}));
+             CloseLoggingWriter out =
+                     new CloseLoggingWriter(output())) {
+
+            in.transferTo(out);
+
+            if (in.wasClosed() || out.wasClosed()) {
+                throw new AssertionError();
+            }
+        }
+    }
+
+    private static void onReturnInputIsAtEnd() throws IOException {
+        try (Reader in = input(new char[]{1, 2, 3});
+             Writer out = output()) {
+
+            in.transferTo(out);
+
+            if (in.read() != -1) {
+                throw new AssertionError();
+            }
+        }
+    }
+
+    private static void contents() throws IOException {
+        checkTransferredContents(new char[0]);
+        checkTransferredContents(createRandomChars(1024, 4096));
+        // to span through several batches
+        checkTransferredContents(createRandomChars(16384, 16384));
+    }
+
+    private static void checkTransferredContents(char[] chars)
+            throws IOException {
+        try (Reader in = input(chars);
+             StringWriter out = new StringWriter()) {
+            in.transferTo(out);
+
+            char[] outChars = out.toString().toCharArray();
+            if (!Arrays.equals(chars, outChars)) {
+                throw new AssertionError(
+                        format("chars.length=%s, outChars.length=%s",
+                                chars.length, outChars.length));
+            }
+        }
+    }
+
+    private static char[] createRandomChars(int min, int maxRandomAdditive) {
+        char[] chars = new char[min + generator.nextInt(maxRandomAdditive)];
+        for (int index=0; index<chars.length; index++) {
+            chars[index] = (char)generator.nextInt();
+        }
+        return chars;
+    }
+
+    private static Writer output() {
+        return output(-1);
+    }
+
+    private static Writer output(int exceptionPosition) {
+        return new Writer() {
+
+            int pos;
+
+            @Override
+            public void write(int b) throws IOException {
+                if (pos++ == exceptionPosition)
+                    throw new IOException();
+            }
+
+            @Override
+            public void write(char[] chars, int off, int len) throws IOException {
+                for (int i=0; i<len; i++) {
+                    write(chars[off + i]);
+                }
+            }
+
+            @Override
+            public Writer append(CharSequence csq, int start, int end) throws IOException {
+                for (int i = start; i < end; i++) {
+                    write(csq.charAt(i));
+                }
+                return this;
+            }
+
+            @Override
+            public void flush() throws IOException {
+            }
+
+            @Override
+            public void close() throws IOException {
+            }
+        };
+    }
+
+    private static Reader input(char... chars) {
+        return input(-1, chars);
+    }
+
+    private static Reader input(int exceptionPosition, char... chars) {
+        return new Reader() {
+
+            int pos;
+
+            @Override
+            public int read() throws IOException {
+                if (pos == exceptionPosition) {
+                    throw new IOException();
+                }
+
+                if (pos >= chars.length)
+                    return -1;
+                return chars[pos++];
+            }
+
+            @Override
+            public int read(char[] cbuf, int off, int len) throws IOException {
+                int c = read();
+                if (c == -1) {
+                    return -1;
+                }
+                cbuf[off] = (char)c;
+
+                int i = 1;
+                for (; i < len ; i++) {
+                    c = read();
+                    if (c == -1) {
+                        break;
+                    }
+                    cbuf[off + i] = (char)c;
+                }
+                return i;
+            }
+
+            @Override
+            public void close() throws IOException {
+            }
+        };
+    }
+
+    private static class ThrowingReader extends Reader {
+
+        boolean closed;
+
+        @Override
+        public int read(char[] b, int off, int len) throws IOException {
+            throw new IOException();
+        }
+
+        @Override
+        public void close() throws IOException {
+            if (!closed) {
+                closed = true;
+                throw new IOException();
+            }
+        }
+        @Override
+        public int read() throws IOException {
+            throw new IOException();
+        }
+    }
+
+    private static class CloseLoggingReader extends FilterReader {
+
+        boolean closed;
+
+        CloseLoggingReader(Reader in) {
+            super(in);
+        }
+
+        @Override
+        public void close() throws IOException {
+            closed = true;
+            super.close();
+        }
+
+        boolean wasClosed() {
+            return closed;
+        }
+    }
+
+    private static class CloseLoggingWriter extends FilterWriter {
+
+        boolean closed;
+
+        CloseLoggingWriter(Writer out) {
+            super(out);
+        }
+
+        @Override
+        public void close() throws IOException {
+            closed = true;
+            super.close();
+        }
+
+        boolean wasClosed() {
+            return closed;
+        }
+    }
+
+    public interface Thrower {
+        public void run() throws Throwable;
+    }
+
+    public static void assertThrowsNPE(Thrower thrower, String message) {
+        assertThrows(thrower, NullPointerException.class, message);
+    }
+
+    public static <T extends Throwable> void assertThrows(Thrower thrower,
+                                                          Class<T> throwable,
+                                                          String message) {
+        Throwable thrown;
+        try {
+            thrower.run();
+            thrown = null;
+        } catch (Throwable caught) {
+            thrown = caught;
+        }
+
+        if (!throwable.isInstance(thrown)) {
+            String caught = thrown == null ?
+                    "nothing" : thrown.getClass().getCanonicalName();
+            throw new AssertionError(
+                    format("Expected to catch %s, but caught %s",
+                            throwable, caught), thrown);
+        }
+
+        if (thrown != null && !message.equals(thrown.getMessage())) {
+            throw new AssertionError(
+                    format("Expected exception message to be '%s', but it's '%s'",
+                            message, thrown.getMessage()));
+        }
+    }
+}
diff --git a/test/jdk/java/lang/ClassLoader/RecursiveSystemLoader.java b/test/jdk/java/lang/ClassLoader/RecursiveSystemLoader.java
new file mode 100644
index 00000000000..16ca74c8a51
--- /dev/null
+++ b/test/jdk/java/lang/ClassLoader/RecursiveSystemLoader.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug 8187222
+ * @run main/othervm -Djava.system.class.loader=RecursiveSystemLoader RecursiveSystemLoader
+ * @summary Test for IllegalStateException if a custom system loader recursively calls getSystemClassLoader()
+ */
+public class RecursiveSystemLoader extends ClassLoader {
+    public static void main(String[] args) {
+        ClassLoader sys = ClassLoader.getSystemClassLoader();
+        if (!(sys instanceof RecursiveSystemLoader)) {
+            throw new RuntimeException("Unexpected system classloader: " + sys);
+        }
+    }
+    public RecursiveSystemLoader(ClassLoader classLoader) {
+        super("RecursiveSystemLoader", classLoader);
+
+        // Calling ClassLoader.getSystemClassLoader() before the VM is booted
+        // should throw an IllegalStateException.
+        try {
+            ClassLoader.getSystemClassLoader();
+        } catch(IllegalStateException ise) {
+            System.err.println("Caught expected exception:");
+            ise.printStackTrace();
+            return;
+        }
+        throw new RuntimeException("Expected IllegalStateException was not thrown.");
+    }
+
+    @Override
+    public Class<?> loadClass(String name) throws ClassNotFoundException {
+        return super.loadClass(name);
+    }
+}
diff --git a/test/jdk/java/lang/ModuleLayer/LayerAndLoadersTest.java b/test/jdk/java/lang/ModuleLayer/LayerAndLoadersTest.java
index e318d7570a5..96449bda594 100644
--- a/test/jdk/java/lang/ModuleLayer/LayerAndLoadersTest.java
+++ b/test/jdk/java/lang/ModuleLayer/LayerAndLoadersTest.java
@@ -52,6 +52,7 @@ import java.util.Optional;
 import java.util.ServiceLoader;
 import java.util.Set;
 import java.util.stream.Collectors;
+
 import jdk.test.lib.compiler.CompilerUtils;
 
 import org.testng.annotations.BeforeTest;
@@ -78,7 +79,7 @@ public class LayerAndLoadersTest {
      * Basic test of ModuleLayer.defineModulesWithOneLoader
      *
      * Test scenario:
-     *   m1 requires m2 and m3
+     * m1 requires m2 and m3
      */
     public void testWithOneLoader() throws Exception {
         Configuration cf = resolve("m1");
@@ -105,7 +106,7 @@ public class LayerAndLoadersTest {
      * Basic test of ModuleLayer.defineModulesWithManyLoaders
      *
      * Test scenario:
-     *   m1 requires m2 and m3
+     * m1 requires m2 and m3
      */
     public void testWithManyLoaders() throws Exception {
         Configuration cf = resolve("m1");
@@ -136,9 +137,9 @@ public class LayerAndLoadersTest {
      * modules is a service provider module.
      *
      * Test scenario:
-     *    m1 requires m2 and m3
-     *    m1 uses S
-     *    m4 provides S with ...
+     * m1 requires m2 and m3
+     * m1 uses S
+     * m4 provides S with ...
      */
     public void testServicesWithOneLoader() throws Exception {
         Configuration cf = resolveAndBind("m1");
@@ -175,9 +176,9 @@ public class LayerAndLoadersTest {
      * modules is a service provider module.
      *
      * Test scenario:
-     *    m1 requires m2 and m3
-     *    m1 uses S
-     *    m4 provides S with ...
+     * m1 requires m2 and m3
+     * m1 uses S
+     * m4 provides S with ...
      */
     public void testServicesWithManyLoaders() throws Exception {
         Configuration cf = resolveAndBind("m1");
@@ -234,7 +235,7 @@ public class LayerAndLoadersTest {
         ModuleLayer layer = ModuleLayer.boot().defineModulesWithOneLoader(cf, parent);
         testLoad(layer, cn);
 
-         // one loader with boot loader as parent
+        // one loader with boot loader as parent
         layer = ModuleLayer.boot().defineModulesWithOneLoader(cf, null);
         testLoadFail(layer, cn);
 
@@ -252,21 +253,21 @@ public class LayerAndLoadersTest {
      * Test defineModulesWithXXX when modules that have overlapping packages.
      *
      * Test scenario:
-     *   m1 exports p
-     *   m2 exports p
+     * m1 exports p
+     * m2 exports p
      */
     public void testOverlappingPackages() {
         ModuleDescriptor descriptor1
-            = ModuleDescriptor.newModule("m1").exports("p").build();
+                = ModuleDescriptor.newModule("m1").exports("p").build();
 
         ModuleDescriptor descriptor2
-            = ModuleDescriptor.newModule("m2").exports("p").build();
+                = ModuleDescriptor.newModule("m2").exports("p").build();
 
         ModuleFinder finder = ModuleUtils.finderOf(descriptor1, descriptor2);
 
         Configuration cf = ModuleLayer.boot()
-            .configuration()
-            .resolve(finder, ModuleFinder.of(), Set.of("m1", "m2"));
+                .configuration()
+                .resolve(finder, ModuleFinder.of(), Set.of("m1", "m2"));
 
         // cannot define both module m1 and m2 to the same class loader
         try {
@@ -284,35 +285,35 @@ public class LayerAndLoadersTest {
      * Test ModuleLayer.defineModulesWithXXX with split delegation.
      *
      * Test scenario:
-     *   layer1: m1 exports p, m2 exports p
-     *   layer2: m3 reads m1, m4 reads m2
+     * layer1: m1 exports p, m2 exports p
+     * layer2: m3 reads m1, m4 reads m2
      */
     public void testSplitDelegation() {
         ModuleDescriptor descriptor1
-            = ModuleDescriptor.newModule("m1").exports("p").build();
+                = ModuleDescriptor.newModule("m1").exports("p").build();
 
         ModuleDescriptor descriptor2
-            = ModuleDescriptor.newModule("m2").exports("p").build();
+                = ModuleDescriptor.newModule("m2").exports("p").build();
 
         ModuleFinder finder1 = ModuleUtils.finderOf(descriptor1, descriptor2);
 
         Configuration cf1 = ModuleLayer.boot()
-            .configuration()
-            .resolve(finder1, ModuleFinder.of(), Set.of("m1", "m2"));
+                .configuration()
+                .resolve(finder1, ModuleFinder.of(), Set.of("m1", "m2"));
 
         ModuleLayer layer1 = ModuleLayer.boot().defineModulesWithManyLoaders(cf1, null);
         checkLayer(layer1, "m1", "m2");
 
         ModuleDescriptor descriptor3
-            = ModuleDescriptor.newModule("m3").requires("m1").build();
+                = ModuleDescriptor.newModule("m3").requires("m1").build();
 
         ModuleDescriptor descriptor4
-            = ModuleDescriptor.newModule("m4").requires("m2").build();
+                = ModuleDescriptor.newModule("m4").requires("m2").build();
 
         ModuleFinder finder2 = ModuleUtils.finderOf(descriptor3, descriptor4);
 
         Configuration cf2 = cf1.resolve(finder2, ModuleFinder.of(),
-                                                Set.of("m3", "m4"));
+                Set.of("m3", "m4"));
 
         // package p cannot be supplied by two class loaders
         try {
@@ -331,8 +332,8 @@ public class LayerAndLoadersTest {
      * named modules in the parent layer.
      *
      * Test scenario:
-     *   layer1: m1, m2, m3 => same loader
-     *   layer2: m1, m2, m4 => same loader
+     * layer1: m1, m2, m3 => same loader
+     * layer2: m1, m2, m4 => same loader
      */
     public void testOverriding1() throws Exception {
         Configuration cf1 = resolve("m1");
@@ -342,7 +343,7 @@ public class LayerAndLoadersTest {
 
         ModuleFinder finder = ModuleFinder.of(MODS_DIR);
         Configuration cf2 = cf1.resolve(finder, ModuleFinder.of(),
-                                                Set.of("m1"));
+                Set.of("m1"));
 
         ModuleLayer layer2 = layer1.defineModulesWithOneLoader(cf2, null);
         checkLayer(layer2, "m1", "m2", "m3");
@@ -378,8 +379,8 @@ public class LayerAndLoadersTest {
      * named modules in the parent layer.
      *
      * Test scenario:
-     *   layer1: m1, m2, m3 => loader pool
-     *   layer2: m1, m2, m3 => loader pool
+     * layer1: m1, m2, m3 => loader pool
+     * layer2: m1, m2, m3 => loader pool
      */
     public void testOverriding2() throws Exception {
         Configuration cf1 = resolve("m1");
@@ -389,7 +390,7 @@ public class LayerAndLoadersTest {
 
         ModuleFinder finder = ModuleFinder.of(MODS_DIR);
         Configuration cf2 = cf1.resolve(finder, ModuleFinder.of(),
-                                                Set.of("m1"));
+                Set.of("m1"));
 
         ModuleLayer layer2 = layer1.defineModulesWithManyLoaders(cf2, null);
         checkLayer(layer2, "m1", "m2", "m3");
@@ -482,7 +483,7 @@ public class LayerAndLoadersTest {
         ModuleFinder finder = finderFor("m1", "m3");
 
         Configuration cf2 = cf1.resolve(finder, ModuleFinder.of(),
-                                                Set.of("m1"));
+                Set.of("m1"));
 
         ModuleLayer layer2 = layer1.defineModulesWithOneLoader(cf2, null);
         checkLayer(layer2, "m1", "m3");
@@ -517,7 +518,7 @@ public class LayerAndLoadersTest {
         ModuleFinder finder = finderFor("m1", "m3");
 
         Configuration cf2 = cf1.resolve(finder, ModuleFinder.of(),
-                                                Set.of("m1"));
+                Set.of("m1"));
 
         ModuleLayer layer2 = layer1.defineModulesWithManyLoaders(cf2, null);
         checkLayer(layer2, "m1", "m3");
@@ -550,18 +551,27 @@ public class LayerAndLoadersTest {
         assertTrue(loader6.loadClass("w.Hello").getClassLoader() == loader6);
     }
 
-
     /**
      * Basic test for locating resources with a class loader created by
      * defineModulesWithOneLoader.
      */
     public void testResourcesWithOneLoader() throws Exception {
+        testResourcesWithOneLoader(ClassLoader.getSystemClassLoader());
+        testResourcesWithOneLoader(null);
+    }
+
+    /**
+     * Test locating resources with the class loader created by
+     * defineModulesWithOneLoader. The class loader has the given class
+     * loader as its parent.
+     */
+    void testResourcesWithOneLoader(ClassLoader parent) throws Exception {
         Configuration cf = resolve("m1");
-        ClassLoader scl = ClassLoader.getSystemClassLoader();
-        ModuleLayer layer = ModuleLayer.boot().defineModulesWithOneLoader(cf, scl);
+        ModuleLayer layer = ModuleLayer.boot().defineModulesWithOneLoader(cf, parent);
 
         ClassLoader loader = layer.findLoader("m1");
         assertNotNull(loader);
+        assertTrue(loader.getParent() == parent);
 
         // check that getResource and getResources are consistent
         URL url1 = loader.getResource("module-info.class");
@@ -607,14 +617,24 @@ public class LayerAndLoadersTest {
      * defineModulesWithManyLoaders.
      */
     public void testResourcesWithManyLoaders() throws Exception {
+        testResourcesWithManyLoaders(ClassLoader.getSystemClassLoader());
+        testResourcesWithManyLoaders(null);
+    }
+
+    /**
+     * Test locating resources with class loaders created by
+     * defineModulesWithManyLoaders. The class loaders have the given class
+     * loader as their parent.
+     */
+    void testResourcesWithManyLoaders(ClassLoader parent) throws Exception {
         Configuration cf = resolve("m1");
-        ClassLoader scl = ClassLoader.getSystemClassLoader();
-        ModuleLayer layer = ModuleLayer.boot().defineModulesWithManyLoaders(cf, scl);
+        ModuleLayer layer = ModuleLayer.boot().defineModulesWithManyLoaders(cf, parent);
 
         for (Module m : layer.modules()) {
             String name = m.getName();
             ClassLoader loader = m.getClassLoader();
             assertNotNull(loader);
+            assertTrue(loader.getParent() == parent);
 
             // getResource should find the module-info.class for the module
             URL url = loader.getResource("module-info.class");
diff --git a/test/jdk/java/lang/module/ClassFileVersionsTest.java b/test/jdk/java/lang/module/ClassFileVersionsTest.java
new file mode 100644
index 00000000000..c7808adb223
--- /dev/null
+++ b/test/jdk/java/lang/module/ClassFileVersionsTest.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/**
+ * @test
+ * @modules java.base/jdk.internal.module
+ * @run testng ClassFileVersionsTest
+ * @summary Test parsing of module-info.class with different class file versions
+ */
+
+import java.lang.module.InvalidModuleDescriptorException;
+import java.lang.module.ModuleDescriptor;
+import java.lang.module.ModuleDescriptor.Requires.Modifier;
+import java.nio.ByteBuffer;
+import java.util.Set;
+
+import static java.lang.module.ModuleDescriptor.Requires.Modifier.*;
+
+import jdk.internal.module.ModuleInfoWriter;
+
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+import static org.testng.Assert.*;
+
+public class ClassFileVersionsTest {
+
+    // major, minor, modifiers for requires java.base
+    @DataProvider(name = "supported")
+    public Object[][] supported() {
+        return new Object[][]{
+                { 53,   0,  Set.of() },                      // JDK 9
+                { 53,   0,  Set.of(STATIC) },
+                { 53,   0,  Set.of(TRANSITIVE) },
+                { 53,   0,  Set.of(STATIC, TRANSITIVE) },
+
+                { 54,   0,  Set.of() },                      // JDK 10
+        };
+    }
+
+    // major, minor, modifiers for requires java.base
+    @DataProvider(name = "unsupported")
+    public Object[][] unsupported() {
+        return new Object[][]{
+                { 50,   0,  Set.of()},                       // JDK 6
+                { 51,   0,  Set.of()},                       // JDK 7
+                { 52,   0,  Set.of()},                       // JDK 8
+
+                { 54,   0,  Set.of(STATIC) },                // JDK 10
+                { 54,   0,  Set.of(TRANSITIVE) },
+                { 54,   0,  Set.of(STATIC, TRANSITIVE) },
+
+                { 55,   0,  Set.of()},                       // JDK 11
+        };
+    }
+
+    @Test(dataProvider = "supported")
+    public void testSupported(int major, int minor, Set<Modifier> ms) {
+        ModuleDescriptor descriptor = ModuleDescriptor.newModule("foo")
+                .requires(ms, "java.base")
+                .build();
+        ByteBuffer bb = ModuleInfoWriter.toByteBuffer(descriptor);
+        classFileVersion(bb, major, minor);
+        descriptor = ModuleDescriptor.read(bb);
+        assertEquals(descriptor.name(), "foo");
+    }
+
+    @Test(dataProvider = "unsupported",
+          expectedExceptions = InvalidModuleDescriptorException.class)
+    public void testUnsupported(int major, int minor, Set<Modifier> ms) {
+        ModuleDescriptor descriptor = ModuleDescriptor.newModule("foo")
+                .requires(ms, "java.base")
+                .build();
+        ByteBuffer bb = ModuleInfoWriter.toByteBuffer(descriptor);
+        classFileVersion(bb, major, minor);
+
+        // throws InvalidModuleDescriptorException
+        ModuleDescriptor.read(bb);
+    }
+
+    private void classFileVersion(ByteBuffer bb, int major, int minor) {
+        bb.putShort(4, (short) minor);
+        bb.putShort(6, (short) major);
+    }
+}
diff --git a/test/jdk/java/net/httpclient/APIErrors.java b/test/jdk/java/net/httpclient/APIErrors.java
deleted file mode 100644
index ea541f00b56..00000000000
--- a/test/jdk/java/net/httpclient/APIErrors.java
+++ /dev/null
@@ -1,193 +0,0 @@
-/*
- * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-
-/*
- * @test
- * @bug 8087112
- * @modules jdk.incubator.httpclient
- *          java.logging
- *          jdk.httpserver
- * @library /lib/testlibrary/
- * @build jdk.testlibrary.SimpleSSLContext ProxyServer
- * @build TestKit
- * @compile ../../../com/sun/net/httpserver/LogFilter.java
- * @compile ../../../com/sun/net/httpserver/FileServerHandler.java
- * @run main/othervm APIErrors
- * @summary  Basic checks for appropriate errors/exceptions thrown from the API
- */
-
-import com.sun.net.httpserver.HttpContext;
-import com.sun.net.httpserver.HttpHandler;
-import com.sun.net.httpserver.HttpServer;
-import com.sun.net.httpserver.HttpsServer;
-import java.io.IOException;
-import java.net.InetSocketAddress;
-import java.net.PasswordAuthentication;
-import java.net.ProxySelector;
-import java.net.URI;
-import jdk.incubator.http.HttpClient;
-import jdk.incubator.http.HttpRequest;
-import jdk.incubator.http.HttpResponse;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.Executors;
-import java.util.concurrent.ExecutorService;
-import java.util.function.Supplier;
-import static jdk.incubator.http.HttpResponse.BodyHandler.discard;
-
-public class APIErrors {
-
-    static ExecutorService serverExecutor = Executors.newCachedThreadPool();
-    static String httproot, fileuri;
-    static List<HttpClient> clients = new LinkedList<>();
-
-    public static void main(String[] args) throws Exception {
-        HttpServer server = createServer();
-
-        int port = server.getAddress().getPort();
-        System.out.println("HTTP server port = " + port);
-
-        httproot = "http://127.0.0.1:" + port + "/files/";
-        fileuri = httproot + "foo.txt";
-
-        HttpClient client = HttpClient.newHttpClient();
-
-        try {
-            test1();
-            test2();
-            //test3();
-        } finally {
-            server.stop(0);
-            serverExecutor.shutdownNow();
-            for (HttpClient c : clients)
-                ((ExecutorService)c.executor()).shutdownNow();
-        }
-    }
-
-    static void checkNonNull(Supplier<?> r) {
-        if (r.get() == null)
-            throw new RuntimeException("Unexpected null return:");
-    }
-
-    static void assertTrue(Supplier<Boolean> r) {
-        if (r.get() == false)
-            throw new RuntimeException("Assertion failure:");
-    }
-
-    // HttpClient.Builder
-    static void test1() throws Exception {
-        System.out.println("Test 1");
-        HttpClient.Builder cb = HttpClient.newBuilder();
-        TestKit.assertThrows(IllegalArgumentException.class, () -> cb.priority(-1));
-        TestKit.assertThrows(IllegalArgumentException.class, () -> cb.priority(0));
-        TestKit.assertThrows(IllegalArgumentException.class, () -> cb.priority(257));
-        TestKit.assertThrows(IllegalArgumentException.class, () -> cb.priority(500));
-        TestKit.assertNotThrows(() -> cb.priority(1));
-        TestKit.assertNotThrows(() -> cb.priority(256));
-        TestKit.assertNotThrows(() -> {
-            clients.add(cb.build());
-            clients.add(cb.build());
-        });
-    }
-
-    static void test2() throws Exception {
-        System.out.println("Test 2");
-        HttpClient.Builder cb = HttpClient.newBuilder();
-        InetSocketAddress addr = new InetSocketAddress("127.0.0.1", 5000);
-        cb.proxy(ProxySelector.of(addr));
-        HttpClient c = cb.build();
-        clients.add(c);
-        checkNonNull(()-> c.executor());
-        assertTrue(()-> c.followRedirects() == HttpClient.Redirect.NEVER);
-        assertTrue(()-> !c.authenticator().isPresent());
-    }
-
-    static URI accessibleURI() {
-        return URI.create(fileuri);
-    }
-
-    static HttpRequest request() {
-        return HttpRequest.newBuilder(accessibleURI()).GET().build();
-    }
-
-//    static void test3() throws Exception {
-//        System.out.println("Test 3");
-//        TestKit.assertThrows(IllegalStateException.class, ()-> {
-//            try {
-//                HttpRequest r1 = request();
-//                HttpResponse<Object> resp = r1.response(discard(null));
-//                HttpResponse<Object> resp1 = r1.response(discard(null));
-//            } catch (IOException |InterruptedException e) {
-//                throw new RuntimeException(e);
-//            }
-//        });
-//
-//        TestKit.assertThrows(IllegalStateException.class, ()-> {
-//            try {
-//                HttpRequest r1 = request();
-//                HttpResponse<Object> resp = r1.response(discard(null));
-//                HttpResponse<Object> resp1 = r1.responseAsync(discard(null)).get();
-//            } catch (IOException |InterruptedException | ExecutionException e) {
-//                throw new RuntimeException(e);
-//            }
-//        });
-//        TestKit.assertThrows(IllegalStateException.class, ()-> {
-//            try {
-//                HttpRequest r1 = request();
-//                HttpResponse<Object> resp1 = r1.responseAsync(discard(null)).get();
-//                HttpResponse<Object> resp = r1.response(discard(null));
-//            } catch (IOException |InterruptedException | ExecutionException e) {
-//                throw new RuntimeException(e);
-//            }
-//        });
-//    }
-
-    static class Auth extends java.net.Authenticator {
-        int count = 0;
-        @Override
-        protected PasswordAuthentication getPasswordAuthentication() {
-            if (count++ == 0) {
-                return new PasswordAuthentication("user", "passwd".toCharArray());
-            } else {
-                return new PasswordAuthentication("user", "goober".toCharArray());
-            }
-        }
-        int count() {
-            return count;
-        }
-    }
-
-    static HttpServer createServer() throws Exception {
-        HttpServer s = HttpServer.create(new InetSocketAddress(0), 0);
-        if (s instanceof HttpsServer)
-            throw new RuntimeException ("should not be httpsserver");
-
-        String root = System.getProperty("test.src") + "/docs";
-        s.createContext("/files", new FileServerHandler(root));
-        s.setExecutor(serverExecutor);
-        s.start();
-
-        return s;
-    }
-}
diff --git a/test/jdk/java/net/httpclient/AbstractNoBody.java b/test/jdk/java/net/httpclient/AbstractNoBody.java
new file mode 100644
index 00000000000..1ddb79ab2b1
--- /dev/null
+++ b/test/jdk/java/net/httpclient/AbstractNoBody.java
@@ -0,0 +1,215 @@
+/*
+ * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.InetSocketAddress;
+import java.util.concurrent.Executor;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import com.sun.net.httpserver.HttpExchange;
+import com.sun.net.httpserver.HttpHandler;
+import com.sun.net.httpserver.HttpServer;
+import com.sun.net.httpserver.HttpsConfigurator;
+import com.sun.net.httpserver.HttpsServer;
+import jdk.incubator.http.HttpClient;
+import javax.net.ssl.SSLContext;
+import jdk.testlibrary.SimpleSSLContext;
+import org.testng.annotations.AfterTest;
+import org.testng.annotations.BeforeTest;
+import org.testng.annotations.DataProvider;
+
+public abstract class AbstractNoBody {
+
+    SSLContext sslContext;
+    HttpServer httpTestServer;         // HTTP/1.1    [ 4 servers ]
+    HttpsServer httpsTestServer;       // HTTPS/1.1
+    Http2TestServer http2TestServer;   // HTTP/2 ( h2c )
+    Http2TestServer https2TestServer;  // HTTP/2 ( h2  )
+    String httpURI_fixed;
+    String httpURI_chunk;
+    String httpsURI_fixed;
+    String httpsURI_chunk;
+    String http2URI_fixed;
+    String http2URI_chunk;
+    String https2URI_fixed;
+    String https2URI_chunk;
+
+    static final String SIMPLE_STRING = "Hello world. Goodbye world";
+    static final int ITERATION_COUNT = 10;
+    // a shared executor helps reduce the amount of threads created by the test
+    static final Executor executor = Executors.newFixedThreadPool(ITERATION_COUNT * 2);
+    static final ExecutorService serverExecutor = Executors.newFixedThreadPool(ITERATION_COUNT * 4);
+
+    @DataProvider(name = "variants")
+    public Object[][] variants() {
+        return new Object[][]{
+                { httpURI_fixed,    false },
+                { httpURI_chunk,    false },
+                { httpsURI_fixed,   false },
+                { httpsURI_chunk,   false },
+                { http2URI_fixed,   false },
+                { http2URI_chunk,   false },
+                { https2URI_fixed,  false,},
+                { https2URI_chunk,  false },
+
+                { httpURI_fixed,    true },
+                { httpURI_chunk,    true },
+                { httpsURI_fixed,   true },
+                { httpsURI_chunk,   true },
+                { http2URI_fixed,   true },
+                { http2URI_chunk,   true },
+                { https2URI_fixed,  true,},
+                { https2URI_chunk,  true },
+        };
+    }
+
+    HttpClient newHttpClient() {
+        return HttpClient.newBuilder()
+                .executor(executor)
+                .sslContext(sslContext)
+                .build();
+    }
+
+    @BeforeTest
+    public void setup() throws Exception {
+        printStamp(START, "setup");
+        sslContext = new SimpleSSLContext().get();
+        if (sslContext == null)
+            throw new AssertionError("Unexpected null sslContext");
+
+        // HTTP/1.1
+        HttpHandler h1_fixedLengthNoBodyHandler = new HTTP1_FixedLengthNoBodyHandler();
+        HttpHandler h1_chunkNoBodyHandler = new HTTP1_ChunkedNoBodyHandler();
+        InetSocketAddress sa = new InetSocketAddress("localhost", 0);
+        httpTestServer = HttpServer.create(sa, 0);
+        httpTestServer.setExecutor(serverExecutor);
+        httpTestServer.createContext("/http1/noBodyFixed", h1_fixedLengthNoBodyHandler);
+        httpTestServer.createContext("/http1/noBodyChunk", h1_chunkNoBodyHandler);
+        httpURI_fixed = "http://127.0.0.1:" + httpTestServer.getAddress().getPort() + "/http1/noBodyFixed";
+        httpURI_chunk = "http://127.0.0.1:" + httpTestServer.getAddress().getPort() + "/http1/noBodyChunk";
+
+        httpsTestServer = HttpsServer.create(sa, 0);
+        httpsTestServer.setExecutor(serverExecutor);
+        httpsTestServer.setHttpsConfigurator(new HttpsConfigurator(sslContext));
+        httpsTestServer.createContext("/https1/noBodyFixed", h1_fixedLengthNoBodyHandler);
+        httpsTestServer.createContext("/https1/noBodyChunk", h1_chunkNoBodyHandler);
+        httpsURI_fixed = "https://127.0.0.1:" + httpsTestServer.getAddress().getPort() + "/https1/noBodyFixed";
+        httpsURI_chunk = "https://127.0.0.1:" + httpsTestServer.getAddress().getPort() + "/https1/noBodyChunk";
+
+        // HTTP/2
+        Http2Handler h2_fixedLengthNoBodyHandler = new HTTP2_FixedLengthNoBodyHandler();
+        Http2Handler h2_chunkedNoBodyHandler = new HTTP2_ChunkedNoBodyHandler();
+
+        http2TestServer = new Http2TestServer("127.0.0.1", false, 0, serverExecutor, null);
+        http2TestServer.addHandler(h2_fixedLengthNoBodyHandler, "/http2/noBodyFixed");
+        http2TestServer.addHandler(h2_chunkedNoBodyHandler, "/http2/noBodyChunk");
+        int port = http2TestServer.getAddress().getPort();
+        http2URI_fixed = "http://127.0.0.1:" + port + "/http2/noBodyFixed";
+        http2URI_chunk = "http://127.0.0.1:" + port + "/http2/noBodyChunk";
+
+        https2TestServer = new Http2TestServer("127.0.0.1", true, 0, serverExecutor, sslContext);
+        https2TestServer.addHandler(h2_fixedLengthNoBodyHandler, "/https2/noBodyFixed");
+        https2TestServer.addHandler(h2_chunkedNoBodyHandler, "/https2/noBodyChunk");
+        port = https2TestServer.getAddress().getPort();
+        https2URI_fixed = "https://127.0.0.1:" + port + "/https2/noBodyFixed";
+        https2URI_chunk = "https://127.0.0.1:" + port + "/https2/noBodyChunk";
+
+        httpTestServer.start();
+        httpsTestServer.start();
+        http2TestServer.start();
+        https2TestServer.start();
+        printStamp(END,"setup");
+    }
+
+    @AfterTest
+    public void teardown() throws Exception {
+        printStamp(START, "teardown");
+        httpTestServer.stop(0);
+        httpsTestServer.stop(0);
+        http2TestServer.stop();
+        https2TestServer.stop();
+        printStamp(END, "teardown");
+    }
+
+    static final long start = System.nanoTime();
+    static final String START = "start";
+    static final String END   = "end  ";
+    static long elapsed() { return (System.nanoTime() - start)/1000_000;}
+    void printStamp(String what, String fmt, Object... args) {
+        long elapsed = elapsed();
+        long sec = elapsed/1000;
+        long ms  = elapsed % 1000;
+        String time = sec > 0 ? sec + "sec " : "";
+        time = time + ms + "ms";
+        System.out.printf("%s: %s \t [%s]\t %s%n",
+                getClass().getSimpleName(), what, time, String.format(fmt,args));
+    }
+
+
+    static class HTTP1_FixedLengthNoBodyHandler implements HttpHandler {
+        @Override
+        public void handle(HttpExchange t) throws IOException {
+            //out.println("NoBodyHandler received request to " + t.getRequestURI());
+            try (InputStream is = t.getRequestBody()) {
+                is.readAllBytes();
+            }
+            t.sendResponseHeaders(200, -1); // no body
+        }
+    }
+
+    static class HTTP1_ChunkedNoBodyHandler implements HttpHandler {
+        @Override
+        public void handle(HttpExchange t) throws IOException {
+            //out.println("NoBodyHandler received request to " + t.getRequestURI());
+            try (InputStream is = t.getRequestBody()) {
+                is.readAllBytes();
+            }
+            t.sendResponseHeaders(200, 0); // chunked
+            t.getResponseBody().close();  // write nothing
+        }
+    }
+
+    static class HTTP2_FixedLengthNoBodyHandler implements Http2Handler {
+        @Override
+        public void handle(Http2TestExchange t) throws IOException {
+            //out.println("NoBodyHandler received request to " + t.getRequestURI());
+            try (InputStream is = t.getRequestBody()) {
+                is.readAllBytes();
+            }
+            t.sendResponseHeaders(200, 0);
+        }
+    }
+
+    static class HTTP2_ChunkedNoBodyHandler implements Http2Handler {
+        @Override
+        public void handle(Http2TestExchange t) throws IOException {
+            //out.println("NoBodyHandler received request to " + t.getRequestURI());
+            try (InputStream is = t.getRequestBody()) {
+                is.readAllBytes();
+            }
+            t.sendResponseHeaders(200, -1);
+            t.getResponseBody().close();  // write nothing
+        }
+    }
+}
diff --git a/test/jdk/java/net/httpclient/BasicAuthTest.java b/test/jdk/java/net/httpclient/BasicAuthTest.java
index d3acad31059..04ae3e8f39a 100644
--- a/test/jdk/java/net/httpclient/BasicAuthTest.java
+++ b/test/jdk/java/net/httpclient/BasicAuthTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2017, 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
@@ -45,7 +45,7 @@ import jdk.incubator.http.*;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
 import static java.nio.charset.StandardCharsets.US_ASCII;
-import static jdk.incubator.http.HttpRequest.BodyProcessor.fromString;
+import static jdk.incubator.http.HttpRequest.BodyPublisher.fromString;
 import static jdk.incubator.http.HttpResponse.BodyHandler.asString;
 
 public class BasicAuthTest {
diff --git a/test/jdk/java/net/httpclient/BodyProcessorInputStreamTest.java b/test/jdk/java/net/httpclient/BodyProcessorInputStreamTest.java
new file mode 100644
index 00000000000..d548c365f6a
--- /dev/null
+++ b/test/jdk/java/net/httpclient/BodyProcessorInputStreamTest.java
@@ -0,0 +1,161 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.net.URI;
+import jdk.incubator.http.HttpClient;
+import jdk.incubator.http.HttpHeaders;
+import jdk.incubator.http.HttpRequest;
+import jdk.incubator.http.HttpResponse;
+import java.nio.ByteBuffer;
+import java.nio.charset.Charset;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Locale;
+import java.util.Optional;
+import java.util.concurrent.ArrayBlockingQueue;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.CompletionStage;
+import java.util.concurrent.Flow;
+import java.util.stream.Stream;
+import static java.lang.System.err;
+
+/*
+ * @test
+ * @bug 8187503
+ * @summary An example on how to read a response body with InputStream...
+ * @run main/othervm -Dtest.debug=true BodyProcessorInputStreamTest
+ * @author daniel fuchs
+ */
+public class BodyProcessorInputStreamTest {
+
+    public static boolean DEBUG = Boolean.getBoolean("test.debug");
+
+    /**
+     * Examine the response headers to figure out the charset used to
+     * encode the body content.
+     * If the content type is not textual, returns an empty Optional.
+     * Otherwise, returns the body content's charset, defaulting to
+     * ISO-8859-1 if none is explicitly specified.
+     * @param headers The response headers.
+     * @return The charset to use for decoding the response body, if
+     *         the response body content is text/...
+     */
+    public static Optional<Charset> getCharset(HttpHeaders headers) {
+        Optional<String> contentType = headers.firstValue("Content-Type");
+        Optional<Charset> charset = Optional.empty();
+        if (contentType.isPresent()) {
+            final String[] values = contentType.get().split(";");
+            if (values[0].startsWith("text/")) {
+                charset = Optional.of(Stream.of(values)
+                    .map(x -> x.toLowerCase(Locale.ROOT))
+                    .map(String::trim)
+                    .filter(x -> x.startsWith("charset="))
+                    .map(x -> x.substring("charset=".length()))
+                    .findFirst()
+                    .orElse("ISO-8859-1"))
+                    .map(Charset::forName);
+            }
+        }
+        return charset;
+    }
+
+    public static void main(String[] args) throws Exception {
+        HttpClient client = HttpClient.newHttpClient();
+        HttpRequest request = HttpRequest
+            .newBuilder(new URI("http://hg.openjdk.java.net/jdk9/sandbox/jdk/shortlog/http-client-branch/"))
+            .GET()
+            .build();
+
+        // This example shows how to return an InputStream that can be used to
+        // start reading the response body before the response is fully received.
+        // In comparison, the snipet below (which uses
+        // HttpResponse.BodyHandler.asString()) obviously will not return before the
+        // response body is fully read:
+        //
+        // System.out.println(
+        //    client.sendAsync(request, HttpResponse.BodyHandler.asString()).get().body());
+
+        CompletableFuture<HttpResponse<InputStream>> handle =
+            client.sendAsync(request, HttpResponse.BodyHandler.asInputStream());
+        if (DEBUG) err.println("Request sent");
+
+        HttpResponse<InputStream> pending = handle.get();
+
+        // At this point, the response headers have been received, but the
+        // response body may not have arrived yet. This comes from
+        // the implementation of HttpResponseInputStream::getBody above,
+        // which returns an already completed completion stage, without
+        // waiting for any data.
+        // We can therefore access the headers - and the body, which
+        // is our live InputStream, without waiting...
+        HttpHeaders responseHeaders = pending.headers();
+
+        // Get the charset declared in the response headers.
+        // The optional will be empty if the content type is not
+        // of type text/...
+        Optional<Charset> charset = getCharset(responseHeaders);
+
+        try (InputStream is = pending.body();
+            // We assume a textual content type. Construct an InputStream
+            // Reader with the appropriate Charset.
+            // charset.get() will throw NPE if the content is not textual.
+            Reader r = new InputStreamReader(is, charset.get())) {
+
+            char[] buff = new char[32];
+            int off=0, n=0;
+            if (DEBUG) err.println("Start receiving response body");
+            if (DEBUG) err.println("Charset: " + charset.get());
+
+            // Start consuming the InputStream as the data arrives.
+            // Will block until there is something to read...
+            while ((n = r.read(buff, off, buff.length - off)) > 0) {
+                assert (buff.length - off) > 0;
+                assert n <= (buff.length - off);
+                if (n == (buff.length - off)) {
+                    System.out.print(buff);
+                    off = 0;
+                } else {
+                    off += n;
+                }
+                assert off < buff.length;
+            }
+
+            // last call to read may not have filled 'buff' completely.
+            // flush out the remaining characters.
+            assert off >= 0 && off < buff.length;
+            for (int i=0; i < off; i++) {
+                System.out.print(buff[i]);
+            }
+
+            // We're done!
+            System.out.println("Done!");
+        }
+    }
+
+}
diff --git a/test/jdk/java/net/httpclient/BufferingSubscriberCancelTest.java b/test/jdk/java/net/httpclient/BufferingSubscriberCancelTest.java
new file mode 100644
index 00000000000..684e1f6ee2d
--- /dev/null
+++ b/test/jdk/java/net/httpclient/BufferingSubscriberCancelTest.java
@@ -0,0 +1,217 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.nio.ByteBuffer;
+import java.util.List;
+import java.util.concurrent.CompletionStage;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Flow.Subscription;
+import java.util.concurrent.SubmissionPublisher;
+import java.util.function.IntSupplier;
+import java.util.stream.IntStream;
+import jdk.incubator.http.HttpResponse.BodySubscriber;
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+import static java.lang.Long.MAX_VALUE;
+import static java.lang.Long.MIN_VALUE;
+import static java.lang.System.out;
+import static java.nio.ByteBuffer.wrap;
+import static java.util.concurrent.TimeUnit.SECONDS;
+import static jdk.incubator.http.HttpResponse.BodySubscriber.buffering;
+import static org.testng.Assert.*;
+
+/*
+ * @test
+ * @summary Direct test for HttpResponse.BodySubscriber.buffering() cancellation
+ * @run testng/othervm BufferingSubscriberCancelTest
+ */
+
+public class BufferingSubscriberCancelTest {
+
+    @DataProvider(name = "bufferSizes")
+    public Object[][] bufferSizes() {
+        return new Object[][]{
+            // bufferSize should be irrelevant
+            {1}, {100}, {511}, {512}, {513}, {1024}, {2047}, {2048}
+        };
+    }
+
+    @Test(dataProvider = "bufferSizes")
+    public void cancelWithoutAnyItemsPublished(int bufferSize) throws Exception {
+        ExecutorService executor = Executors.newFixedThreadPool(1);
+        SubmissionPublisher<List<ByteBuffer>> publisher =
+                new SubmissionPublisher<>(executor, 1);
+
+        CountDownLatch gate = new CountDownLatch(1);  // single onSubscribe
+        ExposingSubscriber exposingSubscriber = new ExposingSubscriber(gate);
+        BodySubscriber subscriber = buffering(exposingSubscriber, bufferSize);
+        publisher.subscribe(subscriber);
+        gate.await(30, SECONDS);
+        assertEqualsWithRetry(publisher::getNumberOfSubscribers, 1);
+        exposingSubscriber.subscription.cancel();
+        assertEqualsWithRetry(publisher::getNumberOfSubscribers, 0);
+
+        // further cancels/requests should be a no-op
+        Subscription s = exposingSubscriber.subscription;
+        s.cancel(); s.request(1);
+        s.cancel(); s.request(100); s.cancel();
+        s.cancel(); s.request(MAX_VALUE); s.cancel(); s.cancel();
+        s.cancel(); s.cancel(); s.cancel(); s.cancel();
+        s.request(MAX_VALUE); s.request(MAX_VALUE); s.request(MAX_VALUE);
+        s.request(-1); s.request(-100); s.request(MIN_VALUE);
+        assertEqualsWithRetry(publisher::getNumberOfSubscribers, 0);
+        executor.shutdown();
+    }
+
+    @DataProvider(name = "sizeAndItems")
+    public Object[][] sizeAndItems() {
+        return new Object[][] {
+            // bufferSize and item bytes must be equal to count onNext calls
+            // bufferSize        items
+            { 1,   List.of(wrap(new byte[] { 1 }))                             },
+            { 2,   List.of(wrap(new byte[] { 1, 2 }))                          },
+            { 3,   List.of(wrap(new byte[] { 1, 2, 3}))                        },
+            { 4,   List.of(wrap(new byte[] { 1, 2 , 3, 4}))                    },
+            { 5,   List.of(wrap(new byte[] { 1, 2 , 3, 4, 5}))                 },
+            { 6,   List.of(wrap(new byte[] { 1, 2 , 3, 4, 5, 6}))              },
+            { 7,   List.of(wrap(new byte[] { 1, 2 , 3, 4, 5, 6, 7}))           },
+            { 8,   List.of(wrap(new byte[] { 1, 2 , 3, 4, 5, 6, 7, 8}))        },
+            { 9,   List.of(wrap(new byte[] { 1, 2 , 3, 4, 5, 6, 7, 8, 9}))     },
+            { 10,  List.of(wrap(new byte[] { 1, 2 , 3, 4, 5, 6, 7, 8, 9, 10})) },
+        };
+    }
+
+    @Test(dataProvider = "sizeAndItems")
+    public void cancelWithItemsPublished(int bufferSize, List<ByteBuffer> items)
+        throws Exception
+    {
+        ExecutorService executor = Executors.newFixedThreadPool(1);
+        SubmissionPublisher<List<ByteBuffer>> publisher =
+                new SubmissionPublisher<>(executor, 24);
+
+        final int ITERATION_TIMES = 10;
+        // onSubscribe + onNext ITERATION_TIMES
+        CountDownLatch gate = new CountDownLatch(1 + ITERATION_TIMES);
+        ExposingSubscriber exposingSubscriber = new ExposingSubscriber(gate);
+        BodySubscriber subscriber = buffering(exposingSubscriber, bufferSize);
+        publisher.subscribe(subscriber);
+
+        assertEqualsWithRetry(publisher::getNumberOfSubscribers, 1);
+        IntStream.range(0, ITERATION_TIMES).forEach(x -> publisher.submit(items));
+        gate.await(30, SECONDS);
+        exposingSubscriber.subscription.cancel();
+        IntStream.range(0, ITERATION_TIMES+1).forEach(x -> publisher.submit(items));
+
+        assertEqualsWithRetry(publisher::getNumberOfSubscribers, 0);
+        assertEquals(exposingSubscriber.onNextInvocations, ITERATION_TIMES);
+        executor.shutdown();
+    }
+
+    // same as above but with more racy conditions, do not wait on the gate
+    @Test(dataProvider = "sizeAndItems")
+    public void cancelWithItemsPublishedNoWait(int bufferSize, List<ByteBuffer> items)
+        throws Exception
+    {
+        ExecutorService executor = Executors.newFixedThreadPool(1);
+        SubmissionPublisher<List<ByteBuffer>> publisher =
+                new SubmissionPublisher<>(executor, 24);
+
+        final int ITERATION_TIMES = 10;
+        // any callback will so, since onSub is guaranteed to be before onNext
+        CountDownLatch gate = new CountDownLatch(1);
+        ExposingSubscriber exposingSubscriber = new ExposingSubscriber(gate);
+        BodySubscriber subscriber = buffering(exposingSubscriber, bufferSize);
+        publisher.subscribe(subscriber);
+
+        IntStream.range(0, ITERATION_TIMES).forEach(x -> publisher.submit(items));
+        gate.await(30, SECONDS);
+        exposingSubscriber.subscription.cancel();
+        IntStream.range(0, ITERATION_TIMES+1).forEach(x -> publisher.submit(items));
+
+        int onNextInvocations = exposingSubscriber.onNextInvocations;
+        assertTrue(onNextInvocations <= ITERATION_TIMES,
+                   "Expected <= " + ITERATION_TIMES + ", got " + onNextInvocations);
+        executor.shutdown();
+    }
+
+    static class ExposingSubscriber implements BodySubscriber<Void> {
+        final CountDownLatch gate;
+        volatile Subscription subscription;
+        volatile int onNextInvocations;
+
+        ExposingSubscriber(CountDownLatch gate) {
+            this.gate = gate;
+        }
+
+        @Override
+        public void onSubscribe(Subscription subscription) {
+            //out.println("onSubscribe " + subscription);
+            this.subscription = subscription;
+            gate.countDown();
+            subscription.request(MAX_VALUE); // forever
+        }
+
+        @Override
+        public void onNext(List<ByteBuffer> item) {
+            //out.println("onNext " + item);
+            onNextInvocations++;
+            gate.countDown();
+        }
+
+        @Override
+        public void onError(Throwable throwable) {
+            out.println("onError " + throwable);
+        }
+
+        @Override
+        public void onComplete() {
+            out.println("onComplete ");
+        }
+
+        @Override
+        public CompletionStage<Void> getBody() {
+            throw new UnsupportedOperationException("getBody is unsupported");
+        }
+    }
+
+    // There is a race between cancellation and subscriber callbacks, the
+    // following mechanism retries a number of times to allow for this race. The
+    // only requirement is that the expected result is actually observed.
+
+    static final int TEST_RECHECK_TIMES = 30;
+
+    static void assertEqualsWithRetry(IntSupplier actualSupplier, int expected)
+        throws Exception
+    {
+        int actual = expected + 1; // anything other than expected
+        for (int i=0; i< TEST_RECHECK_TIMES; i++) {
+            actual = actualSupplier.getAsInt();
+            if (actual == expected)
+                return;
+            Thread.sleep(100);
+        }
+        assertEquals(actual, expected); // will fail with the usual testng message
+    }
+}
diff --git a/test/jdk/java/net/httpclient/BufferingSubscriberErrorCompleteTest.java b/test/jdk/java/net/httpclient/BufferingSubscriberErrorCompleteTest.java
new file mode 100644
index 00000000000..ed875bc3dc6
--- /dev/null
+++ b/test/jdk/java/net/httpclient/BufferingSubscriberErrorCompleteTest.java
@@ -0,0 +1,226 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.CompletionStage;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.CyclicBarrier;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Flow.Subscription;
+import java.util.concurrent.Phaser;
+import java.util.concurrent.SubmissionPublisher;
+import java.util.function.IntSupplier;
+import java.util.stream.IntStream;
+import jdk.incubator.http.HttpResponse.BodySubscriber;
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+import static java.lang.Long.MAX_VALUE;
+import static java.lang.Long.MIN_VALUE;
+import static java.lang.System.out;
+import static java.nio.ByteBuffer.wrap;
+import static java.util.concurrent.TimeUnit.SECONDS;
+import static jdk.incubator.http.HttpResponse.BodySubscriber.buffering;
+import static org.testng.Assert.*;
+
+/*
+ * @test
+ * @summary Test for HttpResponse.BodySubscriber.buffering() onError/onComplete
+ * @run testng/othervm BufferingSubscriberErrorCompleteTest
+ */
+
+public class BufferingSubscriberErrorCompleteTest {
+
+    @DataProvider(name = "illegalDemand")
+    public Object[][] illegalDemand() {
+        return new Object[][]{
+            {0L}, {-1L}, {-5L}, {-100L}, {-101L}, {-100_001L}, {MIN_VALUE}
+        };
+    }
+
+    @Test(dataProvider = "illegalDemand")
+    public void illegalRequest(long demand) throws Exception {
+        ExecutorService executor = Executors.newFixedThreadPool(1);
+        SubmissionPublisher<List<ByteBuffer>> publisher =
+                new SubmissionPublisher<>(executor, 1);
+
+        Phaser gate = new Phaser(2);  // single onSubscribe and onError
+        ExposingSubscriber exposingSubscriber = new ExposingSubscriber(gate);
+        BodySubscriber subscriber = buffering(exposingSubscriber, 1);
+        publisher.subscribe(subscriber);
+        gate.arriveAndAwaitAdvance();
+
+        Subscription s = exposingSubscriber.subscription;
+        int previous = exposingSubscriber.onErrorInvocations;
+        s.request(demand);
+        gate.arriveAndAwaitAdvance();
+
+        assertEquals(previous + 1, exposingSubscriber.onErrorInvocations);
+        assertTrue(exposingSubscriber.throwable instanceof IllegalArgumentException,
+                "Expected IAE, got:" + exposingSubscriber.throwable);
+
+        furtherCancelsRequestsShouldBeNoOp(s);
+        assertEquals(exposingSubscriber.onErrorInvocations, 1);
+        executor.shutdown();
+    }
+
+
+    @DataProvider(name = "bufferAndItemSizes")
+    public Object[][] bufferAndItemSizes() {
+        List<Object[]> values = new ArrayList<>();
+
+        for (int bufferSize : new int[] { 1, 5, 10, 100, 1000 })
+            for (int items : new int[]  { 0, 1, 2, 5, 9, 10, 11, 15, 29, 99 })
+                values.add(new Object[] { bufferSize, items });
+
+        return values.stream().toArray(Object[][]::new);
+    }
+
+    @Test(dataProvider = "bufferAndItemSizes")
+    public void onErrorFromPublisher(int bufferSize,
+                                     int numberOfItems)
+        throws Exception
+    {
+        ExecutorService executor = Executors.newFixedThreadPool(1);
+        SubmissionPublisher<List<ByteBuffer>> publisher =
+                new SubmissionPublisher<>(executor, 1);
+
+        // onSubscribe + onError + this thread
+        Phaser gate = new Phaser(3);
+        ExposingSubscriber exposingSubscriber = new ExposingSubscriber(gate);
+        BodySubscriber subscriber = buffering(exposingSubscriber, bufferSize);
+        publisher.subscribe(subscriber);
+
+        List<ByteBuffer> item = List.of(wrap(new byte[] { 1 }));
+        IntStream.range(0, numberOfItems).forEach(x -> publisher.submit(item));
+        Throwable t = new Throwable("a message from me to me");
+        publisher.closeExceptionally(t);
+
+        gate.arriveAndAwaitAdvance();
+
+        Subscription s = exposingSubscriber.subscription;
+
+        assertEquals(exposingSubscriber.onErrorInvocations, 1);
+        assertEquals(exposingSubscriber.onCompleteInvocations, 0);
+        assertEquals(exposingSubscriber.throwable, t);
+        assertEquals(exposingSubscriber.throwable.getMessage(),
+                     "a message from me to me");
+
+        furtherCancelsRequestsShouldBeNoOp(s);
+        assertEquals(exposingSubscriber.onErrorInvocations, 1);
+        assertEquals(exposingSubscriber.onCompleteInvocations, 0);
+        executor.shutdown();
+    }
+
+    @Test(dataProvider = "bufferAndItemSizes")
+    public void onCompleteFromPublisher(int bufferSize,
+                                        int numberOfItems)
+        throws Exception
+    {
+        ExecutorService executor = Executors.newFixedThreadPool(1);
+        SubmissionPublisher<List<ByteBuffer>> publisher =
+                new SubmissionPublisher<>(executor, 1);
+
+        // onSubscribe + onComplete + this thread
+        Phaser gate = new Phaser(3);
+        ExposingSubscriber exposingSubscriber = new ExposingSubscriber(gate);
+        BodySubscriber subscriber = buffering(exposingSubscriber, bufferSize);
+        publisher.subscribe(subscriber);
+
+        List<ByteBuffer> item = List.of(wrap(new byte[] { 1 }));
+        IntStream.range(0, numberOfItems).forEach(x -> publisher.submit(item));
+        publisher.close();
+
+        gate.arriveAndAwaitAdvance();
+
+        Subscription s = exposingSubscriber.subscription;
+
+        assertEquals(exposingSubscriber.onErrorInvocations, 0);
+        assertEquals(exposingSubscriber.onCompleteInvocations, 1);
+        assertEquals(exposingSubscriber.throwable, null);
+
+        furtherCancelsRequestsShouldBeNoOp(s);
+        assertEquals(exposingSubscriber.onErrorInvocations, 0);
+        assertEquals(exposingSubscriber.onCompleteInvocations, 1);
+        assertEquals(exposingSubscriber.throwable, null);
+        executor.shutdown();
+    }
+
+    static class ExposingSubscriber implements BodySubscriber<Void> {
+        final Phaser gate;
+        volatile Subscription subscription;
+        volatile int onNextInvocations;
+        volatile int onErrorInvocations;
+        volatile int onCompleteInvocations;
+        volatile Throwable throwable;
+
+        ExposingSubscriber(Phaser gate) {
+            this.gate = gate;
+        }
+
+        @Override
+        public void onSubscribe(Subscription subscription) {
+            //out.println("onSubscribe " + subscription);
+            this.subscription = subscription;
+            subscription.request(MAX_VALUE);
+            gate.arrive();
+        }
+
+        @Override
+        public void onNext(List<ByteBuffer> item) {
+            //out.println("onNext " + item);
+            onNextInvocations++;
+        }
+
+        @Override
+        public void onError(Throwable throwable) {
+            //out.println("onError " + throwable);
+            this.throwable = throwable;
+            onErrorInvocations++;
+            gate.arrive();
+        }
+
+        @Override
+        public void onComplete() {
+            //out.println("onComplete ");
+            onCompleteInvocations++;
+            gate.arrive();
+        }
+
+        @Override
+        public CompletionStage<Void> getBody() {
+            throw new UnsupportedOperationException("getBody is unsupported");
+        }
+    }
+
+    static void furtherCancelsRequestsShouldBeNoOp(Subscription s) {
+        s.cancel(); s.request(1);
+        s.cancel(); s.request(100); s.cancel();
+        s.cancel(); s.request(MAX_VALUE); s.cancel(); s.cancel();
+        s.cancel(); s.cancel(); s.cancel(); s.cancel();
+        s.request(MAX_VALUE); s.request(MAX_VALUE); s.request(MAX_VALUE);
+        s.request(-1); s.request(-100); s.request(MIN_VALUE);
+    }
+}
diff --git a/test/jdk/java/net/httpclient/BufferingSubscriberTest.java b/test/jdk/java/net/httpclient/BufferingSubscriberTest.java
new file mode 100644
index 00000000000..1a246f83caa
--- /dev/null
+++ b/test/jdk/java/net/httpclient/BufferingSubscriberTest.java
@@ -0,0 +1,472 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.nio.ByteBuffer;
+import java.util.List;
+import java.util.Random;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.CompletionStage;
+import java.util.concurrent.Executor;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Flow;
+import java.util.concurrent.Flow.Subscription;
+import java.util.concurrent.SubmissionPublisher;
+import java.util.function.BiConsumer;
+import jdk.incubator.http.HttpResponse.BodyHandler;
+import jdk.incubator.http.HttpResponse.BodySubscriber;
+import jdk.test.lib.RandomFactory;
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+import static java.lang.Long.MAX_VALUE;
+import static java.lang.Long.min;
+import static java.lang.System.out;
+import static java.util.concurrent.CompletableFuture.delayedExecutor;
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+import static org.testng.Assert.*;
+
+/*
+ * @test
+ * @bug 8184285
+ * @summary Direct test for HttpResponse.BodySubscriber.buffering() API
+ * @key randomness
+ * @library /test/lib
+ * @build jdk.test.lib.RandomFactory
+ * @run testng/othervm -Djdk.internal.httpclient.debug=true BufferingSubscriberTest
+ */
+
+public class BufferingSubscriberTest {
+
+    // If we compute that a test will take less that 10s
+    // we judge it acceptable
+    static final long LOWER_THRESHOLD = 10_000; // 10 sec.
+    // If we compute that a test will take more than 20 sec
+    // we judge it problematic: we will try to adjust the
+    // buffer sizes, and if we can't we will print a warning
+    static final long UPPER_THRESHOLD = 20_000; // 20 sec.
+
+    static final Random random = RandomFactory.getRandom();
+    static final long start = System.nanoTime();
+    static final String START = "start";
+    static final String END   = "end  ";
+    static long elapsed() { return (System.nanoTime() - start)/1000_000;}
+    static void printStamp(String what, String fmt, Object... args) {
+        long elapsed = elapsed();
+        long sec = elapsed/1000;
+        long ms  = elapsed % 1000;
+        String time = sec > 0 ? sec + "sec " : "";
+        time = time + ms + "ms";
+        out.println(what + "\t ["+time+"]\t "+ String.format(fmt,args));
+    }
+    @DataProvider(name = "negatives")
+    public Object[][] negatives() {
+        return new Object[][] {  { 0 }, { -1 }, { -1000 } };
+    }
+
+    @Test(dataProvider = "negatives", expectedExceptions = IllegalArgumentException.class)
+    public void subscriberThrowsIAE(int bufferSize) {
+        printStamp(START, "subscriberThrowsIAE(%d)", bufferSize);
+        try {
+            BodySubscriber<?> bp = BodySubscriber.asByteArray();
+            BodySubscriber.buffering(bp, bufferSize);
+        } finally {
+            printStamp(END, "subscriberThrowsIAE(%d)", bufferSize);
+        }
+    }
+
+    @Test(dataProvider = "negatives", expectedExceptions = IllegalArgumentException.class)
+    public void handlerThrowsIAE(int bufferSize) {
+        printStamp(START, "handlerThrowsIAE(%d)", bufferSize);
+        try {
+            BodyHandler<?> bp = BodyHandler.asByteArray();
+            BodyHandler.buffering(bp, bufferSize);
+        } finally {
+            printStamp(END, "handlerThrowsIAE(%d)", bufferSize);
+        }
+    }
+
+    // ---
+
+    @DataProvider(name = "config")
+    public Object[][] config() {
+        return new Object[][] {
+            // iterations delayMillis numBuffers bufferSize maxBufferSize minBufferSize
+            { 1,              0,          1,         1,         2,            1   },
+            { 1,              5,          1,         100,       2,            1   },
+            { 1,              0,          1,         10,        1000,         1   },
+            { 1,              10,         1,         10,        1000,         1   },
+            { 1,              0,          1,         1000,      1000,         10  },
+            { 1,              0,          10,        1000,      1000,         50  },
+            { 1,              0,          1000,      10 ,       1000,         50  },
+            { 1,              100,        1,         1000 * 4,  1000,         50  },
+            { 100,            0,          1000,      1,         2,            1   },
+            { 3,              0,          4,         5006,      1000,         50  },
+            { 20,             0,          100,       4888,      1000,         100 },
+            { 16,             10,         1000,      50 ,       1000,         100 },
+            { 16,             10,         1000,      50 ,       657,          657 },
+        };
+    }
+
+    @Test(dataProvider = "config")
+    public void test(int iterations,
+                     int delayMillis,
+                     int numBuffers,
+                     int bufferSize,
+                     int maxBufferSize,
+                     int minbufferSize) {
+        for (long perRequestAmount : new long[] { 1L, MAX_VALUE })
+            test(iterations,
+                 delayMillis,
+                 numBuffers,
+                 bufferSize,
+                 maxBufferSize,
+                 minbufferSize,
+                 perRequestAmount);
+    }
+
+    volatile boolean onNextThrew;
+
+    BiConsumer<Flow.Subscriber<?>, ? super Throwable> onNextThrowHandler =
+            (sub, ex) -> {
+                onNextThrew = true;
+                System.out.println("onNext threw " + ex);
+                ex.printStackTrace();
+    };
+
+    public void test(int iterations,
+                     int delayMillis,
+                     int numBuffers,
+                     int bufferSize,
+                     int maxBufferSize,
+                     int minBufferSize,
+                     long requestAmount) {
+        ExecutorService executor = Executors.newFixedThreadPool(1);
+        try {
+            out.printf("Iterations %d\n", iterations);
+            for (int i=0; i<iterations; i++ ) {
+                printStamp(START, "Iteration %d", i);
+                try {
+                    SubmissionPublisher<List<ByteBuffer>> publisher =
+                            new SubmissionPublisher<>(executor,
+                                                      1, // lock-step with the publisher, for now
+                                                      onNextThrowHandler);
+                    CompletableFuture<?> cf = sink(publisher,
+                            delayMillis,
+                            numBuffers * bufferSize,
+                            requestAmount,
+                            maxBufferSize,
+                            minBufferSize);
+                    source(publisher, numBuffers, bufferSize);
+                    publisher.close();
+                    cf.join();
+                } finally {
+                    printStamp(END, "Iteration %d\n", i);
+                }
+            }
+
+            assertFalse(onNextThrew, "Unexpected onNextThrew, check output");
+
+            out.println("OK");
+        } finally {
+            executor.shutdown();
+        }
+    }
+
+    static long accumulatedDataSize(List<ByteBuffer> bufs) {
+        return bufs.stream().mapToLong(ByteBuffer::remaining).sum();
+    }
+
+    /** Returns a new BB with its contents set to monotonically increasing
+     * values, staring at the given start index and wrapping every 100. */
+    static ByteBuffer allocateBuffer(int size, int startIdx) {
+        ByteBuffer b = ByteBuffer.allocate(size);
+        for (int i=0; i<size; i++)
+            b.put((byte)((startIdx + i) % 100));
+        b.position(0);
+        return b;
+    }
+
+    static class TestSubscriber implements BodySubscriber<Integer> {
+        final int delayMillis;
+        final int bufferSize;
+        final int expectedTotalSize;
+        final long requestAmount;
+        final CompletableFuture<Integer> completion;
+        final Executor delayedExecutor;
+        volatile Flow.Subscription subscription;
+
+        TestSubscriber(int bufferSize,
+                       int delayMillis,
+                       int expectedTotalSize,
+                       long requestAmount) {
+            this.bufferSize = bufferSize;
+            this.completion = new CompletableFuture<>();
+            this.delayMillis = delayMillis;
+            this.delayedExecutor = delayedExecutor(delayMillis, MILLISECONDS);
+            this.expectedTotalSize = expectedTotalSize;
+            this.requestAmount = requestAmount;
+        }
+
+        /**
+         * Example of a factory method which would decorate a buffering
+         * subscriber to create a new subscriber dependent on buffering capability.
+         * <p>
+         * The integer type parameter simulates the body just by counting the
+         * number of bytes in the body.
+         */
+        static BodySubscriber<Integer> createSubscriber(int bufferSize,
+                                                        int delay,
+                                                        int expectedTotalSize,
+                                                        long requestAmount) {
+            TestSubscriber s = new TestSubscriber(bufferSize,
+                                                delay,
+                                                expectedTotalSize,
+                                                requestAmount);
+            return BodySubscriber.buffering(s, bufferSize);
+        }
+
+        private void requestMore() {
+            subscription.request(requestAmount);
+        }
+
+        @Override
+        public void onSubscribe(Subscription subscription) {
+            assertNull(this.subscription);
+            this.subscription = subscription;
+            if (delayMillis > 0)
+                delayedExecutor.execute(this::requestMore);
+            else
+                requestMore();
+        }
+
+        volatile int wrongSizes;
+        volatile int totalBytesReceived;
+        volatile int onNextInvocations;
+        volatile long lastSeenSize = -1;
+        volatile boolean noMoreOnNext; // false
+        volatile int index; // 0
+        volatile long count;
+
+        @Override
+        public void onNext(List<ByteBuffer> items) {
+            try {
+                long sz = accumulatedDataSize(items);
+                boolean printStamp = delayMillis > 0
+                        && requestAmount < Long.MAX_VALUE
+                        && count % 20 == 0;
+                if (printStamp) {
+                    printStamp("stamp", "count=%d sz=%d accumulated=%d",
+                            count, sz, (totalBytesReceived + sz));
+                }
+                count++;
+                onNextInvocations++;
+                assertNotEquals(sz, 0L, "Unexpected empty buffers");
+                items.stream().forEach(b -> assertEquals(b.position(), 0));
+                assertFalse(noMoreOnNext);
+
+                if (sz != bufferSize) {
+                    String msg = sz + ", should be less than bufferSize, " + bufferSize;
+                    assertTrue(sz < bufferSize, msg);
+                    assertTrue(lastSeenSize == -1 || lastSeenSize == bufferSize);
+                    noMoreOnNext = true;
+                    wrongSizes++;
+                    printStamp("onNext",
+                            "Possibly received last buffer: sz=%d, accumulated=%d, total=%d",
+                            sz, totalBytesReceived, totalBytesReceived + sz);
+                } else {
+                    assertEquals(sz, bufferSize, "Expected to receive exactly bufferSize");
+                }
+                lastSeenSize = sz;
+
+                // Ensure expected contents
+                for (ByteBuffer b : items) {
+                    while (b.hasRemaining()) {
+                        assertEquals(b.get(), (byte) (index % 100));
+                        index++;
+                    }
+                }
+
+                totalBytesReceived += sz;
+                assertEquals(totalBytesReceived, index);
+                if (delayMillis > 0 && ((expectedTotalSize - totalBytesReceived) > bufferSize))
+                    delayedExecutor.execute(this::requestMore);
+                else
+                    requestMore();
+            } catch (Throwable t) {
+                completion.completeExceptionally(t);
+            }
+        }
+
+        @Override
+        public void onError(Throwable throwable) {
+            completion.completeExceptionally(throwable);
+        }
+
+        @Override
+        public void onComplete() {
+            if (wrongSizes > 1) { // allow just the final item to be smaller
+                String msg = "Wrong sizes. Expected no more than 1. [" + this + "]";
+                completion.completeExceptionally(new Throwable(msg));
+            }
+            if (totalBytesReceived != expectedTotalSize) {
+                String msg = "Wrong number of bytes. [" + this + "]";
+                completion.completeExceptionally(new Throwable(msg));
+            } else {
+                completion.complete(totalBytesReceived);
+            }
+        }
+
+        @Override
+        public CompletionStage<Integer> getBody() {
+            return completion;
+        }
+
+        @Override
+        public String toString() {
+            StringBuilder sb = new StringBuilder();
+            sb.append(super.toString());
+            sb.append(", bufferSize=").append(bufferSize);
+            sb.append(", onNextInvocations=").append(onNextInvocations);
+            sb.append(", totalBytesReceived=").append(totalBytesReceived);
+            sb.append(", expectedTotalSize=").append(expectedTotalSize);
+            sb.append(", requestAmount=").append(requestAmount);
+            sb.append(", lastSeenSize=").append(lastSeenSize);
+            sb.append(", wrongSizes=").append(wrongSizes);
+            sb.append(", index=").append(index);
+            return sb.toString();
+        }
+    }
+
+    /**
+     * Publishes data, through the given publisher, using the main thread.
+     *
+     * Note: The executor supplied when creating the SubmissionPublisher provides
+     * the threads for executing the Subscribers.
+     *
+     * @param publisher the publisher
+     * @param numBuffers the number of buffers to send ( before splitting in two )
+     * @param bufferSize the total size of the data to send ( before splitting in two )
+     */
+    static void source(SubmissionPublisher<List<ByteBuffer>> publisher,
+                       int numBuffers,
+                       int bufferSize) {
+        printStamp("source","Publishing %d buffers of size %d each", numBuffers, bufferSize);
+        int index = 0;
+        for (int i=0; i<numBuffers; i++) {
+            int chunkSize = random.nextInt(bufferSize);
+            ByteBuffer buf1 = allocateBuffer(chunkSize, index);
+            index += chunkSize;
+            ByteBuffer buf2 = allocateBuffer(bufferSize - chunkSize, index);
+            index += bufferSize - chunkSize;
+            publisher.submit(List.of(buf1, buf2));
+        }
+        printStamp("source", "complete");
+    }
+
+    /**
+     * Creates and subscribes Subscribers that receive data from the given
+     * publisher.
+     *
+     * @param publisher the publisher
+     * @param delayMillis time, in milliseconds, to delay the Subscription
+     *                    requesting more bytes ( for simulating slow consumption )
+     * @param expectedTotalSize the total number of bytes expected to be received
+     *                          by the subscribers
+     * @return a CompletableFuture which completes when the subscription is complete
+     */
+    static CompletableFuture<?> sink(SubmissionPublisher<List<ByteBuffer>> publisher,
+                                     int delayMillis,
+                                     int expectedTotalSize,
+                                     long requestAmount,
+                                     int maxBufferSize,
+                                     int minBufferSize) {
+        int bufferSize = chooseBufferSize(maxBufferSize,
+                                          minBufferSize,
+                                          delayMillis,
+                                          expectedTotalSize,
+                                          requestAmount);
+        assert bufferSize > 0;
+        assert bufferSize >= minBufferSize;
+        assert bufferSize <= maxBufferSize;
+        BodySubscriber<Integer> sub = TestSubscriber.createSubscriber(bufferSize,
+                                                                    delayMillis,
+                                                                    expectedTotalSize,
+                                                                    requestAmount);
+        publisher.subscribe(sub);
+        printStamp("sink","Subscriber reads data with buffer size: %d", bufferSize);
+        out.printf("Subscription delay is %d msec\n", delayMillis);
+        long delay = (((long)delayMillis * expectedTotalSize) / bufferSize) / requestAmount;
+        out.printf("Minimum total delay is %d sec %d ms\n", delay / 1000, delay % 1000);
+        out.printf("Request amount is %d items\n", requestAmount);
+        return sub.getBody().toCompletableFuture();
+    }
+
+    static int chooseBufferSize(int maxBufferSize,
+                                int minBufferSize,
+                                int delaysMillis,
+                                int expectedTotalSize,
+                                long requestAmount) {
+        assert minBufferSize > 0 && maxBufferSize > 0 && requestAmount > 0;
+        int bufferSize = maxBufferSize == minBufferSize ? maxBufferSize :
+                (random.nextInt(maxBufferSize - minBufferSize)
+                    + minBufferSize);
+        if (requestAmount == Long.MAX_VALUE) return bufferSize;
+        long minDelay = (((long)delaysMillis * expectedTotalSize) / maxBufferSize)
+                / requestAmount;
+        long maxDelay = (((long)delaysMillis * expectedTotalSize) / minBufferSize)
+                / requestAmount;
+        // if the maximum delay is < 10s just take a random number between min and max.
+        if (maxDelay <= LOWER_THRESHOLD) {
+            return bufferSize;
+        }
+        // if minimum delay is greater than 20s then print a warning and use max buffer.
+        if (minDelay >= UPPER_THRESHOLD) {
+            System.out.println("Warning: minimum delay is "
+                    + minDelay/1000 + "sec " + minDelay%1000 + "ms");
+            System.err.println("Warning: minimum delay is "
+                    + minDelay/1000 + "sec " + minDelay%1000 + "ms");
+            return maxBufferSize;
+        }
+        // maxDelay could be anything, but minDelay is below the UPPER_THRESHOLD
+        // try to pick up a buffer size that keeps the delay below the
+        // UPPER_THRESHOLD
+        while (minBufferSize < maxBufferSize) {
+            bufferSize = random.nextInt(maxBufferSize - minBufferSize)
+                    + minBufferSize;
+            long delay = (((long)delaysMillis * expectedTotalSize) / bufferSize)
+                    / requestAmount;
+            if (delay < UPPER_THRESHOLD) return bufferSize;
+            minBufferSize++;
+        }
+        return minBufferSize;
+    }
+
+    // ---
+
+    /* Main entry point for standalone testing of the main functional test. */
+    public static void main(String... args) {
+        BufferingSubscriberTest t = new BufferingSubscriberTest();
+        for (Object[] objs : t.config())
+            t.test((int)objs[0], (int)objs[1], (int)objs[2], (int)objs[3], (int)objs[4], (int)objs[5]);
+    }
+}
diff --git a/test/jdk/java/net/httpclient/CancelledResponse.java b/test/jdk/java/net/httpclient/CancelledResponse.java
new file mode 100644
index 00000000000..04c28f6e979
--- /dev/null
+++ b/test/jdk/java/net/httpclient/CancelledResponse.java
@@ -0,0 +1,341 @@
+/*
+ * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import jdk.incubator.http.HttpClient;
+import jdk.incubator.http.HttpClient.Version;
+import jdk.incubator.http.HttpHeaders;
+import jdk.incubator.http.HttpRequest;
+import jdk.incubator.http.HttpResponse;
+import jdk.jshell.spi.ExecutionControl;
+import jdk.testlibrary.SimpleSSLContext;
+
+import javax.net.ServerSocketFactory;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLException;
+import javax.net.ssl.SSLServerSocketFactory;
+import java.io.IOException;
+import java.net.SocketException;
+import java.net.URI;
+import java.nio.ByteBuffer;
+import java.util.List;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.CompletionException;
+import java.util.concurrent.CompletionStage;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Flow;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import jdk.incubator.http.HttpResponse.BodyHandler;
+import jdk.incubator.http.HttpResponse.BodySubscriber;
+
+import static java.lang.String.format;
+import static java.lang.System.out;
+
+/**
+ * @test
+ * @bug 8087112
+ * @library /lib/testlibrary
+ * @build jdk.testlibrary.SimpleSSLContext
+ * @build MockServer
+ * @run main/othervm  CancelledResponse
+ * @run main/othervm  CancelledResponse SSL
+ */
+
+/**
+ * Similar test to SplitResponse except that the client will cancel the response
+ * before receiving it fully.
+ */
+public class CancelledResponse {
+
+    static String response(String body, boolean serverKeepalive) {
+        StringBuilder sb = new StringBuilder();
+        sb.append("HTTP/1.1 200 OK\r\n");
+        if (!serverKeepalive)
+            sb.append("Connection: Close\r\n");
+
+        sb.append("Content-length: ").append(body.length()).append("\r\n");
+        sb.append("\r\n");
+        sb.append(body);
+        return sb.toString();
+    }
+
+    static final String responses[] = {
+        "Lorem ipsum dolor sit amet consectetur adipiscing elit,",
+        "sed do eiusmod tempor quis nostrud exercitation ullamco laboris nisi ut",
+        "aliquip ex ea commodo consequat."
+    };
+
+    final ServerSocketFactory factory;
+    final SSLContext context;
+    final boolean useSSL;
+    CancelledResponse(boolean useSSL) throws IOException {
+        this.useSSL = useSSL;
+        context = new SimpleSSLContext().get();
+        SSLContext.setDefault(context);
+        factory = useSSL ? SSLServerSocketFactory.getDefault()
+                         : ServerSocketFactory.getDefault();
+    }
+
+    public HttpClient newHttpClient() {
+        HttpClient client;
+        if (useSSL) {
+            client = HttpClient.newBuilder()
+                               .sslContext(context)
+                               .build();
+        } else {
+            client = HttpClient.newHttpClient();
+        }
+        return client;
+    }
+
+    public static void main(String[] args) throws Exception {
+        boolean useSSL = false;
+        if (args != null && args.length == 1) {
+            useSSL = "SSL".equals(args[0]);
+        }
+        CancelledResponse sp = new CancelledResponse(useSSL);
+
+        for (Version version : Version.values()) {
+            for (boolean serverKeepalive : new boolean[]{ true, false }) {
+                // Note: the mock server doesn't support Keep-Alive, but
+                // pretending that it might exercises code paths in and out of
+                // the connection pool, and retry logic
+                for (boolean async : new boolean[]{ true, false }) {
+                    sp.test(version, serverKeepalive, async);
+                }
+            }
+        }
+    }
+
+    static class CancelException extends IOException {
+    }
+
+    // @Test
+    void test(Version version, boolean serverKeepalive, boolean async)
+        throws Exception
+    {
+        out.println(format("*** version %s, serverKeepAlive: %s, async: %s ***",
+                           version, serverKeepalive, async));
+        MockServer server = new MockServer(0, factory);
+        URI uri = new URI(server.getURL());
+        out.println("server is: " + uri);
+        server.start();
+
+        HttpClient client = newHttpClient();
+        HttpRequest request = HttpRequest.newBuilder(uri).version(version).build();
+        try {
+            for (int i = 0; i < responses.length; i++) {
+                HttpResponse<String> r = null;
+                CompletableFuture<HttpResponse<String>> cf1;
+                CancelException expected = null;
+                AtomicBoolean cancelled = new AtomicBoolean();
+
+                out.println("----- iteration " + i + " -----");
+                String body = responses[i];
+                Thread t = sendSplitResponse(response(body, serverKeepalive), server, cancelled);
+
+                try {
+                    if (async) {
+                        out.println("send async: " + request);
+                        cf1 = client.sendAsync(request, asString(body, cancelled));
+                        r = cf1.get();
+                    } else { // sync
+                        out.println("send sync: " + request);
+                        r = client.send(request, asString(body, cancelled));
+                    }
+                } catch (CancelException c1) {
+                    System.out.println("Got expected exception: " + c1);
+                    expected = c1;
+                } catch (IOException | ExecutionException | CompletionException c2) {
+                    Throwable c = c2;
+                    while (c != null && !(c instanceof CancelException)) {
+                        c = c.getCause();
+                    }
+                    if (c instanceof CancelException) {
+                        System.out.println("Got expected exception: " + c);
+                        expected = (CancelException)c;
+                    } else throw c2;
+                }
+                if (r != null) {
+                    if (r.statusCode() != 200)
+                        throw new RuntimeException("Failed");
+
+                    String rxbody = r.body();
+                    out.println("received " + rxbody);
+                    if (!rxbody.equals(body))
+                        throw new RuntimeException(format("Expected:%s, got:%s", body, rxbody));
+                }
+                t.join();
+                conn.close();
+                if (expected == null) {
+                    throw new RuntimeException("Expected exception not raised for "
+                            + i + " cancelled=" + cancelled.get());
+                }
+            }
+        } finally {
+            server.close();
+        }
+        System.out.println("OK");
+    }
+
+    static class CancellingSubscriber implements BodySubscriber<String> {
+        private final String expected;
+        private final CompletableFuture<String> result;
+        private Flow.Subscription subscription;
+        final AtomicInteger index = new AtomicInteger();
+        final AtomicBoolean cancelled;
+        CancellingSubscriber(String expected, AtomicBoolean cancelled) {
+            this.cancelled = cancelled;
+            this.expected = expected;
+            result = new CompletableFuture<>();
+        }
+
+        @Override
+        public CompletionStage<String> getBody() {
+            return result;
+        }
+
+        @Override
+        public void onSubscribe(Flow.Subscription subscription) {
+            this.subscription = subscription;
+            subscription.request(1);
+        }
+
+        @Override
+        public void onNext(List<ByteBuffer> item) {
+            //if (result.isDone())
+            for (ByteBuffer b : item) {
+                while (b.hasRemaining() && !result.isDone()) {
+                    int i = index.getAndIncrement();
+                    char at = expected.charAt(i);
+                    byte[] data = new byte[b.remaining()];
+                    b.get(data); // we know that the server writes 1 char
+                    String s = new String(data);
+                    char c = s.charAt(0);
+                    if (c != at) {
+                        Throwable x = new IllegalStateException("char at "
+                                + i + " is '" + c + "' expected '"
+                                + at + "' for \"" + expected +"\"");
+                        out.println("unexpected char received, cancelling");
+                        subscription.cancel();
+                        result.completeExceptionally(x);
+                        return;
+                    }
+                }
+            }
+            if (index.get() > 0 && !result.isDone()) {
+                // we should complete the result here, but let's
+                // see if we get something back...
+                out.println("Cancelling subscription after reading " + index.get());
+                cancelled.set(true);
+                subscription.cancel();
+                result.completeExceptionally(new CancelException());
+                return;
+            }
+            if (!result.isDone()) {
+                out.println("requesting 1 more");
+                subscription.request(1);
+            }
+        }
+
+        @Override
+        public void onError(Throwable throwable) {
+            result.completeExceptionally(throwable);
+        }
+
+        @Override
+        public void onComplete() {
+            int len = index.get();
+            if (len == expected.length()) {
+                result.complete(expected);
+            } else {
+                Throwable x = new IllegalStateException("received only "
+                        + len + " chars, expected " + expected.length()
+                        + " for \"" + expected +"\"");
+                result.completeExceptionally(x);
+            }
+        }
+    }
+
+    static class CancellingHandler implements BodyHandler<String> {
+        final String expected;
+        final AtomicBoolean cancelled;
+        CancellingHandler(String expected, AtomicBoolean cancelled) {
+            this.expected = expected;
+            this.cancelled = cancelled;
+        }
+        @Override
+        public BodySubscriber<String> apply(int statusCode, HttpHeaders responseHeaders) {
+            assert !cancelled.get();
+            return new CancellingSubscriber(expected, cancelled);
+        }
+    }
+
+    BodyHandler<String> asString(String expected, AtomicBoolean cancelled) {
+        return new CancellingHandler(expected, cancelled);
+    }
+
+    // required for cleanup
+    volatile MockServer.Connection conn;
+
+    // Sends the response, mostly, one byte at a time with a small delay
+    // between bytes, to encourage that each byte is read in a separate read
+    Thread sendSplitResponse(String s, MockServer server, AtomicBoolean cancelled) {
+        System.out.println("Sending: ");
+        Thread t = new Thread(() -> {
+            System.out.println("Waiting for server to receive headers");
+            conn = server.activity();
+            System.out.println("Start sending response");
+            int sent = 0;
+            try {
+                int len = s.length();
+                out.println("sending " + s);
+                for (int i = 0; i < len; i++) {
+                    String onechar = s.substring(i, i + 1);
+                    conn.send(onechar);
+                    sent++;
+                    Thread.sleep(10);
+                }
+                out.println("sent " + s);
+            } catch (SSLException | SocketException x) {
+                // if SSL then we might get a "Broken Pipe", otherwise
+                // a "Socket closed".
+                boolean expected = cancelled.get();
+                if (sent > 0 && expected) {
+                    System.out.println("Connection closed by peer as expected: " + x);
+                    return;
+                } else {
+                    System.out.println("Unexpected exception (sent="
+                            + sent + ", cancelled=" + expected + "): " + x);
+                    throw new RuntimeException(x);
+                }
+            } catch (IOException | InterruptedException e) {
+                throw new RuntimeException(e);
+            }
+        });
+        t.setDaemon(true);
+        t.start();
+        return t;
+    }
+}
diff --git a/test/jdk/java/net/httpclient/CustomRequestPublisher.java b/test/jdk/java/net/httpclient/CustomRequestPublisher.java
new file mode 100644
index 00000000000..399921fc8e9
--- /dev/null
+++ b/test/jdk/java/net/httpclient/CustomRequestPublisher.java
@@ -0,0 +1,349 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @summary Checks correct handling of Publishers that call onComplete without demand
+ * @modules java.base/sun.net.www.http
+ *          jdk.incubator.httpclient/jdk.incubator.http.internal.common
+ *          jdk.incubator.httpclient/jdk.incubator.http.internal.frame
+ *          jdk.incubator.httpclient/jdk.incubator.http.internal.hpack
+ *          java.logging
+ *          jdk.httpserver
+ * @library /lib/testlibrary http2/server
+ * @build Http2TestServer
+ * @build jdk.testlibrary.SimpleSSLContext
+ * @run testng/othervm CustomRequestPublisher
+ */
+
+import com.sun.net.httpserver.HttpExchange;
+import com.sun.net.httpserver.HttpHandler;
+import com.sun.net.httpserver.HttpServer;
+import com.sun.net.httpserver.HttpsConfigurator;
+import com.sun.net.httpserver.HttpsServer;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.InetSocketAddress;
+import java.net.URI;
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.Flow;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.function.Supplier;
+import java.util.stream.Collectors;
+import javax.net.ssl.SSLContext;
+import jdk.incubator.http.HttpClient;
+import jdk.incubator.http.HttpRequest;
+import jdk.incubator.http.HttpResponse;
+import jdk.testlibrary.SimpleSSLContext;
+import org.testng.annotations.AfterTest;
+import org.testng.annotations.BeforeTest;
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+import static java.lang.System.out;
+import static java.nio.charset.StandardCharsets.US_ASCII;
+import static jdk.incubator.http.HttpResponse.BodyHandler.asString;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertTrue;
+
+public class CustomRequestPublisher {
+
+    SSLContext sslContext;
+    HttpServer httpTestServer;         // HTTP/1.1    [ 4 servers ]
+    HttpsServer httpsTestServer;       // HTTPS/1.1
+    Http2TestServer http2TestServer;   // HTTP/2 ( h2c )
+    Http2TestServer https2TestServer;  // HTTP/2 ( h2  )
+    String httpURI;
+    String httpsURI;
+    String http2URI;
+    String https2URI;
+
+    @DataProvider(name = "variants")
+    public Object[][] variants() {
+        Supplier<BodyPublisher> fixedSupplier   = () -> new FixedLengthBodyPublisher();
+        Supplier<BodyPublisher> unknownSupplier = () -> new UnknownLengthBodyPublisher();
+
+        return new Object[][]{
+                { httpURI,   fixedSupplier,   false },
+                { httpURI,   unknownSupplier, false },
+                { httpsURI,  fixedSupplier,   false },
+                { httpsURI,  unknownSupplier, false },
+                { http2URI,  fixedSupplier,   false },
+                { http2URI,  unknownSupplier, false },
+                { https2URI, fixedSupplier,   false,},
+                { https2URI, unknownSupplier, false },
+
+                { httpURI,   fixedSupplier,   true },
+                { httpURI,   unknownSupplier, true },
+                { httpsURI,  fixedSupplier,   true },
+                { httpsURI,  unknownSupplier, true },
+                { http2URI,  fixedSupplier,   true },
+                { http2URI,  unknownSupplier, true },
+                { https2URI, fixedSupplier,   true,},
+                { https2URI, unknownSupplier, true },
+        };
+    }
+
+    static final int ITERATION_COUNT = 10;
+
+    @Test(dataProvider = "variants")
+    void test(String uri, Supplier<BodyPublisher> bpSupplier, boolean sameClient)
+            throws Exception
+    {
+        HttpClient client = null;
+        for (int i=0; i< ITERATION_COUNT; i++) {
+            if (!sameClient || client == null)
+                client = HttpClient.newBuilder().sslContext(sslContext).build();
+
+            BodyPublisher bodyPublisher = bpSupplier.get();
+            HttpRequest request = HttpRequest.newBuilder(URI.create(uri))
+                    .POST(bodyPublisher)
+                    .build();
+
+            HttpResponse<String> resp = client.send(request, asString());
+
+            out.println("Got response: " + resp);
+            out.println("Got body: " + resp.body());
+            assertTrue(resp.statusCode() == 200,
+                    "Expected 200, got:" + resp.statusCode());
+            assertEquals(resp.body(), bodyPublisher.bodyAsString());
+        }
+    }
+
+    @Test(dataProvider = "variants")
+    void testAsync(String uri, Supplier<BodyPublisher> bpSupplier, boolean sameClient)
+            throws Exception
+    {
+        HttpClient client = null;
+        for (int i=0; i< ITERATION_COUNT; i++) {
+            if (!sameClient || client == null)
+                client = HttpClient.newBuilder().sslContext(sslContext).build();
+
+            BodyPublisher bodyPublisher = bpSupplier.get();
+            HttpRequest request = HttpRequest.newBuilder(URI.create(uri))
+                    .POST(bodyPublisher)
+                    .build();
+
+            CompletableFuture<HttpResponse<String>> cf = client.sendAsync(request, asString());
+            HttpResponse<String> resp = cf.get();
+
+            out.println("Got response: " + resp);
+            out.println("Got body: " + resp.body());
+            assertTrue(resp.statusCode() == 200,
+                    "Expected 200, got:" + resp.statusCode());
+            assertEquals(resp.body(), bodyPublisher.bodyAsString());
+        }
+    }
+
+    /** A Publisher that returns an UNKNOWN content length. */
+    static class UnknownLengthBodyPublisher extends BodyPublisher {
+        @Override
+        public long contentLength() {
+            return -1;  // unknown
+        }
+    }
+
+    /** A Publisher that returns a FIXED content length. */
+    static class FixedLengthBodyPublisher extends BodyPublisher {
+        final int LENGTH = Arrays.stream(BODY)
+                .mapToInt(s-> s.getBytes(US_ASCII).length)
+                .sum();
+        @Override
+        public long contentLength() {
+            return LENGTH;
+        }
+    }
+
+    /**
+     * A Publisher that ( quite correctly ) invokes onComplete, after the last
+     * item has been published, even without any outstanding demand.
+     */
+    static abstract class BodyPublisher implements HttpRequest.BodyPublisher {
+
+        String[] BODY = new String[]
+                { "Say ", "Hello ", "To ", "My ", "Little ", "Friend" };
+
+        protected volatile Flow.Subscriber subscriber;
+
+        @Override
+        public void subscribe(Flow.Subscriber<? super ByteBuffer> subscriber) {
+            this.subscriber = subscriber;
+            subscriber.onSubscribe(new InternalSubscription());
+        }
+
+        @Override
+        public abstract long contentLength();
+
+        String bodyAsString() {
+            return Arrays.stream(BODY).collect(Collectors.joining());
+        }
+
+        class InternalSubscription implements Flow.Subscription {
+
+            private final AtomicLong demand = new AtomicLong();
+            private final AtomicBoolean cancelled = new AtomicBoolean();
+            private volatile int position;
+
+            private static final int IDLE    =  1;
+            private static final int PUSHING =  2;
+            private static final int AGAIN   =  4;
+            private final AtomicInteger state = new AtomicInteger(IDLE);
+
+            @Override
+            public void request(long n) {
+                if (n <= 0L) {
+                    subscriber.onError(new IllegalArgumentException(
+                            "non-positive subscription request"));
+                    return;
+                }
+                if (cancelled.get()) {
+                    return;
+                }
+
+                while (true) {
+                    long prev = demand.get(), d;
+                    if ((d = prev + n) < prev) // saturate
+                        d = Long.MAX_VALUE;
+                    if (demand.compareAndSet(prev, d))
+                        break;
+                }
+
+                while (true) {
+                    int s = state.get();
+                    if (s == IDLE) {
+                        if (state.compareAndSet(IDLE, PUSHING)) {
+                            while (true) {
+                                push();
+                                if (state.compareAndSet(PUSHING, IDLE))
+                                    return;
+                                else if (state.compareAndSet(AGAIN, PUSHING))
+                                    continue;
+                            }
+                        }
+                    } else if (s == PUSHING) {
+                        if (state.compareAndSet(PUSHING, AGAIN))
+                            return;
+                    } else if (s == AGAIN){
+                        // do nothing, the pusher will already rerun
+                        return;
+                    } else {
+                        throw new AssertionError("Unknown state:" + s);
+                    }
+                }
+            }
+
+            private void push() {
+                long prev;
+                while ((prev = demand.get()) > 0) {
+                    if (!demand.compareAndSet(prev, prev -1))
+                        continue;
+
+                    int index = position;
+                    if (index < BODY.length) {
+                        position++;
+                        subscriber.onNext(ByteBuffer.wrap(BODY[index].getBytes(US_ASCII)));
+                    }
+                }
+
+                if (position == BODY.length && !cancelled.get()) {
+                    cancelled.set(true);
+                    subscriber.onComplete();  // NOTE: onComplete without demand
+                }
+            }
+
+            @Override
+            public void cancel() {
+                if (cancelled.compareAndExchange(false, true))
+                    return;  // already cancelled
+            }
+        }
+    }
+
+    @BeforeTest
+    public void setup() throws Exception {
+        sslContext = new SimpleSSLContext().get();
+        if (sslContext == null)
+            throw new AssertionError("Unexpected null sslContext");
+
+        InetSocketAddress sa = new InetSocketAddress("localhost", 0);
+        httpTestServer = HttpServer.create(sa, 0);
+        httpTestServer.createContext("/http1/echo", new Http1EchoHandler());
+        httpURI = "http://127.0.0.1:" + httpTestServer.getAddress().getPort() + "/http1/echo";
+
+        httpsTestServer = HttpsServer.create(sa, 0);
+        httpsTestServer.setHttpsConfigurator(new HttpsConfigurator(sslContext));
+        httpsTestServer.createContext("/https1/echo", new Http1EchoHandler());
+        httpsURI = "https://127.0.0.1:" + httpsTestServer.getAddress().getPort() + "/https1/echo";
+
+        http2TestServer = new Http2TestServer("127.0.0.1", false, 0);
+        http2TestServer.addHandler(new Http2EchoHandler(), "/http2/echo");
+        int port = http2TestServer.getAddress().getPort();
+        http2URI = "http://127.0.0.1:" + port + "/http2/echo";
+
+        https2TestServer = new Http2TestServer("127.0.0.1", true, 0);
+        https2TestServer.addHandler(new Http2EchoHandler(), "/https2/echo");
+        port = https2TestServer.getAddress().getPort();
+        https2URI = "https://127.0.0.1:" + port + "/https2/echo";
+
+        httpTestServer.start();
+        httpsTestServer.start();
+        http2TestServer.start();
+        https2TestServer.start();
+    }
+
+    @AfterTest
+    public void teardown() throws Exception {
+        httpTestServer.stop(0);
+        httpsTestServer.stop(0);
+        http2TestServer.stop();
+        https2TestServer.stop();
+    }
+
+    static class Http1EchoHandler implements HttpHandler {
+        @Override
+        public void handle(HttpExchange t) throws IOException {
+            try (InputStream is = t.getRequestBody();
+                 OutputStream os = t.getResponseBody()) {
+                byte[] bytes = is.readAllBytes();
+                t.sendResponseHeaders(200, bytes.length);
+                os.write(bytes);
+            }
+        }
+    }
+
+    static class Http2EchoHandler implements Http2Handler {
+        @Override
+        public void handle(Http2TestExchange t) throws IOException {
+            try (InputStream is = t.getRequestBody();
+                 OutputStream os = t.getResponseBody()) {
+                byte[] bytes = is.readAllBytes();
+                t.sendResponseHeaders(200, bytes.length);
+                os.write(bytes);
+            }
+        }
+    }
+}
diff --git a/test/jdk/java/net/httpclient/CustomResponseSubscriber.java b/test/jdk/java/net/httpclient/CustomResponseSubscriber.java
new file mode 100644
index 00000000000..290b4de9262
--- /dev/null
+++ b/test/jdk/java/net/httpclient/CustomResponseSubscriber.java
@@ -0,0 +1,283 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @summary Tests response body subscribers's onComplete is not invoked before onSubscribe
+ * @library /lib/testlibrary http2/server
+ * @build jdk.testlibrary.SimpleSSLContext
+ * @modules java.base/sun.net.www.http
+ *          jdk.incubator.httpclient/jdk.incubator.http.internal.common
+ *          jdk.incubator.httpclient/jdk.incubator.http.internal.frame
+ *          jdk.incubator.httpclient/jdk.incubator.http.internal.hpack
+ * @run testng/othervm CustomResponseSubscriber
+ */
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.InetSocketAddress;
+import java.net.URI;
+import java.nio.ByteBuffer;
+import java.util.List;
+import java.util.concurrent.CompletionStage;
+import java.util.concurrent.Executor;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Flow;
+import com.sun.net.httpserver.HttpExchange;
+import com.sun.net.httpserver.HttpHandler;
+import com.sun.net.httpserver.HttpServer;
+import com.sun.net.httpserver.HttpsConfigurator;
+import com.sun.net.httpserver.HttpsServer;
+import jdk.incubator.http.HttpClient;
+import jdk.incubator.http.HttpHeaders;
+import jdk.incubator.http.HttpRequest;
+import jdk.incubator.http.HttpResponse;
+import jdk.incubator.http.HttpResponse.BodyHandler;
+import  jdk.incubator.http.HttpResponse.BodySubscriber;
+import javax.net.ssl.SSLContext;
+import jdk.testlibrary.SimpleSSLContext;
+import org.testng.annotations.AfterTest;
+import org.testng.annotations.BeforeTest;
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+import static java.lang.System.out;
+import static java.nio.charset.StandardCharsets.UTF_8;
+import static jdk.incubator.http.HttpResponse.BodySubscriber.asString;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertTrue;
+
+public class CustomResponseSubscriber {
+
+    SSLContext sslContext;
+    HttpServer httpTestServer;         // HTTP/1.1    [ 4 servers ]
+    HttpsServer httpsTestServer;       // HTTPS/1.1
+    Http2TestServer http2TestServer;   // HTTP/2 ( h2c )
+    Http2TestServer https2TestServer;  // HTTP/2 ( h2  )
+    String httpURI_fixed;
+    String httpURI_chunk;
+    String httpsURI_fixed;
+    String httpsURI_chunk;
+    String http2URI_fixed;
+    String http2URI_chunk;
+    String https2URI_fixed;
+    String https2URI_chunk;
+
+    static final int ITERATION_COUNT = 10;
+    // a shared executor helps reduce the amount of threads created by the test
+    static final Executor executor = Executors.newCachedThreadPool();
+
+    @DataProvider(name = "variants")
+    public Object[][] variants() {
+        return new Object[][]{
+                { httpURI_fixed,    false },
+                { httpURI_chunk,    false },
+                { httpsURI_fixed,   false },
+                { httpsURI_chunk,   false },
+                { http2URI_fixed,   false },
+                { http2URI_chunk,   false },
+                { https2URI_fixed,  false,},
+                { https2URI_chunk,  false },
+
+                { httpURI_fixed,    true },
+                { httpURI_chunk,    true },
+                { httpsURI_fixed,   true },
+                { httpsURI_chunk,   true },
+                { http2URI_fixed,   true },
+                { http2URI_chunk,   true },
+                { https2URI_fixed,  true,},
+                { https2URI_chunk,  true },
+        };
+    }
+
+    HttpClient newHttpClient() {
+        return HttpClient.newBuilder()
+                         .executor(executor)
+                         .sslContext(sslContext)
+                         .build();
+    }
+
+    @Test(dataProvider = "variants")
+    public void testAsString(String uri, boolean sameClient) throws Exception {
+        HttpClient client = null;
+        for (int i=0; i< ITERATION_COUNT; i++) {
+            if (!sameClient || client == null)
+                client = newHttpClient();
+
+            HttpRequest req = HttpRequest.newBuilder(URI.create(uri))
+                                         .build();
+            BodyHandler<String> handler = new CRSBodyHandler();
+            HttpResponse<String> response = client.send(req, handler);
+            String body = response.body();
+            assertEquals(body, "");
+        }
+    }
+
+    static class CRSBodyHandler implements BodyHandler<String> {
+        @Override
+        public BodySubscriber<String> apply(int statusCode, HttpHeaders responseHeaders) {
+            assertEquals(statusCode, 200);
+            return new CRSBodySubscriber();
+        }
+    }
+
+    static class CRSBodySubscriber implements BodySubscriber<String> {
+        private final BodySubscriber<String> asString = asString(UTF_8);
+        volatile boolean onSubscribeCalled;
+
+        @Override
+        public void onSubscribe(Flow.Subscription subscription) {
+            //out.println("onSubscribe ");
+            onSubscribeCalled = true;
+            asString.onSubscribe(subscription);
+        }
+
+        @Override
+        public void onNext(List<ByteBuffer> item) {
+           // out.println("onNext " + item);
+            assertTrue(onSubscribeCalled);
+            asString.onNext(item);
+        }
+
+        @Override
+        public void onError(Throwable throwable) {
+            //out.println("onError");
+            assertTrue(onSubscribeCalled);
+            asString.onError(throwable);
+        }
+
+        @Override
+        public void onComplete() {
+            //out.println("onComplete");
+            assertTrue(onSubscribeCalled, "onComplete called before onSubscribe");
+            asString.onComplete();
+        }
+
+        @Override
+        public CompletionStage<String> getBody() {
+            return asString.getBody();
+        }
+    }
+
+
+    @BeforeTest
+    public void setup() throws Exception {
+        sslContext = new SimpleSSLContext().get();
+        if (sslContext == null)
+            throw new AssertionError("Unexpected null sslContext");
+
+        // HTTP/1.1
+        HttpHandler h1_fixedLengthHandler = new HTTP1_FixedLengthHandler();
+        HttpHandler h1_chunkHandler = new HTTP1_ChunkedHandler();
+        InetSocketAddress sa = new InetSocketAddress("localhost", 0);
+        httpTestServer = HttpServer.create(sa, 0);
+        httpTestServer.createContext("/http1/fixed", h1_fixedLengthHandler);
+        httpTestServer.createContext("/http1/chunk", h1_chunkHandler);
+        httpURI_fixed = "http://127.0.0.1:" + httpTestServer.getAddress().getPort() + "/http1/fixed";
+        httpURI_chunk = "http://127.0.0.1:" + httpTestServer.getAddress().getPort() + "/http1/chunk";
+
+        httpsTestServer = HttpsServer.create(sa, 0);
+        httpsTestServer.setHttpsConfigurator(new HttpsConfigurator(sslContext));
+        httpsTestServer.createContext("/https1/fixed", h1_fixedLengthHandler);
+        httpsTestServer.createContext("/https1/chunk", h1_chunkHandler);
+        httpsURI_fixed = "https://127.0.0.1:" + httpsTestServer.getAddress().getPort() + "/https1/fixed";
+        httpsURI_chunk = "https://127.0.0.1:" + httpsTestServer.getAddress().getPort() + "/https1/chunk";
+
+        // HTTP/2
+        Http2Handler h2_fixedLengthHandler = new HTTP2_FixedLengthHandler();
+        Http2Handler h2_chunkedHandler = new HTTP2_VariableHandler();
+
+        http2TestServer = new Http2TestServer("127.0.0.1", false, 0);
+        http2TestServer.addHandler(h2_fixedLengthHandler, "/http2/fixed");
+        http2TestServer.addHandler(h2_chunkedHandler, "/http2/chunk");
+        int port = http2TestServer.getAddress().getPort();
+        http2URI_fixed = "http://127.0.0.1:" + port + "/http2/fixed";
+        http2URI_chunk = "http://127.0.0.1:" + port + "/http2/chunk";
+
+        https2TestServer = new Http2TestServer("127.0.0.1", true, 0);
+        https2TestServer.addHandler(h2_fixedLengthHandler, "/https2/fixed");
+        https2TestServer.addHandler(h2_chunkedHandler, "/https2/chunk");
+        port = https2TestServer.getAddress().getPort();
+        https2URI_fixed = "https://127.0.0.1:" + port + "/https2/fixed";
+        https2URI_chunk = "https://127.0.0.1:" + port + "/https2/chunk";
+
+        httpTestServer.start();
+        httpsTestServer.start();
+        http2TestServer.start();
+        https2TestServer.start();
+    }
+
+    @AfterTest
+    public void teardown() throws Exception {
+        httpTestServer.stop(0);
+        httpsTestServer.stop(0);
+        http2TestServer.stop();
+        https2TestServer.stop();
+    }
+
+    static class HTTP1_FixedLengthHandler implements HttpHandler {
+        @Override
+        public void handle(HttpExchange t) throws IOException {
+            out.println("HTTP1_FixedLengthHandler received request to " + t.getRequestURI());
+            try (InputStream is = t.getRequestBody()) {
+                is.readAllBytes();
+            }
+            t.sendResponseHeaders(200, -1);  //no body
+        }
+    }
+
+    static class HTTP1_ChunkedHandler implements HttpHandler {
+        @Override
+        public void handle(HttpExchange t) throws IOException {
+            out.println("HTTP1_ChunkedHandler received request to " + t.getRequestURI());
+            try (InputStream is = t.getRequestBody()) {
+                is.readAllBytes();
+            }
+            t.sendResponseHeaders(200, 0); // chunked
+            t.getResponseBody().close();   // no body
+        }
+    }
+
+    static class HTTP2_FixedLengthHandler implements Http2Handler {
+        @Override
+        public void handle(Http2TestExchange t) throws IOException {
+            out.println("HTTP2_FixedLengthHandler received request to " + t.getRequestURI());
+            try (InputStream is = t.getRequestBody()) {
+                is.readAllBytes();
+            }
+            t.sendResponseHeaders(200, 0);
+            t.getResponseBody().close();
+        }
+    }
+
+    static class HTTP2_VariableHandler implements Http2Handler {
+        @Override
+        public void handle(Http2TestExchange t) throws IOException {
+            out.println("HTTP2_VariableHandler received request to " + t.getRequestURI());
+            try (InputStream is = t.getRequestBody()) {
+                is.readAllBytes();
+            }
+            t.sendResponseHeaders(200, -1); // variable
+            t.getResponseBody().close();  //no body
+        }
+    }
+}
diff --git a/test/jdk/java/net/httpclient/EchoHandler.java b/test/jdk/java/net/httpclient/EchoHandler.java
index 37c8614409f..2fb3a9a1acc 100644
--- a/test/jdk/java/net/httpclient/EchoHandler.java
+++ b/test/jdk/java/net/httpclient/EchoHandler.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2017, 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
diff --git a/test/jdk/java/net/httpclient/HandshakeFailureTest.java b/test/jdk/java/net/httpclient/HandshakeFailureTest.java
new file mode 100644
index 00000000000..760e5a947c2
--- /dev/null
+++ b/test/jdk/java/net/httpclient/HandshakeFailureTest.java
@@ -0,0 +1,288 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import javax.net.ServerSocketFactory;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLHandshakeException;
+import javax.net.ssl.SSLSocket;
+import java.io.DataInputStream;
+import java.io.IOException;
+import java.io.UncheckedIOException;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.net.URI;
+import java.util.List;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.CompletionException;
+import jdk.incubator.http.HttpClient;
+import jdk.incubator.http.HttpClient.Version;
+import jdk.incubator.http.HttpResponse;
+import jdk.incubator.http.HttpRequest;
+import static java.lang.System.out;
+import static jdk.incubator.http.HttpResponse.BodyHandler.discard;
+
+/**
+ * @test
+ * @run main/othervm HandshakeFailureTest
+ * @summary Verify SSLHandshakeException is received when the handshake fails,
+ * either because the server closes ( EOF ) the connection during handshaking
+ * or no cipher suite ( or similar ) can be negotiated.
+ */
+// To switch on debugging use:
+// @run main/othervm -Djdk.internal.httpclient.debug=true HandshakeFailureTest
+public class HandshakeFailureTest {
+
+    // The number of iterations each testXXXClient performs. Can be increased
+    // when running standalone testing.
+    static final int TIMES = 10;
+
+    public static void main(String[] args) throws Exception {
+        HandshakeFailureTest test = new HandshakeFailureTest();
+        List<AbstractServer> servers = List.of( new PlainServer(), new SSLServer());
+
+        for (AbstractServer server : servers) {
+            try (server) {
+                out.format("%n%n------ Testing with server:%s ------%n", server);
+                URI uri = new URI("https://127.0.0.1:" + server.getPort() + "/");
+
+                test.testSyncSameClient(uri, Version.HTTP_1_1);
+                test.testSyncSameClient(uri, Version.HTTP_2);
+                test.testSyncDiffClient(uri, Version.HTTP_1_1);
+                test.testSyncDiffClient(uri, Version.HTTP_2);
+
+                test.testAsyncSameClient(uri, Version.HTTP_1_1);
+                test.testAsyncSameClient(uri, Version.HTTP_2);
+                test.testAsyncDiffClient(uri, Version.HTTP_1_1);
+                test.testAsyncDiffClient(uri, Version.HTTP_2);
+            }
+        }
+    }
+
+    void testSyncSameClient(URI uri, Version version) throws Exception {
+        out.printf("%n--- testSyncSameClient %s ---%n", version);
+        HttpClient client = HttpClient.newHttpClient();
+        for (int i = 0; i < TIMES; i++) {
+            out.printf("iteration %d%n", i);
+            HttpRequest request = HttpRequest.newBuilder(uri)
+                                             .version(version)
+                                             .build();
+            try {
+                HttpResponse<Void> response = client.send(request, discard(null));
+                String msg = String.format("UNEXPECTED response=%s%n", response);
+                throw new RuntimeException(msg);
+            } catch (SSLHandshakeException expected) {
+                out.printf("Client: caught expected exception: %s%n", expected);
+            }
+        }
+    }
+
+    void testSyncDiffClient(URI uri, Version version) throws Exception {
+        out.printf("%n--- testSyncDiffClient %s ---%n", version);
+        for (int i = 0; i < TIMES; i++) {
+            out.printf("iteration %d%n", i);
+            // a new client each time
+            HttpClient client = HttpClient.newHttpClient();
+            HttpRequest request = HttpRequest.newBuilder(uri)
+                                             .version(version)
+                                             .build();
+            try {
+                HttpResponse<Void> response = client.send(request, discard(null));
+                String msg = String.format("UNEXPECTED response=%s%n", response);
+                throw new RuntimeException(msg);
+            } catch (SSLHandshakeException expected) {
+                out.printf("Client: caught expected exception: %s%n", expected);
+            }
+        }
+    }
+
+    void testAsyncSameClient(URI uri, Version version) throws Exception {
+        out.printf("%n--- testAsyncSameClient %s ---%n", version);
+        HttpClient client = HttpClient.newHttpClient();
+        for (int i = 0; i < TIMES; i++) {
+            out.printf("iteration %d%n", i);
+            HttpRequest request = HttpRequest.newBuilder(uri)
+                                             .version(version)
+                                             .build();
+            CompletableFuture<HttpResponse<Void>> response =
+                        client.sendAsync(request, discard(null));
+            try {
+                response.join();
+                String msg = String.format("UNEXPECTED response=%s%n", response);
+                throw new RuntimeException(msg);
+            } catch (CompletionException ce) {
+                if (ce.getCause() instanceof SSLHandshakeException) {
+                    out.printf("Client: caught expected exception: %s%n", ce.getCause());
+                } else {
+                    out.printf("Client: caught UNEXPECTED exception: %s%n", ce.getCause());
+                    throw ce;
+                }
+            }
+        }
+    }
+
+    void testAsyncDiffClient(URI uri, Version version) throws Exception {
+        out.printf("%n--- testAsyncDiffClient %s ---%n", version);
+        for (int i = 0; i < TIMES; i++) {
+            out.printf("iteration %d%n", i);
+            // a new client each time
+            HttpClient client = HttpClient.newHttpClient();
+            HttpRequest request = HttpRequest.newBuilder(uri)
+                                             .version(version)
+                                             .build();
+            CompletableFuture<HttpResponse<Void>> response =
+                    client.sendAsync(request, discard(null));
+            try {
+                response.join();
+                String msg = String.format("UNEXPECTED response=%s%n", response);
+                throw new RuntimeException(msg);
+            } catch (CompletionException ce) {
+                if (ce.getCause() instanceof SSLHandshakeException) {
+                    out.printf("Client: caught expected exception: %s%n", ce.getCause());
+                } else {
+                    out.printf("Client: caught UNEXPECTED exception: %s%n", ce.getCause());
+                    throw ce;
+                }
+            }
+        }
+    }
+
+    /** Common supertype for PlainServer and SSLServer. */
+    static abstract class AbstractServer extends Thread implements AutoCloseable {
+        protected final ServerSocket ss;
+        protected volatile boolean closed;
+
+        AbstractServer(String name, ServerSocket ss) throws IOException {
+            super(name);
+            this.ss = ss;
+            this.start();
+        }
+
+        int getPort() { return ss.getLocalPort(); }
+
+        @Override
+        public void close() {
+            if (closed)
+                return;
+            closed = true;
+            try {
+                ss.close();
+            } catch (IOException e) {
+                throw new UncheckedIOException("Unexpected", e);
+            }
+        }
+    }
+
+    /** Emulates a server-side, using plain cleartext Sockets, that just closes
+     * the connection, after a small variable delay. */
+    static class PlainServer extends AbstractServer {
+        private volatile int count;
+
+        PlainServer() throws IOException {
+            super("PlainServer", new ServerSocket(0));
+        }
+
+        @Override
+        public void run() {
+            while (!closed) {
+                try (Socket s = ss.accept()) {
+                    count++;
+
+                    /*   SSL record layer - contains the client hello
+                    struct {
+                        uint8 major, minor;
+                    } ProtocolVersion;
+
+                    enum {
+                        change_cipher_spec(20), alert(21), handshake(22),
+                        application_data(23), (255)
+                    } ContentType;
+
+                    struct {
+                        ContentType type;
+                        ProtocolVersion version;
+                        uint16 length;
+                        opaque fragment[SSLPlaintext.length];
+                    } SSLPlaintext;   */
+                    DataInputStream din =  new DataInputStream(s.getInputStream());
+                    int contentType = din.read();
+                    out.println("ContentType:" + contentType);
+                    int majorVersion = din.read();
+                    out.println("Major:" + majorVersion);
+                    int minorVersion = din.read();
+                    out.println("Minor:" + minorVersion);
+                    int length = din.readShort();
+                    out.println("length:" + length);
+                    byte[] ba = new byte[length];
+                    din.readFully(ba);
+
+                    // simulate various delays in response
+                    Thread.sleep(10 * (count % 10));
+                    s.close(); // close without giving any reply
+                } catch (IOException e) {
+                    if (!closed)
+                        out.println("Unexpected" + e);
+                } catch (InterruptedException e) {
+                    throw new RuntimeException(e);
+                }
+            }
+        }
+    }
+
+    /** Emulates a server-side, using SSL Sockets, that will fail during
+     * handshaking, as there are no cipher suites in common. */
+    static class SSLServer extends AbstractServer {
+        static final SSLContext sslContext = createUntrustingContext();
+        static final ServerSocketFactory factory = sslContext.getServerSocketFactory();
+
+        static SSLContext createUntrustingContext() {
+            try {
+                SSLContext sslContext = SSLContext.getInstance("TLSv1.2");
+                sslContext.init(null, null, null);
+                return sslContext;
+            } catch (Throwable t) {
+                throw new AssertionError(t);
+            }
+        }
+
+        SSLServer() throws IOException {
+            super("SSLServer", factory.createServerSocket(0));
+        }
+
+        @Override
+        public void run() {
+            while (!closed) {
+                try (SSLSocket s = (SSLSocket)ss.accept()) {
+                    s.getInputStream().read();  // will throw SHE here
+
+                    throw new AssertionError("Should not reach here");
+                } catch (SSLHandshakeException expected) {
+                    // Expected: SSLHandshakeException: no cipher suites in common
+                    out.printf("Server: caught expected exception: %s%n", expected);
+                } catch (IOException e) {
+                    if (!closed)
+                        out.printf("UNEXPECTED %s", e);
+                }
+            }
+        }
+    }
+}
diff --git a/test/jdk/java/net/httpclient/HeadersTest.java b/test/jdk/java/net/httpclient/HeadersTest.java
index 086025c8388..d724873e447 100644
--- a/test/jdk/java/net/httpclient/HeadersTest.java
+++ b/test/jdk/java/net/httpclient/HeadersTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2017, 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
diff --git a/test/jdk/java/net/httpclient/HeadersTest1.java b/test/jdk/java/net/httpclient/HeadersTest1.java
index 2777c347568..d03405ae234 100644
--- a/test/jdk/java/net/httpclient/HeadersTest1.java
+++ b/test/jdk/java/net/httpclient/HeadersTest1.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2017, 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
@@ -95,16 +95,26 @@ public class HeadersTest1 {
             assertTrue(v1.isEmpty(), String.valueOf(v1));
             TestKit.assertUnmodifiableList(v1);
 
-            List<String> v2 = hd.allValues("X-Foo-Response");
-            assertNotNull(v2);
-            assertEquals(new HashSet<>(v2), Set.of("resp1", "resp2"));
-            TestKit.assertUnmodifiableList(v2);
+            // case insensitive
+            List<String> headernames = List.of("X-Foo-Response",
+                                               "x-foo-Response",
+                                               "x-fOo-REspoNse");
+            for (String headerName : headernames) {
+                List<String> v2 = hd.allValues(headerName);
+                assertNotNull(v2);
+                assertEquals(new HashSet<>(v2), Set.of("resp1", "resp2"));
+                TestKit.assertUnmodifiableList(v2);
+            }
 
             Map<String, List<String>> map = hd.map();
             TestKit.assertUnmodifiableMap(map);
             for (List<String> values : map.values()) {
                 TestKit.assertUnmodifiableList(values);
             }
+
+            // toString
+            hd.toString().toLowerCase().contains("content-length");
+            hd.toString().toLowerCase().contains("x-foo-response");
         } finally {
             server.stop(0);
             e.shutdownNow();
diff --git a/test/jdk/java/net/httpclient/HeadersTest2.java b/test/jdk/java/net/httpclient/HeadersTest2.java
new file mode 100644
index 00000000000..54ebac13d68
--- /dev/null
+++ b/test/jdk/java/net/httpclient/HeadersTest2.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/**
+ * @test
+ * @bug 8087112
+ * @summary Basic test for headers
+ */
+
+import jdk.incubator.http.HttpHeaders;
+import jdk.incubator.http.HttpRequest;
+import java.net.URI;
+import java.util.List;
+import java.util.Iterator;
+
+public class HeadersTest2 {
+    static URI uri = URI.create("http://www.foo.com/");
+
+    static class CompareTest {
+        boolean succeed;
+        List<String> nameValues1;
+        List<String> nameValues2;
+
+
+        /**
+         * Each list contains header-name, header-value, header-name, header-value
+         * sequences. The test creates two requests with the two lists
+         * and compares the HttpHeaders objects returned from the requests
+         */
+        CompareTest(boolean succeed, List<String> l1, List<String> l2) {
+            this.succeed = succeed;
+            this.nameValues1 = l1;
+            this.nameValues2 = l2;
+        }
+
+        public void run() {
+            HttpRequest r1 = getRequest(nameValues1);
+            HttpRequest r2 = getRequest(nameValues2);
+            HttpHeaders h1 = r1.headers();
+            HttpHeaders h2 = r2.headers();
+            boolean equal = h1.equals(h2);
+            if (equal && !succeed) {
+                System.err.println("Expected failure");
+                print(nameValues1);
+                print(nameValues2);
+                throw new RuntimeException();
+            } else if (!equal && succeed) {
+                System.err.println("Expected success");
+                print(nameValues1);
+                print(nameValues2);
+                throw new RuntimeException();
+            }
+
+            // Ensures that headers never equal a non-HttpHeaders type
+            if (h1.equals(new Object()))
+                throw new RuntimeException("Unexpected h1 equals Object");
+
+            if (h2.equals(r1))
+                throw new RuntimeException("Unexpected h2 equals r1");
+        }
+
+        static void print(List<String> list) {
+            System.err.print("{");
+            for (String s : list) {
+                System.err.print(s + " ");
+            }
+            System.err.println("}");
+        }
+
+        HttpRequest getRequest(List<String> headers) {
+            HttpRequest.Builder builder = HttpRequest.newBuilder(uri);
+            Iterator<String> iterator = headers.iterator();
+            while (iterator.hasNext()) {
+                String name = iterator.next();
+                String value = iterator.next();
+                builder.header(name, value);
+            }
+            return builder.GET().build();
+        }
+    }
+
+    static CompareTest test(boolean s, List<String> l1, List<String> l2) {
+        return new CompareTest(s, l1, l2);
+    }
+
+    static CompareTest[] compareTests = new CompareTest[] {
+        test(true, List.of("Dontent-length", "99"), List.of("dontent-length", "99")),
+        test(false, List.of("Dontent-length", "99"), List.of("dontent-length", "100")),
+        test(false, List.of("Name1", "val1", "Name1", "val2", "name1", "val3"),
+                    List.of("Name1", "val1", "Name1", "val2")),
+        test(true, List.of("Name1", "val1", "Name1", "val2", "name1", "val3"),
+                   List.of("NaMe1", "val1", "NAme1", "val2", "name1", "val3"))
+    };
+
+    public static void main(String[] args) {
+        for (CompareTest test : compareTests) {
+            test.run();
+        }
+    }
+}
+
diff --git a/test/jdk/java/net/httpclient/HttpClientBuilderTest.java b/test/jdk/java/net/httpclient/HttpClientBuilderTest.java
new file mode 100644
index 00000000000..b0aaefdd90a
--- /dev/null
+++ b/test/jdk/java/net/httpclient/HttpClientBuilderTest.java
@@ -0,0 +1,244 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.lang.reflect.Method;
+import java.net.Authenticator;
+import java.net.CookieHandler;
+import java.net.CookieManager;
+import java.net.InetSocketAddress;
+import java.net.ProxySelector;
+import java.util.List;
+import java.util.concurrent.Executor;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLParameters;
+import jdk.incubator.http.HttpClient;
+import jdk.incubator.http.HttpClient.Redirect;
+import jdk.incubator.http.HttpClient.Version;
+import jdk.testlibrary.SimpleSSLContext;
+import org.testng.annotations.Test;
+import static org.testng.Assert.*;
+
+/*
+ * @test
+ * @summary HttpClient[.Builder] API and behaviour checks
+ * @library /lib/testlibrary/
+ * @build jdk.testlibrary.SimpleSSLContext
+ * @run testng HttpClientBuilderTest
+ */
+
+public class HttpClientBuilderTest {
+
+    @Test
+    public void testDefaults() throws Exception {
+        List<HttpClient> clients = List.of(HttpClient.newHttpClient(),
+                                           HttpClient.newBuilder().build());
+
+        for (HttpClient client : clients) {
+            // Empty optionals and defaults
+            assertFalse(client.authenticator().isPresent());
+            assertFalse(client.cookieHandler().isPresent());
+            assertFalse(client.executor().isPresent());
+            assertFalse(client.proxy().isPresent());
+            assertTrue(client.sslParameters() != null);
+            assertTrue(client.followRedirects().equals(HttpClient.Redirect.NEVER));
+            assertTrue(client.sslContext() == SSLContext.getDefault());
+            assertTrue(client.version().equals(HttpClient.Version.HTTP_2));
+        }
+    }
+
+    @Test
+    public void testNull() throws Exception {
+        HttpClient.Builder builder = HttpClient.newBuilder();
+        assertThrows(NullPointerException.class, () -> builder.authenticator(null));
+        assertThrows(NullPointerException.class, () -> builder.cookieHandler(null));
+        assertThrows(NullPointerException.class, () -> builder.executor(null));
+        assertThrows(NullPointerException.class, () -> builder.proxy(null));
+        assertThrows(NullPointerException.class, () -> builder.sslParameters(null));
+        assertThrows(NullPointerException.class, () -> builder.followRedirects(null));
+        assertThrows(NullPointerException.class, () -> builder.sslContext(null));
+        assertThrows(NullPointerException.class, () -> builder.version(null));
+    }
+
+    static class TestAuthenticator extends Authenticator { }
+
+    @Test
+    public void testAuthenticator() {
+        HttpClient.Builder builder = HttpClient.newBuilder();
+        Authenticator a = new TestAuthenticator();
+        builder.authenticator(a);
+        assertTrue(builder.build().authenticator().get() == a);
+        Authenticator b = new TestAuthenticator();
+        builder.authenticator(b);
+        assertTrue(builder.build().authenticator().get() == b);
+        assertThrows(NullPointerException.class, () -> builder.authenticator(null));
+        Authenticator c = new TestAuthenticator();
+        builder.authenticator(c);
+        assertTrue(builder.build().authenticator().get() == c);
+    }
+
+    @Test
+    public void testCookieHandler() {
+        HttpClient.Builder builder = HttpClient.newBuilder();
+        CookieHandler a = new CookieManager();
+        builder.cookieHandler(a);
+        assertTrue(builder.build().cookieHandler().get() == a);
+        CookieHandler b = new CookieManager();
+        builder.cookieHandler(b);
+        assertTrue(builder.build().cookieHandler().get() == b);
+        assertThrows(NullPointerException.class, () -> builder.cookieHandler(null));
+        CookieManager c = new CookieManager();
+        builder.cookieHandler(c);
+        assertTrue(builder.build().cookieHandler().get() == c);
+    }
+
+    static class TestExecutor implements Executor {
+        public void execute(Runnable r) { }
+    }
+
+    @Test
+    public void testExecutor() {
+        HttpClient.Builder builder = HttpClient.newBuilder();
+        TestExecutor a = new TestExecutor();
+        builder.executor(a);
+        assertTrue(builder.build().executor().get() == a);
+        TestExecutor b = new TestExecutor();
+        builder.executor(b);
+        assertTrue(builder.build().executor().get() == b);
+        assertThrows(NullPointerException.class, () -> builder.executor(null));
+        TestExecutor c = new TestExecutor();
+        builder.executor(c);
+        assertTrue(builder.build().executor().get() == c);
+    }
+
+    @Test
+    public void testProxySelector() {
+        HttpClient.Builder builder = HttpClient.newBuilder();
+        ProxySelector a = ProxySelector.of(null);
+        builder.proxy(a);
+        assertTrue(builder.build().proxy().get() == a);
+        ProxySelector b = ProxySelector.of(InetSocketAddress.createUnresolved("foo", 80));
+        builder.proxy(b);
+        assertTrue(builder.build().proxy().get() == b);
+        assertThrows(NullPointerException.class, () -> builder.proxy(null));
+        ProxySelector c = ProxySelector.of(InetSocketAddress.createUnresolved("bar", 80));
+        builder.proxy(c);
+        assertTrue(builder.build().proxy().get() == c);
+    }
+
+    @Test
+    public void testSSLParameters() {
+        HttpClient.Builder builder = HttpClient.newBuilder();
+        SSLParameters a = new SSLParameters();
+        a.setCipherSuites(new String[] { "A" });
+        builder.sslParameters(a);
+        a.setCipherSuites(new String[] { "Z" });
+        assertTrue(builder.build().sslParameters() != (a));
+        assertTrue(builder.build().sslParameters().getCipherSuites()[0].equals("A"));
+        SSLParameters b = new SSLParameters();
+        b.setEnableRetransmissions(true);
+        builder.sslParameters(b);
+        assertTrue(builder.build().sslParameters() != b);
+        assertTrue(builder.build().sslParameters().getEnableRetransmissions());
+        assertThrows(NullPointerException.class, () -> builder.sslParameters(null));
+        SSLParameters c = new SSLParameters();
+        c.setProtocols(new String[] { "C" });
+        builder.sslParameters(c);
+        c.setProtocols(new String[] { "D" });
+        assertTrue(builder.build().sslParameters().getProtocols()[0].equals("C"));
+    }
+
+    @Test
+    public void testSSLContext() throws Exception {
+        HttpClient.Builder builder = HttpClient.newBuilder();
+        SSLContext a = (new SimpleSSLContext()).get();
+        builder.sslContext(a);
+        assertTrue(builder.build().sslContext() == a);
+        SSLContext b = (new SimpleSSLContext()).get();
+        builder.sslContext(b);
+        assertTrue(builder.build().sslContext() == b);
+        assertThrows(NullPointerException.class, () -> builder.sslContext(null));
+        SSLContext c = (new SimpleSSLContext()).get();
+        builder.sslContext(c);
+        assertTrue(builder.build().sslContext() == c);
+    }
+
+    @Test
+    public void testFollowRedirects() {
+        HttpClient.Builder builder = HttpClient.newBuilder();
+        builder.followRedirects(Redirect.ALWAYS);
+        assertTrue(builder.build().followRedirects() == Redirect.ALWAYS);
+        builder.followRedirects(Redirect.NEVER);
+        assertTrue(builder.build().followRedirects() == Redirect.NEVER);
+        assertThrows(NullPointerException.class, () -> builder.followRedirects(null));
+        builder.followRedirects(Redirect.SAME_PROTOCOL);
+        assertTrue(builder.build().followRedirects() == Redirect.SAME_PROTOCOL);
+        builder.followRedirects(Redirect.SECURE);
+        assertTrue(builder.build().followRedirects() == Redirect.SECURE);
+    }
+
+    @Test
+    public void testVersion() {
+        HttpClient.Builder builder = HttpClient.newBuilder();
+        builder.version(Version.HTTP_2);
+        assertTrue(builder.build().version() == Version.HTTP_2);
+        builder.version(Version.HTTP_1_1);
+        assertTrue(builder.build().version() == Version.HTTP_1_1);
+        assertThrows(NullPointerException.class, () -> builder.version(null));
+        builder.version(Version.HTTP_2);
+        assertTrue(builder.build().version() == Version.HTTP_2);
+        builder.version(Version.HTTP_1_1);
+        assertTrue(builder.build().version() == Version.HTTP_1_1);
+    }
+
+    @Test
+    static void testPriority() throws Exception {
+        HttpClient.Builder builder = HttpClient.newBuilder();
+        assertThrows(IllegalArgumentException.class, () -> builder.priority(-1));
+        assertThrows(IllegalArgumentException.class, () -> builder.priority(0));
+        assertThrows(IllegalArgumentException.class, () -> builder.priority(257));
+        assertThrows(IllegalArgumentException.class, () -> builder.priority(500));
+
+        builder.priority(1);
+        builder.build();
+        builder.priority(256);
+        builder.build();
+    }
+
+
+    /* ---- standalone entry point ---- */
+
+    public static void main(String[] args) throws Exception {
+        HttpClientBuilderTest test = new HttpClientBuilderTest();
+        for (Method m : HttpClientBuilderTest.class.getDeclaredMethods()) {
+            if (m.isAnnotationPresent(Test.class)) {
+                try {
+                    m.invoke(test);
+                    System.out.printf("test %s: success%n", m.getName());
+                } catch (Throwable t ) {
+                    System.out.printf("test %s: failed%n", m.getName());
+                    t.printStackTrace();
+                }
+            }
+        }
+    }
+}
diff --git a/test/jdk/java/net/httpclient/HttpInputStreamTest.java b/test/jdk/java/net/httpclient/HttpInputStreamTest.java
index 0851fed7db8..150cfabaf12 100644
--- a/test/jdk/java/net/httpclient/HttpInputStreamTest.java
+++ b/test/jdk/java/net/httpclient/HttpInputStreamTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2017, 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,6 +32,8 @@ import jdk.incubator.http.HttpRequest;
 import jdk.incubator.http.HttpResponse;
 import java.nio.ByteBuffer;
 import java.nio.charset.Charset;
+import java.util.Iterator;
+import java.util.List;
 import java.util.Locale;
 import java.util.Optional;
 import java.util.concurrent.ArrayBlockingQueue;
@@ -40,11 +42,12 @@ import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.CompletionStage;
 import java.util.concurrent.Flow;
 import java.util.stream.Stream;
+import static java.lang.System.err;
 
 /*
  * @test
  * @summary An example on how to read a response body with InputStream...
- * @run main/othervm HttpInputStreamTest
+ * @run main/othervm -Dtest.debug=true HttpInputStreamTest
  * @author daniel fuchs
  */
 public class HttpInputStreamTest {
@@ -61,7 +64,7 @@ public class HttpInputStreamTest {
     public static class HttpInputStreamHandler
         implements HttpResponse.BodyHandler<InputStream>    {
 
-        public static final int MAX_BUFFERS_IN_QUEUE = 1;
+        public static final int MAX_BUFFERS_IN_QUEUE = 1;  // lock-step with the producer
 
         private final int maxBuffers;
 
@@ -74,7 +77,7 @@ public class HttpInputStreamTest {
         }
 
         @Override
-        public synchronized HttpResponse.BodyProcessor<InputStream>
+        public HttpResponse.BodySubscriber<InputStream>
                 apply(int i, HttpHeaders hh) {
             return new HttpResponseInputStream(maxBuffers);
         }
@@ -83,17 +86,19 @@ public class HttpInputStreamTest {
          * An InputStream built on top of the Flow API.
          */
         private static class HttpResponseInputStream extends InputStream
-                    implements HttpResponse.BodyProcessor<InputStream> {
+                    implements HttpResponse.BodySubscriber<InputStream> {
 
             // An immutable ByteBuffer sentinel to mark that the last byte was received.
-            private static final ByteBuffer LAST = ByteBuffer.wrap(new byte[0]);
+            private static final ByteBuffer LAST_BUFFER = ByteBuffer.wrap(new byte[0]);
+            private static final List<ByteBuffer> LAST_LIST = List.of(LAST_BUFFER);
 
             // A queue of yet unprocessed ByteBuffers received from the flow API.
-            private final BlockingQueue<ByteBuffer> buffers;
+            private final BlockingQueue<List<ByteBuffer>> buffers;
             private volatile Flow.Subscription subscription;
             private volatile boolean closed;
             private volatile Throwable failed;
-            private volatile ByteBuffer current;
+            private volatile Iterator<ByteBuffer> currentListItr;
+            private volatile ByteBuffer currentBuffer;
 
             HttpResponseInputStream() {
                 this(MAX_BUFFERS_IN_QUEUE);
@@ -101,7 +106,8 @@ public class HttpInputStreamTest {
 
             HttpResponseInputStream(int maxBuffers) {
                 int capacity = maxBuffers <= 0 ? MAX_BUFFERS_IN_QUEUE : maxBuffers;
-                this.buffers = new ArrayBlockingQueue<>(capacity);
+                // 1 additional slot for LAST_LIST added by onComplete
+                this.buffers = new ArrayBlockingQueue<>(capacity + 1);
             }
 
             @Override
@@ -119,40 +125,49 @@ public class HttpInputStreamTest {
             // a new buffer is made available through the Flow API, or the
             // end of the flow is reached.
             private ByteBuffer current() throws IOException {
-                while (current == null || !current.hasRemaining()) {
-                    // Check whether the stream is claused or exhausted
+                while (currentBuffer == null || !currentBuffer.hasRemaining()) {
+                    // Check whether the stream is closed or exhausted
                     if (closed || failed != null) {
                         throw new IOException("closed", failed);
                     }
-                    if (current == LAST) break;
+                    if (currentBuffer == LAST_BUFFER) break;
 
                     try {
-                        // Take a new buffer from the queue, blocking
-                        // if none is available yet...
-                        if (DEBUG) System.err.println("Taking Buffer");
-                        current = buffers.take();
-                        if (DEBUG) System.err.println("Buffer Taken");
+                        if (currentListItr == null || !currentListItr.hasNext()) {
+                            // Take a new list of buffers from the queue, blocking
+                            // if none is available yet...
 
-                        // Check whether some exception was encountered
-                        // upstream
-                        if (closed || failed != null) {
-                            throw new IOException("closed", failed);
+                            if (DEBUG) err.println("Taking list of Buffers");
+                            List<ByteBuffer> lb = buffers.take();
+                            currentListItr = lb.iterator();
+                            if (DEBUG) err.println("List of Buffers Taken");
+
+                            // Check whether an exception was encountered upstream
+                            if (closed || failed != null)
+                                throw new IOException("closed", failed);
+
+                            // Check whether we're done.
+                            if (lb == LAST_LIST) {
+                                currentListItr = null;
+                                currentBuffer = LAST_BUFFER;
+                                break;
+                            }
+
+                            // Request another upstream item ( list of buffers )
+                            Flow.Subscription s = subscription;
+                            if (s != null)
+                                s.request(1);
                         }
-
-                        // Check whether we're done.
-                        if (current == LAST) break;
-
-                        // Inform the producer that it can start sending
-                        // us a new buffer
-                        Flow.Subscription s = subscription;
-                        if (s != null) s.request(1);
-
+                        assert currentListItr != null;
+                        assert currentListItr.hasNext();
+                        if (DEBUG) err.println("Next Buffer");
+                        currentBuffer = currentListItr.next();
                     } catch (InterruptedException ex) {
                         // continue
                     }
                 }
-                assert current == LAST || current.hasRemaining();
-                return current;
+                assert currentBuffer == LAST_BUFFER || currentBuffer.hasRemaining();
+                return currentBuffer;
             }
 
             @Override
@@ -160,7 +175,7 @@ public class HttpInputStreamTest {
                 // get the buffer to read from, possibly blocking if
                 // none is available
                 ByteBuffer buffer;
-                if ((buffer = current()) == LAST) return -1;
+                if ((buffer = current()) == LAST_BUFFER) return -1;
 
                 // don't attempt to read more than what is available
                 // in the current buffer.
@@ -175,22 +190,31 @@ public class HttpInputStreamTest {
             @Override
             public int read() throws IOException {
                 ByteBuffer buffer;
-                if ((buffer = current()) == LAST) return -1;
+                if ((buffer = current()) == LAST_BUFFER) return -1;
                 return buffer.get() & 0xFF;
             }
 
             @Override
             public void onSubscribe(Flow.Subscription s) {
+                if (this.subscription != null) {
+                    s.cancel();
+                    return;
+                }
                 this.subscription = s;
-                s.request(Math.max(2, buffers.remainingCapacity() + 1));
+                assert buffers.remainingCapacity() > 1; // should at least be 2
+                if (DEBUG) err.println("onSubscribe: requesting "
+                     + Math.max(1, buffers.remainingCapacity() - 1));
+                s.request(Math.max(1, buffers.remainingCapacity() - 1));
             }
 
             @Override
-            public synchronized void onNext(ByteBuffer t) {
+            public void onNext(List<ByteBuffer> t) {
                 try {
-                    if (DEBUG) System.err.println("next buffer received");
-                    buffers.put(t);
-                    if (DEBUG) System.err.println("buffered offered");
+                    if (DEBUG) err.println("next item received");
+                    if (!buffers.offer(t)) {
+                        throw new IllegalStateException("queue is full");
+                    }
+                    if (DEBUG) err.println("item offered");
                 } catch (Exception ex) {
                     failed = ex;
                     try {
@@ -203,24 +227,26 @@ public class HttpInputStreamTest {
 
             @Override
             public void onError(Throwable thrwbl) {
+                subscription = null;
                 failed = thrwbl;
             }
 
             @Override
-            public synchronized void onComplete() {
+            public void onComplete() {
                 subscription = null;
-                onNext(LAST);
+                onNext(LAST_LIST);
             }
 
             @Override
             public void close() throws IOException {
                 synchronized (this) {
+                    if (closed) return;
                     closed = true;
-                    Flow.Subscription s = subscription;
-                    if (s != null) {
-                        s.cancel();
-                    }
-                    subscription = null;
+                }
+                Flow.Subscription s = subscription;
+                subscription = null;
+                if (s != null) {
+                     s.cancel();
                 }
                 super.close();
             }
@@ -274,8 +300,8 @@ public class HttpInputStreamTest {
         //    client.sendAsync(request, HttpResponse.BodyHandler.asString()).get().body());
 
         CompletableFuture<HttpResponse<InputStream>> handle =
-            client.sendAsync(request, new HttpInputStreamHandler());
-        if (DEBUG) System.err.println("Request sent");
+            client.sendAsync(request, new HttpInputStreamHandler(3));
+        if (DEBUG) err.println("Request sent");
 
         HttpResponse<InputStream> pending = handle.get();
 
@@ -301,8 +327,8 @@ public class HttpInputStreamTest {
 
             char[] buff = new char[32];
             int off=0, n=0;
-            if (DEBUG) System.err.println("Start receiving response body");
-            if (DEBUG) System.err.println("Charset: " + charset.get());
+            if (DEBUG) err.println("Start receiving response body");
+            if (DEBUG) err.println("Charset: " + charset.get());
 
             // Start consuming the InputStream as the data arrives.
             // Will block until there is something to read...
diff --git a/test/jdk/java/net/httpclient/HttpRequestBuilderTest.java b/test/jdk/java/net/httpclient/HttpRequestBuilderTest.java
index 6df6e5a67b4..b2023783d63 100644
--- a/test/jdk/java/net/httpclient/HttpRequestBuilderTest.java
+++ b/test/jdk/java/net/httpclient/HttpRequestBuilderTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2017, 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
@@ -21,21 +21,23 @@
  * questions.
  */
 
-import jdk.incubator.http.HttpRequest;
 import java.net.URI;
 import jdk.incubator.http.HttpClient;
 import java.time.Duration;
+import java.util.Arrays;
 import java.util.function.BiFunction;
 import java.util.function.Function;
+import java.util.function.Supplier;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
+import jdk.incubator.http.HttpRequest;
+import static jdk.incubator.http.HttpRequest.BodyPublisher.fromString;
+import static jdk.incubator.http.HttpRequest.BodyPublisher.noBody;
 
 /**
  * @test
  * @bug 8170064
- * @summary  HttpRequest API documentation says:" Unless otherwise stated,
- * {@code null} parameter values will cause methods
- * of this class to throw {@code NullPointerException}".
+ * @summary  HttpRequest[.Builder] API and behaviour checks
  */
 public class HttpRequestBuilderTest {
 
@@ -44,70 +46,166 @@ public class HttpRequestBuilderTest {
 
     public static void main(String[] args) throws Exception {
 
+        test0("newBuilder().build()",
+              () -> HttpRequest.newBuilder().build(),
+              IllegalStateException.class);
+
+        test0("newBuilder(null)",
+              () -> HttpRequest.newBuilder(null),
+              NullPointerException.class);
+
+        test0("newBuilder(URI.create(\"badScheme://www.foo.com/\")",
+              () -> HttpRequest.newBuilder(URI.create("badScheme://www.foo.com/")),
+              IllegalArgumentException.class);
+
+        test0("newBuilder(URI.create(\"http://www.foo.com:-1/\")",
+                () -> HttpRequest.newBuilder(URI.create("http://www.foo.com:-1/")),
+                IllegalArgumentException.class);
+
+        test0("newBuilder(URI.create(\"https://www.foo.com:-1/\")",
+                () -> HttpRequest.newBuilder(URI.create("https://www.foo.com:-1/")),
+                IllegalArgumentException.class);
+
+        test0("newBuilder(" + TEST_URI + ").uri(null)",
+              () -> HttpRequest.newBuilder(TEST_URI).uri(null),
+              NullPointerException.class);
+
+        test0("newBuilder(uri).build()",
+              () -> HttpRequest.newBuilder(TEST_URI).build()
+              /* no expected exceptions */ );
+
         HttpRequest.Builder builder = HttpRequest.newBuilder();
+
         builder = test1("uri", builder, builder::uri, (URI)null,
                         NullPointerException.class);
+
+        builder = test1("uri", builder, builder::uri, URI.create("http://www.foo.com:-1/"),
+                        IllegalArgumentException.class);
+
+        builder = test1("uri", builder, builder::uri, URI.create("https://www.foo.com:-1/"),
+                        IllegalArgumentException.class);
+
         builder = test2("header", builder, builder::header, (String) null, "bar",
                         NullPointerException.class);
+
         builder = test2("header", builder, builder::header, "foo", (String) null,
                         NullPointerException.class);
+
         builder = test2("header", builder, builder::header, (String)null,
                         (String) null, NullPointerException.class);
+
+        builder = test2("header", builder, builder::header, "", "bar",
+                        IllegalArgumentException.class);
+
+        builder = test2("header", builder, builder::header, "foo", "\r",
+                        IllegalArgumentException.class);
+
         builder = test1("headers", builder, builder::headers, (String[]) null,
                         NullPointerException.class);
+
+        builder = test1("headers", builder, builder::headers, new String[0],
+                        IllegalArgumentException.class);
+
         builder = test1("headers", builder, builder::headers,
                         (String[]) new String[] {null, "bar"},
                         NullPointerException.class);
+
         builder = test1("headers", builder, builder::headers,
                         (String[]) new String[] {"foo", null},
                         NullPointerException.class);
+
         builder = test1("headers", builder, builder::headers,
                         (String[]) new String[] {null, null},
                         NullPointerException.class);
+
         builder = test1("headers", builder, builder::headers,
-                       (String[]) new String[] {"foo", "bar", null},
-                       NullPointerException.class,
-                       IllegalArgumentException.class);
+                        (String[]) new String[] {"foo", "bar", null},
+                        NullPointerException.class,
+                        IllegalArgumentException.class);
+
         builder = test1("headers", builder, builder::headers,
-                       (String[]) new String[] {"foo", "bar", null, null},
-                       NullPointerException.class);
+                        (String[]) new String[] {"foo", "bar", null, null},
+                        NullPointerException.class);
+
         builder = test1("headers", builder, builder::headers,
-                       (String[]) new String[] {"foo", "bar", "baz", null},
-                       NullPointerException.class);
+                        (String[]) new String[] {"foo", "bar", "baz", null},
+                        NullPointerException.class);
+
         builder = test1("headers", builder, builder::headers,
-                       (String[]) new String[] {"foo", "bar", null, "baz"},
-                       NullPointerException.class);
+                        (String[]) new String[] {"foo", "bar", "\r", "baz"},
+                        IllegalArgumentException.class);
+
         builder = test1("headers", builder, builder::headers,
-                       (String[]) new String[] {"foo", "bar", "baz"},
-                       IllegalArgumentException.class);
+                        (String[]) new String[] {"foo", "bar", "baz", "\n"},
+                        IllegalArgumentException.class);
+
         builder = test1("headers", builder, builder::headers,
-                       (String[]) new String[] {"foo"},
-                       IllegalArgumentException.class);
+                        (String[]) new String[] {"foo", "bar", "", "baz"},
+                        IllegalArgumentException.class);
+
+        builder = test1("headers", builder, builder::headers,
+                        (String[]) new String[] {"foo", "bar", null, "baz"},
+                        NullPointerException.class);
+
+        builder = test1("headers", builder, builder::headers,
+                        (String[]) new String[] {"foo", "bar", "baz"},
+                        IllegalArgumentException.class);
+
+        builder = test1("headers", builder, builder::headers,
+                        (String[]) new String[] {"foo"},
+                        IllegalArgumentException.class);
+
         builder = test1("DELETE", builder, builder::DELETE,
-                        (HttpRequest.BodyProcessor)null, null);
+                        noBody(), null);
+
         builder = test1("POST", builder, builder::POST,
-                        (HttpRequest.BodyProcessor)null, null);
+                        noBody(), null);
+
         builder = test1("PUT", builder, builder::PUT,
-                        (HttpRequest.BodyProcessor)null, null);
+                        noBody(), null);
+
         builder = test2("method", builder, builder::method, "GET",
-                        (HttpRequest.BodyProcessor) null, null);
+                        noBody(), null);
+
+        builder = test1("DELETE", builder, builder::DELETE,
+                        (HttpRequest.BodyPublisher)null,
+                        NullPointerException.class);
+
+        builder = test1("POST", builder, builder::POST,
+                        (HttpRequest.BodyPublisher)null,
+                        NullPointerException.class);
+
+        builder = test1("PUT", builder, builder::PUT,
+                        (HttpRequest.BodyPublisher)null,
+                        NullPointerException.class);
+
+        builder = test2("method", builder, builder::method, "GET",
+                        (HttpRequest.BodyPublisher) null,
+                        NullPointerException.class);
+
         builder = test2("setHeader", builder, builder::setHeader,
                         (String) null, "bar",
                         NullPointerException.class);
+
         builder = test2("setHeader", builder, builder::setHeader,
                         "foo", (String) null,
                         NullPointerException.class);
+
         builder = test2("setHeader", builder, builder::setHeader,
                         (String)null, (String) null,
                         NullPointerException.class);
+
         builder = test1("timeout", builder, builder::timeout,
-                        (Duration)null, NullPointerException.class);
+                        (Duration)null,
+                        NullPointerException.class);
+
         builder = test1("version", builder, builder::version,
                         (HttpClient.Version)null,
                         NullPointerException.class);
+
         builder = test2("method", builder, builder::method, null,
-                       HttpRequest.BodyProcessor.fromString("foo"),
-                       NullPointerException.class);
+                        fromString("foo"),
+                        NullPointerException.class);
 // see JDK-8170093
 //
 //        builder = test2("method", builder, builder::method, "foo",
@@ -116,6 +214,53 @@ public class HttpRequestBuilderTest {
 //
 //        builder.build();
 
+
+        method("newBuilder(TEST_URI).build().method() == GET",
+               () -> HttpRequest.newBuilder(TEST_URI),
+               "GET");
+
+        method("newBuilder(TEST_URI).GET().build().method() == GET",
+               () -> HttpRequest.newBuilder(TEST_URI).GET(),
+               "GET");
+
+        method("newBuilder(TEST_URI).POST(fromString(\"\")).GET().build().method() == GET",
+               () -> HttpRequest.newBuilder(TEST_URI).POST(fromString("")).GET(),
+               "GET");
+
+        method("newBuilder(TEST_URI).PUT(fromString(\"\")).GET().build().method() == GET",
+               () -> HttpRequest.newBuilder(TEST_URI).PUT(fromString("")).GET(),
+               "GET");
+
+        method("newBuilder(TEST_URI).DELETE(fromString(\"\")).GET().build().method() == GET",
+               () -> HttpRequest.newBuilder(TEST_URI).DELETE(fromString("")).GET(),
+               "GET");
+
+        method("newBuilder(TEST_URI).POST(fromString(\"\")).build().method() == POST",
+               () -> HttpRequest.newBuilder(TEST_URI).POST(fromString("")),
+               "POST");
+
+        method("newBuilder(TEST_URI).PUT(fromString(\"\")).build().method() == PUT",
+               () -> HttpRequest.newBuilder(TEST_URI).PUT(fromString("")),
+               "PUT");
+
+        method("newBuilder(TEST_URI).DELETE(fromString(\"\")).build().method() == DELETE",
+               () -> HttpRequest.newBuilder(TEST_URI).DELETE(fromString("")),
+               "DELETE");
+
+        method("newBuilder(TEST_URI).GET().POST(fromString(\"\")).build().method() == POST",
+               () -> HttpRequest.newBuilder(TEST_URI).GET().POST(fromString("")),
+               "POST");
+
+        method("newBuilder(TEST_URI).GET().PUT(fromString(\"\")).build().method() == PUT",
+               () -> HttpRequest.newBuilder(TEST_URI).GET().PUT(fromString("")),
+               "PUT");
+
+        method("newBuilder(TEST_URI).GET().DELETE(fromString(\"\")).build().method() == DELETE",
+               () -> HttpRequest.newBuilder(TEST_URI).GET().DELETE(fromString("")),
+               "DELETE");
+
+
+
     }
 
     private static boolean shouldFail(Class<? extends Exception> ...exceptions) {
@@ -133,22 +278,66 @@ public class HttpRequestBuilderTest {
                 .findAny().isPresent();
     }
 
-    public static <R,P> R test1(String name, R receiver, Function<P, R> m, P arg,
-                               Class<? extends Exception> ...ex) {
+    static void method(String name,
+                       Supplier<HttpRequest.Builder> supplier,
+                       String expectedMethod) {
+        HttpRequest request = supplier.get().build();
+        String method = request.method();
+        if (request.method().equals("GET") && request.bodyPublisher().isPresent())
+            throw new AssertionError("failed: " + name
+                    + ". Unexpected body processor for GET: "
+                    + request.bodyPublisher().get());
+
+        if (expectedMethod.equals(method)) {
+            System.out.println("success: " + name);
+        } else {
+            throw new AssertionError("failed: " + name
+                    + ". Expected " + expectedMethod + ", got " + method);
+        }
+    }
+
+    static void test0(String name,
+                      Runnable r,
+                      Class<? extends Exception> ...ex) {
         try {
-            R result =  m.apply(arg);
+            r.run();
             if (!shouldFail(ex)) {
-                System.out.println("success: " + name + "(" + arg + ")");
-                return result;
+                System.out.println("success: " + name);
+                return;
             } else {
                 throw new AssertionError("Expected " + expectedNames(ex)
-                    + " not raised for " + name + "(" + arg + ")");
+                        + " not raised for " + name);
             }
         } catch (Exception x) {
             if (!isExpected(x, ex)) {
                 throw x;
             } else {
-                System.out.println("success: " + name + "(" + arg + ")" +
+                System.out.println("success: " + name +
+                        " - Got expected exception: " + x);
+            }
+        }
+    }
+
+    public static <R,P> R test1(String name, R receiver, Function<P, R> m, P arg,
+                               Class<? extends Exception> ...ex) {
+        String argMessage = arg == null ? "null" : arg.toString();
+        if (arg instanceof String[]) {
+            argMessage = Arrays.asList((String[])arg).toString();
+        }
+        try {
+            R result =  m.apply(arg);
+            if (!shouldFail(ex)) {
+                System.out.println("success: " + name + "(" + argMessage + ")");
+                return result;
+            } else {
+                throw new AssertionError("Expected " + expectedNames(ex)
+                    + " not raised for " + name + "(" + argMessage + ")");
+            }
+        } catch (Exception x) {
+            if (!isExpected(x, ex)) {
+                throw x;
+            } else {
+                System.out.println("success: " + name + "(" + argMessage + ")" +
                         " - Got expected exception: " + x);
                 return receiver;
             }
diff --git a/test/jdk/java/net/httpclient/HttpResponseInputStreamTest.java b/test/jdk/java/net/httpclient/HttpResponseInputStreamTest.java
new file mode 100644
index 00000000000..3480fabeffe
--- /dev/null
+++ b/test/jdk/java/net/httpclient/HttpResponseInputStreamTest.java
@@ -0,0 +1,246 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import jdk.incubator.http.HttpResponse;
+import jdk.incubator.http.HttpResponse.BodySubscriber;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.UncheckedIOException;
+import java.nio.ByteBuffer;
+import java.util.List;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.CompletionException;
+import java.util.concurrent.CompletionStage;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Flow;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicLong;
+
+import org.testng.annotations.Test;
+
+/*
+ * @test
+ * @summary Simple smoke test for BodySubscriber.asInputStream();
+ * @run testng/othervm HttpResponseInputStreamTest
+ * @author daniel fuchs
+ */
+public class HttpResponseInputStreamTest {
+
+    static class TestException extends IOException {}
+
+    public static void main(String[] args) throws InterruptedException, ExecutionException {
+        testOnError();
+    }
+
+    /**
+     * Tests that a client will be unblocked and will throw an IOException
+     * if an error occurs while the client is waiting for more data.
+     * @throws InterruptedException
+     * @throws ExecutionException
+     */
+    @Test
+    public static void testOnError() throws InterruptedException, ExecutionException {
+        CountDownLatch latch = new CountDownLatch(1);
+        BodySubscriber<InputStream> isb = BodySubscriber.asInputStream();
+        ErrorTestSubscription s = new ErrorTestSubscription(isb);
+        CompletionStage<Throwable> cs =
+                isb.getBody().thenApplyAsync((is) -> s.accept(latch, is));
+        latch.await();
+        isb.onSubscribe(s);
+        s.t.join();
+        Throwable result = cs.toCompletableFuture().get();
+        Throwable t = result;
+        if (!(t instanceof IOException)) {
+            throw new RuntimeException("Failed to receive excpected IOException", result);
+        } else {
+            System.out.println("Got expected exception: " + t);
+        }
+        while (t != null) {
+            if (t instanceof TestException) break;
+            t = t.getCause();
+        }
+        if (t instanceof TestException) {
+            System.out.println("Got expected cause: " + t);
+        } else {
+            throw new RuntimeException("Failed to receive excpected TestException", result);
+        }
+    }
+
+    static class ErrorTestSubscription implements Flow.Subscription {
+        final BodySubscriber<InputStream> isb;
+        final Thread t = new Thread() {
+            @Override
+            public void run() {
+                try {
+                    // Give time to
+                    System.out.println("waiting...");
+                    Thread.sleep(1000);
+                } catch (InterruptedException e) {
+
+                }
+                System.out.println("Calling onError...");
+                isb.onError(new TestException());
+            }
+        };
+
+        ErrorTestSubscription(BodySubscriber<InputStream> isb) {
+            this.isb = isb;
+        }
+
+        int requested = 0;
+
+        @Override
+        public void request(long n) {
+            System.out.println("Got request: " + n);
+            if (requested == 0 && n > 0) {
+                //isb.onNext(List.of(java.nio.ByteBuffer.wrap(new byte[] {0x01})));
+                requested += n;
+                t.start();
+            }
+        }
+
+        @Override
+        public void cancel() {
+        }
+
+        public Throwable accept(CountDownLatch latch, InputStream is) {
+            System.out.println("got " + is);
+            try {
+                latch.countDown();
+                System.out.println("reading all bytes");
+                is.readAllBytes();
+                System.out.println("all bytes read");
+            } catch (IOException e) {
+                return e;
+            } finally {
+                try {
+                    is.close();
+                } catch (IOException e) {
+                    return e;
+                }
+            }
+            return is == null ? new NullPointerException() : null;
+        }
+    }
+
+    static InputStream close(InputStream is) {
+        try {
+            is.close();
+        } catch (IOException io) {
+            throw new CompletionException(io);
+        }
+        return is;
+    }
+
+    @Test
+    public static void testCloseAndSubscribe()
+            throws InterruptedException, ExecutionException
+    {
+        BodySubscriber<InputStream> isb = BodySubscriber.asInputStream();
+        TestCancelOnCloseSubscription s = new TestCancelOnCloseSubscription();
+        InputStream is = isb.getBody()
+                .thenApply(HttpResponseInputStreamTest::close)
+                .toCompletableFuture()
+                .get();
+        isb.onSubscribe(s);
+        System.out.println(s);
+        if (!s.cancelled.get()) {
+            throw new RuntimeException("subscription not cancelled");
+        }
+        if (s.request.get() > 0) {
+            throw new RuntimeException("subscription has demand");
+        }
+    }
+
+    static byte[] readAllBytes(InputStream is) {
+        try {
+            return is.readAllBytes();
+        } catch (IOException io) {
+            io.printStackTrace();
+            throw new CompletionException(io);
+        }
+    }
+
+    @Test
+    public static void testSubscribeAndClose()
+            throws InterruptedException, ExecutionException
+    {
+        BodySubscriber<InputStream> isb = BodySubscriber.asInputStream();
+        TestCancelOnCloseSubscription s = new TestCancelOnCloseSubscription();
+        InputStream is = isb.getBody().toCompletableFuture().get();
+        isb.onSubscribe(s);
+        if (s.cancelled.get()) {
+            throw new RuntimeException("subscription cancelled");
+        }
+        CompletableFuture<String> cf = CompletableFuture.supplyAsync(
+                () -> HttpResponseInputStreamTest.readAllBytes(is))
+                .thenApply(String::new);
+        while (s.request.get() == 0) {
+            Thread.sleep(100);
+        }
+        isb.onNext(List.of(ByteBuffer.wrap("coucou".getBytes())));
+        close(is);
+        System.out.println(s);
+        if (!s.cancelled.get()) {
+            throw new RuntimeException("subscription not cancelled");
+        }
+        if (s.request.get() == 0) {
+            throw new RuntimeException("subscription has no demand");
+        }
+        try {
+            System.out.println("read " + cf.get() + "!");
+            throw new RuntimeException("expected IOException not raised");
+        } catch (ExecutionException | CompletionException x) {
+            if (x.getCause() instanceof IOException) {
+                System.out.println("Got expected IOException: " + x.getCause());
+            } else {
+                throw x;
+            }
+        }
+    }
+
+    static class TestCancelOnCloseSubscription implements Flow.Subscription {
+        final AtomicLong request = new AtomicLong();
+        final AtomicBoolean cancelled = new AtomicBoolean();
+
+        @Override
+        public void request(long n) {
+            request.addAndGet(n);
+        }
+
+        @Override
+        public void cancel() {
+            cancelled.set(true);
+        }
+
+        @Override
+        public String toString() {
+            return String.format("%s(request=%d, cancelled=%s)",
+                    this.getClass().getSimpleName(),
+                    request.get(),
+                    cancelled.get());
+        }
+    }
+}
diff --git a/test/jdk/java/net/httpclient/ImmutableHeaders.java b/test/jdk/java/net/httpclient/ImmutableHeaders.java
index bd6a2f7e733..48304ad1afc 100644
--- a/test/jdk/java/net/httpclient/ImmutableHeaders.java
+++ b/test/jdk/java/net/httpclient/ImmutableHeaders.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2017, 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
diff --git a/test/jdk/java/net/httpclient/InterruptedBlockingSend.java b/test/jdk/java/net/httpclient/InterruptedBlockingSend.java
new file mode 100644
index 00000000000..b3514eac0b5
--- /dev/null
+++ b/test/jdk/java/net/httpclient/InterruptedBlockingSend.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.net.ServerSocket;
+import java.net.URI;
+import jdk.incubator.http.HttpClient;
+import jdk.incubator.http.HttpRequest;
+import static java.lang.System.out;
+import static jdk.incubator.http.HttpResponse.BodyHandler.discard;
+
+/**
+ * @test
+ * @summary Basic test for interrupted blocking send
+ */
+
+public class InterruptedBlockingSend {
+
+    static volatile Throwable throwable;
+
+    public static void main(String[] args) throws Exception {
+        HttpClient client = HttpClient.newHttpClient();
+        try (ServerSocket ss = new ServerSocket(0, 20)) {
+            int port = ss.getLocalPort();
+            URI uri = new URI("http://127.0.0.1:" + port + "/");
+
+            HttpRequest request = HttpRequest.newBuilder(uri).build();
+
+            Thread t = new Thread(() -> {
+                try {
+                    client.send(request, discard(null));
+                } catch (InterruptedException e) {
+                    throwable = e;
+                } catch (Throwable th) {
+                    throwable = th;
+                }
+            });
+            t.start();
+            Thread.sleep(5000);
+            t.interrupt();
+            t.join();
+
+            if (!(throwable instanceof InterruptedException)) {
+                throw new RuntimeException("Expected InterruptedException, got " + throwable);
+            } else {
+                out.println("Caught expected InterruptedException: " + throwable);
+            }
+        }
+    }
+}
diff --git a/test/jdk/java/net/httpclient/LightWeightHttpServer.java b/test/jdk/java/net/httpclient/LightWeightHttpServer.java
index d80a382d669..38242dd5221 100644
--- a/test/jdk/java/net/httpclient/LightWeightHttpServer.java
+++ b/test/jdk/java/net/httpclient/LightWeightHttpServer.java
@@ -80,7 +80,7 @@ public class LightWeightHttpServer {
         ch.setLevel(Level.ALL);
         logger.addHandler(ch);
 
-        String root = System.getProperty("test.src") + "/docs";
+        String root = System.getProperty("test.src", ".") + "/docs";
         InetSocketAddress addr = new InetSocketAddress(0);
         httpServer = HttpServer.create(addr, 0);
         if (httpServer instanceof HttpsServer) {
@@ -301,11 +301,12 @@ public class LightWeightHttpServer {
 
         @Override
         public synchronized void handle(HttpExchange he) throws IOException {
-            byte[] buf = Util.readAll(he.getRequestBody());
-            try {
+            try(InputStream is = he.getRequestBody()) {
+                is.readAllBytes();
                 bar1.await();
                 bar2.await();
             } catch (InterruptedException | BrokenBarrierException e) {
+                throw new IOException(e);
             }
             he.sendResponseHeaders(200, -1); // will probably fail
             he.close();
diff --git a/test/jdk/java/net/httpclient/ManyRequests.java b/test/jdk/java/net/httpclient/ManyRequests.java
index 8803b397528..dfcd2acd0bc 100644
--- a/test/jdk/java/net/httpclient/ManyRequests.java
+++ b/test/jdk/java/net/httpclient/ManyRequests.java
@@ -56,13 +56,12 @@ import java.util.Formatter;
 import java.util.HashMap;
 import java.util.LinkedList;
 import java.util.Random;
-import java.util.concurrent.ExecutorService;
 import java.util.logging.Logger;
 import java.util.logging.Level;
 import java.util.concurrent.CompletableFuture;
 import javax.net.ssl.SSLContext;
 import jdk.testlibrary.SimpleSSLContext;
-import static jdk.incubator.http.HttpRequest.BodyProcessor.fromByteArray;
+import static jdk.incubator.http.HttpRequest.BodyPublisher.fromByteArray;
 import static jdk.incubator.http.HttpResponse.BodyHandler.asByteArray;
 
 public class ManyRequests {
@@ -91,7 +90,6 @@ public class ManyRequests {
             System.out.println("OK");
         } finally {
             server.stop(0);
-            ((ExecutorService)client.executor()).shutdownNow();
         }
     }
 
@@ -109,17 +107,23 @@ public class ManyRequests {
             System.out.println("Server: received " + e.getRequestURI());
             super.handle(e);
         }
-        protected void close(OutputStream os) throws IOException {
+        @Override
+        protected void close(HttpExchange t, OutputStream os) throws IOException {
             if (INSERT_DELAY) {
-                try { Thread.sleep(rand.nextInt(200)); } catch (InterruptedException e) {}
+                try { Thread.sleep(rand.nextInt(200)); }
+                catch (InterruptedException e) {}
             }
-            super.close(os);
+            System.out.println("Server: close outbound: " + t.getRequestURI());
+            super.close(t, os);
         }
-        protected void close(InputStream is) throws IOException {
+        @Override
+        protected void close(HttpExchange t, InputStream is) throws IOException {
             if (INSERT_DELAY) {
-                try { Thread.sleep(rand.nextInt(200)); } catch (InterruptedException e) {}
+                try { Thread.sleep(rand.nextInt(200)); }
+                catch (InterruptedException e) {}
             }
-            super.close(is);
+            System.out.println("Server: close inbound: " + t.getRequestURI());
+            super.close(t, is);
         }
     }
 
diff --git a/test/jdk/java/net/httpclient/ManyRequests2.java b/test/jdk/java/net/httpclient/ManyRequests2.java
index 26c281fca61..af0189147b3 100644
--- a/test/jdk/java/net/httpclient/ManyRequests2.java
+++ b/test/jdk/java/net/httpclient/ManyRequests2.java
@@ -36,7 +36,7 @@
  * @run main/othervm/timeout=40 -Dtest.XFixed=true ManyRequests2
  * @run main/othervm/timeout=40 -Dtest.XFixed=true -Dtest.insertDelay=true ManyRequests2
  * @run main/othervm/timeout=40 -Dtest.XFixed=true -Dtest.chunkSize=64 ManyRequests2
- * @run main/othervm/timeout=40 -Dtest.XFixed=true -Dtest.insertDelay=true -Dtest.chunkSize=64 ManyRequests2
+ * @run main/othervm/timeout=40 -Djdk.internal.httpclient.debug=true -Dtest.XFixed=true -Dtest.insertDelay=true -Dtest.chunkSize=64 ManyRequests2
  * @summary Send a large number of requests asynchronously. The server echoes back using known content length.
  */
  // * @run main/othervm/timeout=40 -Djdk.httpclient.HttpClient.log=ssl ManyRequests
diff --git a/test/jdk/java/net/httpclient/ManyRequestsLegacy.java b/test/jdk/java/net/httpclient/ManyRequestsLegacy.java
new file mode 100644
index 00000000000..16707741095
--- /dev/null
+++ b/test/jdk/java/net/httpclient/ManyRequestsLegacy.java
@@ -0,0 +1,377 @@
+/*
+ * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @modules jdk.incubator.httpclient
+ *          java.logging
+ *          jdk.httpserver
+ * @library /lib/testlibrary/ /
+ * @build jdk.testlibrary.SimpleSSLContext
+ * @compile ../../../com/sun/net/httpserver/LogFilter.java
+ * @compile ../../../com/sun/net/httpserver/EchoHandler.java
+ * @compile ../../../com/sun/net/httpserver/FileServerHandler.java
+ * @run main/othervm/timeout=40 ManyRequestsLegacy
+ * @run main/othervm/timeout=40 -Dtest.insertDelay=true ManyRequestsLegacy
+ * @run main/othervm/timeout=40 -Dtest.chunkSize=64 ManyRequestsLegacy
+ * @run main/othervm/timeout=40 -Dtest.insertDelay=true -Dtest.chunkSize=64 ManyRequestsLegacy
+ * @summary Send a large number of requests asynchronously using the legacy URL.openConnection(), to help sanitize results of the test ManyRequest.java.
+ */
+
+import javax.net.ssl.HttpsURLConnection;
+import javax.net.ssl.HostnameVerifier;
+import com.sun.net.httpserver.HttpsConfigurator;
+import com.sun.net.httpserver.HttpsParameters;
+import com.sun.net.httpserver.HttpsServer;
+import com.sun.net.httpserver.HttpExchange;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.HttpURLConnection;
+import java.net.URI;
+import java.net.URLConnection;
+import java.security.NoSuchAlgorithmException;
+import java.util.Optional;
+import java.util.concurrent.CompletableFuture;
+import java.util.stream.Collectors;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLParameters;
+import javax.net.ssl.SSLSession;
+import jdk.incubator.http.HttpClient;
+import jdk.incubator.http.HttpClient.Version;
+import jdk.incubator.http.HttpHeaders;
+import jdk.incubator.http.HttpRequest;
+import jdk.incubator.http.HttpResponse;
+import java.net.InetSocketAddress;
+import java.util.Arrays;
+import java.util.Formatter;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.Random;
+import java.util.concurrent.ExecutorService;
+import java.util.logging.Logger;
+import java.util.logging.Level;
+import jdk.testlibrary.SimpleSSLContext;
+import static jdk.incubator.http.HttpRequest.BodyPublisher.fromByteArray;
+import static jdk.incubator.http.HttpResponse.BodyHandler.asByteArray;
+
+public class ManyRequestsLegacy {
+
+    volatile static int counter = 0;
+
+    public static void main(String[] args) throws Exception {
+        Logger logger = Logger.getLogger("com.sun.net.httpserver");
+        logger.setLevel(Level.ALL);
+        logger.info("TEST");
+        System.out.println("Sending " + REQUESTS
+                         + " requests; delay=" + INSERT_DELAY
+                         + ", chunks=" + CHUNK_SIZE
+                         + ", XFixed=" + XFIXED);
+        SSLContext ctx = new SimpleSSLContext().get();
+        SSLContext.setDefault(ctx);
+        HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() {
+                public boolean verify(String hostname, SSLSession session) {
+                    return true;
+                }
+            });
+        InetSocketAddress addr = new InetSocketAddress(0);
+        HttpsServer server = HttpsServer.create(addr, 0);
+        server.setHttpsConfigurator(new Configurator(ctx));
+
+        LegacyHttpClient client = new LegacyHttpClient();
+
+        try {
+            test(server, client);
+            System.out.println("OK");
+        } finally {
+            server.stop(0);
+        }
+    }
+
+    //static final int REQUESTS = 1000;
+    static final int REQUESTS = 20;
+    static final boolean INSERT_DELAY = Boolean.getBoolean("test.insertDelay");
+    static final int CHUNK_SIZE = Math.max(0,
+           Integer.parseInt(System.getProperty("test.chunkSize", "0")));
+    static final boolean XFIXED = Boolean.getBoolean("test.XFixed");
+
+    static class LegacyHttpClient {
+        static final class LegacyHttpResponse extends HttpResponse<byte[]> {
+            final HttpRequest request;
+            final byte[] response;
+            final int statusCode;
+            public LegacyHttpResponse(HttpRequest request, int statusCode, byte[] response) {
+                this.request = request;
+                this.statusCode = statusCode;
+                this.response = response;
+            }
+            private <T> T error() {
+                throw new UnsupportedOperationException("Not supported yet.");
+            }
+            @Override
+            public int statusCode() { return statusCode;}
+            @Override
+            public HttpRequest request() {return request;}
+            @Override
+            public Optional<HttpResponse<byte[]>> previousResponse() {return Optional.empty();}
+            @Override
+            public HttpHeaders headers() { return error(); }
+            @Override
+            public byte[] body() {return response;}
+            @Override
+            public SSLParameters sslParameters() {
+                try {
+                    return SSLContext.getDefault().getDefaultSSLParameters();
+                } catch (NoSuchAlgorithmException ex) {
+                    throw new UnsupportedOperationException(ex);
+                }
+            }
+            @Override
+            public URI uri() { return request.uri();}
+            @Override
+            public HttpClient.Version version() { return Version.HTTP_1_1;}
+        }
+
+        private void debugCompleted(String tag, long startNanos, HttpRequest req) {
+            System.err.println(tag + " elapsed "
+                    + (System.nanoTime() - startNanos)/1000_000L
+                    + " millis for " + req.method()
+                    + " to " + req.uri());
+        }
+
+        CompletableFuture<? extends HttpResponse<byte[]>> sendAsync(HttpRequest r, byte[] buf) {
+            long start = System.nanoTime();
+            try {
+                CompletableFuture<LegacyHttpResponse> cf = new CompletableFuture<>();
+                URLConnection urlc = r.uri().toURL().openConnection();
+                HttpURLConnection httpc = (HttpURLConnection)urlc;
+                httpc.setRequestMethod(r.method());
+                for (String s : r.headers().map().keySet()) {
+                    httpc.setRequestProperty(s, r.headers().allValues(s)
+                        .stream().collect(Collectors.joining(",")));
+                }
+                httpc.setDoInput(true);
+                if (buf != null) httpc.setDoOutput(true);
+                Thread t = new Thread(() -> {
+                    try {
+                        if (buf != null) {
+                            try (OutputStream os = httpc.getOutputStream()) {
+                                os.write(buf);
+                                os.flush();
+                            }
+                        }
+                        LegacyHttpResponse response = new LegacyHttpResponse(r,
+                                httpc.getResponseCode(),httpc.getInputStream().readAllBytes());
+                        cf.complete(response);
+                    } catch(Throwable x) {
+                        cf.completeExceptionally(x);
+                    }
+                });
+                t.start();
+                return cf.whenComplete((b,x) -> debugCompleted("ClientImpl (async)", start, r));
+            } catch(Throwable t) {
+                debugCompleted("ClientImpl (async)", start, r);
+                return CompletableFuture.failedFuture(t);
+            }
+        }
+    }
+
+    static class TestEchoHandler extends EchoHandler {
+        final Random rand = new Random();
+        @Override
+        public void handle(HttpExchange e) throws IOException {
+            System.out.println("Server: received " + e.getRequestURI());
+            super.handle(e);
+        }
+        @Override
+        protected void close(HttpExchange t, OutputStream os) throws IOException {
+            if (INSERT_DELAY) {
+                try { Thread.sleep(rand.nextInt(200)); }
+                catch (InterruptedException e) {}
+            }
+            System.out.println("Server: close outbound: " + t.getRequestURI());
+            os.close();
+        }
+        @Override
+        protected void close(HttpExchange t, InputStream is) throws IOException {
+            if (INSERT_DELAY) {
+                try { Thread.sleep(rand.nextInt(200)); }
+                catch (InterruptedException e) {}
+            }
+            System.out.println("Server: close inbound: " + t.getRequestURI());
+            is.close();
+        }
+    }
+
+    static void test(HttpsServer server, LegacyHttpClient client) throws Exception {
+        int port = server.getAddress().getPort();
+        URI baseURI = new URI("https://127.0.0.1:" + port + "/foo/x");
+        server.createContext("/foo", new TestEchoHandler());
+        server.start();
+
+        RequestLimiter limiter = new RequestLimiter(40);
+        Random rand = new Random();
+        CompletableFuture<?>[] results = new CompletableFuture<?>[REQUESTS];
+        HashMap<HttpRequest,byte[]> bodies = new HashMap<>();
+
+        for (int i=0; i<REQUESTS; i++) {
+            byte[] buf = new byte[(i+1)*CHUNK_SIZE+i+1];  // different size bodies
+            rand.nextBytes(buf);
+            URI uri = new URI(baseURI.toString() + String.valueOf(i+1));
+            HttpRequest r = HttpRequest.newBuilder(uri)
+                                       .header("XFixed", "true")
+                                       .POST(fromByteArray(buf))
+                                       .build();
+            bodies.put(r, buf);
+
+            results[i] =
+                limiter.whenOkToSend()
+                       .thenCompose((v) -> {
+                           System.out.println("Client: sendAsync: " + r.uri());
+                           return client.sendAsync(r, buf);
+                       })
+                       .thenCompose((resp) -> {
+                           limiter.requestComplete();
+                           if (resp.statusCode() != 200) {
+                               String s = "Expected 200, got: " + resp.statusCode();
+                               System.out.println(s + " from "
+                                                  + resp.request().uri().getPath());
+                               return completedWithIOException(s);
+                           } else {
+                               counter++;
+                               System.out.println("Result (" + counter + ") from "
+                                                   + resp.request().uri().getPath());
+                           }
+                           return CompletableFuture.completedStage(resp.body())
+                                      .thenApply((b) -> new Pair<>(resp, b));
+                       })
+                      .thenAccept((pair) -> {
+                          HttpRequest request = pair.t.request();
+                          byte[] requestBody = bodies.get(request);
+                          check(Arrays.equals(requestBody, pair.u),
+                                "bodies not equal:[" + bytesToHexString(requestBody)
+                                + "] [" + bytesToHexString(pair.u) + "]");
+
+                      });
+        }
+
+        // wait for them all to complete and throw exception in case of error
+        CompletableFuture.allOf(results).join();
+    }
+
+    static <T> CompletableFuture<T> completedWithIOException(String message) {
+        return CompletableFuture.failedFuture(new IOException(message));
+    }
+
+    static String bytesToHexString(byte[] bytes) {
+        if (bytes == null)
+            return "null";
+
+        StringBuilder sb = new StringBuilder(bytes.length * 2);
+
+        Formatter formatter = new Formatter(sb);
+        for (byte b : bytes) {
+            formatter.format("%02x", b);
+        }
+
+        return sb.toString();
+    }
+
+    static final class Pair<T,U> {
+        Pair(T t, U u) {
+            this.t = t; this.u = u;
+        }
+        T t;
+        U u;
+    }
+
+    /**
+     * A simple limiter for controlling the number of requests to be run in
+     * parallel whenOkToSend() is called which returns a CF<Void> that allows
+     * each individual request to proceed, or block temporarily (blocking occurs
+     * on the waiters list here. As each request actually completes
+     * requestComplete() is called to notify this object, and allow some
+     * requests to continue.
+     */
+    static class RequestLimiter {
+
+        static final CompletableFuture<Void> COMPLETED_FUTURE =
+                CompletableFuture.completedFuture(null);
+
+        final int maxnumber;
+        final LinkedList<CompletableFuture<Void>> waiters;
+        int number;
+        boolean blocked;
+
+        RequestLimiter(int maximum) {
+            waiters = new LinkedList<>();
+            maxnumber = maximum;
+        }
+
+        synchronized void requestComplete() {
+            number--;
+            // don't unblock until number of requests has halved.
+            if ((blocked && number <= maxnumber / 2) ||
+                        (!blocked && waiters.size() > 0)) {
+                int toRelease = Math.min(maxnumber - number, waiters.size());
+                for (int i=0; i<toRelease; i++) {
+                    CompletableFuture<Void> f = waiters.remove();
+                    number ++;
+                    f.complete(null);
+                }
+                blocked = number >= maxnumber;
+            }
+        }
+
+        synchronized CompletableFuture<Void> whenOkToSend() {
+            if (blocked || number + 1 >= maxnumber) {
+                blocked = true;
+                CompletableFuture<Void> r = new CompletableFuture<>();
+                waiters.add(r);
+                return r;
+            } else {
+                number++;
+                return COMPLETED_FUTURE;
+            }
+        }
+    }
+
+    static void check(boolean cond, Object... msg) {
+        if (cond)
+            return;
+        StringBuilder sb = new StringBuilder();
+        for (Object o : msg)
+            sb.append(o);
+        throw new RuntimeException(sb.toString());
+    }
+
+    static class Configurator extends HttpsConfigurator {
+        public Configurator(SSLContext ctx) {
+            super(ctx);
+        }
+
+        public void configure(HttpsParameters params) {
+            params.setSSLParameters(getSSLContext().getSupportedSSLParameters());
+        }
+    }
+}
diff --git a/test/jdk/java/net/httpclient/MessageHeadersTest.java b/test/jdk/java/net/httpclient/MessageHeadersTest.java
index eedff118b4a..a11511cb817 100644
--- a/test/jdk/java/net/httpclient/MessageHeadersTest.java
+++ b/test/jdk/java/net/httpclient/MessageHeadersTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2017, 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
diff --git a/test/jdk/java/net/httpclient/Server.java b/test/jdk/java/net/httpclient/MockServer.java
similarity index 64%
rename from test/jdk/java/net/httpclient/Server.java
rename to test/jdk/java/net/httpclient/MockServer.java
index c8f9864cb59..f7c578f36c2 100644
--- a/test/jdk/java/net/httpclient/Server.java
+++ b/test/jdk/java/net/httpclient/MockServer.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2017, 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
@@ -25,6 +25,8 @@ import java.io.Closeable;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
+import javax.net.ServerSocketFactory;
+import javax.net.ssl.SSLServerSocket;
 import java.net.ServerSocket;
 import java.net.Socket;
 import java.nio.charset.StandardCharsets;
@@ -41,13 +43,22 @@ import java.util.concurrent.atomic.AtomicInteger;
  *
  * use interrupt() to halt
  */
-public class Server extends Thread implements Closeable {
+public class MockServer extends Thread implements Closeable {
 
-    ServerSocket ss;
+    final ServerSocket ss;
     private final List<Connection> sockets;
     private final List<Connection> removals;
     private final List<Connection> additions;
     AtomicInteger counter = new AtomicInteger(0);
+    // if specified (not null), only requests which
+    // contain this value in their status line
+    // will be taken into account and returned by activity().
+    // Other requests will get summarily closed.
+    // When specified, this can prevent answering to rogue
+    // (external) clients that might be lurking
+    // on the test machine instead of answering
+    // to the test client.
+   final String root;
 
     // waits up to 20 seconds for something to happen
     // dont use this unless certain activity coming.
@@ -56,6 +67,19 @@ public class Server extends Thread implements Closeable {
             doRemovalsAndAdditions();
             for (Connection c : sockets) {
                 if (c.poll()) {
+                    if (root != null) {
+                        // if a root was specified in MockServer
+                        // constructor, rejects (by closing) all
+                        // requests whose statusLine does not contain
+                        // root.
+                        if (!c.statusLine.contains(root)) {
+                            System.out.println("Bad statusLine: "
+                                    + c.statusLine
+                                    + " closing connection");
+                            c.close();
+                            continue;
+                        }
+                    }
                     return c;
                 }
             }
@@ -69,17 +93,25 @@ public class Server extends Thread implements Closeable {
     }
 
     private void doRemovalsAndAdditions() {
-        if (removals.isEmpty() && additions.isEmpty())
-            return;
-        Iterator<Connection> i = removals.iterator();
-        while (i.hasNext())
-            sockets.remove(i.next());
-        removals.clear();
+        synchronized (removals) {
+            Iterator<Connection> i = removals.iterator();
+            while (i.hasNext()) {
+                Connection c = i.next();
+                System.out.println("socket removed: " + c);
+                sockets.remove(c);
+            }
+            removals.clear();
+        }
 
-        i = additions.iterator();
-        while (i.hasNext())
-            sockets.add(i.next());
-        additions.clear();
+        synchronized (additions) {
+            Iterator<Connection> i = additions.iterator();
+            while (i.hasNext()) {
+                Connection c = i.next();
+                System.out.println("socket added: " + c);
+                sockets.add(c);
+            }
+            additions.clear();
+        }
     }
 
     // clears all current connections on Server.
@@ -108,12 +140,14 @@ public class Server extends Thread implements Closeable {
         final InputStream is;
         final OutputStream os;
         final ArrayBlockingQueue<String> incoming;
+        volatile String statusLine;
 
         final static String CRLF = "\r\n";
 
         // sentinel indicating connection closed
         final static String CLOSED = "C.L.O.S.E.D";
         volatile boolean closed = false;
+        volatile boolean released = false;
 
         @Override
         public void run() {
@@ -131,6 +165,8 @@ public class Server extends Thread implements Closeable {
                     int i;
                     while ((i=s.indexOf(CRLF)) != -1) {
                         String s1 = s.substring(0, i+2);
+                        System.out.println("Server got: " + s1.substring(0,i));
+                        if (statusLine == null) statusLine = s1.substring(0,i);
                         incoming.put(s1);
                         if (i+2 == s.length()) {
                             s = "";
@@ -224,23 +260,40 @@ public class Server extends Thread implements Closeable {
         }
 
         private void cleanup() {
+            if (released) return;
+            synchronized(this) {
+                if (released) return;
+                released = true;
+            }
             try {
                 socket.close();
             } catch (IOException e) {}
-            removals.add(this);
+            synchronized (removals) {
+                removals.add(this);
+            }
         }
     }
 
-    Server(int port) throws IOException {
-        ss = new ServerSocket(port);
+    MockServer(int port, ServerSocketFactory factory, String root) throws IOException {
+        ss = factory.createServerSocket(port);
+        this.root = root; // if specified, any request which don't have this value
+                          // in their statusLine will be rejected.
         sockets = Collections.synchronizedList(new LinkedList<>());
-        removals = Collections.synchronizedList(new LinkedList<>());
-        additions = Collections.synchronizedList(new LinkedList<>());
+        removals = new LinkedList<>();
+        additions = new LinkedList<>();
         setName("Test-Server");
         setDaemon(true);
     }
 
-    Server() throws IOException {
+    MockServer(int port, ServerSocketFactory factory) throws IOException {
+        this(port, factory, "/foo/");
+    }
+
+    MockServer(int port) throws IOException {
+        this(port, ServerSocketFactory.getDefault());
+    }
+
+    MockServer() throws IOException {
         this(0);
     }
 
@@ -249,7 +302,11 @@ public class Server extends Thread implements Closeable {
     }
 
     public String getURL() {
-        return "http://127.0.0.1:" + port() + "/foo/";
+        if (ss instanceof SSLServerSocket) {
+            return "https://127.0.0.1:" + port() + "/foo/";
+        } else {
+            return "http://127.0.0.1:" + port() + "/foo/";
+        }
     }
 
     private volatile boolean closed;
@@ -269,16 +326,32 @@ public class Server extends Thread implements Closeable {
 
     @Override
     public void run() {
-        while (!closed) {
-            try {
-                Socket s = ss.accept();
-                Connection c = new Connection(s);
-                c.start();
-                additions.add(c);
-            } catch (IOException e) {
-                if (closed)
-                    return;
-                e.printStackTrace();
+        try {
+            while (!closed) {
+                try {
+                    System.out.println("Server waiting for connection");
+                    Socket s = ss.accept();
+                    Connection c = new Connection(s);
+                    c.start();
+                    System.out.println("Server got new connection: " + c);
+                    synchronized (additions) {
+                        additions.add(c);
+                    }
+                } catch (IOException e) {
+                    if (closed)
+                        return;
+                    e.printStackTrace(System.out);
+                }
+            }
+        } catch (Throwable t) {
+            System.out.println("Unexpected exception in accept loop: " + t);
+            t.printStackTrace(System.out);
+        } finally {
+            if (closed) {
+                System.out.println("Server closed: exiting accept loop");
+            } else {
+                System.out.println("Server not closed: exiting accept loop and closing");
+                close();
             }
         }
     }
diff --git a/test/jdk/java/net/httpclient/MultiAuthTest.java b/test/jdk/java/net/httpclient/MultiAuthTest.java
index 1ef86e668a2..6d049041981 100644
--- a/test/jdk/java/net/httpclient/MultiAuthTest.java
+++ b/test/jdk/java/net/httpclient/MultiAuthTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2017, 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
@@ -46,7 +46,7 @@ import jdk.incubator.http.*;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
 import static java.nio.charset.StandardCharsets.US_ASCII;
-import static jdk.incubator.http.HttpRequest.BodyProcessor.fromString;
+import static jdk.incubator.http.HttpRequest.BodyPublisher.fromString;
 import static jdk.incubator.http.HttpResponse.BodyHandler.asString;
 import java.util.UUID;
 import java.util.concurrent.atomic.AtomicInteger;
diff --git a/test/jdk/java/net/httpclient/NoBodyPartOne.java b/test/jdk/java/net/httpclient/NoBodyPartOne.java
new file mode 100644
index 00000000000..d2a979394db
--- /dev/null
+++ b/test/jdk/java/net/httpclient/NoBodyPartOne.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug 8161157
+ * @summary Test response body handlers/subscribers when there is no body
+ * @library /lib/testlibrary http2/server
+ * @build jdk.testlibrary.SimpleSSLContext
+ * @modules java.base/sun.net.www.http
+ *          jdk.incubator.httpclient/jdk.incubator.http.internal.common
+ *          jdk.incubator.httpclient/jdk.incubator.http.internal.frame
+ *          jdk.incubator.httpclient/jdk.incubator.http.internal.hpack
+ * @run testng/othervm -Djdk.internal.httpclient.debug=true -Djdk.httpclient.HttpClient.log=all NoBodyPartOne
+ */
+
+import java.net.URI;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import jdk.incubator.http.HttpClient;
+import jdk.incubator.http.HttpRequest;
+import jdk.incubator.http.HttpResponse;
+import jdk.incubator.http.HttpResponse.BodyHandler;
+import org.testng.annotations.Test;
+import static java.nio.charset.StandardCharsets.UTF_8;
+import static jdk.incubator.http.HttpRequest.BodyPublisher.fromString;
+import static jdk.incubator.http.HttpResponse.BodyHandler.asByteArray;
+import static jdk.incubator.http.HttpResponse.BodyHandler.asFile;
+import static jdk.incubator.http.HttpResponse.BodyHandler.asString;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertTrue;
+
+public class NoBodyPartOne extends AbstractNoBody {
+
+    @Test(dataProvider = "variants")
+    public void testAsString(String uri, boolean sameClient) throws Exception {
+        printStamp(START, "testAsString(\"%s\", %s)", uri, sameClient);
+        HttpClient client = null;
+        for (int i=0; i< ITERATION_COUNT; i++) {
+            if (!sameClient || client == null)
+                client = newHttpClient();
+
+            HttpRequest req = HttpRequest.newBuilder(URI.create(uri))
+                                         .PUT(fromString(SIMPLE_STRING))
+                                         .build();
+            BodyHandler<String> handler = i % 2 == 0 ? asString() : asString(UTF_8);
+            HttpResponse<String> response = client.send(req, handler);
+            String body = response.body();
+            assertEquals(body, "");
+        }
+        // We have created many clients here. Try to speed up their release.
+        if (!sameClient) System.gc();
+    }
+
+    @Test(dataProvider = "variants")
+    public void testAsFile(String uri, boolean sameClient) throws Exception {
+        printStamp(START, "testAsFile(\"%s\", %s)", uri, sameClient);
+        HttpClient client = null;
+        for (int i=0; i< ITERATION_COUNT; i++) {
+            if (!sameClient || client == null)
+                client = newHttpClient();
+
+            HttpRequest req = HttpRequest.newBuilder(URI.create(uri))
+                                         .PUT(fromString(SIMPLE_STRING))
+                                         .build();
+            Path p = Paths.get("NoBody_testAsFile.txt");
+            HttpResponse<Path> response = client.send(req, asFile(p));
+            Path bodyPath = response.body();
+            assertTrue(Files.exists(bodyPath));
+            assertEquals(Files.size(bodyPath), 0);
+        }
+        // We have created many clients here. Try to speed up their release.
+        if (!sameClient) System.gc();
+    }
+
+    @Test(dataProvider = "variants")
+    public void testAsByteArray(String uri, boolean sameClient) throws Exception {
+        printStamp(START, "testAsByteArray(\"%s\", %s)", uri, sameClient);
+        HttpClient client = null;
+        for (int i=0; i< ITERATION_COUNT; i++) {
+            if (!sameClient || client == null)
+                client = newHttpClient();
+
+            HttpRequest req = HttpRequest.newBuilder(URI.create(uri))
+                                         .PUT(fromString(SIMPLE_STRING))
+                                         .build();
+            HttpResponse<byte[]> response = client.send(req, asByteArray());
+            byte[] body = response.body();
+            assertEquals(body.length, 0);
+        }
+        // We have created many clients here. Try to speed up their release.
+        if (!sameClient) System.gc();
+    }
+}
diff --git a/test/jdk/java/net/httpclient/NoBodyPartTwo.java b/test/jdk/java/net/httpclient/NoBodyPartTwo.java
new file mode 100644
index 00000000000..a9fbb95b937
--- /dev/null
+++ b/test/jdk/java/net/httpclient/NoBodyPartTwo.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug 8161157
+ * @summary Test response body handlers/subscribers when there is no body
+ * @library /lib/testlibrary http2/server
+ * @build jdk.testlibrary.SimpleSSLContext
+ * @modules java.base/sun.net.www.http
+ *          jdk.incubator.httpclient/jdk.incubator.http.internal.common
+ *          jdk.incubator.httpclient/jdk.incubator.http.internal.frame
+ *          jdk.incubator.httpclient/jdk.incubator.http.internal.hpack
+ * @run testng/othervm -Djdk.internal.httpclient.debug=true -Djdk.httpclient.HttpClient.log=all NoBodyPartTwo
+ */
+
+import java.io.InputStream;
+import java.net.URI;
+import java.util.Optional;
+import java.util.function.Consumer;
+import jdk.incubator.http.HttpClient;
+import jdk.incubator.http.HttpRequest;
+import jdk.incubator.http.HttpResponse;
+import org.testng.annotations.Test;
+import static jdk.incubator.http.HttpRequest.BodyPublisher.fromString;
+import static jdk.incubator.http.HttpResponse.BodyHandler.asByteArray;
+import static jdk.incubator.http.HttpResponse.BodyHandler.asByteArrayConsumer;
+import static jdk.incubator.http.HttpResponse.BodyHandler.asInputStream;
+import static jdk.incubator.http.HttpResponse.BodyHandler.buffering;
+import static jdk.incubator.http.HttpResponse.BodyHandler.discard;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertTrue;
+import static org.testng.Assert.fail;
+
+public class NoBodyPartTwo extends AbstractNoBody {
+
+    volatile boolean consumerHasBeenCalled;
+    @Test(dataProvider = "variants")
+    public void testAsByteArrayConsumer(String uri, boolean sameClient) throws Exception {
+        printStamp(START, "testAsByteArrayConsumer(\"%s\", %s)", uri, sameClient);
+        HttpClient client = null;
+        for (int i=0; i< ITERATION_COUNT; i++) {
+            if (!sameClient || client == null)
+                client = newHttpClient();
+
+            HttpRequest req = HttpRequest.newBuilder(URI.create(uri))
+                    .PUT(fromString(SIMPLE_STRING))
+                    .build();
+            Consumer<Optional<byte[]>>  consumer = oba -> {
+                consumerHasBeenCalled = true;
+                oba.ifPresent(ba -> fail("Unexpected non-empty optional:" + ba));
+            };
+            consumerHasBeenCalled = false;
+            client.send(req, asByteArrayConsumer(consumer));
+            assertTrue(consumerHasBeenCalled);
+        }
+        // We have created many clients here. Try to speed up their release.
+        if (!sameClient) System.gc();
+    }
+
+    @Test(dataProvider = "variants")
+    public void testAsInputStream(String uri, boolean sameClient) throws Exception {
+        printStamp(START, "testAsInputStream(\"%s\", %s)", uri, sameClient);
+        HttpClient client = null;
+        for (int i=0; i< ITERATION_COUNT; i++) {
+            if (!sameClient || client == null)
+                client = newHttpClient();
+
+            HttpRequest req = HttpRequest.newBuilder(URI.create(uri))
+                    .PUT(fromString(SIMPLE_STRING))
+                    .build();
+            HttpResponse<InputStream> response = client.send(req, asInputStream());
+            byte[] body = response.body().readAllBytes();
+            assertEquals(body.length, 0);
+        }
+        // We have created many clients here. Try to speed up their release.
+        if (!sameClient) System.gc();
+    }
+
+    @Test(dataProvider = "variants")
+    public void testBuffering(String uri, boolean sameClient) throws Exception {
+        printStamp(START, "testBuffering(\"%s\", %s)", uri, sameClient);
+        HttpClient client = null;
+        for (int i=0; i< ITERATION_COUNT; i++) {
+            if (!sameClient || client == null)
+                client = newHttpClient();
+
+            HttpRequest req = HttpRequest.newBuilder(URI.create(uri))
+                    .PUT(fromString(SIMPLE_STRING))
+                    .build();
+            HttpResponse<byte[]> response = client.send(req, buffering(asByteArray(), 1024));
+            byte[] body = response.body();
+            assertEquals(body.length, 0);
+        }
+        // We have created many clients here. Try to speed up their release.
+        if (!sameClient) System.gc();
+    }
+
+    @Test(dataProvider = "variants")
+    public void testDiscard(String uri, boolean sameClient) throws Exception {
+        printStamp(START, "testDiscard(\"%s\", %s)", uri, sameClient);
+        HttpClient client = null;
+        for (int i=0; i< ITERATION_COUNT; i++) {
+            if (!sameClient || client == null)
+                client = newHttpClient();
+
+            HttpRequest req = HttpRequest.newBuilder(URI.create(uri))
+                    .PUT(fromString(SIMPLE_STRING))
+                    .build();
+            Object obj = new Object();
+            HttpResponse<Object> response = client.send(req, discard(obj));
+            assertEquals(response.body(), obj);
+        }
+        // We have created many clients here. Try to speed up their release.
+        if (!sameClient) System.gc();
+    }
+}
diff --git a/test/jdk/java/net/httpclient/ProxyAuthTest.java b/test/jdk/java/net/httpclient/ProxyAuthTest.java
index 43ba69032e0..d6e310ddd22 100644
--- a/test/jdk/java/net/httpclient/ProxyAuthTest.java
+++ b/test/jdk/java/net/httpclient/ProxyAuthTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2017, 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
@@ -29,7 +29,6 @@
  * @modules java.base/sun.net.www
  *          jdk.incubator.httpclient
  * @summary Verify that Proxy-Authenticate header is correctly handled
- *
  * @run main/othervm ProxyAuthTest
  */
 
@@ -42,14 +41,17 @@ import java.io.PrintWriter;
 import java.net.Authenticator;
 import java.net.InetSocketAddress;
 import java.net.PasswordAuthentication;
+import java.net.Proxy;
 import java.net.ProxySelector;
 import java.net.ServerSocket;
 import java.net.Socket;
+import java.net.SocketAddress;
 import java.net.URI;
 import jdk.incubator.http.HttpClient;
 import jdk.incubator.http.HttpRequest;
 import jdk.incubator.http.HttpResponse;
 import java.util.Base64;
+import java.util.List;
 import sun.net.www.MessageHeader;
 import static jdk.incubator.http.HttpResponse.BodyHandler.discard;
 
@@ -68,8 +70,9 @@ public class ProxyAuthTest {
             InetSocketAddress paddr = new InetSocketAddress("localhost", port);
 
             URI uri = new URI("http://www.google.ie/");
+            CountingProxySelector ps = CountingProxySelector.of(paddr);
             HttpClient client = HttpClient.newBuilder()
-                                          .proxy(ProxySelector.of(paddr))
+                                          .proxy(ps)
                                           .authenticator(auth)
                                           .build();
             HttpRequest req = HttpRequest.newBuilder(uri).GET().build();
@@ -87,6 +90,9 @@ public class ProxyAuthTest {
             if (!proxy.matched) {
                 throw new RuntimeException("Proxy authentication failed");
             }
+            if (ps.count() > 1) {
+                throw new RuntimeException("CountingProxySelector. Expected 1, got " + ps.count());
+            }
         }
     }
 
@@ -102,6 +108,37 @@ public class ProxyAuthTest {
         }
     }
 
+    /**
+     * A Proxy Selector that wraps a ProxySelector.of(), and counts the number
+     * of times its select method has been invoked. This can be used to ensure
+     * that the Proxy Selector is invoked only once per HttpClient.sendXXX
+     * invocation.
+     */
+    static class CountingProxySelector extends ProxySelector {
+        private final ProxySelector proxySelector;
+        private volatile int count; // 0
+        private CountingProxySelector(InetSocketAddress proxyAddress) {
+            proxySelector = ProxySelector.of(proxyAddress);
+        }
+
+        public static CountingProxySelector of(InetSocketAddress proxyAddress) {
+            return new CountingProxySelector(proxyAddress);
+        }
+
+        int count() { return count; }
+
+        @Override
+        public List<Proxy> select(URI uri) {
+            count++;
+            return proxySelector.select(uri);
+        }
+
+        @Override
+        public void connectFailed(URI uri, SocketAddress sa, IOException ioe) {
+            proxySelector.connectFailed(uri, sa, ioe);
+        }
+    }
+
     static class MyProxy implements Runnable {
         final ServerSocket ss;
         private volatile boolean matched;
diff --git a/test/jdk/java/net/httpclient/ProxyServer.java b/test/jdk/java/net/httpclient/ProxyServer.java
index 4da9b5e1c55..c8d3dd64ca3 100644
--- a/test/jdk/java/net/httpclient/ProxyServer.java
+++ b/test/jdk/java/net/httpclient/ProxyServer.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2017, 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
@@ -207,7 +207,7 @@ public class ProxyServer extends Thread implements Closeable {
                 } else {
                     doProxy(params[1], buf, p, cmd);
                 }
-            } catch (IOException e) {
+            } catch (Throwable e) {
                 if (debug) {
                     System.out.println (e);
                 }
diff --git a/test/jdk/java/net/httpclient/ProxyTest.java b/test/jdk/java/net/httpclient/ProxyTest.java
index f6590d89f17..0a992b98a7d 100644
--- a/test/jdk/java/net/httpclient/ProxyTest.java
+++ b/test/jdk/java/net/httpclient/ProxyTest.java
@@ -41,10 +41,12 @@ import java.net.Proxy;
 import java.net.ProxySelector;
 import java.net.ServerSocket;
 import java.net.Socket;
+import java.net.SocketAddress;
 import java.net.URI;
 import java.net.URISyntaxException;
 import java.nio.charset.StandardCharsets;
 import java.security.NoSuchAlgorithmException;
+import java.util.List;
 import javax.net.ssl.HostnameVerifier;
 import javax.net.ssl.HttpsURLConnection;
 import javax.net.ssl.SSLContext;
@@ -121,6 +123,37 @@ public class ProxyTest {
         }
     }
 
+    /**
+     * A Proxy Selector that wraps a ProxySelector.of(), and counts the number
+     * of times its select method has been invoked. This can be used to ensure
+     * that the Proxy Selector is invoked only once per HttpClient.sendXXX
+     * invocation.
+     */
+    static class CountingProxySelector extends ProxySelector {
+        private final ProxySelector proxySelector;
+        private volatile int count; // 0
+        private CountingProxySelector(InetSocketAddress proxyAddress) {
+            proxySelector = ProxySelector.of(proxyAddress);
+        }
+
+        public static CountingProxySelector of(InetSocketAddress proxyAddress) {
+            return new CountingProxySelector(proxyAddress);
+        }
+
+        int count() { return count; }
+
+        @Override
+        public List<Proxy> select(URI uri) {
+            count++;
+            return proxySelector.select(uri);
+        }
+
+        @Override
+        public void connectFailed(URI uri, SocketAddress sa, IOException ioe) {
+            proxySelector.connectFailed(uri, sa, ioe);
+        }
+    }
+
     public static void test(HttpServer server, HttpClient.Version version)
             throws IOException,
             URISyntaxException,
@@ -158,7 +191,7 @@ public class ProxyTest {
             System.out.println("\nReal test begins here.");
             System.out.println("Setting up request with HttpClient for version: "
                     + version.name());
-            ProxySelector ps = ProxySelector.of(
+            CountingProxySelector ps = CountingProxySelector.of(
                     InetSocketAddress.createUnresolved("localhost", proxy.getAddress().getPort()));
             HttpClient client = HttpClient.newBuilder()
                 .version(version)
@@ -178,6 +211,9 @@ public class ProxyTest {
             if (!RESPONSE.equals(resp)) {
                 throw new AssertionError("Unexpected response");
             }
+            if (ps.count() > 1) {
+                throw new AssertionError("CountingProxySelector. Expected 1, got " + ps.count());
+            }
         } finally {
             System.out.println("Stopping proxy");
             proxy.stop();
diff --git a/test/jdk/java/net/httpclient/RequestBodyTest.java b/test/jdk/java/net/httpclient/RequestBodyTest.java
index 637cbc31aff..b3bf1495248 100644
--- a/test/jdk/java/net/httpclient/RequestBodyTest.java
+++ b/test/jdk/java/net/httpclient/RequestBodyTest.java
@@ -22,7 +22,8 @@
  */
 
 /*
- * @test @bug 8087112
+ * @test
+ * @bug 8087112
  * @modules jdk.incubator.httpclient
  *          java.logging
  *          jdk.httpserver
@@ -42,6 +43,7 @@ import java.net.URI;
 import jdk.incubator.http.HttpClient;
 import jdk.incubator.http.HttpRequest;
 import jdk.incubator.http.HttpResponse;
+import jdk.incubator.http.HttpResponse.BodyHandler;
 import java.nio.charset.Charset;
 import java.nio.charset.StandardCharsets;
 import java.nio.file.Files;
@@ -51,14 +53,15 @@ import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
 import java.util.Optional;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.function.Consumer;
 import java.util.function.Supplier;
 import javax.net.ssl.SSLContext;
 import jdk.test.lib.util.FileUtils;
+import static java.lang.System.out;
 import static java.nio.charset.StandardCharsets.*;
 import static java.nio.file.StandardOpenOption.*;
-import static jdk.incubator.http.HttpRequest.BodyProcessor.*;
+import static jdk.incubator.http.HttpRequest.BodyPublisher.*;
 import static jdk.incubator.http.HttpResponse.BodyHandler.*;
 
 import org.testng.annotations.AfterTest;
@@ -69,12 +72,12 @@ import static org.testng.Assert.*;
 
 public class RequestBodyTest {
 
-    static final String fileroot = System.getProperty("test.src") + "/docs";
+    static final String fileroot = System.getProperty("test.src", ".") + "/docs";
     static final String midSizedFilename = "/files/notsobigfile.txt";
     static final String smallFilename = "/files/smallfile.txt";
+    final ConcurrentHashMap<String,Throwable> failures = new ConcurrentHashMap<>();
 
     HttpClient client;
-    ExecutorService exec = Executors.newCachedThreadPool();
     String httpURI;
     String httpsURI;
 
@@ -109,14 +112,25 @@ public class RequestBodyTest {
                            .sslContext(ctx)
                            .version(HttpClient.Version.HTTP_1_1)
                            .followRedirects(HttpClient.Redirect.ALWAYS)
-                           .executor(exec)
                            .build();
     }
 
     @AfterTest
     public void teardown() throws Exception {
-        exec.shutdownNow();
-        LightWeightHttpServer.stop();
+        try {
+            LightWeightHttpServer.stop();
+        } finally {
+            System.out.println("RequestBodyTest: " + failures.size() + " failures");
+            int i = 0;
+            for (String key: failures.keySet()) {
+                System.out.println("test" + key + " failed: " + failures.get(key));
+                failures.get(key).printStackTrace(System.out);
+                if (i++ > 3) {
+                   System.out.println("..... other failures not printed ....");
+                   break;
+                }
+            }
+        }
     }
 
     @DataProvider
@@ -128,8 +142,9 @@ public class RequestBodyTest {
                 for (String file : new String[] { smallFilename, midSizedFilename })
                     for (RequestBody requestBodyType : RequestBody.values())
                         for (ResponseBody responseBodyType : ResponseBody.values())
-                            values.add(new Object[]
-                                {uri, requestBodyType, responseBodyType, file, async});
+                            for (boolean bufferResponseBody : new boolean[] { false, true })
+                                values.add(new Object[]
+                                    {uri, requestBodyType, responseBodyType, file, async, bufferResponseBody});
 
         return values.stream().toArray(Object[][]::new);
     }
@@ -139,19 +154,29 @@ public class RequestBodyTest {
                   RequestBody requestBodyType,
                   ResponseBody responseBodyType,
                   String file,
-                  boolean async)
+                  boolean async,
+                  boolean bufferResponseBody)
         throws Exception
     {
-        Path filePath = Paths.get(fileroot + file);
-        URI uri = new URI(target);
+        try {
+            Path filePath = Paths.get(fileroot + file);
+            URI uri = new URI(target);
 
-        HttpRequest request = createRequest(uri, requestBodyType, filePath);
+            HttpRequest request = createRequest(uri, requestBodyType, filePath);
 
-        checkResponse(client, request, requestBodyType, responseBodyType, filePath, async);
+            checkResponse(client, request, requestBodyType, responseBodyType, filePath, async, bufferResponseBody);
+        } catch (Exception | Error x) {
+            Object[] params = new Object[] {
+                target, requestBodyType, responseBodyType,
+                file, "async=" + async, "buffer=" + bufferResponseBody
+            };
+            failures.put(java.util.Arrays.toString(params), x);
+            throw x;
+        }
     }
 
     static final int DEFAULT_OFFSET = 10;
-    static final int DEFAULT_LENGTH = 1000;
+    static final int DEFAULT_LENGTH_FACTOR = 4/5;
 
     HttpRequest createRequest(URI uri,
                               RequestBody requestBodyType,
@@ -169,7 +194,9 @@ public class RequestBodyTest {
                 rb.POST(fromByteArray(fileAsBytes));
                 break;
             case BYTE_ARRAY_OFFSET:
-                rb.POST(fromByteArray(fileAsBytes, DEFAULT_OFFSET, DEFAULT_LENGTH));
+                rb.POST(fromByteArray(fileAsBytes,
+                                      DEFAULT_OFFSET,
+                                      fileAsBytes.length * DEFAULT_LENGTH_FACTOR));
                 break;
             case BYTE_ARRAYS:
                 Iterable<byte[]> iterable = Arrays.asList(fileAsBytes);
@@ -198,16 +225,16 @@ public class RequestBodyTest {
                        RequestBody requestBodyType,
                        ResponseBody responseBodyType,
                        Path file,
-                       boolean async)
+                       boolean async,
+                       boolean bufferResponseBody)
         throws InterruptedException, IOException
     {
         String filename = file.toFile().getAbsolutePath();
         byte[] fileAsBytes = getFileBytes(filename);
         if (requestBodyType == RequestBody.BYTE_ARRAY_OFFSET) {
             // Truncate the expected response body, if only a portion was sent
-            fileAsBytes = Arrays.copyOfRange(fileAsBytes,
-                                             DEFAULT_OFFSET,
-                                             DEFAULT_OFFSET + DEFAULT_LENGTH);
+            int length = DEFAULT_OFFSET + (fileAsBytes.length * DEFAULT_LENGTH_FACTOR);
+            fileAsBytes = Arrays.copyOfRange(fileAsBytes, DEFAULT_OFFSET, length);
         }
         String fileAsString = new String(fileAsBytes, UTF_8);
         Path tempFile = Paths.get("RequestBodyTest.tmp");
@@ -215,43 +242,57 @@ public class RequestBodyTest {
 
         switch (responseBodyType) {
             case BYTE_ARRAY:
-                HttpResponse<byte[]> bar = getResponse(client, request, asByteArray(), async);
+                BodyHandler<byte[]> bh = asByteArray();
+                if (bufferResponseBody) bh = buffering(bh, 50);
+                HttpResponse<byte[]> bar = getResponse(client, request, bh, async);
                 assertEquals(bar.statusCode(), 200);
                 assertEquals(bar.body(), fileAsBytes);
                 break;
             case BYTE_ARRAY_CONSUMER:
                 ByteArrayOutputStream baos = new ByteArrayOutputStream();
-                HttpResponse<Void> v = getResponse(client, request,
-                        asByteArrayConsumer(o -> consumerBytes(o, baos) ), async);
+                Consumer<Optional<byte[]>> consumer = o -> consumerBytes(o, baos);
+                BodyHandler<Void> bh1 = asByteArrayConsumer(consumer);
+                if (bufferResponseBody) bh1 = buffering(bh1, 49);
+                HttpResponse<Void> v = getResponse(client, request, bh1, async);
                 byte[] ba = baos.toByteArray();
                 assertEquals(v.statusCode(), 200);
                 assertEquals(ba, fileAsBytes);
                 break;
             case DISCARD:
                 Object o = new Object();
-                HttpResponse<Object> or = getResponse(client, request, discard(o), async);
+                BodyHandler<Object> bh2 = discard(o);
+                if (bufferResponseBody) bh2 = buffering(bh2, 51);
+                HttpResponse<Object> or = getResponse(client, request, bh2, async);
                 assertEquals(or.statusCode(), 200);
                 assertSame(or.body(), o);
                 break;
             case FILE:
-                HttpResponse<Path> fr = getResponse(client, request, asFile(tempFile), async);
+                BodyHandler<Path> bh3 = asFile(tempFile);
+                if (bufferResponseBody) bh3 = buffering(bh3, 48);
+                HttpResponse<Path> fr = getResponse(client, request, bh3, async);
                 assertEquals(fr.statusCode(), 200);
                 assertEquals(Files.size(tempFile), fileAsString.length());
                 assertEquals(Files.readAllBytes(tempFile), fileAsBytes);
                 break;
             case FILE_WITH_OPTION:
-                fr = getResponse(client, request, asFile(tempFile, CREATE_NEW, WRITE), async);
+                BodyHandler<Path> bh4 = asFile(tempFile, CREATE_NEW, WRITE);
+                if (bufferResponseBody) bh4 = buffering(bh4, 52);
+                fr = getResponse(client, request, bh4, async);
                 assertEquals(fr.statusCode(), 200);
                 assertEquals(Files.size(tempFile), fileAsString.length());
                 assertEquals(Files.readAllBytes(tempFile), fileAsBytes);
                 break;
             case STRING:
-                HttpResponse<String> sr = getResponse(client, request, asString(), async);
+                BodyHandler<String> bh5 = asString();
+                if(bufferResponseBody) bh5 = buffering(bh5, 47);
+                HttpResponse<String> sr = getResponse(client, request, bh5, async);
                 assertEquals(sr.statusCode(), 200);
                 assertEquals(sr.body(), fileAsString);
                 break;
             case STRING_WITH_CHARSET:
-                HttpResponse<String> r = getResponse(client, request, asString(StandardCharsets.UTF_8), async);
+                BodyHandler<String> bh6 = asString(StandardCharsets.UTF_8);
+                if (bufferResponseBody) bh6 = buffering(bh6, 53);
+                HttpResponse<String> r = getResponse(client, request, bh6, async);
                 assertEquals(r.statusCode(), 200);
                 assertEquals(r.body(), fileAsString);
                 break;
@@ -303,4 +344,28 @@ public class RequestBodyTest {
             throw new UncheckedIOException(x);
         }
     }
+
+    // ---
+
+    /* Main entry point for standalone testing of the main functional test. */
+    public static void main(String... args) throws Exception {
+        RequestBodyTest t = new RequestBodyTest();
+        t.setup();
+        int count = 0;
+        try {
+            for (Object[] objs : t.exchanges()) {
+                count++;
+                out.printf("********* iteration: %d %s %s %s %s %s %s *********%n",
+                           count, objs[0], objs[1], objs[2], objs[3], objs[4], objs[5]);
+                t.exchange((String) objs[0],
+                           (RequestBody) objs[1],
+                           (ResponseBody) objs[2],
+                           (String) objs[3],
+                           (boolean) objs[4],
+                           (boolean) objs[5]);
+            }
+        } finally {
+            t.teardown();
+        }
+    }
 }
diff --git a/test/jdk/java/net/httpclient/RequestBuilderTest.java b/test/jdk/java/net/httpclient/RequestBuilderTest.java
new file mode 100644
index 00000000000..49876499222
--- /dev/null
+++ b/test/jdk/java/net/httpclient/RequestBuilderTest.java
@@ -0,0 +1,378 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @summary HttpRequest[.Builder] API and behaviour checks
+ * @run testng RequestBuilderTest
+ */
+
+import java.net.URI;
+import java.util.List;
+import jdk.incubator.http.HttpRequest;
+import static java.time.Duration.ofNanos;
+import static java.time.Duration.ofMinutes;
+import static java.time.Duration.ofSeconds;
+import static java.time.Duration.ZERO;
+import static jdk.incubator.http.HttpClient.Version.HTTP_1_1;
+import static jdk.incubator.http.HttpClient.Version.HTTP_2;
+import static jdk.incubator.http.HttpRequest.BodyPublisher.fromString;
+import static jdk.incubator.http.HttpRequest.BodyPublisher.noBody;
+import static jdk.incubator.http.HttpRequest.newBuilder;
+import static org.testng.Assert.*;
+import org.testng.annotations.Test;
+
+public class RequestBuilderTest {
+
+    static final URI uri = URI.create("http://foo.com/");
+    static final Class<NullPointerException> NPE = NullPointerException.class;
+    static final Class<IllegalArgumentException> IAE = IllegalArgumentException.class;
+    static final Class<IllegalStateException> ISE = IllegalStateException.class;
+    static final Class<NumberFormatException> NFE = NumberFormatException.class;
+    static final Class<UnsupportedOperationException> UOE = UnsupportedOperationException.class;
+
+    @Test
+    public void testDefaults() {
+        List<HttpRequest.Builder> builders = List.of(newBuilder().uri(uri),
+                                                     newBuilder(uri));
+        for (HttpRequest.Builder builder : builders) {
+            assertFalse(builder.build().expectContinue());
+            assertEquals(builder.build().method(), "GET");
+            assertFalse(builder.build().bodyPublisher().isPresent());
+            assertFalse(builder.build().version().isPresent());
+            assertFalse(builder.build().timeout().isPresent());
+            assertTrue(builder.build().headers() != null);
+            assertEquals(builder.build().headers().map().size(), 0);
+        }
+    }
+
+    @Test
+    public void testNull() {
+        HttpRequest.Builder builder = newBuilder();
+
+        assertThrows(NPE, () -> newBuilder(null).build());
+        assertThrows(NPE, () -> newBuilder(uri).uri(null).build());
+        assertThrows(NPE, () -> builder.uri(null));
+        assertThrows(NPE, () -> builder.version(null));
+        assertThrows(NPE, () -> builder.header(null, null));
+        assertThrows(NPE, () -> builder.header("name", null));
+        assertThrows(NPE, () -> builder.header(null, "value"));
+        assertThrows(NPE, () -> builder.headers(null));
+        assertThrows(NPE, () -> builder.headers(new String[] { null, null }));
+        assertThrows(NPE, () -> builder.headers(new String[] { "name", null }));
+        assertThrows(NPE, () -> builder.headers(new String[] { null, "value" }));
+        assertThrows(NPE, () -> builder.method(null, null));
+        assertThrows(NPE, () -> builder.method("GET", null));
+        assertThrows(NPE, () -> builder.method("POST", null));
+        assertThrows(NPE, () -> builder.method("PUT", null));
+        assertThrows(NPE, () -> builder.method("DELETE", null));
+        assertThrows(NPE, () -> builder.setHeader(null, null));
+        assertThrows(NPE, () -> builder.setHeader("name", null));
+        assertThrows(NPE, () -> builder.setHeader(null, "value"));
+        assertThrows(NPE, () -> builder.timeout(null));
+        assertThrows(NPE, () -> builder.DELETE(null));
+        assertThrows(NPE, () -> builder.POST(null));
+        assertThrows(NPE, () -> builder.PUT(null));
+    }
+
+    @Test
+    public void testURI() {
+        assertThrows(ISE, () -> newBuilder().build());
+        List<URI> uris = List.of(
+                URI.create("ws://foo.com"),
+                URI.create("wss://foo.com"),
+                URI.create("ftp://foo.com"),
+                URI.create("gopher://foo.com"),
+                URI.create("mailto:a@b.com"),
+                URI.create("scheme:example.com"),
+                URI.create("scheme:example.com"),
+                URI.create("scheme:example.com/path"),
+                URI.create("path"),
+                URI.create("/path")
+        );
+        for (URI u : uris) {
+            assertThrows(IAE, () -> newBuilder(u));
+            assertThrows(IAE, () -> newBuilder().uri(u));
+        }
+
+        assertEquals(newBuilder(uri).build().uri(), uri);
+        assertEquals(newBuilder().uri(uri).build().uri(), uri);
+        URI https = URI.create("https://foo.com");
+        assertEquals(newBuilder(https).build().uri(), https);
+        assertEquals(newBuilder().uri(https).build().uri(), https);
+    }
+
+    @Test
+    public void testMethod() {
+        HttpRequest request = newBuilder(uri).build();
+        assertEquals(request.method(), "GET");
+        assertTrue(!request.bodyPublisher().isPresent());
+
+        request = newBuilder(uri).GET().build();
+        assertEquals(request.method(), "GET");
+        assertTrue(!request.bodyPublisher().isPresent());
+
+        request = newBuilder(uri).POST(fromString("")).GET().build();
+        assertEquals(request.method(), "GET");
+        assertTrue(!request.bodyPublisher().isPresent());
+
+        request = newBuilder(uri).PUT(fromString("")).GET().build();
+        assertEquals(request.method(), "GET");
+        assertTrue(!request.bodyPublisher().isPresent());
+
+        request = newBuilder(uri).DELETE(fromString("")).GET().build();
+        assertEquals(request.method(), "GET");
+        assertTrue(!request.bodyPublisher().isPresent());
+
+        request = newBuilder(uri).POST(fromString("")).build();
+        assertEquals(request.method(), "POST");
+        assertTrue(request.bodyPublisher().isPresent());
+
+        request = newBuilder(uri).PUT(fromString("")).build();
+        assertEquals(request.method(), "PUT");
+        assertTrue(request.bodyPublisher().isPresent());
+
+        request = newBuilder(uri).DELETE(fromString("")).build();
+        assertEquals(request.method(), "DELETE");
+        assertTrue(request.bodyPublisher().isPresent());
+
+        request = newBuilder(uri).GET().POST(fromString("")).build();
+        assertEquals(request.method(), "POST");
+        assertTrue(request.bodyPublisher().isPresent());
+
+        request = newBuilder(uri).GET().PUT(fromString("")).build();
+        assertEquals(request.method(), "PUT");
+        assertTrue(request.bodyPublisher().isPresent());
+
+        request = newBuilder(uri).GET().DELETE(fromString("")).build();
+        assertEquals(request.method(), "DELETE");
+        assertTrue(request.bodyPublisher().isPresent());
+
+        // CONNECT is disallowed in the implementation, since it is used for
+        // tunneling, and is handled separately for security checks.
+        assertThrows(IAE, () -> newBuilder(uri).method("CONNECT", noBody()).build());
+
+        request = newBuilder(uri).method("GET", noBody()).build();
+        assertEquals(request.method(), "GET");
+        assertTrue(request.bodyPublisher().isPresent());
+
+        request = newBuilder(uri).method("POST", fromString("")).build();
+        assertEquals(request.method(), "POST");
+        assertTrue(request.bodyPublisher().isPresent());
+    }
+
+    @Test
+    public void testHeaders() {
+        HttpRequest.Builder builder = newBuilder(uri);
+
+        String[] empty = new String[0];
+        assertThrows(IAE, () -> builder.headers(empty).build());
+        assertThrows(IAE, () -> builder.headers("1").build());
+        assertThrows(IAE, () -> builder.headers("1", "2", "3").build());
+        assertThrows(IAE, () -> builder.headers("1", "2", "3", "4", "5").build());
+        assertEquals(builder.build().headers().map().size(),0);
+
+        List<HttpRequest> requests = List.of(
+                // same header built from different combinations of the API
+                newBuilder(uri).header("A", "B").build(),
+                newBuilder(uri).headers("A", "B").build(),
+                newBuilder(uri).setHeader("A", "B").build(),
+                newBuilder(uri).header("A", "F").setHeader("A", "B").build(),
+                newBuilder(uri).headers("A", "F").setHeader("A", "B").build()
+        );
+
+        for (HttpRequest r : requests) {
+            assertEquals(r.headers().map().size(), 1);
+            assertTrue(r.headers().firstValue("A").isPresent());
+            assertEquals(r.headers().firstValue("A").get(), "B");
+            assertEquals(r.headers().allValues("A"), List.of("B"));
+            assertEquals(r.headers().allValues("C").size(), 0);
+            assertEquals(r.headers().map().get("A"), List.of("B"));
+            assertThrows(NFE, () -> r.headers().firstValueAsLong("A"));
+            assertFalse(r.headers().firstValue("C").isPresent());
+            // a non-exhaustive list of mutators
+            assertThrows(UOE, () -> r.headers().map().put("Z", List.of("Z")));
+            assertThrows(UOE, () -> r.headers().map().remove("A"));
+            assertThrows(UOE, () -> r.headers().map().remove("A", "B"));
+            assertThrows(UOE, () -> r.headers().map().clear());
+            assertThrows(UOE, () -> r.headers().allValues("A").remove("B"));
+            assertThrows(UOE, () -> r.headers().allValues("A").remove(1));
+            assertThrows(UOE, () -> r.headers().allValues("A").clear());
+            assertThrows(UOE, () -> r.headers().allValues("A").add("Z"));
+            assertThrows(UOE, () -> r.headers().allValues("A").addAll(List.of("Z")));
+            assertThrows(UOE, () -> r.headers().allValues("A").add(1, "Z"));
+        }
+
+        requests = List.of(
+                // same headers built from different combinations of the API
+                newBuilder(uri).header("A", "B")
+                               .header("C", "D").build(),
+                newBuilder(uri).header("A", "B")
+                               .headers("C", "D").build(),
+                newBuilder(uri).header("A", "B")
+                               .setHeader("C", "D").build(),
+                newBuilder(uri).headers("A", "B")
+                               .headers("C", "D").build(),
+                newBuilder(uri).headers("A", "B")
+                               .header("C", "D").build(),
+                newBuilder(uri).headers("A", "B")
+                               .setHeader("C", "D").build(),
+                newBuilder(uri).setHeader("A", "B")
+                               .setHeader("C", "D").build(),
+                newBuilder(uri).setHeader("A", "B")
+                               .header("C", "D").build(),
+                newBuilder(uri).setHeader("A", "B")
+                               .headers("C", "D").build(),
+                newBuilder(uri).headers("A", "B", "C", "D").build()
+        );
+
+        for (HttpRequest r : requests) {
+            assertEquals(r.headers().map().size(), 2);
+            assertTrue(r.headers().firstValue("A").isPresent());
+            assertEquals(r.headers().firstValue("A").get(), "B");
+            assertEquals(r.headers().allValues("A"), List.of("B"));
+            assertTrue(r.headers().firstValue("C").isPresent());
+            assertEquals(r.headers().firstValue("C").get(), "D");
+            assertEquals(r.headers().allValues("C"), List.of("D"));
+            assertEquals(r.headers().map().get("C"), List.of("D"));
+            assertThrows(NFE, () -> r.headers().firstValueAsLong("C"));
+            assertFalse(r.headers().firstValue("E").isPresent());
+            // a smaller non-exhaustive list of mutators
+            assertThrows(UOE, () -> r.headers().map().put("Z", List.of("Z")));
+            assertThrows(UOE, () -> r.headers().map().remove("C"));
+            assertThrows(UOE, () -> r.headers().allValues("A").remove("B"));
+            assertThrows(UOE, () -> r.headers().allValues("A").clear());
+            assertThrows(UOE, () -> r.headers().allValues("C").add("Z"));
+        }
+
+        requests = List.of(
+                // same multi-value headers built from different combinations of the API
+                newBuilder(uri).header("A", "B")
+                               .header("A", "C").build(),
+                newBuilder(uri).header("A", "B")
+                               .headers("A", "C").build(),
+                newBuilder(uri).headers("A", "B")
+                               .headers("A", "C").build(),
+                newBuilder(uri).headers("A", "B")
+                               .header("A", "C").build(),
+                newBuilder(uri).setHeader("A", "B")
+                               .header("A", "C").build(),
+                newBuilder(uri).setHeader("A", "B")
+                               .headers("A", "C").build(),
+                newBuilder(uri).header("A", "D")
+                               .setHeader("A", "B")
+                               .headers("A", "C").build(),
+                newBuilder(uri).headers("A", "B", "A", "C").build()
+        );
+
+        for (HttpRequest r : requests) {
+            assertEquals(r.headers().map().size(), 1);
+            assertTrue(r.headers().firstValue("A").isPresent());
+            assertTrue(r.headers().allValues("A").containsAll(List.of("B", "C")));
+            assertEquals(r.headers().allValues("C").size(), 0);
+            assertEquals(r.headers().map().get("A"), List.of("B", "C"));
+            assertThrows(NFE, () -> r.headers().firstValueAsLong("A"));
+            assertFalse(r.headers().firstValue("C").isPresent());
+            // a non-exhaustive list of mutators
+            assertThrows(UOE, () -> r.headers().map().put("Z", List.of("Z")));
+            assertThrows(UOE, () -> r.headers().map().remove("A"));
+            assertThrows(UOE, () -> r.headers().map().remove("A", "B"));
+            assertThrows(UOE, () -> r.headers().map().clear());
+            assertThrows(UOE, () -> r.headers().allValues("A").remove("B"));
+            assertThrows(UOE, () -> r.headers().allValues("A").remove(1));
+            assertThrows(UOE, () -> r.headers().allValues("A").clear());
+            assertThrows(UOE, () -> r.headers().allValues("A").add("Z"));
+            assertThrows(UOE, () -> r.headers().allValues("A").addAll(List.of("Z")));
+            assertThrows(UOE, () -> r.headers().allValues("A").add(1, "Z"));
+        }
+    }
+
+    @Test
+    public void testCopy() {
+        HttpRequest.Builder builder = newBuilder(uri).expectContinue(true)
+                                                     .header("A", "B")
+                                                     .POST(fromString(""))
+                                                     .timeout(ofSeconds(30))
+                                                     .version(HTTP_1_1);
+        HttpRequest.Builder copy = builder.copy();
+        assertTrue(builder != copy);
+
+        // modify the original builder before building from the copy
+        builder.GET().timeout(ofSeconds(5)).version(HTTP_2).setHeader("A", "C");
+
+        HttpRequest copyRequest = copy.build();
+        assertEquals(copyRequest.uri(), uri);
+        assertEquals(copyRequest.expectContinue(), true);
+        assertEquals(copyRequest.headers().map().get("A"), List.of("B"));
+        assertEquals(copyRequest.method(), "POST");
+        assertEquals(copyRequest.bodyPublisher().isPresent(), true);
+        assertEquals(copyRequest.timeout().get(), ofSeconds(30));
+        assertTrue(copyRequest.version().isPresent());
+        assertEquals(copyRequest.version().get(), HTTP_1_1);
+    }
+
+    @Test
+    public void testTimeout() {
+        HttpRequest.Builder builder = newBuilder(uri);
+        assertThrows(IAE, () -> builder.timeout(ZERO));
+        assertThrows(IAE, () -> builder.timeout(ofSeconds(0)));
+        assertThrows(IAE, () -> builder.timeout(ofSeconds(-1)));
+        assertThrows(IAE, () -> builder.timeout(ofNanos(-100)));
+        assertEquals(builder.timeout(ofNanos(15)).build().timeout().get(), ofNanos(15));
+        assertEquals(builder.timeout(ofSeconds(50)).build().timeout().get(), ofSeconds(50));
+        assertEquals(builder.timeout(ofMinutes(30)).build().timeout().get(), ofMinutes(30));
+    }
+
+    @Test
+    public void testExpect() {
+        HttpRequest.Builder builder = newBuilder(uri);
+        assertEquals(builder.build().expectContinue(), false);
+        assertEquals(builder.expectContinue(true).build().expectContinue(), true);
+        assertEquals(builder.expectContinue(false).build().expectContinue(), false);
+        assertEquals(builder.expectContinue(true).build().expectContinue(), true);
+    }
+
+    @Test
+    public void testEquals() {
+        assertNotEquals(newBuilder(URI.create("http://foo.com")),
+                        newBuilder(URI.create("http://bar.com")));
+
+        HttpRequest.Builder builder = newBuilder(uri);
+        assertEquals(builder.build(), builder.build());
+        assertEquals(builder.build(), newBuilder(uri).build());
+
+        builder.POST(noBody());
+        assertEquals(builder.build(), builder.build());
+        assertEquals(builder.build(), newBuilder(uri).POST(noBody()).build());
+        assertEquals(builder.build(), newBuilder(uri).POST(fromString("")).build());
+        assertNotEquals(builder.build(), newBuilder(uri).build());
+        assertNotEquals(builder.build(), newBuilder(uri).GET().build());
+        assertNotEquals(builder.build(), newBuilder(uri).PUT(noBody()).build());
+
+        builder = newBuilder(uri).header("x", "y");
+        assertEquals(builder.build(), builder.build());
+        assertEquals(builder.build(), newBuilder(uri).header("x", "y").build());
+        assertNotEquals(builder.build(), newBuilder(uri).header("x", "Z").build());
+        assertNotEquals(builder.build(), newBuilder(uri).header("z", "y").build());
+    }
+}
diff --git a/test/jdk/java/net/httpclient/RequestProcessorExceptions.java b/test/jdk/java/net/httpclient/RequestProcessorExceptions.java
new file mode 100644
index 00000000000..c84d5b059c1
--- /dev/null
+++ b/test/jdk/java/net/httpclient/RequestProcessorExceptions.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @run testng RequestProcessorExceptions
+ */
+
+import java.io.FileNotFoundException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.List;
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+import static jdk.incubator.http.HttpRequest.BodyPublisher.fromByteArray;
+import static jdk.incubator.http.HttpRequest.BodyPublisher.fromFile;
+
+public class RequestProcessorExceptions {
+
+    @DataProvider(name = "byteArrayOOBs")
+    public Object[][] byteArrayOOBs() {
+        return new Object[][] {
+                { new byte[100],    1,  100 },
+                { new byte[100],   -1,   10 },
+                { new byte[100],   99,    2 },
+                { new byte[1],   -100,    1 } };
+    }
+
+    @Test(dataProvider = "byteArrayOOBs", expectedExceptions = IndexOutOfBoundsException.class)
+    public void fromByteArrayCheck(byte[] buf, int offset, int length) {
+        fromByteArray(buf, offset, length);
+    }
+
+    @DataProvider(name = "nonExistentFiles")
+    public Object[][] nonExistentFiles() {
+        List<Path> paths = List.of(Paths.get("doesNotExist"),
+                                   Paths.get("tsixEtoNseod"),
+                                   Paths.get("doesNotExist2"));
+        paths.forEach(p -> {
+            if (Files.exists(p))
+                throw new AssertionError("Unexpected " + p);
+        });
+
+        return paths.stream().map(p -> new Object[] { p }).toArray(Object[][]::new);
+    }
+
+    @Test(dataProvider = "nonExistentFiles", expectedExceptions = FileNotFoundException.class)
+    public void fromFileCheck(Path path) throws Exception {
+        fromFile(path);
+    }
+
+    // ---
+
+    /* Main entry point for standalone testing of the main functional test. */
+    public static void main(String... args) throws Exception {
+        RequestProcessorExceptions t = new RequestProcessorExceptions();
+        for (Object[] objs : t.byteArrayOOBs()) {
+            try {
+                t.fromByteArrayCheck((byte[]) objs[0], (int) objs[1], (int) objs[2]);
+                throw new RuntimeException("fromByteArrayCheck failed");
+            } catch (IndexOutOfBoundsException expected) { /* Ok */ }
+        }
+        for (Object[] objs : t.nonExistentFiles()) {
+            try {
+                t.fromFileCheck((Path) objs[0]);
+                throw new RuntimeException("fromFileCheck failed");
+            } catch (FileNotFoundException expected) { /* Ok */ }
+        }
+    }
+}
diff --git a/test/jdk/java/net/httpclient/ShortRequestBody.java b/test/jdk/java/net/httpclient/ShortRequestBody.java
index 9740bb30dcd..9fd3187e25a 100644
--- a/test/jdk/java/net/httpclient/ShortRequestBody.java
+++ b/test/jdk/java/net/httpclient/ShortRequestBody.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2017, 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
@@ -21,10 +21,10 @@
  * questions.
  */
 
-import java.io.*;
-import jdk.incubator.http.HttpClient;
-import jdk.incubator.http.HttpResponse;
-import jdk.incubator.http.HttpRequest;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.UncheckedIOException;
 import java.net.ServerSocket;
 import java.net.Socket;
 import java.net.URI;
@@ -32,14 +32,20 @@ import java.nio.file.Files;
 import java.nio.file.Path;
 import java.nio.file.Paths;
 import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.List;
 import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.Executor;
-import java.util.concurrent.ExecutorService;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.Flow;
 import java.util.concurrent.TimeoutException;
 import java.util.concurrent.TimeUnit;
-import static java.lang.System.out;
+import java.util.function.Supplier;
+import jdk.incubator.http.HttpClient;
+import jdk.incubator.http.HttpResponse;
+import jdk.incubator.http.HttpRequest;
+import jdk.incubator.http.HttpTimeoutException;
+
+import static java.lang.System.err;
 import static java.nio.charset.StandardCharsets.US_ASCII;
 import static jdk.incubator.http.HttpResponse.BodyHandler.discard;
 import static java.nio.charset.StandardCharsets.UTF_8;
@@ -48,23 +54,13 @@ import static java.nio.charset.StandardCharsets.UTF_8;
  * @test
  * @bug 8151441
  * @summary Request body of incorrect (larger or smaller) sizes than that
- *          reported by the body processor
+ *          reported by the body publisher
  * @run main/othervm ShortRequestBody
  */
 
 public class ShortRequestBody {
 
     static final Path testSrc = Paths.get(System.getProperty("test.src", "."));
-    static volatile HttpClient staticDefaultClient;
-
-    static HttpClient defaultClient() {
-        if (staticDefaultClient == null) {
-            synchronized (ShortRequestBody.class) {
-                staticDefaultClient = HttpClient.newHttpClient();
-            }
-        }
-        return staticDefaultClient;
-    }
 
     // Some body types ( sources ) for testing.
     static final String STRING_BODY = "Hello world";
@@ -79,14 +75,14 @@ public class ShortRequestBody {
                                                   fileSize(FILE_BODY) };
     static final int[] BODY_OFFSETS = new int[] { 0, +1, -1, +2, -2, +3, -3 };
 
-    // A delegating body processor. Subtypes will have a concrete body type.
+    // A delegating Body Publisher. Subtypes will have a concrete body type.
     static abstract class AbstractDelegateRequestBody
-            implements HttpRequest.BodyProcessor {
+            implements HttpRequest.BodyPublisher {
 
-        final HttpRequest.BodyProcessor delegate;
+        final HttpRequest.BodyPublisher delegate;
         final long contentLength;
 
-        AbstractDelegateRequestBody(HttpRequest.BodyProcessor delegate,
+        AbstractDelegateRequestBody(HttpRequest.BodyPublisher delegate,
                                     long contentLength) {
             this.delegate = delegate;
             this.contentLength = contentLength;
@@ -101,26 +97,26 @@ public class ShortRequestBody {
         public long contentLength() { return contentLength; /* may be wrong! */ }
     }
 
-    // Request body processors that may generate a different number of actual
+    // Request body Publishers that may generate a different number of actual
     // bytes to that of what is reported through their {@code contentLength}.
 
     static class StringRequestBody extends AbstractDelegateRequestBody {
         StringRequestBody(String body, int additionalLength) {
-            super(HttpRequest.BodyProcessor.fromString(body),
+            super(HttpRequest.BodyPublisher.fromString(body),
                   body.getBytes(UTF_8).length + additionalLength);
         }
     }
 
     static class ByteArrayRequestBody extends AbstractDelegateRequestBody {
         ByteArrayRequestBody(byte[] body, int additionalLength) {
-            super(HttpRequest.BodyProcessor.fromByteArray(body),
+            super(HttpRequest.BodyPublisher.fromByteArray(body),
                   body.length + additionalLength);
         }
     }
 
     static class FileRequestBody extends AbstractDelegateRequestBody {
         FileRequestBody(Path path, int additionalLength) throws IOException {
-            super(HttpRequest.BodyProcessor.fromFile(path),
+            super(HttpRequest.BodyPublisher.fromFile(path),
                   Files.size(path) + additionalLength);
         }
     }
@@ -128,53 +124,63 @@ public class ShortRequestBody {
     // ---
 
     public static void main(String[] args) throws Exception {
+        HttpClient sharedClient = HttpClient.newHttpClient();
+        List<Supplier<HttpClient>> clientSuppliers = new ArrayList<>();
+        clientSuppliers.add(() -> HttpClient.newHttpClient());
+        clientSuppliers.add(() -> sharedClient);
+
         try (Server server = new Server()) {
-            URI uri = new URI("http://127.0.0.1:" + server.getPort() + "/");
+            for (Supplier<HttpClient> cs : clientSuppliers) {
+                err.println("\n---- next supplier ----\n");
+                URI uri = new URI("http://127.0.0.1:" + server.getPort() + "/");
 
-            // sanity
-            success(uri, new StringRequestBody(STRING_BODY, 0));
-            success(uri, new ByteArrayRequestBody(BYTE_ARRAY_BODY, 0));
-            success(uri, new FileRequestBody(FILE_BODY, 0));
+                // sanity ( 6 requests to keep client and server offsets easy to workout )
+                success(cs, uri, new StringRequestBody(STRING_BODY, 0));
+                success(cs, uri, new ByteArrayRequestBody(BYTE_ARRAY_BODY, 0));
+                success(cs, uri, new FileRequestBody(FILE_BODY, 0));
+                success(cs, uri, new StringRequestBody(STRING_BODY, 0));
+                success(cs, uri, new ByteArrayRequestBody(BYTE_ARRAY_BODY, 0));
+                success(cs, uri, new FileRequestBody(FILE_BODY, 0));
 
-            for (int i=1; i< BODY_OFFSETS.length; i++) {
-                failureBlocking(uri, new StringRequestBody(STRING_BODY, BODY_OFFSETS[i]));
-                failureBlocking(uri, new ByteArrayRequestBody(BYTE_ARRAY_BODY, BODY_OFFSETS[i]));
-                failureBlocking(uri, new FileRequestBody(FILE_BODY, BODY_OFFSETS[i]));
+                for (int i = 1; i < BODY_OFFSETS.length; i++) {
+                    failureBlocking(cs, uri, new StringRequestBody(STRING_BODY, BODY_OFFSETS[i]));
+                    failureBlocking(cs, uri, new ByteArrayRequestBody(BYTE_ARRAY_BODY, BODY_OFFSETS[i]));
+                    failureBlocking(cs, uri, new FileRequestBody(FILE_BODY, BODY_OFFSETS[i]));
 
-                failureNonBlocking(uri, new StringRequestBody(STRING_BODY, BODY_OFFSETS[i]));
-                failureNonBlocking(uri, new ByteArrayRequestBody(BYTE_ARRAY_BODY, BODY_OFFSETS[i]));
-                failureNonBlocking(uri, new FileRequestBody(FILE_BODY, BODY_OFFSETS[i]));
-            }
-        } finally {
-            Executor def = defaultClient().executor();
-            if (def instanceof ExecutorService) {
-               ((ExecutorService)def).shutdownNow();
+                    failureNonBlocking(cs, uri, new StringRequestBody(STRING_BODY, BODY_OFFSETS[i]));
+                    failureNonBlocking(cs, uri, new ByteArrayRequestBody(BYTE_ARRAY_BODY, BODY_OFFSETS[i]));
+                    failureNonBlocking(cs, uri, new FileRequestBody(FILE_BODY, BODY_OFFSETS[i]));
+                }
             }
         }
     }
 
-    static void success(URI uri, HttpRequest.BodyProcessor processor)
+    static void success(Supplier<HttpClient> clientSupplier,
+                        URI uri,
+                        HttpRequest.BodyPublisher publisher)
         throws Exception
     {
         CompletableFuture<HttpResponse<Void>> cf;
         HttpRequest request = HttpRequest.newBuilder(uri)
-                                         .POST(processor)
+                                         .POST(publisher)
                                          .build();
-        cf = defaultClient().sendAsync(request, discard(null));
+        cf = clientSupplier.get().sendAsync(request, discard(null));
 
         HttpResponse<Void> resp = cf.get(30, TimeUnit.SECONDS);
-        out.println("Response code: " + resp.statusCode());
+        err.println("Response code: " + resp.statusCode());
         check(resp.statusCode() == 200, "Expected 200, got ", resp.statusCode());
     }
 
-    static void failureNonBlocking(URI uri, HttpRequest.BodyProcessor processor)
+    static void failureNonBlocking(Supplier<HttpClient> clientSupplier,
+                                   URI uri,
+                                   HttpRequest.BodyPublisher publisher)
         throws Exception
     {
         CompletableFuture<HttpResponse<Void>> cf;
         HttpRequest request = HttpRequest.newBuilder(uri)
-                                         .POST(processor)
+                                         .POST(publisher)
                                          .build();
-        cf = defaultClient().sendAsync(request, discard(null));
+        cf = clientSupplier.get().sendAsync(request, discard(null));
 
         try {
             HttpResponse<Void> r = cf.get(30, TimeUnit.SECONDS);
@@ -182,23 +188,34 @@ public class ShortRequestBody {
         } catch (TimeoutException x) {
             throw new RuntimeException("Unexpected timeout", x);
         } catch (ExecutionException expected) {
-            out.println("Caught expected: " + expected);
-            check(expected.getCause() instanceof IOException,
+            err.println("Caught expected: " + expected);
+            Throwable t = expected.getCause();
+            check(t instanceof IOException,
                   "Expected cause IOException, but got: ", expected.getCause());
+            String msg = t.getMessage();
+            check(msg.contains("Too many") || msg.contains("Too few"),
+                    "Expected Too many|Too few, got: ", t);
         }
     }
 
-    static void failureBlocking(URI uri, HttpRequest.BodyProcessor processor)
+    static void failureBlocking(Supplier<HttpClient> clientSupplier,
+                                URI uri,
+                                HttpRequest.BodyPublisher publisher)
         throws Exception
     {
         HttpRequest request = HttpRequest.newBuilder(uri)
-                                         .POST(processor)
+                                         .POST(publisher)
                                          .build();
         try {
-            HttpResponse<Void> r = defaultClient().send(request, discard(null));
+            HttpResponse<Void> r = clientSupplier.get().send(request, discard(null));
             throw new RuntimeException("Unexpected response: " + r.statusCode());
+        } catch (HttpTimeoutException x) {
+            throw new RuntimeException("Unexpected timeout", x);
         } catch (IOException expected) {
-            out.println("Caught expected: " + expected);
+            err.println("Caught expected: " + expected);
+            String msg = expected.getMessage();
+            check(msg.contains("Too many") || msg.contains("Too few"),
+                    "Expected Too many|Too few, got: ", expected);
         }
     }
 
@@ -225,20 +242,40 @@ public class ShortRequestBody {
 
             while (!closed) {
                 try (Socket s = ss.accept()) {
+                    err.println("Server: got connection");
                     InputStream is = s.getInputStream();
                     readRequestHeaders(is);
                     byte[] ba = new byte[1024];
 
                     int length = BODY_LENGTHS[count % 3];
                     length += BODY_OFFSETS[offset];
+                    err.println("Server: count=" + count + ", offset=" + offset);
+                    err.println("Server: expecting " +length+ " bytes");
+                    int read = is.readNBytes(ba, 0, length);
+                    err.println("Server: actually read " + read + " bytes");
 
-                    is.readNBytes(ba, 0, length);
-
-                    OutputStream os = s.getOutputStream();
-                    os.write(RESPONSE.getBytes(US_ASCII));
+                    // Update the counts before replying, to prevent the
+                    // client-side racing reset with this thread.
                     count++;
                     if (count % 6 == 0) // 6 is the number of failure requests per offset
                         offset++;
+                    if (count % 42 == 0) {
+                        count = 0;  // reset, for second iteration
+                        offset = 0;
+                    }
+
+                    if (read < length) {
+                        // no need to reply, client has already closed
+                        // ensure closed
+                        if (is.read() != -1)
+                            new AssertionError("Unexpected read");
+                    } else {
+                        OutputStream os = s.getOutputStream();
+                        err.println("Server: writing "
+                                + RESPONSE.getBytes(US_ASCII).length + " bytes");
+                        os.write(RESPONSE.getBytes(US_ASCII));
+                    }
+
                 } catch (IOException e) {
                     if (!closed)
                         System.out.println("Unexpected" + e);
diff --git a/test/jdk/java/net/httpclient/SmallTimeout.java b/test/jdk/java/net/httpclient/SmallTimeout.java
index c06994c417b..28e6958d4ff 100644
--- a/test/jdk/java/net/httpclient/SmallTimeout.java
+++ b/test/jdk/java/net/httpclient/SmallTimeout.java
@@ -40,7 +40,7 @@ import static jdk.incubator.http.HttpResponse.BodyHandler.discard;
  * @test
  * @bug 8178147
  * @summary Ensures that small timeouts do not cause hangs due to race conditions
- * @run main/othervm SmallTimeout
+ * @run main/othervm -Djdk.incubator.http.internal.common.DEBUG=true SmallTimeout
  */
 
 // To enable logging use. Not enabled by default as it changes the dynamics
@@ -52,7 +52,25 @@ public class SmallTimeout {
     static int[] TIMEOUTS = {2, 1, 3, 2, 100, 1};
 
     // A queue for placing timed out requests so that their order can be checked.
-    static LinkedBlockingQueue<HttpRequest> queue = new LinkedBlockingQueue<>();
+    static LinkedBlockingQueue<HttpResult> queue = new LinkedBlockingQueue<>();
+
+    static final class HttpResult {
+         final HttpRequest request;
+         final Throwable   failed;
+         HttpResult(HttpRequest request, Throwable   failed) {
+             this.request = request;
+             this.failed = failed;
+         }
+
+         static HttpResult of(HttpRequest request) {
+             return new HttpResult(request, null);
+         }
+
+         static HttpResult of(HttpRequest request, Throwable t) {
+             return new HttpResult(request, t);
+         }
+
+    }
 
     static volatile boolean error;
 
@@ -76,8 +94,10 @@ public class SmallTimeout {
                 CompletableFuture<HttpResponse<Object>> response = client
                     .sendAsync(req, discard(null))
                     .whenComplete((HttpResponse<Object> r, Throwable t) -> {
+                        Throwable cause = null;
                         if (r != null) {
                             out.println("Unexpected response: " + r);
+                            cause = new RuntimeException("Unexpected response");
                             error = true;
                         }
                         if (t != null) {
@@ -85,6 +105,7 @@ public class SmallTimeout {
                                 out.println("Wrong exception type:" + t.toString());
                                 Throwable c = t.getCause() == null ? t : t.getCause();
                                 c.printStackTrace();
+                                cause = c;
                                 error = true;
                             } else {
                                 out.println("Caught expected timeout: " + t.getCause());
@@ -92,9 +113,10 @@ public class SmallTimeout {
                         }
                         if (t == null && r == null) {
                             out.println("Both response and throwable are null!");
+                            cause = new RuntimeException("Both response and throwable are null!");
                             error = true;
                         }
-                        queue.add(req);
+                        queue.add(HttpResult.of(req,cause));
                     });
             }
             System.out.println("All requests submitted. Waiting ...");
@@ -118,15 +140,18 @@ public class SmallTimeout {
 
                 final HttpRequest req = requests[i];
                 executor.execute(() -> {
+                    Throwable cause = null;
                     try {
                         client.send(req, discard(null));
                     } catch (HttpTimeoutException e) {
                         out.println("Caught expected timeout: " + e);
-                        queue.offer(req);
-                    } catch (IOException | InterruptedException ee) {
+                    } catch (Throwable ee) {
                         Throwable c = ee.getCause() == null ? ee : ee.getCause();
                         c.printStackTrace();
+                        cause = c;
                         error = true;
+                    } finally {
+                        queue.offer(HttpResult.of(req, cause));
                     }
                 });
             }
@@ -139,18 +164,20 @@ public class SmallTimeout {
             if (error)
                 throw new RuntimeException("Failed. Check output");
 
-        } finally {
-            ((ExecutorService) client.executor()).shutdownNow();
         }
     }
 
     static void checkReturn(HttpRequest[] requests) throws InterruptedException {
         // wait for exceptions and check order
+        boolean ok = true;
         for (int j = 0; j < TIMEOUTS.length; j++) {
-            HttpRequest req = queue.take();
-            out.println("Got request from queue " + req + ", order: " + getRequest(req, requests));
+            HttpResult res = queue.take();
+            HttpRequest req = res.request;
+            out.println("Got request from queue " + req + ", order: " + getRequest(req, requests)
+                         + (res.failed == null ? "" : " failed: " + res.failed));
+            ok = ok && res.failed == null;
         }
-        out.println("Return ok");
+        out.println("Return " + (ok ? "ok" : "nok"));
     }
 
     /** Returns the index of the request in the array. */
diff --git a/test/jdk/java/net/httpclient/SmokeTest.java b/test/jdk/java/net/httpclient/SmokeTest.java
index 7d18ae0015b..55f9d4e1aa7 100644
--- a/test/jdk/java/net/httpclient/SmokeTest.java
+++ b/test/jdk/java/net/httpclient/SmokeTest.java
@@ -32,7 +32,7 @@
  * @compile ../../../com/sun/net/httpserver/LogFilter.java
  * @compile ../../../com/sun/net/httpserver/EchoHandler.java
  * @compile ../../../com/sun/net/httpserver/FileServerHandler.java
- * @run main/othervm -Djdk.httpclient.HttpClient.log=errors,trace SmokeTest
+ * @run main/othervm -Djdk.internal.httpclient.debug=true -Djdk.httpclient.HttpClient.log=errors,trace SmokeTest
  */
 
 import com.sun.net.httpserver.Headers;
@@ -43,12 +43,12 @@ import com.sun.net.httpserver.HttpServer;
 import com.sun.net.httpserver.HttpsConfigurator;
 import com.sun.net.httpserver.HttpsParameters;
 import com.sun.net.httpserver.HttpsServer;
+import java.net.Proxy;
+import java.net.SocketAddress;
 import java.util.concurrent.atomic.AtomicInteger;
 import java.net.InetSocketAddress;
 import java.net.PasswordAuthentication;
 import java.net.ProxySelector;
-import java.net.ServerSocket;
-import java.net.Socket;
 import java.net.URI;
 import jdk.incubator.http.HttpClient;
 import jdk.incubator.http.HttpRequest;
@@ -81,9 +81,9 @@ import java.util.LinkedList;
 import java.util.List;
 import java.util.Random;
 import jdk.testlibrary.SimpleSSLContext;
-import static jdk.incubator.http.HttpRequest.BodyProcessor.fromFile;
-import static jdk.incubator.http.HttpRequest.BodyProcessor.fromInputStream;
-import static jdk.incubator.http.HttpRequest.BodyProcessor.fromString;
+import static jdk.incubator.http.HttpRequest.BodyPublisher.fromFile;
+import static jdk.incubator.http.HttpRequest.BodyPublisher.fromInputStream;
+import static jdk.incubator.http.HttpRequest.BodyPublisher.fromString;
 import static jdk.incubator.http.HttpResponse.*;
 import static jdk.incubator.http.HttpResponse.BodyHandler.asFile;
 import static jdk.incubator.http.HttpResponse.BodyHandler.asString;
@@ -172,7 +172,6 @@ public class SmokeTest {
                            .build();
 
         try {
-
             test1(httproot + "files/foo.txt", true);
             test1(httproot + "files/foo.txt", false);
             test1(httpsroot + "files/foo.txt", true);
@@ -255,7 +254,10 @@ public class SmokeTest {
 
         String body = response.body();
         if (!body.equals("This is foo.txt\r\n")) {
-            throw new RuntimeException();
+            throw new RuntimeException("Did not get expected body: "
+                + "\n\t expected \"This is foo.txt\\r\\n\""
+                + "\n\t received \""
+                + body.replace("\r", "\\r").replace("\n","\\n") + "\"");
         }
 
         // repeat async
@@ -296,14 +298,13 @@ public class SmokeTest {
     static void test2a(String s) throws Exception {
         System.out.print("test2a: " + s);
         URI uri = new URI(s);
-        Path p = Util.getTempFile(128 * 1024);
-        //Path p = Util.getTempFile(1 * 1024);
+        Path p = getTempFile(128 * 1024);
 
         HttpRequest request = HttpRequest.newBuilder(uri)
                                          .POST(fromFile(p))
                                          .build();
 
-        Path resp = Util.getTempFile(1); // will be overwritten
+        Path resp = getTempFile(1); // will be overwritten
 
         HttpResponse<Path> response =
                 client.send(request,
@@ -315,6 +316,11 @@ public class SmokeTest {
             throw new RuntimeException(
                 "Expected 200, got [ " + response.statusCode() + " ]");
         }
+        // no redirection, etc, should be no previous response
+        if (response.previousResponse().isPresent()) {
+            throw new RuntimeException(
+                "Unexpected previous response: " + response.previousResponse().get());
+        }
         Path reply = response.body();
         //System.out.println("Reply stored in " + reply.toString());
         cmpFileContent(reply, p);
@@ -347,7 +353,7 @@ public class SmokeTest {
         if (Files.size(downloaded) != Files.size(midSizedFile)) {
             throw new RuntimeException("Size mismatch");
         }
-
+        checkPreviousRedirectResponses(request, response);
         System.out.printf(" (count: %d) ", handler.count());
         // repeat with async api
 
@@ -368,10 +374,77 @@ public class SmokeTest {
         if (Files.size(downloaded) != Files.size(midSizedFile)) {
             throw new RuntimeException("Size mismatch 2");
         }
+
+        checkPreviousRedirectResponses(request, response);
         System.out.printf(" (count: %d) ", handler.count());
         System.out.println(" OK");
     }
 
+    static void checkPreviousRedirectResponses(HttpRequest initialRequest,
+                                               HttpResponse<?> finalResponse) {
+        // there must be at least one previous response
+        finalResponse.previousResponse()
+                .orElseThrow(() -> new RuntimeException("no previous response"));
+
+        HttpResponse<?> response = finalResponse;
+        do {
+            URI uri = response.uri();
+            response = response.previousResponse().get();
+            check(300 <= response.statusCode() && response.statusCode() <= 309,
+                  "Expected 300 <= code <= 309, got:" + response.statusCode());
+            check(response.body() == null, "Unexpected body: " + response.body());
+            String locationHeader = response.headers().firstValue("Location")
+                    .orElseThrow(() -> new RuntimeException("no previous Location"));
+            check(uri.toString().endsWith(locationHeader),
+                  "URI: " + uri + ", Location: " + locationHeader);
+        } while (response.previousResponse().isPresent());
+
+        // initial
+        check(initialRequest.equals(response.request()),
+              "Expected initial request [%s] to equal last prev req [%s]",
+              initialRequest, response.request());
+    }
+
+    static void check(boolean cond, Object... msg) {
+        if (cond)
+            return;
+        StringBuilder sb = new StringBuilder();
+        for (Object o : msg)
+            sb.append(o);
+        throw new RuntimeException(sb.toString());
+    }
+
+    /**
+     * A Proxy Selector that wraps a ProxySelector.of(), and counts the number
+     * of times its select method has been invoked. This can be used to ensure
+     * that the Proxy Selector is invoked only once per HttpClient.sendXXX
+     * invocation.
+     */
+    static class CountingProxySelector extends ProxySelector {
+        private final ProxySelector proxySelector;
+        private volatile int count; // 0
+        private CountingProxySelector(InetSocketAddress proxyAddress) {
+            proxySelector = ProxySelector.of(proxyAddress);
+        }
+
+        public static CountingProxySelector of(InetSocketAddress proxyAddress) {
+            return new CountingProxySelector(proxyAddress);
+        }
+
+        int count() { return count; }
+
+        @Override
+        public List<Proxy> select(URI uri) {
+            count++;
+            return proxySelector.select(uri);
+        }
+
+        @Override
+        public void connectFailed(URI uri, SocketAddress sa, IOException ioe) {
+            proxySelector.connectFailed(uri, sa, ioe);
+        }
+    }
+
     // Proxies
     static void test4(String s) throws Exception {
         System.out.print("test4: " + s);
@@ -381,16 +454,17 @@ public class SmokeTest {
 
         ExecutorService e = Executors.newCachedThreadPool();
 
+        CountingProxySelector ps = CountingProxySelector.of(proxyAddr);
         HttpClient cl = HttpClient.newBuilder()
                                   .executor(e)
-                                  .proxy(ProxySelector.of(proxyAddr))
+                                  .proxy(ps)
                                   .sslContext(ctx)
                                   .sslParameters(sslparams)
                                   .build();
 
         HttpRequest request = HttpRequest.newBuilder(uri).GET().build();
 
-        CompletableFuture<String> fut = client.sendAsync(request, asString())
+        CompletableFuture<String> fut = cl.sendAsync(request, asString())
                 .thenApply((response) -> response.body());
 
         String body = fut.get(5, TimeUnit.HOURS);
@@ -401,6 +475,9 @@ public class SmokeTest {
             throw new RuntimeException(
                     "Body mismatch: expected [" + body + "], got [" + fc + "]");
         }
+        if (ps.count() != 1) {
+            throw new RuntimeException("CountingProxySelector. Expected 1, got " + ps.count());
+        }
         e.shutdownNow();
         System.out.println(" OK");
     }
@@ -465,7 +542,7 @@ public class SmokeTest {
     @SuppressWarnings("rawtypes")
     static void test7(String target) throws Exception {
         System.out.print("test7: " + target);
-        Path requestBody = Util.getTempFile(128 * 1024);
+        Path requestBody = getTempFile(128 * 1024);
         // First test
         URI uri = new URI(target);
         HttpRequest request = HttpRequest.newBuilder().uri(uri).GET().build();
@@ -644,7 +721,7 @@ public class SmokeTest {
         ch.setLevel(Level.SEVERE);
         logger.addHandler(ch);
 
-        String root = System.getProperty ("test.src")+ "/docs";
+        String root = System.getProperty ("test.src", ".")+ "/docs";
         InetSocketAddress addr = new InetSocketAddress (0);
         s1 = HttpServer.create (addr, 0);
         if (s1 instanceof HttpsServer) {
@@ -690,167 +767,112 @@ public class SmokeTest {
         proxyPort = proxy.getPort();
         System.out.println("Proxy port = " + proxyPort);
     }
-}
 
-class Configurator extends HttpsConfigurator {
-    public Configurator(SSLContext ctx) {
-        super(ctx);
-    }
+    static class RedirectHandler implements HttpHandler {
+        private final String root;
+        private volatile int count = 0;
 
-    public void configure (HttpsParameters params) {
-        params.setSSLParameters (getSSLContext().getSupportedSSLParameters());
-    }
-}
+        RedirectHandler(String root) {
+            this.root = root;
+        }
 
-class UploadServer extends Thread {
-    int statusCode;
-    ServerSocket ss;
-    int port;
-    int size;
-    Object lock;
-    boolean failed = false;
+        @Override
+        public synchronized void handle(HttpExchange t) throws IOException {
+            byte[] buf = new byte[2048];
+            try (InputStream is = t.getRequestBody()) {
+                while (is.read(buf) != -1) ;
+            }
 
-    UploadServer(int size) throws IOException {
-        this.statusCode = statusCode;
-        this.size = size;
-        ss = new ServerSocket(0);
-        port = ss.getLocalPort();
-        lock = new Object();
-    }
+            Headers responseHeaders = t.getResponseHeaders();
 
-    int port() {
-          return port;
-    }
+            if (count++ < 1) {
+                responseHeaders.add("Location", root + "/foo/" + count);
+            } else {
+                responseHeaders.add("Location", SmokeTest.midSizedFilename);
+            }
+            t.sendResponseHeaders(301, 64 * 1024);
+            byte[] bb = new byte[1024];
+            OutputStream os = t.getResponseBody();
+            for (int i=0; i<64; i++) {
+                os.write(bb);
+            }
+            os.close();
+            t.close();
+        }
 
-    int size() {
-          return size;
-    }
+        int count() {
+            return count;
+        }
 
-    // wait a sec before calling this
-    boolean failed() {
-        synchronized(lock) {
-            return failed;
+        void reset() {
+            count = 0;
         }
     }
 
-    @Override
-    public void run () {
-        int nbytes = 0;
-        Socket s = null;
+    static class RedirectErrorHandler implements HttpHandler {
+        private final String root;
+        private volatile int count = 1;
 
-        synchronized(lock) {
+        RedirectErrorHandler(String root) {
+            this.root = root;
+        }
+
+        synchronized int count() {
+            return count;
+        }
+
+        synchronized void increment() {
+            count++;
+        }
+
+        @Override
+        public synchronized void handle(HttpExchange t) throws IOException {
+            try (InputStream is = t.getRequestBody()) {
+                is.readAllBytes();
+            }
+
+            Headers map = t.getResponseHeaders();
+            String redirect = root + "/foo/" + Integer.toString(count);
+            increment();
+            map.add("Location", redirect);
+            t.sendResponseHeaders(301, -1);
+            t.close();
+        }
+    }
+
+    static class DelayHandler implements HttpHandler {
+
+        CyclicBarrier bar1 = new CyclicBarrier(2);
+        CyclicBarrier bar2 = new CyclicBarrier(2);
+        CyclicBarrier bar3 = new CyclicBarrier(2);
+
+        CyclicBarrier barrier1() {
+            return bar1;
+        }
+
+        CyclicBarrier barrier2() {
+            return bar2;
+        }
+
+        @Override
+        public synchronized void handle(HttpExchange he) throws IOException {
+            he.getRequestBody().readAllBytes();
             try {
-                s = ss.accept();
-
-                InputStream is = s.getInputStream();
-                OutputStream os = s.getOutputStream();
-                os.write("HTTP/1.1 201 OK\r\nContent-length: 0\r\n\r\n".getBytes());
-                int n;
-                byte[] buf = new byte[8000];
-                while ((n=is.read(buf)) != -1) {
-                    nbytes += n;
-                }
-            } catch (IOException e) {
-                System.out.println ("read " + nbytes);
-                System.out.println ("size " + size);
-                failed = nbytes >= size;
-            } finally {
-                try {
-                    ss.close();
-                    if (s != null)
-                        s.close();
-                } catch (IOException e) {}
-            }
+                bar1.await();
+                bar2.await();
+            } catch (Exception e) { }
+            he.sendResponseHeaders(200, -1); // will probably fail
+            he.close();
         }
     }
-}
 
-class RedirectHandler implements HttpHandler {
-    String root;
-    volatile int count = 0;
-
-    RedirectHandler(String root) {
-        this.root = root;
-    }
-
-    @Override
-    public synchronized void handle(HttpExchange t)
-        throws IOException
-    {
-        byte[] buf = new byte[2048];
-        try (InputStream is = t.getRequestBody()) {
-            while (is.read(buf) != -1) ;
+    static class Configurator extends HttpsConfigurator {
+        public Configurator(SSLContext ctx) {
+            super(ctx);
         }
 
-        Headers responseHeaders = t.getResponseHeaders();
-
-        if (count++ < 1) {
-            responseHeaders.add("Location", root + "/foo/" + count);
-        } else {
-            responseHeaders.add("Location", SmokeTest.midSizedFilename);
-        }
-        t.sendResponseHeaders(301, -1);
-        t.close();
-    }
-
-    int count() {
-        return count;
-    }
-
-    void reset() {
-        count = 0;
-    }
-}
-
-class RedirectErrorHandler implements HttpHandler {
-    String root;
-    volatile int count = 1;
-
-    RedirectErrorHandler(String root) {
-        this.root = root;
-    }
-
-    synchronized int count() {
-        return count;
-    }
-
-    synchronized void increment() {
-        count++;
-    }
-
-    @Override
-    public synchronized void handle (HttpExchange t)
-        throws IOException
-    {
-        byte[] buf = new byte[2048];
-        try (InputStream is = t.getRequestBody()) {
-            while (is.read(buf) != -1) ;
-        }
-
-        Headers map = t.getResponseHeaders();
-        String redirect = root + "/foo/" + Integer.toString(count);
-        increment();
-        map.add("Location", redirect);
-        t.sendResponseHeaders(301, -1);
-        t.close();
-    }
-}
-
-class Util {
-    static byte[] readAll(InputStream is) throws IOException {
-        byte[] buf = new byte[1024];
-        byte[] result = new byte[0];
-
-        while (true) {
-            int n = is.read(buf);
-            if (n > 0) {
-                byte[] b1 = new byte[result.length + n];
-                System.arraycopy(result, 0, b1, 0, result.length);
-                System.arraycopy(buf, 0, b1, result.length, n);
-                result = b1;
-            } else if (n == -1) {
-                return result;
-            }
+        public void configure (HttpsParameters params) {
+            params.setSSLParameters (getSSLContext().getSupportedSSLParameters());
         }
     }
 
@@ -858,8 +880,8 @@ class Util {
         File f = File.createTempFile("test", "txt");
         f.deleteOnExit();
         byte[] buf = new byte[2048];
-        for (int i=0; i<buf.length; i++)
-            buf[i] = (byte)i;
+        for (int i = 0; i < buf.length; i++)
+            buf[i] = (byte) i;
 
         FileOutputStream fos = new FileOutputStream(f);
         while (size > 0) {
@@ -872,33 +894,6 @@ class Util {
     }
 }
 
-class DelayHandler implements HttpHandler {
-
-    CyclicBarrier bar1 = new CyclicBarrier(2);
-    CyclicBarrier bar2 = new CyclicBarrier(2);
-    CyclicBarrier bar3 = new CyclicBarrier(2);
-
-    CyclicBarrier barrier1() {
-        return bar1;
-    }
-
-    CyclicBarrier barrier2() {
-        return bar2;
-    }
-
-    @Override
-    public synchronized void handle(HttpExchange he) throws IOException {
-        byte[] buf = Util.readAll(he.getRequestBody());
-        try {
-            bar1.await();
-            bar2.await();
-        } catch (Exception e) {}
-        he.sendResponseHeaders(200, -1); // will probably fail
-        he.close();
-    }
-
-}
-
 // check for simple hardcoded sequence and use remote address
 // to check.
 // First 4 requests executed in sequence (should use same connection/address)
diff --git a/test/jdk/java/net/httpclient/SplitResponse.java b/test/jdk/java/net/httpclient/SplitResponse.java
index 645e862699e..c2434f57e61 100644
--- a/test/jdk/java/net/httpclient/SplitResponse.java
+++ b/test/jdk/java/net/httpclient/SplitResponse.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2017, 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
@@ -21,38 +21,49 @@
  * questions.
  */
 
-
 import java.io.IOException;
-import jdk.incubator.http.HttpClient;
-import jdk.incubator.http.HttpRequest;
-import jdk.incubator.http.HttpResponse;
 import java.net.URI;
 import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.Executor;
-import java.util.concurrent.ExecutorService;
+import javax.net.ssl.SSLContext;
+import javax.net.ServerSocketFactory;
+import javax.net.ssl.SSLServerSocketFactory;
+import jdk.incubator.http.HttpClient;
+import jdk.incubator.http.HttpClient.Version;
+import jdk.incubator.http.HttpRequest;
+import jdk.incubator.http.HttpResponse;
+import jdk.testlibrary.SimpleSSLContext;
+import static java.lang.System.out;
+import static java.lang.String.format;
 import static jdk.incubator.http.HttpResponse.BodyHandler.asString;
 
 /**
  * @test
  * @bug 8087112
- * @key intermittent
- * @build Server
- * @run main/othervm -Djava.net.HttpClient.log=all SplitResponse
+ * @library /lib/testlibrary
+ * @build jdk.testlibrary.SimpleSSLContext
+ * @build MockServer
+ * @run main/othervm -Djdk.internal.httpclient.debug=true -Djdk.httpclient.HttpClient.log=all SplitResponse
  */
 
 /**
  * Similar test to QuickResponses except that each byte of the response
  * is sent in a separate packet, which tests the stability of the implementation
- * for receiving unusual packet sizes.
+ * for receiving unusual packet sizes. Additionally, tests scenarios there
+ * connections that are retrieved from the connection pool may reach EOF before
+ * being reused.
  */
 public class SplitResponse {
 
-    static Server server;
+    static String response(String body, boolean serverKeepalive) {
+        StringBuilder sb = new StringBuilder();
+        sb.append("HTTP/1.1 200 OK\r\n");
+        if (!serverKeepalive)
+            sb.append("Connection: Close\r\n");
 
-    static String response(String body) {
-        return "HTTP/1.1 200 OK\r\nConnection: Close\r\nContent-length: "
-                + Integer.toString(body.length())
-                + "\r\n\r\n" + body;
+        sb.append("Content-length: ").append(body.length()).append("\r\n");
+        sb.append("\r\n");
+        sb.append(body);
+        return sb.toString();
     }
 
     static final String responses[] = {
@@ -68,59 +79,151 @@ public class SplitResponse {
         "Excepteur sint occaecat cupidatat non proident."
     };
 
+    final ServerSocketFactory factory;
+    final SSLContext context;
+    final boolean useSSL;
+    SplitResponse(boolean useSSL) throws IOException {
+        this.useSSL = useSSL;
+        context = new SimpleSSLContext().get();
+        SSLContext.setDefault(context);
+        factory = useSSL ? SSLServerSocketFactory.getDefault()
+                         : ServerSocketFactory.getDefault();
+    }
+
+    public HttpClient newHttpClient() {
+        HttpClient client;
+        if (useSSL) {
+            client = HttpClient.newBuilder()
+                               .sslContext(context)
+                               .build();
+        } else {
+            client = HttpClient.newHttpClient();
+        }
+        return client;
+    }
+
     public static void main(String[] args) throws Exception {
-        server = new Server(0);
+        boolean useSSL = false;
+        if (args != null && args.length == 1) {
+            useSSL = "SSL".equals(args[0]);
+        }
+        SplitResponse sp = new SplitResponse(useSSL);
+
+        for (Version version : Version.values()) {
+            for (boolean serverKeepalive : new boolean[]{ true, false }) {
+                // Note: the mock server doesn't support Keep-Alive, but
+                // pretending that it might exercises code paths in and out of
+                // the connection pool, and retry logic
+                for (boolean async : new boolean[]{ true, false }) {
+                    sp.test(version, serverKeepalive, async);
+                }
+            }
+        }
+    }
+
+    // @Test
+    void test(Version version, boolean serverKeepalive, boolean async)
+        throws Exception
+    {
+        out.println(format("*** version %s, serverKeepAlive: %s, async: %s ***",
+                           version, serverKeepalive, async));
+        MockServer server = new MockServer(0, factory);
         URI uri = new URI(server.getURL());
+        out.println("server is: " + uri);
         server.start();
 
-        HttpClient client = HttpClient.newHttpClient();
-        HttpRequest request = HttpRequest.newBuilder(uri).build();
+
+        // The following code can be uncommented to verify that the
+        // MockServer will reject rogue requests whose URI does not
+        // contain "/foo/".
+        //
+        //        Thread rogue = new Thread() {
+        //            public void run() {
+        //                try {
+        //                    HttpClient client = newHttpClient();
+        //                    URI uri2 = URI.create(uri.toString().replace("/foo/","/"));
+        //                    HttpRequest request = HttpRequest
+        //                        .newBuilder(uri2).version(version).build();
+        //                    while (true) {
+        //                        try {
+        //                            client.send(request, HttpResponse.BodyHandler.asString());
+        //                        } catch (IOException ex) {
+        //                            System.out.println("Client rejected " + request);
+        //                        }
+        //                        sleep(250);
+        //                    }
+        //                } catch ( Throwable x) {
+        //                }
+        //            }
+        //        };
+        //        rogue.setDaemon(true);
+        //        rogue.start();
+
+
+        HttpClient client = newHttpClient();
+        HttpRequest request = HttpRequest.newBuilder(uri).version(version).build();
         HttpResponse<String> r;
         CompletableFuture<HttpResponse<String>> cf1;
 
         try {
             for (int i=0; i<responses.length; i++) {
-                cf1 = client.sendAsync(request, asString());
+                out.println("----- iteration " + i + " -----");
                 String body = responses[i];
+                Thread t = sendSplitResponse(response(body, serverKeepalive), server);
 
-                Server.Connection c = server.activity();
-                sendSplitResponse(response(body), c);
-                r = cf1.get();
-                if (r.statusCode()!= 200)
+                if (async) {
+                    out.println("send async: " + request);
+                    cf1 = client.sendAsync(request, asString());
+                    r = cf1.get();
+                } else { // sync
+                    out.println("send sync: " + request);
+                    r = client.send(request, asString());
+                }
+
+                if (r.statusCode() != 200)
                     throw new RuntimeException("Failed");
 
                 String rxbody = r.body();
-                System.out.println("received " + rxbody);
+                out.println("received " + rxbody);
                 if (!rxbody.equals(body))
-                    throw new RuntimeException("Failed");
-                c.close();
+                    throw new RuntimeException(format("Expected:%s, got:%s", body, rxbody));
+
+                t.join();
+                conn.close();
             }
         } finally {
-            Executor def = client.executor();
-            if (def instanceof ExecutorService) {
-                ((ExecutorService)def).shutdownNow();
-            }
+            server.close();
         }
         System.out.println("OK");
     }
 
-    // send the response one byte at a time with a small delay between bytes
-    // to ensure that each byte is read in a separate read
-    static void sendSplitResponse(String s, Server.Connection conn) {
+    // required for cleanup
+    volatile MockServer.Connection conn;
+
+    // Sends the response, mostly, one byte at a time with a small delay
+    // between bytes, to encourage that each byte is read in a separate read
+    Thread sendSplitResponse(String s, MockServer server) {
         System.out.println("Sending: ");
         Thread t = new Thread(() -> {
+            System.out.println("Waiting for server to receive headers");
+            conn = server.activity();
+            System.out.println("Start sending response");
+
             try {
                 int len = s.length();
+                out.println("sending " + s);
                 for (int i = 0; i < len; i++) {
                     String onechar = s.substring(i, i + 1);
                     conn.send(onechar);
-                    Thread.sleep(30);
+                    Thread.sleep(10);
                 }
-                System.out.println("sent");
+                out.println("sent " + s);
             } catch (IOException | InterruptedException e) {
+                throw new RuntimeException(e);
             }
         });
         t.setDaemon(true);
         t.start();
+        return t;
     }
 }
diff --git a/test/jdk/java/net/httpclient/websocket/LoggingHelper.java b/test/jdk/java/net/httpclient/SplitResponseSSL.java
similarity index 69%
rename from test/jdk/java/net/httpclient/websocket/LoggingHelper.java
rename to test/jdk/java/net/httpclient/SplitResponseSSL.java
index 75d445c1a9d..3b0ebc151f8 100644
--- a/test/jdk/java/net/httpclient/websocket/LoggingHelper.java
+++ b/test/jdk/java/net/httpclient/SplitResponseSSL.java
@@ -21,18 +21,16 @@
  * questions.
  */
 
-import java.io.File;
-
-public final class LoggingHelper {
-
-    /*
-     * I wish we had a support for java.util.logging in jtreg similar to what we
-     * have for security policy files:
-     *
-     *     @run main/othervm/jul=logging.properties ClassUnderTest
-     */
-    public static void setupLogging() {
-        String path = System.getProperty("test.src", ".") + File.separator + "logging.properties";
-        System.setProperty("java.util.logging.config.file", path);
+/**
+ * @test
+ * @bug 8087112
+ * @library /lib/testlibrary
+ * @build jdk.testlibrary.SimpleSSLContext
+ * @build MockServer SplitResponse
+ * @run main/othervm -Djdk.internal.httpclient.debug=true -Djdk.httpclient.HttpClient.log=all SplitResponseSSL SSL
+ */
+public class SplitResponseSSL {
+    public static void main(String[] args) throws Exception {
+        SplitResponse.main(args);
     }
 }
diff --git a/test/jdk/java/net/httpclient/TestKit.java b/test/jdk/java/net/httpclient/TestKit.java
index 930e5e47395..c68d4bcdc36 100644
--- a/test/jdk/java/net/httpclient/TestKit.java
+++ b/test/jdk/java/net/httpclient/TestKit.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2017, 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
diff --git a/test/jdk/java/net/httpclient/TestKitTest.java b/test/jdk/java/net/httpclient/TestKitTest.java
index 82293c4676b..2fd084cd3a3 100644
--- a/test/jdk/java/net/httpclient/TestKitTest.java
+++ b/test/jdk/java/net/httpclient/TestKitTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2017, 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
diff --git a/test/jdk/java/net/httpclient/TimeoutBasic.java b/test/jdk/java/net/httpclient/TimeoutBasic.java
index b5dd58240ca..85e88b4a009 100644
--- a/test/jdk/java/net/httpclient/TimeoutBasic.java
+++ b/test/jdk/java/net/httpclient/TimeoutBasic.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2017, 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,66 +28,157 @@ import jdk.incubator.http.HttpClient;
 import jdk.incubator.http.HttpRequest;
 import jdk.incubator.http.HttpResponse;
 import jdk.incubator.http.HttpTimeoutException;
+import jdk.testlibrary.SimpleSSLContext;
+
+import javax.net.ServerSocketFactory;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLServerSocketFactory;
 import java.time.Duration;
+import java.util.Arrays;
 import java.util.List;
-import java.util.concurrent.*;
+import java.util.concurrent.CompletionException;
+import java.util.function.Function;
 
 import static java.lang.System.out;
 import static jdk.incubator.http.HttpResponse.BodyHandler.discard;
 
 /**
  * @test
+ * @library /lib/testlibrary
+ * @build jdk.testlibrary.SimpleSSLContext
  * @summary Basic tests for response timeouts
  * @run main/othervm TimeoutBasic
- * @ignore
  */
 
 public class TimeoutBasic {
 
-    static List<Duration> TIMEOUTS = List.of(/*Duration.ofSeconds(1),
-                                             Duration.ofMillis(100),*/
-                                             Duration.ofNanos(99)
-                                            /* Duration.ofNanos(1)*/);
+    static List<Duration> TIMEOUTS = List.of(Duration.ofSeconds(1),
+                                             Duration.ofMillis(100),
+                                             Duration.ofNanos(99),
+                                             Duration.ofNanos(1));
+
+    static final List<Function<HttpRequest.Builder, HttpRequest.Builder>> METHODS =
+            Arrays.asList(HttpRequest.Builder::GET,
+                          TimeoutBasic::DELETE,
+                          TimeoutBasic::PUT,
+                          TimeoutBasic::POST,
+                          null);
+
+    static final List<HttpClient.Version> VERSIONS =
+            Arrays.asList(HttpClient.Version.HTTP_2, HttpClient.Version.HTTP_1_1, null);
+
+    static final List<String> SCHEMES = List.of("https", "http");
+
+    static {
+        try {
+            SSLContext.setDefault(new SimpleSSLContext().get());
+        } catch (IOException x) {
+            throw new ExceptionInInitializerError(x);
+        }
+    }
 
     public static void main(String[] args) throws Exception {
-        HttpClient client = HttpClient.newHttpClient();
-        try (ServerSocket ss = new ServerSocket(0, 20)) {
+        for (Function<HttpRequest.Builder, HttpRequest.Builder> m : METHODS) {
+            for (HttpClient.Version version : List.of(HttpClient.Version.HTTP_1_1)) {
+                for (HttpClient.Version reqVersion : VERSIONS) {
+                    for (String scheme : SCHEMES) {
+                        ServerSocketFactory ssf;
+                        if (scheme.equalsIgnoreCase("https")) {
+                            ssf = SSLServerSocketFactory.getDefault();
+                        } else {
+                            ssf = ServerSocketFactory.getDefault();
+                        }
+                        test(version, reqVersion, scheme, m, ssf);
+                    }
+                }
+            }
+        }
+    }
+
+    static HttpRequest.Builder DELETE(HttpRequest.Builder builder) {
+        HttpRequest.BodyPublisher noBody = HttpRequest.BodyPublisher.noBody();
+        return builder.DELETE(noBody);
+    }
+
+    static HttpRequest.Builder PUT(HttpRequest.Builder builder) {
+        HttpRequest.BodyPublisher noBody = HttpRequest.BodyPublisher.noBody();
+        return builder.PUT(noBody);
+    }
+
+    static HttpRequest.Builder POST(HttpRequest.Builder builder) {
+        HttpRequest.BodyPublisher noBody = HttpRequest.BodyPublisher.noBody();
+        return builder.POST(noBody);
+    }
+
+    static HttpRequest newRequest(URI uri,
+                                  Duration duration,
+                                  HttpClient.Version reqVersion,
+                                  Function<HttpRequest.Builder, HttpRequest.Builder> method) {
+        HttpRequest.Builder reqBuilder = HttpRequest.newBuilder(uri)
+                .timeout(duration);
+        if (method != null) reqBuilder = method.apply(reqBuilder);
+        if (reqVersion != null) reqBuilder = reqBuilder.version(reqVersion);
+        HttpRequest request = reqBuilder.build();
+        if (duration.compareTo(Duration.ofSeconds(1)) >= 0) {
+            if (method == null || !request.method().equalsIgnoreCase("get")) {
+                out.println("Skipping " + duration + " for " + request.method());
+                return null;
+            }
+        }
+        return request;
+    }
+
+    public static void test(HttpClient.Version version,
+                            HttpClient.Version reqVersion,
+                            String scheme,
+                            Function<HttpRequest.Builder, HttpRequest.Builder> method,
+                            ServerSocketFactory ssf)
+            throws Exception
+    {
+        HttpClient.Builder builder = HttpClient.newBuilder()
+                .proxy(HttpClient.Builder.NO_PROXY);
+        if (version != null) builder.version(version);
+        HttpClient client = builder.build();
+        out.printf("%ntest(version=%s, reqVersion=%s, scheme=%s)%n", version, reqVersion, scheme);
+        try (ServerSocket ss = ssf.createServerSocket(0, 20)) {
             int port = ss.getLocalPort();
-            URI uri = new URI("http://127.0.0.1:" + port + "/");
+            URI uri = new URI(scheme +"://127.0.0.1:" + port + "/");
 
-//            out.println("--- TESTING Async");
-//            for (Duration duration : TIMEOUTS) {
-//                out.println("  with duration of " + duration);
-//                HttpRequest request = HttpRequest.newBuilder(uri)
-//                                                 .timeout(duration)
-//                                                 .GET().build();
-//                try {
-//                    HttpResponse<?> resp = client.sendAsync(request, discard(null)).join();
-//                    throw new RuntimeException("Unexpected response: " + resp.statusCode());
-//                } catch (CompletionException e) {
-//                    if (!(e.getCause() instanceof HttpTimeoutException)) {
-//                        throw new RuntimeException("Unexpected exception: " + e.getCause());
-//                    } else {
-//                        out.println("Caught expected timeout: " + e.getCause());
-//                    }
-//                }
-//            }
-
-            out.println("--- TESTING Sync");
+            out.println("--- TESTING Async");
+            int count = 0;
             for (Duration duration : TIMEOUTS) {
                 out.println("  with duration of " + duration);
-                HttpRequest request = HttpRequest.newBuilder(uri)
-                                                 .timeout(duration)
-                                                 .GET()
-                                                 .build();
+                HttpRequest request = newRequest(uri, duration, reqVersion, method);
+                if (request == null) continue;
+                count++;
+                try {
+                    HttpResponse<?> resp = client.sendAsync(request, discard(null)).join();
+                    throw new RuntimeException("Unexpected response: " + resp.statusCode());
+                } catch (CompletionException e) {
+                    if (!(e.getCause() instanceof HttpTimeoutException)) {
+                        throw new RuntimeException("Unexpected exception: " + e.getCause());
+                    } else {
+                        out.println("Caught expected timeout: " + e.getCause());
+                    }
+                }
+            }
+            assert count >= TIMEOUTS.size() -1;
+
+            out.println("--- TESTING Sync");
+            count = 0;
+            for (Duration duration : TIMEOUTS) {
+                out.println("  with duration of " + duration);
+                HttpRequest request = newRequest(uri, duration, reqVersion, method);
+                if (request == null) continue;
+                count++;
                 try {
                     client.send(request, discard(null));
                 } catch (HttpTimeoutException e) {
                     out.println("Caught expected timeout: " + e);
                 }
             }
-        } finally {
-            ((ExecutorService) client.executor()).shutdownNow();
+            assert count >= TIMEOUTS.size() -1;
+
         }
     }
 }
diff --git a/test/jdk/java/net/httpclient/TimeoutOrdering.java b/test/jdk/java/net/httpclient/TimeoutOrdering.java
index 8ead371cfa5..7505e27ecd7 100644
--- a/test/jdk/java/net/httpclient/TimeoutOrdering.java
+++ b/test/jdk/java/net/httpclient/TimeoutOrdering.java
@@ -135,8 +135,6 @@ public class TimeoutOrdering {
             if (error)
                 throw new RuntimeException("Failed. Check output");
 
-        } finally {
-            ((ExecutorService) client.executor()).shutdownNow();
         }
     }
 
diff --git a/test/jdk/java/net/httpclient/VersionTest.java b/test/jdk/java/net/httpclient/VersionTest.java
index 9b791b5782b..6433281e18a 100644
--- a/test/jdk/java/net/httpclient/VersionTest.java
+++ b/test/jdk/java/net/httpclient/VersionTest.java
@@ -42,8 +42,7 @@ import java.net.InetSocketAddress;
 import jdk.incubator.http.HttpClient;
 import jdk.incubator.http.HttpRequest;
 import jdk.incubator.http.HttpResponse;
-import static jdk.incubator.http.HttpRequest.BodyProcessor.fromString;
-import static jdk.incubator.http.HttpResponse.*;
+import static jdk.incubator.http.HttpRequest.BodyPublisher.fromString;
 import static jdk.incubator.http.HttpResponse.BodyHandler.asString;
 import static jdk.incubator.http.HttpResponse.BodyHandler.discard;
 import static jdk.incubator.http.HttpClient.Version.HTTP_1_1;
diff --git a/test/jdk/java/net/httpclient/ZeroRedirects.java b/test/jdk/java/net/httpclient/ZeroRedirects.java
new file mode 100644
index 00000000000..d8968ce742b
--- /dev/null
+++ b/test/jdk/java/net/httpclient/ZeroRedirects.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug 8164941
+ * @modules jdk.incubator.httpclient java.logging jdk.httpserver
+ * @run main/othervm ZeroRedirects
+ */
+
+import com.sun.net.httpserver.Headers;
+import com.sun.net.httpserver.HttpContext;
+import com.sun.net.httpserver.HttpExchange;
+import com.sun.net.httpserver.HttpHandler;
+import com.sun.net.httpserver.HttpServer;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.net.URI;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ExecutorService;
+import java.net.InetSocketAddress;
+import jdk.incubator.http.HttpClient;
+import jdk.incubator.http.HttpRequest;
+import jdk.incubator.http.HttpResponse;
+import static jdk.incubator.http.HttpRequest.BodyPublisher.fromString;
+import static jdk.incubator.http.HttpResponse.BodyHandler.asString;
+import static jdk.incubator.http.HttpResponse.BodyHandler.discard;
+import static jdk.incubator.http.HttpClient.Version.HTTP_1_1;
+import static jdk.incubator.http.HttpClient.Version.HTTP_2;
+
+/**
+ */
+public class ZeroRedirects {
+    static HttpServer s1 ;
+    static ExecutorService executor;
+    static int port;
+    static HttpClient client;
+    static URI uri;
+
+    public static void main(String[] args) throws Exception {
+        initServer();
+
+        client = HttpClient.newBuilder()
+                           .executor(executor)
+                           .followRedirects(HttpClient.Redirect.ALWAYS)
+                           .build();
+        try {
+            test();
+        } finally {
+            s1.stop(0);
+            executor.shutdownNow();
+        }
+    }
+
+    public static void test() throws Exception {
+        System.setProperty("java.net.httpclient.redirects.retrylimit", "0");
+        HttpRequest r = HttpRequest.newBuilder(uri)
+                .GET()
+                .build();
+        HttpResponse<Void> resp = client.send(r, discard(null));
+        System.out.printf("Client: response is %d\n", resp.statusCode());
+        if (resp.statusCode() != 200)
+            throw new RuntimeException();
+    }
+
+    static void initServer() throws Exception {
+        InetSocketAddress addr = new InetSocketAddress (0);
+        s1 = HttpServer.create (addr, 0);
+        HttpHandler h = new Handler();
+
+        HttpContext c1 = s1.createContext("/", h);
+
+        executor = Executors.newCachedThreadPool();
+        s1.setExecutor(executor);
+        s1.start();
+
+        port = s1.getAddress().getPort();
+        uri = new URI("http://127.0.0.1:" + Integer.toString(port) + "/foo");
+        System.out.println("HTTP server port = " + port);
+    }
+}
+
+class Handler implements HttpHandler {
+
+    @Override
+    public synchronized void handle(HttpExchange t)
+        throws IOException
+    {
+        String reply = "Hello world";
+        int len = reply.length();
+        System.out.printf("Sending response 200\n");
+        t.sendResponseHeaders(200, len);
+        OutputStream o = t.getResponseBody();
+        o.write(reply.getBytes());
+        t.close();
+    }
+}
diff --git a/test/jdk/java/net/httpclient/docs/files/notsobigfile.txt b/test/jdk/java/net/httpclient/docs/files/notsobigfile.txt
index bb219e18873..584209bb192 100644
--- a/test/jdk/java/net/httpclient/docs/files/notsobigfile.txt
+++ b/test/jdk/java/net/httpclient/docs/files/notsobigfile.txt
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2017, 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
diff --git a/test/jdk/java/net/httpclient/examples/WebSocketExample.java b/test/jdk/java/net/httpclient/examples/WebSocketExample.java
index 82ff0cbb462..2bb5a4f980b 100644
--- a/test/jdk/java/net/httpclient/examples/WebSocketExample.java
+++ b/test/jdk/java/net/httpclient/examples/WebSocketExample.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2017, 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
@@ -24,6 +24,8 @@
 import java.net.InetSocketAddress;
 import java.net.ProxySelector;
 import java.net.URI;
+import java.util.concurrent.CompletableFuture;
+
 import jdk.incubator.http.HttpClient;
 import jdk.incubator.http.WebSocket;
 
@@ -42,9 +44,8 @@ public class WebSocketExample {
 
     public void newBuilderExample0() {
         HttpClient client = HttpClient.newHttpClient();
-        WebSocket.Builder builder = client.newWebSocketBuilder(
-                URI.create("ws://websocket.example.com"),
-                listener);
+        CompletableFuture<WebSocket> ws = client.newWebSocketBuilder()
+                .buildAsync(URI.create("ws://websocket.example.com"), listener);
     }
 
     public void newBuilderExample1() {
@@ -52,8 +53,7 @@ public class WebSocketExample {
         HttpClient client = HttpClient.newBuilder()
                 .proxy(ProxySelector.of(addr))
                 .build();
-        WebSocket.Builder builder = client.newWebSocketBuilder(
-                URI.create("ws://websocket.example.com"),
-                listener);
+        CompletableFuture<WebSocket> ws = client.newWebSocketBuilder()
+                .buildAsync(URI.create("ws://websocket.example.com"), listener);
     }
 }
diff --git a/test/jdk/java/net/httpclient/http2/BasicTest.java b/test/jdk/java/net/httpclient/http2/BasicTest.java
index 9744eda9034..2494a6516ea 100644
--- a/test/jdk/java/net/httpclient/http2/BasicTest.java
+++ b/test/jdk/java/net/httpclient/http2/BasicTest.java
@@ -26,21 +26,27 @@
  * @bug 8087112
  * @library /lib/testlibrary server
  * @build jdk.testlibrary.SimpleSSLContext
- * @modules jdk.incubator.httpclient/jdk.incubator.http.internal.common
+ * @modules java.base/sun.net.www.http
+ *          jdk.incubator.httpclient/jdk.incubator.http.internal.common
  *          jdk.incubator.httpclient/jdk.incubator.http.internal.frame
  *          jdk.incubator.httpclient/jdk.incubator.http.internal.hpack
  * @run testng/othervm -Djdk.httpclient.HttpClient.log=ssl,requests,responses,errors BasicTest
  */
 
+import java.io.IOException;
 import java.net.*;
 import jdk.incubator.http.*;
 import static jdk.incubator.http.HttpClient.Version.HTTP_2;
 import javax.net.ssl.*;
 import java.nio.file.*;
 import java.util.concurrent.*;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
 import jdk.testlibrary.SimpleSSLContext;
-import static jdk.incubator.http.HttpRequest.BodyProcessor.fromFile;
-import static jdk.incubator.http.HttpRequest.BodyProcessor.fromString;
+import static jdk.incubator.http.HttpRequest.BodyPublisher.fromFile;
+import static jdk.incubator.http.HttpRequest.BodyPublisher.fromString;
 import static jdk.incubator.http.HttpResponse.BodyHandler.asFile;
 import static jdk.incubator.http.HttpResponse.BodyHandler.asString;
 
@@ -51,25 +57,28 @@ public class BasicTest {
     static int httpPort, httpsPort;
     static Http2TestServer httpServer, httpsServer;
     static HttpClient client = null;
-    static ExecutorService exec;
+    static ExecutorService clientExec;
+    static ExecutorService serverExec;
     static SSLContext sslContext;
 
-    static String httpURIString, httpsURIString;
+    static String pingURIString, httpURIString, httpsURIString;
 
     static void initialize() throws Exception {
         try {
             SimpleSSLContext sslct = new SimpleSSLContext();
             sslContext = sslct.get();
             client = getClient();
-            httpServer = new Http2TestServer(false, 0, exec, sslContext);
+            httpServer = new Http2TestServer(false, 0, serverExec, sslContext);
             httpServer.addHandler(new Http2EchoHandler(), "/");
+            httpServer.addHandler(new EchoWithPingHandler(), "/ping");
             httpPort = httpServer.getAddress().getPort();
 
-            httpsServer = new Http2TestServer(true, 0, exec, sslContext);
+            httpsServer = new Http2TestServer(true, 0, serverExec, sslContext);
             httpsServer.addHandler(new Http2EchoHandler(), "/");
 
             httpsPort = httpsServer.getAddress().getPort();
             httpURIString = "http://127.0.0.1:" + httpPort + "/foo/";
+            pingURIString = "http://127.0.0.1:" + httpPort + "/ping/";
             httpsURIString = "https://127.0.0.1:" + httpsPort + "/bar/";
 
             httpServer.start();
@@ -81,16 +90,48 @@ public class BasicTest {
         }
     }
 
-    @Test(timeOut=3000000)
+    static List<CompletableFuture<Long>> cfs = Collections
+        .synchronizedList( new LinkedList<>());
+
+    static CompletableFuture<Long> currentCF;
+
+    static class EchoWithPingHandler extends Http2EchoHandler {
+        private final Object lock = new Object();
+
+        @Override
+        public void handle(Http2TestExchange exchange) throws IOException {
+            // for now only one ping active at a time. don't want to saturate
+            synchronized(lock) {
+                CompletableFuture<Long> cf = currentCF;
+                if (cf == null || cf.isDone()) {
+                    cf = exchange.sendPing();
+                    assert cf != null;
+                    cfs.add(cf);
+                    currentCF = cf;
+                }
+            }
+            super.handle(exchange);
+        }
+    }
+
+    @Test
     public static void test() throws Exception {
         try {
             initialize();
-            simpleTest(false);
-            simpleTest(true);
+            warmup(false);
+            warmup(true);
+            simpleTest(false, false);
+            simpleTest(false, true);
+            simpleTest(true, false);
             streamTest(false);
             streamTest(true);
             paramsTest();
-            Thread.sleep(1000 * 4);
+            CompletableFuture.allOf(cfs.toArray(new CompletableFuture[0])).join();
+            synchronized (cfs) {
+                for (CompletableFuture<Long> cf : cfs) {
+                    System.out.printf("Ping ack received in %d millisec\n", cf.get());
+                }
+            }
         } catch (Throwable tt) {
             System.err.println("tt caught");
             tt.printStackTrace();
@@ -98,15 +139,16 @@ public class BasicTest {
         } finally {
             httpServer.stop();
             httpsServer.stop();
-            exec.shutdownNow();
+            //clientExec.shutdown();
         }
     }
 
     static HttpClient getClient() {
         if (client == null) {
-            exec = Executors.newCachedThreadPool();
+            serverExec = Executors.newCachedThreadPool();
+            clientExec = Executors.newCachedThreadPool();
             client = HttpClient.newBuilder()
-                               .executor(exec)
+                               .executor(clientExec)
                                .sslContext(sslContext)
                                .version(HTTP_2)
                                .build();
@@ -115,10 +157,14 @@ public class BasicTest {
     }
 
     static URI getURI(boolean secure) {
+        return getURI(secure, false);
+    }
+
+    static URI getURI(boolean secure, boolean ping) {
         if (secure)
             return URI.create(httpsURIString);
         else
-            return URI.create(httpURIString);
+            return URI.create(ping ? pingURIString: httpURIString);
     }
 
     static void checkStatus(int expected, int found) throws Exception {
@@ -170,12 +216,11 @@ public class BasicTest {
                 });
         response.join();
         compareFiles(src, dest);
-        System.err.println("DONE");
+        System.err.println("streamTest: DONE");
     }
 
     static void paramsTest() throws Exception {
-        Http2TestServer server = new Http2TestServer(true, 0, exec, sslContext);
-        server.addHandler((t -> {
+        httpsServer.addHandler((t -> {
             SSLSession s = t.getSSLSession();
             String prot = s.getProtocol();
             if (prot.equals("TLSv1.2")) {
@@ -185,9 +230,7 @@ public class BasicTest {
                 t.sendResponseHeaders(500, -1);
             }
         }), "/");
-        server.start();
-        int port = server.getAddress().getPort();
-        URI u = new URI("https://127.0.0.1:"+port+"/foo");
+        URI u = new URI("https://127.0.0.1:"+httpsPort+"/foo");
         HttpClient client = getClient();
         HttpRequest req = HttpRequest.newBuilder(u).build();
         HttpResponse<String> resp = client.send(req, asString());
@@ -196,9 +239,10 @@ public class BasicTest {
             throw new RuntimeException("paramsTest failed "
                 + Integer.toString(stat));
         }
+        System.err.println("paramsTest: DONE");
     }
 
-    static void simpleTest(boolean secure) throws Exception {
+    static void warmup(boolean secure) throws Exception {
         URI uri = getURI(secure);
         System.err.println("Request to " + uri);
 
@@ -209,15 +253,17 @@ public class BasicTest {
                                      .POST(fromString(SIMPLE_STRING))
                                      .build();
         HttpResponse<String> response = client.send(req, asString());
-        HttpHeaders h = response.headers();
-
         checkStatus(200, response.statusCode());
-
         String responseBody = response.body();
+        HttpHeaders h = response.headers();
         checkStrings(SIMPLE_STRING, responseBody);
-
         checkStrings(h.firstValue("x-hello").get(), "world");
         checkStrings(h.firstValue("x-bye").get(), "universe");
+    }
+
+    static void simpleTest(boolean secure, boolean ping) throws Exception {
+        URI uri = getURI(secure, ping);
+        System.err.println("Request to " + uri);
 
         // Do loops asynchronously
 
@@ -237,6 +283,6 @@ public class BasicTest {
             Thread.sleep(100);
         }
         CompletableFuture.allOf(responses).join();
-        System.err.println("DONE");
+        System.err.println("simpleTest: DONE");
     }
 }
diff --git a/test/jdk/java/net/httpclient/http2/ContinuationFrameTest.java b/test/jdk/java/net/httpclient/http2/ContinuationFrameTest.java
new file mode 100644
index 00000000000..58b353ecfb0
--- /dev/null
+++ b/test/jdk/java/net/httpclient/http2/ContinuationFrameTest.java
@@ -0,0 +1,249 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @summary Test for CONTINUATION frame handling
+ * @modules java.base/sun.net.www.http
+ *          jdk.incubator.httpclient/jdk.incubator.http.internal.common
+ *          jdk.incubator.httpclient/jdk.incubator.http.internal.frame
+ *          jdk.incubator.httpclient/jdk.incubator.http.internal.hpack
+ * @library /lib/testlibrary server
+ * @build Http2TestServer
+ * @build jdk.testlibrary.SimpleSSLContext
+ * @run testng/othervm ContinuationFrameTest
+ */
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.URI;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.BiFunction;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLSession;
+import jdk.incubator.http.HttpClient;
+import jdk.incubator.http.HttpRequest;
+import jdk.incubator.http.HttpResponse;
+import jdk.incubator.http.internal.common.HttpHeadersImpl;
+import jdk.incubator.http.internal.frame.ContinuationFrame;
+import jdk.incubator.http.internal.frame.HeaderFrame;
+import jdk.incubator.http.internal.frame.HeadersFrame;
+import jdk.incubator.http.internal.frame.Http2Frame;
+import jdk.testlibrary.SimpleSSLContext;
+import org.testng.annotations.AfterTest;
+import org.testng.annotations.BeforeTest;
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+import static java.lang.System.out;
+import static jdk.incubator.http.HttpClient.Version.HTTP_2;
+import static jdk.incubator.http.HttpRequest.BodyPublisher.fromString;
+import static jdk.incubator.http.HttpResponse.BodyHandler.asString;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertTrue;
+
+public class ContinuationFrameTest {
+
+    SSLContext sslContext;
+    Http2TestServer http2TestServer;   // HTTP/2 ( h2c )
+    Http2TestServer https2TestServer;  // HTTP/2 ( h2  )
+    String http2URI;
+    String https2URI;
+
+    /**
+     * A function that returns a list of 1) a HEADERS frame ( with an empty
+     * payload ), and 2) a CONTINUATION frame with the actual headers.
+     */
+    static BiFunction<Integer,List<ByteBuffer>,List<Http2Frame>> oneContinuation =
+        (Integer streamid, List<ByteBuffer> encodedHeaders) -> {
+            List<ByteBuffer> empty =  List.of(ByteBuffer.wrap(new byte[0]));
+            HeadersFrame hf = new HeadersFrame(streamid, 0, empty);
+            ContinuationFrame cf = new ContinuationFrame(streamid,
+                                                         HeaderFrame.END_HEADERS,
+                                                         encodedHeaders);
+            return List.of(hf, cf);
+        };
+
+    /**
+     * A function that returns a list of a HEADERS frame followed by a number of
+     * CONTINUATION frames. Each frame contains just a single byte of payload.
+     */
+    static BiFunction<Integer,List<ByteBuffer>,List<Http2Frame>> byteAtATime =
+        (Integer streamid, List<ByteBuffer> encodedHeaders) -> {
+            assert encodedHeaders.get(0).hasRemaining();
+            List<Http2Frame> frames = new ArrayList<>();
+            ByteBuffer hb = ByteBuffer.wrap(new byte[] {encodedHeaders.get(0).get()});
+            HeadersFrame hf = new HeadersFrame(streamid, 0, hb);
+            frames.add(hf);
+            for (ByteBuffer bb : encodedHeaders) {
+                while (bb.hasRemaining()) {
+                    List<ByteBuffer> data = List.of(ByteBuffer.wrap(new byte[] {bb.get()}));
+                    ContinuationFrame cf = new ContinuationFrame(streamid, 0, data);
+                    frames.add(cf);
+                }
+            }
+            frames.get(frames.size() - 1).setFlag(HeaderFrame.END_HEADERS);
+            return frames;
+        };
+
+    @DataProvider(name = "variants")
+    public Object[][] variants() {
+        return new Object[][] {
+                { http2URI,  false, oneContinuation },
+                { https2URI, false, oneContinuation },
+                { http2URI,  true,  oneContinuation },
+                { https2URI, true,  oneContinuation },
+
+                { http2URI,  false, byteAtATime },
+                { https2URI, false, byteAtATime },
+                { http2URI,  true,  byteAtATime },
+                { https2URI, true,  byteAtATime },
+        };
+    }
+
+    static final int ITERATION_COUNT = 20;
+
+    @Test(dataProvider = "variants")
+    void test(String uri,
+              boolean sameClient,
+              BiFunction<Integer,List<ByteBuffer>,List<Http2Frame>> headerFramesSupplier)
+        throws Exception
+    {
+        CFTHttp2TestExchange.setHeaderFrameSupplier(headerFramesSupplier);
+
+        HttpClient client = null;
+        for (int i=0; i< ITERATION_COUNT; i++) {
+            if (!sameClient || client == null)
+                client = HttpClient.newBuilder().sslContext(sslContext).build();
+
+            HttpRequest request = HttpRequest.newBuilder(URI.create(uri))
+                                             .POST(fromString("Hello there!"))
+                                             .build();
+            HttpResponse<String> resp;
+            if (i % 2 == 0) {
+                resp = client.send(request, asString());
+            } else {
+                resp = client.sendAsync(request, asString()).join();
+            }
+
+            out.println("Got response: " + resp);
+            out.println("Got body: " + resp.body());
+            assertTrue(resp.statusCode() == 200,
+                       "Expected 200, got:" + resp.statusCode());
+            assertEquals(resp.body(), "Hello there!");
+            assertEquals(resp.version(), HTTP_2);
+        }
+    }
+
+    @BeforeTest
+    public void setup() throws Exception {
+        sslContext = new SimpleSSLContext().get();
+        if (sslContext == null)
+            throw new AssertionError("Unexpected null sslContext");
+
+        http2TestServer = new Http2TestServer("127.0.0.1", false, 0);
+        http2TestServer.addHandler(new Http2EchoHandler(), "/http2/echo");
+        int port = http2TestServer.getAddress().getPort();
+        http2URI = "http://127.0.0.1:" + port + "/http2/echo";
+
+        https2TestServer = new Http2TestServer("127.0.0.1", true, 0);
+        https2TestServer.addHandler(new Http2EchoHandler(), "/https2/echo");
+        port = https2TestServer.getAddress().getPort();
+        https2URI = "https://127.0.0.1:" + port + "/https2/echo";
+
+        // Override the default exchange supplier with a custom one to enable
+        // particular test scenarios
+        http2TestServer.setExchangeSupplier(CFTHttp2TestExchange::new);
+        https2TestServer.setExchangeSupplier(CFTHttp2TestExchange::new);
+
+        http2TestServer.start();
+        https2TestServer.start();
+    }
+
+    @AfterTest
+    public void teardown() throws Exception {
+        http2TestServer.stop();
+        https2TestServer.stop();
+    }
+
+    static class Http2EchoHandler implements Http2Handler {
+        @Override
+        public void handle(Http2TestExchange t) throws IOException {
+            try (InputStream is = t.getRequestBody();
+                 OutputStream os = t.getResponseBody()) {
+                byte[] bytes = is.readAllBytes();
+                t.getResponseHeaders().addHeader("just some", "noise");
+                t.getResponseHeaders().addHeader("to add ", "payload in ");
+                t.getResponseHeaders().addHeader("the header", "frames");
+                t.sendResponseHeaders(200, bytes.length);
+                os.write(bytes);
+            }
+        }
+    }
+
+    // A custom Http2TestExchangeImpl that overrides sendResponseHeaders to
+    // allow headers to be sent with a number of CONTINUATION frames.
+    static class CFTHttp2TestExchange extends Http2TestExchangeImpl {
+        static volatile BiFunction<Integer,List<ByteBuffer>,List<Http2Frame>> headerFrameSupplier;
+
+        static void setHeaderFrameSupplier(BiFunction<Integer,List<ByteBuffer>,List<Http2Frame>> hfs) {
+            headerFrameSupplier = hfs;
+        }
+
+        CFTHttp2TestExchange(int streamid, String method, HttpHeadersImpl reqheaders,
+                             HttpHeadersImpl rspheaders, URI uri, InputStream is,
+                             SSLSession sslSession, BodyOutputStream os,
+                             Http2TestServerConnection conn, boolean pushAllowed) {
+            super(streamid, method, reqheaders, rspheaders, uri, is, sslSession,
+                  os, conn, pushAllowed);
+
+        }
+
+        @Override
+        public void sendResponseHeaders(int rCode, long responseLength) throws IOException {
+            this.responseLength = responseLength;
+            if (responseLength > 0 || responseLength < 0) {
+                long clen = responseLength > 0 ? responseLength : 0;
+                rspheaders.setHeader("Content-length", Long.toString(clen));
+            }
+            rspheaders.setHeader(":status", Integer.toString(rCode));
+
+            List<ByteBuffer> encodeHeaders = conn.encodeHeaders(rspheaders);
+            List<Http2Frame> headerFrames = headerFrameSupplier.apply(streamid, encodeHeaders);
+            assert headerFrames.size() > 0;  // there must always be at least 1
+
+            if (responseLength < 0) {
+                headerFrames.get(headerFrames.size() -1).setFlag(HeadersFrame.END_STREAM);
+                os.closeInternal();
+            }
+
+            for (Http2Frame f : headerFrames)
+                conn.outputQ.put(f);
+
+            os.goodToGo();
+            System.err.println("Sent response headers " + rCode);
+        }
+    }
+}
diff --git a/test/jdk/java/net/httpclient/http2/ErrorTest.java b/test/jdk/java/net/httpclient/http2/ErrorTest.java
index dd7a79761dc..921118cf60a 100644
--- a/test/jdk/java/net/httpclient/http2/ErrorTest.java
+++ b/test/jdk/java/net/httpclient/http2/ErrorTest.java
@@ -26,7 +26,8 @@
  * @bug 8157105
  * @library /lib/testlibrary server
  * @build jdk.testlibrary.SimpleSSLContext
- * @modules jdk.incubator.httpclient/jdk.incubator.http.internal.common
+ * @modules java.base/sun.net.www.http
+ *          jdk.incubator.httpclient/jdk.incubator.http.internal.common
  *          jdk.incubator.httpclient/jdk.incubator.http.internal.frame
  *          jdk.incubator.httpclient/jdk.incubator.http.internal.hpack
  *          java.security.jgss
@@ -45,7 +46,7 @@ import java.util.concurrent.Executors;
 import java.util.concurrent.ExecutorService;
 import jdk.testlibrary.SimpleSSLContext;
 import static jdk.incubator.http.HttpClient.Version.HTTP_2;
-import static jdk.incubator.http.HttpRequest.BodyProcessor.fromString;
+import static jdk.incubator.http.HttpRequest.BodyPublisher.fromString;
 import static jdk.incubator.http.HttpResponse.BodyHandler.discard;
 
 import org.testng.annotations.Test;
diff --git a/test/jdk/java/net/httpclient/http2/FixedThreadPoolTest.java b/test/jdk/java/net/httpclient/http2/FixedThreadPoolTest.java
index 36a5255c54b..3d72c581628 100644
--- a/test/jdk/java/net/httpclient/http2/FixedThreadPoolTest.java
+++ b/test/jdk/java/net/httpclient/http2/FixedThreadPoolTest.java
@@ -26,7 +26,8 @@
  * @bug 8087112 8177935
  * @library /lib/testlibrary server
  * @build jdk.testlibrary.SimpleSSLContext
- * @modules jdk.incubator.httpclient/jdk.incubator.http.internal.common
+ * @modules java.base/sun.net.www.http
+ *          jdk.incubator.httpclient/jdk.incubator.http.internal.common
  *          jdk.incubator.httpclient/jdk.incubator.http.internal.frame
  *          jdk.incubator.httpclient/jdk.incubator.http.internal.hpack
  * @run testng/othervm -Djdk.httpclient.HttpClient.log=ssl,requests,responses,errors FixedThreadPoolTest
@@ -39,8 +40,8 @@ import javax.net.ssl.*;
 import java.nio.file.*;
 import java.util.concurrent.*;
 import jdk.testlibrary.SimpleSSLContext;
-import static jdk.incubator.http.HttpRequest.BodyProcessor.fromFile;
-import static jdk.incubator.http.HttpRequest.BodyProcessor.fromString;
+import static jdk.incubator.http.HttpRequest.BodyPublisher.fromFile;
+import static jdk.incubator.http.HttpRequest.BodyPublisher.fromString;
 import static jdk.incubator.http.HttpResponse.BodyHandler.asFile;
 import static jdk.incubator.http.HttpResponse.BodyHandler.asString;
 
@@ -81,7 +82,7 @@ public class FixedThreadPoolTest {
         }
     }
 
-    @Test(timeOut=3000000)
+    @Test
     public static void test() throws Exception {
         try {
             initialize();
@@ -104,6 +105,13 @@ public class FixedThreadPoolTest {
     static HttpClient getClient() {
         if (client == null) {
             exec = Executors.newCachedThreadPool();
+            // Executor e1 = Executors.newFixedThreadPool(1);
+            // Executor e = (Runnable r) -> e1.execute(() -> {
+            //    System.out.println("[" + Thread.currentThread().getName()
+            //                       + "] Executing: "
+            //                       + r.getClass().getName());
+            //    r.run();
+            // });
             client = HttpClient.newBuilder()
                                .executor(Executors.newFixedThreadPool(2))
                                .sslContext(sslContext)
diff --git a/test/jdk/java/net/httpclient/http2/HpackDriver.java b/test/jdk/java/net/httpclient/http2/HpackBinaryTestDriver.java
similarity index 72%
rename from test/jdk/java/net/httpclient/http2/HpackDriver.java
rename to test/jdk/java/net/httpclient/http2/HpackBinaryTestDriver.java
index 11ecf83030e..7f04afe8478 100644
--- a/test/jdk/java/net/httpclient/http2/HpackDriver.java
+++ b/test/jdk/java/net/httpclient/http2/HpackBinaryTestDriver.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2017, 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
@@ -30,10 +30,5 @@
  * @compile/module=jdk.incubator.httpclient jdk/incubator/http/internal/hpack/TestHelper.java
  * @compile/module=jdk.incubator.httpclient jdk/incubator/http/internal/hpack/BuffersTestingKit.java
  * @run testng/othervm jdk.incubator.httpclient/jdk.incubator.http.internal.hpack.BinaryPrimitivesTest
- * @run testng/othervm jdk.incubator.httpclient/jdk.incubator.http.internal.hpack.CircularBufferTest
- * @run testng/othervm jdk.incubator.httpclient/jdk.incubator.http.internal.hpack.DecoderTest
- * @run testng/othervm jdk.incubator.httpclient/jdk.incubator.http.internal.hpack.EncoderTest
- * @run testng/othervm jdk.incubator.httpclient/jdk.incubator.http.internal.hpack.HuffmanTest
- * @run testng/othervm jdk.incubator.httpclient/jdk.incubator.http.internal.hpack.TestHelper
  */
-public class HpackDriver { }
+public class HpackBinaryTestDriver { }
diff --git a/test/jdk/java/net/httpclient/http2/HpackCircularBufferDriver.java b/test/jdk/java/net/httpclient/http2/HpackCircularBufferDriver.java
new file mode 100644
index 00000000000..7a9f034d437
--- /dev/null
+++ b/test/jdk/java/net/httpclient/http2/HpackCircularBufferDriver.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug 8153353
+ * @modules jdk.incubator.httpclient/jdk.incubator.http.internal.hpack
+ * @key randomness
+ * @compile/module=jdk.incubator.httpclient jdk/incubator/http/internal/hpack/SpecHelper.java
+ * @compile/module=jdk.incubator.httpclient jdk/incubator/http/internal/hpack/TestHelper.java
+ * @compile/module=jdk.incubator.httpclient jdk/incubator/http/internal/hpack/BuffersTestingKit.java
+ * @run testng/othervm jdk.incubator.httpclient/jdk.incubator.http.internal.hpack.CircularBufferTest
+ */
+public class HpackCircularBufferDriver { }
diff --git a/test/jdk/java/net/httpclient/http2/HpackDecoderDriver.java b/test/jdk/java/net/httpclient/http2/HpackDecoderDriver.java
new file mode 100644
index 00000000000..c05c3113c52
--- /dev/null
+++ b/test/jdk/java/net/httpclient/http2/HpackDecoderDriver.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug 8153353
+ * @modules jdk.incubator.httpclient/jdk.incubator.http.internal.hpack
+ * @key randomness
+ * @compile/module=jdk.incubator.httpclient jdk/incubator/http/internal/hpack/SpecHelper.java
+ * @compile/module=jdk.incubator.httpclient jdk/incubator/http/internal/hpack/TestHelper.java
+ * @compile/module=jdk.incubator.httpclient jdk/incubator/http/internal/hpack/BuffersTestingKit.java
+ * @run testng/othervm jdk.incubator.httpclient/jdk.incubator.http.internal.hpack.DecoderTest
+ */
+public class HpackDecoderDriver { }
diff --git a/test/jdk/java/net/httpclient/http2/HpackEncoderDriver.java b/test/jdk/java/net/httpclient/http2/HpackEncoderDriver.java
new file mode 100644
index 00000000000..5d4f169814f
--- /dev/null
+++ b/test/jdk/java/net/httpclient/http2/HpackEncoderDriver.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug 8153353
+ * @modules jdk.incubator.httpclient/jdk.incubator.http.internal.hpack
+ * @key randomness
+ * @compile/module=jdk.incubator.httpclient jdk/incubator/http/internal/hpack/SpecHelper.java
+ * @compile/module=jdk.incubator.httpclient jdk/incubator/http/internal/hpack/TestHelper.java
+ * @compile/module=jdk.incubator.httpclient jdk/incubator/http/internal/hpack/BuffersTestingKit.java
+ * @run testng/othervm jdk.incubator.httpclient/jdk.incubator.http.internal.hpack.EncoderTest
+ */
+public class HpackEncoderDriver { }
diff --git a/test/jdk/java/net/httpclient/http2/HpackDriverHeaderTable.java b/test/jdk/java/net/httpclient/http2/HpackHeaderTableDriver.java
similarity index 92%
rename from test/jdk/java/net/httpclient/http2/HpackDriverHeaderTable.java
rename to test/jdk/java/net/httpclient/http2/HpackHeaderTableDriver.java
index 5865852ef87..6d056bde87b 100644
--- a/test/jdk/java/net/httpclient/http2/HpackDriverHeaderTable.java
+++ b/test/jdk/java/net/httpclient/http2/HpackHeaderTableDriver.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2017, 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,4 +32,4 @@
  * @compile/module=jdk.incubator.httpclient jdk/incubator/http/internal/hpack/BuffersTestingKit.java
  * @run testng/othervm jdk.incubator.httpclient/jdk.incubator.http.internal.hpack.HeaderTableTest
  */
-public class HpackDriverHeaderTable { }
+public class HpackHeaderTableDriver { }
diff --git a/test/jdk/java/net/httpclient/http2/HpackHuffmanDriver.java b/test/jdk/java/net/httpclient/http2/HpackHuffmanDriver.java
new file mode 100644
index 00000000000..681ccf3ed1e
--- /dev/null
+++ b/test/jdk/java/net/httpclient/http2/HpackHuffmanDriver.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug 8153353
+ * @modules jdk.incubator.httpclient/jdk.incubator.http.internal.hpack
+ * @key randomness
+ * @compile/module=jdk.incubator.httpclient jdk/incubator/http/internal/hpack/SpecHelper.java
+ * @compile/module=jdk.incubator.httpclient jdk/incubator/http/internal/hpack/TestHelper.java
+ * @compile/module=jdk.incubator.httpclient jdk/incubator/http/internal/hpack/BuffersTestingKit.java
+ * @run testng/othervm jdk.incubator.httpclient/jdk.incubator.http.internal.hpack.HuffmanTest
+ */
+public class HpackHuffmanDriver { }
diff --git a/test/jdk/java/net/httpclient/http2/HpackTestHelper.java b/test/jdk/java/net/httpclient/http2/HpackTestHelper.java
new file mode 100644
index 00000000000..bc231b75f5c
--- /dev/null
+++ b/test/jdk/java/net/httpclient/http2/HpackTestHelper.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug 8153353
+ * @modules jdk.incubator.httpclient/jdk.incubator.http.internal.hpack
+ * @key randomness
+ * @compile/module=jdk.incubator.httpclient jdk/incubator/http/internal/hpack/SpecHelper.java
+ * @compile/module=jdk.incubator.httpclient jdk/incubator/http/internal/hpack/TestHelper.java
+ * @compile/module=jdk.incubator.httpclient jdk/incubator/http/internal/hpack/BuffersTestingKit.java
+ * @run testng/othervm jdk.incubator.httpclient/jdk.incubator.http.internal.hpack.TestHelper
+ */
+public class HpackTestHelperDriver { }
diff --git a/test/jdk/java/net/httpclient/http2/NoBody.java b/test/jdk/java/net/httpclient/http2/NoBody.java
deleted file mode 100644
index a9644b6c20c..00000000000
--- a/test/jdk/java/net/httpclient/http2/NoBody.java
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-
-/*
- * @test
- * @bug 8161157
- * @library /lib/testlibrary server
- * @build jdk.testlibrary.SimpleSSLContext
- * @modules jdk.incubator.httpclient/jdk.incubator.http.internal.common
- *          jdk.incubator.httpclient/jdk.incubator.http.internal.frame
- *          jdk.incubator.httpclient/jdk.incubator.http.internal.hpack
- * @run testng/othervm -Djdk.httpclient.HttpClient.log=ssl,frames,errors NoBody
- */
-
-import java.io.IOException;
-import java.net.URI;
-import jdk.incubator.http.HttpClient;
-import jdk.incubator.http.HttpRequest;
-import jdk.incubator.http.HttpResponse;
-import javax.net.ssl.SSLContext;
-import javax.net.ssl.SSLParameters;
-import java.util.concurrent.Executors;
-import java.util.concurrent.ExecutorService;
-import jdk.testlibrary.SimpleSSLContext;
-import static jdk.incubator.http.HttpClient.Version.HTTP_2;
-import static jdk.incubator.http.HttpRequest.BodyProcessor.fromString;
-import static jdk.incubator.http.HttpResponse.BodyHandler.asString;
-
-import org.testng.annotations.Test;
-
-public class NoBody {
-
-    static final String SIMPLE_STRING = "Hello world. Goodbye world";
-
-    @Test(timeOut=60000)
-    public void test() throws Exception {
-        SSLContext sslContext = (new SimpleSSLContext()).get();
-        ExecutorService exec = Executors.newCachedThreadPool();
-        HttpClient client = HttpClient.newBuilder()
-                                      .executor(exec)
-                                      .sslContext(sslContext)
-                                      .version(HTTP_2)
-                                      .build();
-
-        Http2TestServer httpsServer = null;
-        try {
-            httpsServer = new Http2TestServer(true,
-                                              0,
-                                              exec,
-                                              sslContext);
-            httpsServer.addHandler(new NoBodyHandler(), "/");
-
-            int httpsPort = httpsServer.getAddress().getPort();
-            String httpsURIString = "https://127.0.0.1:" + httpsPort + "/bar/";
-
-            httpsServer.start();
-            URI uri = URI.create(httpsURIString);
-            System.err.println("Request to " + uri);
-
-            HttpRequest req = HttpRequest.newBuilder(uri)
-                                         .PUT(fromString(SIMPLE_STRING))
-                                         .build();
-            HttpResponse<String> response = client.send(req, asString());
-            String body = response.body();
-            if (!body.equals(""))
-                throw new RuntimeException("expected empty body");
-            System.err.println("DONE");
-        } finally {
-            if (httpsServer != null )  { httpsServer.stop(); }
-            exec.shutdownNow();
-        }
-    }
-}
diff --git a/test/jdk/java/net/httpclient/http2/ProxyTest2.java b/test/jdk/java/net/httpclient/http2/ProxyTest2.java
index 3a06bd304e1..b00e182afb9 100644
--- a/test/jdk/java/net/httpclient/http2/ProxyTest2.java
+++ b/test/jdk/java/net/httpclient/http2/ProxyTest2.java
@@ -62,7 +62,8 @@ import java.util.concurrent.*;
  *           tunnelling through an HTTP/1.1 proxy.
  * @modules jdk.incubator.httpclient
  * @library /lib/testlibrary server
- * @modules jdk.incubator.httpclient/jdk.incubator.http.internal.common
+ * @modules java.base/sun.net.www.http
+ *          jdk.incubator.httpclient/jdk.incubator.http.internal.common
  *          jdk.incubator.httpclient/jdk.incubator.http.internal.frame
  *          jdk.incubator.httpclient/jdk.incubator.http.internal.hpack
  * @build jdk.testlibrary.SimpleSSLContext ProxyTest2
diff --git a/test/jdk/java/net/httpclient/http2/RedirectTest.java b/test/jdk/java/net/httpclient/http2/RedirectTest.java
index cf0278585f3..8a5130bd375 100644
--- a/test/jdk/java/net/httpclient/http2/RedirectTest.java
+++ b/test/jdk/java/net/httpclient/http2/RedirectTest.java
@@ -24,36 +24,34 @@
 /*
  * @test
  * @bug 8156514
- * @key intermittent
  * @library /lib/testlibrary server
  * @build jdk.testlibrary.SimpleSSLContext
- * @modules jdk.incubator.httpclient/jdk.incubator.http.internal.common
- * @modules jdk.incubator.httpclient/jdk.incubator.http.internal.frame
- * @modules jdk.incubator.httpclient/jdk.incubator.http.internal.hpack
+ * @modules java.base/sun.net.www.http
+ *          jdk.incubator.httpclient/jdk.incubator.http.internal.common
+ *          jdk.incubator.httpclient/jdk.incubator.http.internal.frame
+ *          jdk.incubator.httpclient/jdk.incubator.http.internal.hpack
  * @run testng/othervm -Djdk.httpclient.HttpClient.log=frames,ssl,requests,responses,errors RedirectTest
  */
 
 import java.net.*;
 import jdk.incubator.http.*;
-import static jdk.incubator.http.HttpClient.Version.HTTP_2;
-import java.nio.file.*;
+import java.util.Optional;
 import java.util.concurrent.*;
 import java.util.function.*;
 import java.util.Arrays;
 import java.util.Iterator;
-import static jdk.incubator.http.HttpRequest.BodyProcessor.fromString;
+import org.testng.annotations.Test;
+import static jdk.incubator.http.HttpClient.Version.HTTP_2;
+import static jdk.incubator.http.HttpRequest.BodyPublisher.fromString;
 import static jdk.incubator.http.HttpResponse.BodyHandler.asString;
 
-import org.testng.annotations.Test;
-
-@Test
 public class RedirectTest {
-    static int httpPort, altPort;
-    static Http2TestServer httpServer, altServer;
+    static int httpPort;
+    static Http2TestServer httpServer;
     static HttpClient client;
-    static ExecutorService exec;
 
     static String httpURIString, altURIString1, altURIString2;
+    static URI httpURI, altURI1, altURI2;
 
     static Supplier<String> sup(String... args) {
         Iterator<String> i = Arrays.asList(args).iterator();
@@ -61,28 +59,56 @@ public class RedirectTest {
         return () -> i.next();
     }
 
+    static class Redirector extends Http2RedirectHandler {
+        private InetSocketAddress remoteAddr;
+        private boolean error = false;
+
+        Redirector(Supplier<String> supplier) {
+            super(supplier);
+        }
+
+        protected synchronized void examineExchange(Http2TestExchange ex) {
+            InetSocketAddress addr = ex.getRemoteAddress();
+            if (remoteAddr == null) {
+                remoteAddr = addr;
+                return;
+            }
+            // check that the client addr/port stays the same, proving
+            // that the connection didn't get dropped.
+            if (!remoteAddr.equals(addr)) {
+                System.err.printf("Error %s/%s\n", remoteAddr.toString(),
+                        addr.toString());
+                error = true;
+            }
+        }
+
+        public synchronized boolean error() {
+            return error;
+        }
+    }
+
     static void initialize() throws Exception {
         try {
             client = getClient();
-            httpServer = new Http2TestServer(false, 0, exec, null);
-
+            httpServer = new Http2TestServer(false, 0, null, null);
             httpPort = httpServer.getAddress().getPort();
-            altServer = new Http2TestServer(false, 0, exec, null);
-            altPort = altServer.getAddress().getPort();
 
-            // urls are accessed in sequence below
-            // first two on different servers. Third on same server
-            // as second. So, the client should use the same http connection
+            // urls are accessed in sequence below. The first two are on
+            // different servers. Third on same server as second. So, the
+            // client should use the same http connection.
             httpURIString = "http://127.0.0.1:" + httpPort + "/foo/";
-            altURIString1 = "http://127.0.0.1:" + altPort + "/redir";
-            altURIString2 = "http://127.0.0.1:" + altPort + "/redir/again";
+            httpURI = URI.create(httpURIString);
+            altURIString1 = "http://127.0.0.1:" + httpPort + "/redir";
+            altURI1 = URI.create(altURIString1);
+            altURIString2 = "http://127.0.0.1:" + httpPort + "/redir_again";
+            altURI2 = URI.create(altURIString2);
 
-            httpServer.addHandler(new RedirectHandler(sup(altURIString1)), "/foo");
-            altServer.addHandler(new RedirectHandler(sup(altURIString2)), "/redir");
-            altServer.addHandler(new Http2EchoHandler(), "/redir/again");
+            Redirector r = new Redirector(sup(altURIString1, altURIString2));
+            httpServer.addHandler(r, "/foo");
+            httpServer.addHandler(r, "/redir");
+            httpServer.addHandler(new Http2EchoHandler(), "/redir_again");
 
             httpServer.start();
-            altServer.start();
         } catch (Throwable e) {
             System.err.println("Throwing now");
             e.printStackTrace();
@@ -90,26 +116,19 @@ public class RedirectTest {
         }
     }
 
-    @Test(timeOut=3000000)
+    @Test
     public static void test() throws Exception {
         try {
             initialize();
             simpleTest();
-        } catch (Throwable tt) {
-            System.err.println("tt caught");
-            tt.printStackTrace();
         } finally {
             httpServer.stop();
-            altServer.stop();
-            exec.shutdownNow();
         }
     }
 
     static HttpClient getClient() {
         if (client == null) {
-            exec = Executors.newCachedThreadPool();
             client = HttpClient.newBuilder()
-                               .executor(exec)
                                .followRedirects(HttpClient.Redirect.ALWAYS)
                                .version(HTTP_2)
                                .build();
@@ -129,6 +148,15 @@ public class RedirectTest {
         }
     }
 
+    static void checkURIs(URI expected, URI found) throws Exception {
+        System.out.printf ("Expected: %s, Found: %s\n", expected.toString(), found.toString());
+        if (!expected.equals(found)) {
+            System.err.printf ("Test failed: wrong URI %s/%s\n",
+                expected.toString(), found.toString());
+            throw new RuntimeException("Test failed");
+        }
+    }
+
     static void checkStrings(String expected, String found) throws Exception {
         if (!expected.equals(found)) {
             System.err.printf ("Test failed: wrong string %s/%s\n",
@@ -137,18 +165,17 @@ public class RedirectTest {
         }
     }
 
-    static Void compareFiles(Path path1, Path path2) {
-        return TestUtil.compareFiles(path1, path2);
-    }
-
-    static Path tempFile() {
-        return TestUtil.tempFile();
+    static void check(boolean cond, Object... msg) {
+        if (cond)
+            return;
+        StringBuilder sb = new StringBuilder();
+        for (Object o : msg)
+            sb.append(o);
+        throw new RuntimeException(sb.toString());
     }
 
     static final String SIMPLE_STRING = "Hello world Goodbye world";
 
-    static final int FILESIZE = 64 * 1024 + 200;
-
     static void simpleTest() throws Exception {
         URI uri = getURI();
         System.err.println("Request to " + uri);
@@ -163,8 +190,44 @@ public class RedirectTest {
         checkStatus(200, response.statusCode());
         String responseBody = response.body();
         checkStrings(SIMPLE_STRING, responseBody);
+        checkURIs(response.uri(), altURI2);
+
+        // check two previous responses
+        HttpResponse<String> prev = response.previousResponse()
+            .orElseThrow(() -> new RuntimeException("no previous response"));
+        checkURIs(prev.uri(), altURI1);
+
+        prev = prev.previousResponse()
+            .orElseThrow(() -> new RuntimeException("no previous response"));
+        checkURIs(prev.uri(), httpURI);
+
+        checkPreviousRedirectResponses(req, response);
 
         System.err.println("DONE");
-        Thread.sleep (6000);
+    }
+
+    static void checkPreviousRedirectResponses(HttpRequest initialRequest,
+                                               HttpResponse<?> finalResponse) {
+        // there must be at least one previous response
+        finalResponse.previousResponse()
+                .orElseThrow(() -> new RuntimeException("no previous response"));
+
+        HttpResponse<?> response = finalResponse;
+        do {
+            URI uri = response.uri();
+            response = response.previousResponse().get();
+            check(300 <= response.statusCode() && response.statusCode() <= 309,
+                    "Expected 300 <= code <= 309, got:" + response.statusCode());
+            check(response.body() == null, "Unexpected body: " + response.body());
+            String locationHeader = response.headers().firstValue("Location")
+                    .orElseThrow(() -> new RuntimeException("no previous Location"));
+            check(uri.toString().endsWith(locationHeader),
+                    "URI: " + uri + ", Location: " + locationHeader);
+        } while (response.previousResponse().isPresent());
+
+        // initial
+        check(initialRequest.equals(response.request()),
+                "Expected initial request [%s] to equal last prev req [%s]",
+                initialRequest, response.request());
     }
 }
diff --git a/test/jdk/java/net/httpclient/http2/ServerPush.java b/test/jdk/java/net/httpclient/http2/ServerPush.java
index 85ef2e1736a..63079b12019 100644
--- a/test/jdk/java/net/httpclient/http2/ServerPush.java
+++ b/test/jdk/java/net/httpclient/http2/ServerPush.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2017, 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
@@ -26,10 +26,11 @@
  * @bug 8087112 8159814
  * @library /lib/testlibrary server
  * @build jdk.testlibrary.SimpleSSLContext
- * @modules jdk.incubator.httpclient/jdk.incubator.http.internal.common
+ * @modules java.base/sun.net.www.http
+ *          jdk.incubator.httpclient/jdk.incubator.http.internal.common
  *          jdk.incubator.httpclient/jdk.incubator.http.internal.frame
  *          jdk.incubator.httpclient/jdk.incubator.http.internal.hpack
- * @run testng/othervm -Djdk.httpclient.HttpClient.log=errors,requests,responses ServerPush
+ * @run testng/othervm -Djdk.internal.httpclient.hpack.debug=true -Djdk.internal.httpclient.debug=true -Djdk.httpclient.HttpClient.log=errors,requests,responses ServerPush
  */
 
 import java.io.*;
@@ -37,7 +38,7 @@ import java.net.*;
 import java.nio.file.*;
 import java.nio.file.attribute.*;
 import jdk.incubator.http.*;
-import jdk.incubator.http.HttpResponse.MultiProcessor;
+import jdk.incubator.http.HttpResponse.MultiSubscriber;
 import jdk.incubator.http.HttpResponse.BodyHandler;
 import java.util.*;
 import java.util.concurrent.*;
@@ -52,7 +53,7 @@ public class ServerPush {
 
     static Path tempFile;
 
-    @Test(timeOut=30000)
+    @Test
     public static void test() throws Exception {
         Http2TestServer server = null;
         final Path dir = Files.createTempDirectory("serverPush");
@@ -72,7 +73,7 @@ public class ServerPush {
             CompletableFuture<MultiMapResult<Path>> cf =
                 HttpClient.newBuilder().version(HttpClient.Version.HTTP_2)
                     .executor(e).build().sendAsync(
-                        request, MultiProcessor.asMap((req) -> {
+                        request, MultiSubscriber.asMap((req) -> {
                             URI u = req.uri();
                             Path path = Paths.get(dir.toString(), u.getPath());
                             try {
diff --git a/test/jdk/java/net/httpclient/http2/TLSConnection.java b/test/jdk/java/net/httpclient/http2/TLSConnection.java
index 55bccc0b093..0e816d063b8 100644
--- a/test/jdk/java/net/httpclient/http2/TLSConnection.java
+++ b/test/jdk/java/net/httpclient/http2/TLSConnection.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2017, 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
@@ -29,19 +29,19 @@ import java.net.URI;
 import java.net.URISyntaxException;
 import jdk.incubator.http.HttpClient;
 import jdk.incubator.http.HttpRequest;
-import jdk.incubator.http.HttpResponse;
+
 import javax.net.ssl.SSLParameters;
 import javax.net.ssl.SSLSession;
-import static jdk.incubator.http.HttpRequest.BodyProcessor.fromString;
+import static jdk.incubator.http.HttpRequest.BodyPublisher.fromString;
 import static jdk.incubator.http.HttpResponse.BodyHandler.asString;
 
 /*
  * @test
  * @bug 8150769 8157107
- * @key intermittent
  * @library server
  * @summary Checks that SSL parameters can be set for HTTP/2 connection
- * @modules jdk.incubator.httpclient/jdk.incubator.http.internal.common
+ * @modules java.base/sun.net.www.http
+ *          jdk.incubator.httpclient/jdk.incubator.http.internal.common
  *          jdk.incubator.httpclient/jdk.incubator.http.internal.frame
  *          jdk.incubator.httpclient/jdk.incubator.http.internal.hpack
  * @run main/othervm TLSConnection
diff --git a/test/jdk/java/net/httpclient/http2/Timeout.java b/test/jdk/java/net/httpclient/http2/Timeout.java
index 04f0ca93c85..178e80f6363 100644
--- a/test/jdk/java/net/httpclient/http2/Timeout.java
+++ b/test/jdk/java/net/httpclient/http2/Timeout.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2017, 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
@@ -29,13 +29,12 @@ import jdk.incubator.http.HttpRequest;
 import jdk.incubator.http.HttpResponse;
 import jdk.incubator.http.HttpTimeoutException;
 import java.time.Duration;
-import java.util.concurrent.TimeUnit;
 import java.util.concurrent.CompletionException;
 import javax.net.ssl.SSLServerSocket;
 import javax.net.ssl.SSLParameters;
 import javax.net.ssl.SSLServerSocketFactory;
 import javax.net.ssl.SSLSocket;
-import static jdk.incubator.http.HttpRequest.BodyProcessor.fromString;
+import static jdk.incubator.http.HttpRequest.BodyPublisher.fromString;
 import static jdk.incubator.http.HttpResponse.BodyHandler.asString;
 
 /*
diff --git a/test/jdk/java/net/httpclient/http2/jdk.incubator.httpclient/jdk/incubator/http/internal/hpack/BinaryPrimitivesTest.java b/test/jdk/java/net/httpclient/http2/jdk.incubator.httpclient/jdk/incubator/http/internal/hpack/BinaryPrimitivesTest.java
index 429685bcef0..ee8c99c0887 100644
--- a/test/jdk/java/net/httpclient/http2/jdk.incubator.httpclient/jdk/incubator/http/internal/hpack/BinaryPrimitivesTest.java
+++ b/test/jdk/java/net/httpclient/http2/jdk.incubator.httpclient/jdk/incubator/http/internal/hpack/BinaryPrimitivesTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2014, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2014, 2017, 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
@@ -24,6 +24,8 @@ package jdk.incubator.http.internal.hpack;
 
 import org.testng.annotations.Test;
 
+import java.io.IOException;
+import java.io.UncheckedIOException;
 import java.nio.ByteBuffer;
 import java.nio.CharBuffer;
 import java.nio.charset.StandardCharsets;
@@ -80,7 +82,7 @@ public final class BinaryPrimitivesTest {
     // for all x: readInteger(writeInteger(x)) == x
     //
     @Test
-    public void integerIdentity() {
+    public void integerIdentity() throws IOException {
         final int MAX_VALUE = 1 << 22;
         int totalCases = 0;
         int maxFilling = 0;
@@ -119,7 +121,11 @@ public final class BinaryPrimitivesTest {
                         Iterable<? extends ByteBuffer> buf = relocateBuffers(injectEmptyBuffers(buffers));
                         r.configure(N);
                         for (ByteBuffer b : buf) {
-                            r.read(b);
+                            try {
+                                r.read(b);
+                            } catch (IOException e) {
+                                throw new UncheckedIOException(e);
+                            }
                         }
                         assertEquals(r.get(), expected);
                         r.reset();
@@ -155,7 +161,11 @@ public final class BinaryPrimitivesTest {
                         if (!written) {
                             fail("please increase bb size");
                         }
-                        r.configure(N).read(concat(buf));
+                        try {
+                            r.configure(N).read(concat(buf));
+                        } catch (IOException e) {
+                            throw new UncheckedIOException(e);
+                        }
                         // TODO: check payload here
                         assertEquals(r.get(), expected);
                         w.reset();
@@ -172,7 +182,7 @@ public final class BinaryPrimitivesTest {
     // for all x: readString(writeString(x)) == x
     //
     @Test
-    public void stringIdentity() {
+    public void stringIdentity() throws IOException {
         final int MAX_STRING_LENGTH = 4096;
         ByteBuffer bytes = ByteBuffer.allocate(MAX_STRING_LENGTH + 6); // it takes 6 bytes to encode string length of Integer.MAX_VALUE
         CharBuffer chars = CharBuffer.allocate(MAX_STRING_LENGTH);
@@ -241,7 +251,11 @@ public final class BinaryPrimitivesTest {
                 if (!written) {
                     fail("please increase 'bytes' size");
                 }
-                reader.read(concat(buffers), chars);
+                try {
+                    reader.read(concat(buffers), chars);
+                } catch (IOException e) {
+                    throw new UncheckedIOException(e);
+                }
                 chars.flip();
                 assertEquals(chars.toString(), expected);
                 reader.reset();
@@ -279,7 +293,11 @@ public final class BinaryPrimitivesTest {
             forEachSplit(bytes, (buffers) -> {
                 for (ByteBuffer buf : buffers) {
                     int p0 = buf.position();
-                    reader.read(buf, chars);
+                    try {
+                        reader.read(buf, chars);
+                    } catch (IOException e) {
+                        throw new UncheckedIOException(e);
+                    }
                     buf.position(p0);
                 }
                 chars.flip();
@@ -333,7 +351,11 @@ public final class BinaryPrimitivesTest {
     private static void verifyRead(byte[] data, int expected, int N) {
         ByteBuffer buf = ByteBuffer.wrap(data, 0, data.length);
         IntegerReader reader = new IntegerReader();
-        reader.configure(N).read(buf);
+        try {
+            reader.configure(N).read(buf);
+        } catch (IOException e) {
+            throw new UncheckedIOException(e);
+        }
         assertEquals(expected, reader.get());
     }
 
diff --git a/test/jdk/java/net/httpclient/http2/jdk.incubator.httpclient/jdk/incubator/http/internal/hpack/BuffersTestingKit.java b/test/jdk/java/net/httpclient/http2/jdk.incubator.httpclient/jdk/incubator/http/internal/hpack/BuffersTestingKit.java
index 3187a329265..0b4165eb11a 100644
--- a/test/jdk/java/net/httpclient/http2/jdk.incubator.httpclient/jdk/incubator/http/internal/hpack/BuffersTestingKit.java
+++ b/test/jdk/java/net/httpclient/http2/jdk.incubator.httpclient/jdk/incubator/http/internal/hpack/BuffersTestingKit.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2017, 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
diff --git a/test/jdk/java/net/httpclient/http2/jdk.incubator.httpclient/jdk/incubator/http/internal/hpack/CircularBufferTest.java b/test/jdk/java/net/httpclient/http2/jdk.incubator.httpclient/jdk/incubator/http/internal/hpack/CircularBufferTest.java
index 0e2e6d31c9c..6cbfc0b7a3d 100644
--- a/test/jdk/java/net/httpclient/http2/jdk.incubator.httpclient/jdk/incubator/http/internal/hpack/CircularBufferTest.java
+++ b/test/jdk/java/net/httpclient/http2/jdk.incubator.httpclient/jdk/incubator/http/internal/hpack/CircularBufferTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2017, 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
diff --git a/test/jdk/java/net/httpclient/http2/jdk.incubator.httpclient/jdk/incubator/http/internal/hpack/DecoderTest.java b/test/jdk/java/net/httpclient/http2/jdk.incubator.httpclient/jdk/incubator/http/internal/hpack/DecoderTest.java
index 79645730759..155603f241a 100644
--- a/test/jdk/java/net/httpclient/http2/jdk.incubator.httpclient/jdk/incubator/http/internal/hpack/DecoderTest.java
+++ b/test/jdk/java/net/httpclient/http2/jdk.incubator.httpclient/jdk/incubator/http/internal/hpack/DecoderTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2017, 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
@@ -24,8 +24,8 @@ package jdk.incubator.http.internal.hpack;
 
 import org.testng.annotations.Test;
 
+import java.io.IOException;
 import java.io.UncheckedIOException;
-import java.net.ProtocolException;
 import java.nio.ByteBuffer;
 import java.util.Iterator;
 import java.util.LinkedList;
@@ -400,7 +400,7 @@ public final class DecoderTest {
     // This test is missing in the spec
     //
     @Test
-    public void sizeUpdate() {
+    public void sizeUpdate() throws IOException {
         Decoder d = new Decoder(4096);
         assertEquals(d.getTable().maxSize(), 4096);
         d.decode(ByteBuffer.wrap(new byte[]{0b00111110}), true, nopCallback()); // newSize = 30
@@ -421,20 +421,14 @@ public final class DecoderTest {
         b.flip();
         {
             Decoder d = new Decoder(4096);
-            UncheckedIOException ex = assertVoidThrows(UncheckedIOException.class,
+            assertVoidThrows(IOException.class,
                     () -> d.decode(b, true, (name, value) -> { }));
-
-            assertNotNull(ex.getCause());
-            assertEquals(ex.getCause().getClass(), ProtocolException.class);
         }
         b.flip();
         {
             Decoder d = new Decoder(4096);
-            UncheckedIOException ex = assertVoidThrows(UncheckedIOException.class,
+            assertVoidThrows(IOException.class,
                     () -> d.decode(b, false, (name, value) -> { }));
-
-            assertNotNull(ex.getCause());
-            assertEquals(ex.getCause().getClass(), ProtocolException.class);
         }
     }
 
@@ -445,10 +439,8 @@ public final class DecoderTest {
                 (byte) 0b11111111, // indexed
                 (byte) 0b10011010  // 25 + ...
         });
-        UncheckedIOException e = assertVoidThrows(UncheckedIOException.class,
+        IOException e = assertVoidThrows(IOException.class,
                 () -> d.decode(data, true, nopCallback()));
-        assertNotNull(e.getCause());
-        assertEquals(e.getCause().getClass(), ProtocolException.class);
         assertExceptionMessageContains(e, "Unexpected end of header block");
     }
 
@@ -471,10 +463,10 @@ public final class DecoderTest {
                 (byte) 0b00000111
         });
 
-        IllegalArgumentException e = assertVoidThrows(IllegalArgumentException.class,
+        IOException e = assertVoidThrows(IOException.class,
                 () -> d.decode(data, true, nopCallback()));
 
-        assertExceptionMessageContains(e, "index=2147483647");
+        assertExceptionMessageContains(e.getCause(), "index=2147483647");
     }
 
     @Test
@@ -490,7 +482,7 @@ public final class DecoderTest {
                 (byte) 0b00000111
         });
 
-        IllegalArgumentException e = assertVoidThrows(IllegalArgumentException.class,
+        IOException e = assertVoidThrows(IOException.class,
                 () -> d.decode(data, true, nopCallback()));
 
         assertExceptionMessageContains(e, "Integer overflow");
@@ -507,10 +499,8 @@ public final class DecoderTest {
                 0b00000000, //  but only 3 octets available...
                 0b00000000  // /
         });
-        UncheckedIOException e = assertVoidThrows(UncheckedIOException.class,
+        IOException e = assertVoidThrows(IOException.class,
                 () -> d.decode(data, true, nopCallback()));
-        assertNotNull(e.getCause());
-        assertEquals(e.getCause().getClass(), ProtocolException.class);
         assertExceptionMessageContains(e, "Unexpected end of header block");
     }
 
@@ -527,10 +517,8 @@ public final class DecoderTest {
                 0b00000000, //  /
                 0b00000000  // /
         });
-        UncheckedIOException e = assertVoidThrows(UncheckedIOException.class,
+        IOException e = assertVoidThrows(IOException.class,
                 () -> d.decode(data, true, nopCallback()));
-        assertNotNull(e.getCause());
-        assertEquals(e.getCause().getClass(), ProtocolException.class);
         assertExceptionMessageContains(e, "Unexpected end of header block");
     }
 
@@ -547,7 +535,7 @@ public final class DecoderTest {
                 0b00011001, 0b01001101, (byte) 0b11111111,
                 (byte) 0b11111111, (byte) 0b11111111, (byte) 0b11111100
         });
-        IllegalArgumentException e = assertVoidThrows(IllegalArgumentException.class,
+        IOException e = assertVoidThrows(IOException.class,
                 () -> d.decode(data, true, nopCallback()));
 
         assertExceptionMessageContains(e, "Encountered EOS");
@@ -566,7 +554,7 @@ public final class DecoderTest {
                 0b00011001, 0b01001101, (byte) 0b11111111
                 // len("aei") + len(padding) = (5 + 5 + 5) + (9)
         });
-        IllegalArgumentException e = assertVoidThrows(IllegalArgumentException.class,
+        IOException e = assertVoidThrows(IOException.class,
                 () -> d.decode(data, true, nopCallback()));
 
         assertExceptionMessageContains(e, "Padding is too long", "len=9");
@@ -597,7 +585,7 @@ public final class DecoderTest {
                 (byte) 0b10000011, // huffman=true, length=3
                 0b00011001, 0b01111010, (byte) 0b11111110
         });
-        IllegalArgumentException e = assertVoidThrows(IllegalArgumentException.class,
+        IOException e = assertVoidThrows(IOException.class,
                 () -> d.decode(data, true, nopCallback()));
 
         assertExceptionMessageContains(e, "Not a EOS prefix");
@@ -648,13 +636,17 @@ public final class DecoderTest {
             Decoder d = supplier.get();
             do {
                 ByteBuffer n = i.next();
-                d.decode(n, !i.hasNext(), (name, value) -> {
-                    if (value == null) {
-                        actual.add(name.toString());
-                    } else {
-                        actual.add(name + ": " + value);
-                    }
-                });
+                try {
+                    d.decode(n, !i.hasNext(), (name, value) -> {
+                        if (value == null) {
+                            actual.add(name.toString());
+                        } else {
+                            actual.add(name + ": " + value);
+                        }
+                    });
+                } catch (IOException e) {
+                    throw new UncheckedIOException(e);
+                }
             } while (i.hasNext());
             assertEquals(d.getTable().getStateString(), expectedHeaderTable);
             assertEquals(actual.stream().collect(Collectors.joining("\n")), expectedHeaderList);
@@ -671,13 +663,17 @@ public final class DecoderTest {
         ByteBuffer source = SpecHelper.toBytes(hexdump);
 
         List<String> actual = new LinkedList<>();
-        d.decode(source, true, (name, value) -> {
-            if (value == null) {
-                actual.add(name.toString());
-            } else {
-                actual.add(name + ": " + value);
-            }
-        });
+        try {
+            d.decode(source, true, (name, value) -> {
+                if (value == null) {
+                    actual.add(name.toString());
+                } else {
+                    actual.add(name + ": " + value);
+                }
+            });
+        } catch (IOException e) {
+            throw new UncheckedIOException(e);
+        }
 
         assertEquals(d.getTable().getStateString(), expectedHeaderTable);
         assertEquals(actual.stream().collect(Collectors.joining("\n")), expectedHeaderList);
diff --git a/test/jdk/java/net/httpclient/http2/jdk.incubator.httpclient/jdk/incubator/http/internal/hpack/EncoderTest.java b/test/jdk/java/net/httpclient/http2/jdk.incubator.httpclient/jdk/incubator/http/internal/hpack/EncoderTest.java
index 50b07a63e52..fa2ce93e32f 100644
--- a/test/jdk/java/net/httpclient/http2/jdk.incubator.httpclient/jdk/incubator/http/internal/hpack/EncoderTest.java
+++ b/test/jdk/java/net/httpclient/http2/jdk.incubator.httpclient/jdk/incubator/http/internal/hpack/EncoderTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2014, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2014, 2017, 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
@@ -24,6 +24,7 @@ package jdk.incubator.http.internal.hpack;
 
 import org.testng.annotations.Test;
 
+import java.io.IOException;
 import java.nio.Buffer;
 import java.nio.ByteBuffer;
 import java.util.ArrayList;
@@ -520,7 +521,7 @@ public final class EncoderTest {
     }
 
     @Test
-    public void initialSizeUpdateDefaultEncoder() {
+    public void initialSizeUpdateDefaultEncoder() throws IOException {
         Function<Integer, Encoder> e = Encoder::new;
         testSizeUpdate(e, 1024, asList(), asList(0));
         testSizeUpdate(e, 1024, asList(1024), asList(0));
@@ -531,7 +532,7 @@ public final class EncoderTest {
     }
 
     @Test
-    public void initialSizeUpdateCustomEncoder() {
+    public void initialSizeUpdateCustomEncoder() throws IOException {
         Function<Integer, Encoder> e = EncoderTest::newCustomEncoder;
         testSizeUpdate(e, 1024, asList(), asList(1024));
         testSizeUpdate(e, 1024, asList(1024), asList(1024));
@@ -542,7 +543,7 @@ public final class EncoderTest {
     }
 
     @Test
-    public void seriesOfSizeUpdatesDefaultEncoder() {
+    public void seriesOfSizeUpdatesDefaultEncoder() throws IOException {
         Function<Integer, Encoder> e = c -> {
             Encoder encoder = new Encoder(c);
             drainInitialUpdate(encoder);
@@ -563,7 +564,7 @@ public final class EncoderTest {
     // https://tools.ietf.org/html/rfc7541#section-4.2
     //
     @Test
-    public void seriesOfSizeUpdatesCustomEncoder() {
+    public void seriesOfSizeUpdatesCustomEncoder() throws IOException {
         Function<Integer, Encoder> e = c -> {
             Encoder encoder = newCustomEncoder(c);
             drainInitialUpdate(encoder);
@@ -638,7 +639,7 @@ public final class EncoderTest {
     private void testSizeUpdate(Function<Integer, Encoder> encoder,
                                 int initialSize,
                                 List<Integer> updates,
-                                List<Integer> expected) {
+                                List<Integer> expected) throws IOException {
         Encoder e = encoder.apply(initialSize);
         updates.forEach(e::setMaxCapacity);
         ByteBuffer b = ByteBuffer.allocate(64);
diff --git a/test/jdk/java/net/httpclient/http2/jdk.incubator.httpclient/jdk/incubator/http/internal/hpack/HeaderTableTest.java b/test/jdk/java/net/httpclient/http2/jdk.incubator.httpclient/jdk/incubator/http/internal/hpack/HeaderTableTest.java
index 4d0e08fc20f..6018e5859c8 100644
--- a/test/jdk/java/net/httpclient/http2/jdk.incubator.httpclient/jdk/incubator/http/internal/hpack/HeaderTableTest.java
+++ b/test/jdk/java/net/httpclient/http2/jdk.incubator.httpclient/jdk/incubator/http/internal/hpack/HeaderTableTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2014, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2014, 2017, 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
@@ -116,7 +116,7 @@ public class HeaderTableTest {
 
     @Test
     public void staticData() {
-        HeaderTable table = new HeaderTable(0);
+        HeaderTable table = new HeaderTable(0, HPACK.getLogger());
         Map<Integer, HeaderField> staticHeaderFields = createStaticEntries();
 
         Map<String, Integer> minimalIndexes = new HashMap<>();
@@ -159,7 +159,7 @@ public class HeaderTableTest {
     @Test
     public void constructorSetsMaxSize() {
         int size = rnd.nextInt(64);
-        HeaderTable t = new HeaderTable(size);
+        HeaderTable t = new HeaderTable(size, HPACK.getLogger());
         assertEquals(t.size(), 0);
         assertEquals(t.maxSize(), size);
     }
@@ -169,13 +169,13 @@ public class HeaderTableTest {
         int maxSize = -(rnd.nextInt(100) + 1); // [-100, -1]
         IllegalArgumentException e =
                 assertVoidThrows(IllegalArgumentException.class,
-                        () -> new HeaderTable(0).setMaxSize(maxSize));
+                        () -> new HeaderTable(0, HPACK.getLogger()).setMaxSize(maxSize));
         assertExceptionMessageContains(e, "maxSize");
     }
 
     @Test
     public void zeroMaximumSize() {
-        HeaderTable table = new HeaderTable(0);
+        HeaderTable table = new HeaderTable(0, HPACK.getLogger());
         table.setMaxSize(0);
         assertEquals(table.maxSize(), 0);
     }
@@ -183,41 +183,41 @@ public class HeaderTableTest {
     @Test
     public void negativeIndex() {
         int idx = -(rnd.nextInt(256) + 1); // [-256, -1]
-        IllegalArgumentException e =
-                assertVoidThrows(IllegalArgumentException.class,
-                        () -> new HeaderTable(0).get(idx));
+        IndexOutOfBoundsException e =
+                assertVoidThrows(IndexOutOfBoundsException.class,
+                        () -> new HeaderTable(0, HPACK.getLogger()).get(idx));
         assertExceptionMessageContains(e, "index");
     }
 
     @Test
     public void zeroIndex() {
-        IllegalArgumentException e =
-                assertThrows(IllegalArgumentException.class,
-                        () -> new HeaderTable(0).get(0));
+        IndexOutOfBoundsException e =
+                assertThrows(IndexOutOfBoundsException.class,
+                        () -> new HeaderTable(0, HPACK.getLogger()).get(0));
         assertExceptionMessageContains(e, "index");
     }
 
     @Test
     public void length() {
-        HeaderTable table = new HeaderTable(0);
+        HeaderTable table = new HeaderTable(0, HPACK.getLogger());
         assertEquals(table.length(), STATIC_TABLE_LENGTH);
     }
 
     @Test
     public void indexOutsideStaticRange() {
-        HeaderTable table = new HeaderTable(0);
+        HeaderTable table = new HeaderTable(0, HPACK.getLogger());
         int idx = table.length() + (rnd.nextInt(256) + 1);
-        IllegalArgumentException e =
-                assertThrows(IllegalArgumentException.class,
+        IndexOutOfBoundsException e =
+                assertThrows(IndexOutOfBoundsException.class,
                         () -> table.get(idx));
         assertExceptionMessageContains(e, "index");
     }
 
     @Test
     public void entryPutAfterStaticArea() {
-        HeaderTable table = new HeaderTable(256);
+        HeaderTable table = new HeaderTable(256, HPACK.getLogger());
         int idx = table.length() + 1;
-        assertThrows(IllegalArgumentException.class, () -> table.get(idx));
+        assertThrows(IndexOutOfBoundsException.class, () -> table.get(idx));
 
         byte[] bytes = new byte[32];
         rnd.nextBytes(bytes);
@@ -232,13 +232,13 @@ public class HeaderTableTest {
 
     @Test
     public void staticTableHasZeroSize() {
-        HeaderTable table = new HeaderTable(0);
+        HeaderTable table = new HeaderTable(0, HPACK.getLogger());
         assertEquals(0, table.size());
     }
 
     @Test
     public void lowerIndexPriority() {
-        HeaderTable table = new HeaderTable(256);
+        HeaderTable table = new HeaderTable(256, HPACK.getLogger());
         int oldLength = table.length();
         table.put("bender", "rodriguez");
         table.put("bender", "rodriguez");
@@ -251,7 +251,7 @@ public class HeaderTableTest {
 
     @Test
     public void lowerIndexPriority2() {
-        HeaderTable table = new HeaderTable(256);
+        HeaderTable table = new HeaderTable(256, HPACK.getLogger());
         int oldLength = table.length();
         int idx = rnd.nextInt(oldLength) + 1;
         HeaderField f = table.get(idx);
@@ -267,9 +267,11 @@ public class HeaderTableTest {
 
     @Test
     public void fifo() {
-        HeaderTable t = new HeaderTable(Integer.MAX_VALUE);
         // Let's add a series of header fields
         int NUM_HEADERS = 32;
+        HeaderTable t = new HeaderTable((32 + 4) * NUM_HEADERS, HPACK.getLogger());
+        //                                ^   ^
+        //                   entry overhead   symbols per entry (max 2x2 digits)
         for (int i = 1; i <= NUM_HEADERS; i++) {
             String s = String.valueOf(i);
             t.put(s, s);
@@ -293,9 +295,11 @@ public class HeaderTableTest {
 
     @Test
     public void indexOf() {
-        HeaderTable t = new HeaderTable(Integer.MAX_VALUE);
         // Let's put a series of header fields
         int NUM_HEADERS = 32;
+        HeaderTable t = new HeaderTable((32 + 4) * NUM_HEADERS, HPACK.getLogger());
+        //                                ^   ^
+        //                   entry overhead   symbols per entry (max 2x2 digits)
         for (int i = 1; i <= NUM_HEADERS; i++) {
             String s = String.valueOf(i);
             t.put(s, s);
@@ -326,18 +330,25 @@ public class HeaderTableTest {
 
     @Test
     public void testToStringDifferentLocale() {
+        Locale locale = Locale.getDefault();
         Locale.setDefault(Locale.FRENCH);
-        String s = format("%.1f", 3.1);
-        assertEquals("3,1", s); // assumption of the test, otherwise the test is useless
-        testToString0();
+        try {
+            String s = format("%.1f", 3.1);
+            assertEquals("3,1", s); // assumption of the test, otherwise the test is useless
+            testToString0();
+        } finally {
+            Locale.setDefault(locale);
+        }
     }
 
     private void testToString0() {
-        HeaderTable table = new HeaderTable(0);
+        HeaderTable table = new HeaderTable(0, HPACK.getLogger());
         {
-            table.setMaxSize(2048);
-            String expected =
-                    format("entries: %d; used %s/%s (%.1f%%)", 0, 0, 2048, 0.0);
+            int maxSize = 2048;
+            table.setMaxSize(maxSize);
+            String expected = format(
+                    "dynamic length: %s, full length: %s, used space: %s/%s (%.1f%%)",
+                    0, STATIC_TABLE_LENGTH, 0, maxSize, 0.0);
             assertEquals(expected, table.toString());
         }
 
@@ -353,8 +364,9 @@ public class HeaderTableTest {
             int used = name.length() + value.length() + 32;
             double ratio = used * 100.0 / size;
 
-            String expected =
-                    format("entries: 1; used %s/%s (%.1f%%)", used, size, ratio);
+            String expected = format(
+                    "dynamic length: %s, full length: %s, used space: %s/%s (%.1f%%)",
+                    1, STATIC_TABLE_LENGTH + 1, used, size, ratio);
             assertEquals(expected, s);
         }
 
@@ -364,14 +376,15 @@ public class HeaderTableTest {
             table.put(":status", "");
             String s = table.toString();
             String expected =
-                    format("entries: %d; used %s/%s (%.1f%%)", 2, 78, 78, 100.0);
+                    format("dynamic length: %s, full length: %s, used space: %s/%s (%.1f%%)",
+                           2, STATIC_TABLE_LENGTH + 2, 78, 78, 100.0);
             assertEquals(expected, s);
         }
     }
 
     @Test
     public void stateString() {
-        HeaderTable table = new HeaderTable(256);
+        HeaderTable table = new HeaderTable(256, HPACK.getLogger());
         table.put("custom-key", "custom-header");
         // @formatter:off
         assertEquals("[  1] (s =  55) custom-key: custom-header\n" +
diff --git a/test/jdk/java/net/httpclient/http2/jdk.incubator.httpclient/jdk/incubator/http/internal/hpack/HuffmanTest.java b/test/jdk/java/net/httpclient/http2/jdk.incubator.httpclient/jdk/incubator/http/internal/hpack/HuffmanTest.java
index adb24a7ffec..d8b8b9397b9 100644
--- a/test/jdk/java/net/httpclient/http2/jdk.incubator.httpclient/jdk/incubator/http/internal/hpack/HuffmanTest.java
+++ b/test/jdk/java/net/httpclient/http2/jdk.incubator.httpclient/jdk/incubator/http/internal/hpack/HuffmanTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2017, 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
@@ -24,6 +24,8 @@ package jdk.incubator.http.internal.hpack;
 
 import org.testng.annotations.Test;
 
+import java.io.IOException;
+import java.io.UncheckedIOException;
 import java.nio.ByteBuffer;
 import java.util.Stack;
 import java.util.regex.Matcher;
@@ -302,7 +304,7 @@ public final class HuffmanTest {
     // @formatter:on
 
     @Test
-    public void read_table() {
+    public void read_table() throws IOException {
         Pattern line = Pattern.compile(
                 "\\(\\s*(?<ascii>\\d+)\\s*\\)\\s*(?<binary>(\\|(0|1)+)+)\\s*" +
                         "(?<hex>[0-9a-zA-Z]+)\\s*\\[\\s*(?<len>\\d+)\\s*\\]");
@@ -555,7 +557,11 @@ public final class HuffmanTest {
     private static void read(String hexdump, String decoded) {
         ByteBuffer source = SpecHelper.toBytes(hexdump);
         Appendable actual = new StringBuilder();
-        new Huffman.Reader().read(source, actual, true);
+        try {
+            new Huffman.Reader().read(source, actual, true);
+        } catch (IOException e) {
+            throw new UncheckedIOException(e);
+        }
         assertEquals(actual.toString(), decoded);
     }
 
diff --git a/test/jdk/java/net/httpclient/http2/jdk.incubator.httpclient/jdk/incubator/http/internal/hpack/SpecHelper.java b/test/jdk/java/net/httpclient/http2/jdk.incubator.httpclient/jdk/incubator/http/internal/hpack/SpecHelper.java
index 6bf441bd5a4..e0c01d18405 100644
--- a/test/jdk/java/net/httpclient/http2/jdk.incubator.httpclient/jdk/incubator/http/internal/hpack/SpecHelper.java
+++ b/test/jdk/java/net/httpclient/http2/jdk.incubator.httpclient/jdk/incubator/http/internal/hpack/SpecHelper.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2014, 2017, 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
diff --git a/test/jdk/java/net/httpclient/http2/jdk.incubator.httpclient/jdk/incubator/http/internal/hpack/TestHelper.java b/test/jdk/java/net/httpclient/http2/jdk.incubator.httpclient/jdk/incubator/http/internal/hpack/TestHelper.java
index 86cc17016a6..b8ebd5644bf 100644
--- a/test/jdk/java/net/httpclient/http2/jdk.incubator.httpclient/jdk/incubator/http/internal/hpack/TestHelper.java
+++ b/test/jdk/java/net/httpclient/http2/jdk.incubator.httpclient/jdk/incubator/http/internal/hpack/TestHelper.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2014, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2014, 2017, 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
diff --git a/test/jdk/java/net/httpclient/http2/server/BodyInputStream.java b/test/jdk/java/net/httpclient/http2/server/BodyInputStream.java
index 47cf2fa872c..dbfefe58e78 100644
--- a/test/jdk/java/net/httpclient/http2/server/BodyInputStream.java
+++ b/test/jdk/java/net/httpclient/http2/server/BodyInputStream.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2017, 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
@@ -23,9 +23,8 @@
 
 import java.io.*;
 import java.nio.ByteBuffer;
+import java.util.List;
 
-import jdk.incubator.http.internal.common.ByteBufferReference;
-import jdk.incubator.http.internal.common.Queue;
 import jdk.incubator.http.internal.common.Utils;
 import jdk.incubator.http.internal.frame.DataFrame;
 import jdk.incubator.http.internal.frame.Http2Frame;
@@ -63,9 +62,6 @@ class BodyInputStream extends InputStream {
         Http2Frame frame;
         do {
             frame = q.take();
-            if (frame.type() == ResetFrame.TYPE) {
-                conn.handleStreamReset((ResetFrame) frame); // throws IOException
-            }
             // ignoring others for now Wupdates handled elsewhere
             if (frame.type() != DataFrame.TYPE) {
                 System.out.println("Ignoring " + frame.toString() + " CHECK THIS");
@@ -87,13 +83,13 @@ class BodyInputStream extends InputStream {
                 if (df == null) {
                     return null;
                 }
-                ByteBufferReference[] data = df.getData();
-                int len = Utils.remaining(data);
+                List<ByteBuffer> data = df.getData();
+                long len = Utils.remaining(data);
                 if ((len == 0) && eof) {
                     return null;
                 }
 
-                buffers = ByteBufferReference.toBuffers(data);
+                buffers = data.toArray(Utils.EMPTY_BB_ARRAY);
                 nextIndex = 0;
             }
             buffer = buffers[nextIndex++];
diff --git a/test/jdk/java/net/httpclient/http2/server/BodyOutputStream.java b/test/jdk/java/net/httpclient/http2/server/BodyOutputStream.java
index 759005aa34f..95a18007461 100644
--- a/test/jdk/java/net/httpclient/http2/server/BodyOutputStream.java
+++ b/test/jdk/java/net/httpclient/http2/server/BodyOutputStream.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2017, 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
@@ -24,8 +24,6 @@
 import java.io.*;
 import java.nio.ByteBuffer;
 
-import jdk.incubator.http.internal.common.ByteBufferReference;
-import jdk.incubator.http.internal.common.Queue;
 import jdk.incubator.http.internal.frame.DataFrame;
 
 /**
@@ -38,7 +36,7 @@ class BodyOutputStream extends OutputStream {
 
     final int streamid;
     int window;
-    boolean closed;
+    volatile boolean closed;
     boolean goodToGo = false; // not allowed to send until headers sent
     final Http2TestServerConnection conn;
     final Queue outputQ;
@@ -100,7 +98,7 @@ class BodyOutputStream extends OutputStream {
         buffer.put(buf, offset, len);
         buffer.flip();
         assert streamid != 0;
-        DataFrame df = new DataFrame(streamid, flags, ByteBufferReference.of(buffer));
+        DataFrame df = new DataFrame(streamid, flags, buffer);
         outputQ.put(df);
     }
 
@@ -118,10 +116,11 @@ class BodyOutputStream extends OutputStream {
 
     @Override
     public void close() {
-        if (closed) {
-            return;
+        if (closed) return;
+        synchronized (this) {
+            if (closed) return;
+            closed = true;
         }
-        closed = true;
         try {
             send(EMPTY_BARRAY, 0, 0, DataFrame.END_STREAM);
         } catch (IOException ex) {
diff --git a/test/jdk/java/net/httpclient/http2/server/EchoHandler.java b/test/jdk/java/net/httpclient/http2/server/EchoHandler.java
index 77a2a19494d..c36410317ee 100644
--- a/test/jdk/java/net/httpclient/http2/server/EchoHandler.java
+++ b/test/jdk/java/net/httpclient/http2/server/EchoHandler.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2005, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2005, 2017, 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
diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/common/ExceptionallyCloseable.java b/test/jdk/java/net/httpclient/http2/server/ExceptionallyCloseable.java
similarity index 86%
rename from src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/common/ExceptionallyCloseable.java
rename to test/jdk/java/net/httpclient/http2/server/ExceptionallyCloseable.java
index d0d5689bf8b..b27117602f2 100644
--- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/common/ExceptionallyCloseable.java
+++ b/test/jdk/java/net/httpclient/http2/server/ExceptionallyCloseable.java
@@ -1,12 +1,10 @@
 /*
- * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2017, 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.  Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
+ * 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
@@ -23,8 +21,6 @@
  * questions.
  */
 
-package jdk.incubator.http.internal.common;
-
 import java.io.Closeable;
 import java.io.IOException;
 
diff --git a/test/jdk/java/net/httpclient/http2/server/Http2EchoHandler.java b/test/jdk/java/net/httpclient/http2/server/Http2EchoHandler.java
index c1ebebe0d2f..b6872e54227 100644
--- a/test/jdk/java/net/httpclient/http2/server/Http2EchoHandler.java
+++ b/test/jdk/java/net/httpclient/http2/server/Http2EchoHandler.java
@@ -31,7 +31,7 @@ public class Http2EchoHandler implements Http2Handler {
     public void handle(Http2TestExchange t)
             throws IOException {
         try {
-            System.err.println("EchoHandler received request to " + t.getRequestURI());
+            System.err.printf("EchoHandler received request to %s from %s\n", t.getRequestURI(), t.getRemoteAddress());
             InputStream is = t.getRequestBody();
             HttpHeadersImpl map = t.getRequestHeaders();
             HttpHeadersImpl map1 = t.getResponseHeaders();
diff --git a/test/jdk/java/net/httpclient/http2/server/Http2Handler.java b/test/jdk/java/net/httpclient/http2/server/Http2Handler.java
index a6402d3f850..1addb8bffdd 100644
--- a/test/jdk/java/net/httpclient/http2/server/Http2Handler.java
+++ b/test/jdk/java/net/httpclient/http2/server/Http2Handler.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2017, 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
diff --git a/test/jdk/java/net/httpclient/http2/server/RedirectHandler.java b/test/jdk/java/net/httpclient/http2/server/Http2RedirectHandler.java
similarity index 61%
rename from test/jdk/java/net/httpclient/http2/server/RedirectHandler.java
rename to test/jdk/java/net/httpclient/http2/server/Http2RedirectHandler.java
index 5c68d053a8b..5b7fcb79748 100644
--- a/test/jdk/java/net/httpclient/http2/server/RedirectHandler.java
+++ b/test/jdk/java/net/httpclient/http2/server/Http2RedirectHandler.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2017, 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
@@ -21,46 +21,42 @@
  * questions.
  */
 
-import java.io.*;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
 import java.util.function.Supplier;
 import jdk.incubator.http.internal.common.HttpHeadersImpl;
-import static java.nio.charset.StandardCharsets.ISO_8859_1;
 
-public class RedirectHandler implements Http2Handler {
+public class Http2RedirectHandler implements Http2Handler {
 
     final Supplier<String> supplier;
 
-    public RedirectHandler(Supplier<String> redirectSupplier) {
+    public Http2RedirectHandler(Supplier<String> redirectSupplier) {
         supplier = redirectSupplier;
     }
 
-    static String consume(InputStream is) throws IOException {
-        byte[] b = new byte[1024];
-        int i;
-        StringBuilder sb = new StringBuilder();
-
-        while ((i=is.read(b)) != -1) {
-            sb.append(new String(b, 0, i, ISO_8859_1));
-        }
-        is.close();
-        return sb.toString();
-    }
-
     @Override
     public void handle(Http2TestExchange t) throws IOException {
-        try {
-            consume(t.getRequestBody());
+        examineExchange(t);
+        try (InputStream is = t.getRequestBody()) {
+            is.readAllBytes();
             String location = supplier.get();
-            System.err.println("RedirectHandler received request to " + t.getRequestURI());
+            System.err.printf("RedirectHandler request to %s from %s\n",
+                t.getRequestURI().toString(), t.getRemoteAddress().toString());
             System.err.println("Redirecting to: " + location);
             HttpHeadersImpl map1 = t.getResponseHeaders();
             map1.addHeader("Location", location);
-            t.sendResponseHeaders(301, 0);
-            // return the number of bytes received (no echo)
+            t.sendResponseHeaders(301, 1024);
+            byte[] bb = new byte[1024];
+            OutputStream os = t.getResponseBody();
+            os.write(bb);
+            os.close();
             t.close();
-        } catch (Throwable e) {
-            e.printStackTrace();
-            throw new IOException(e);
         }
     }
+
+    // override in sub-class to examine the exchange, but don't
+    // alter transaction state by reading the request body etc.
+    protected void examineExchange(Http2TestExchange t) {
+    }
 }
diff --git a/test/jdk/java/net/httpclient/http2/server/Http2TestExchange.java b/test/jdk/java/net/httpclient/http2/server/Http2TestExchange.java
index 325aaa23247..6e55021af9e 100644
--- a/test/jdk/java/net/httpclient/http2/server/Http2TestExchange.java
+++ b/test/jdk/java/net/httpclient/http2/server/Http2TestExchange.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2017, 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
@@ -21,148 +21,52 @@
  * questions.
  */
 
+import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
-import java.io.IOException;
 import java.net.URI;
 import java.net.InetSocketAddress;
+import java.util.concurrent.CompletableFuture;
+import java.util.function.Consumer;
 import javax.net.ssl.SSLSession;
 import jdk.incubator.http.internal.common.HttpHeadersImpl;
-import jdk.incubator.http.internal.frame.HeaderFrame;
-import jdk.incubator.http.internal.frame.HeadersFrame;
 
-public class Http2TestExchange {
+public interface Http2TestExchange {
 
-    final HttpHeadersImpl reqheaders;
-    final HttpHeadersImpl rspheaders;
-    final URI uri;
-    final String method;
-    final InputStream is;
-    final BodyOutputStream os;
-    final SSLSession sslSession;
-    final int streamid;
-    final boolean pushAllowed;
-    final Http2TestServerConnection conn;
-    final Http2TestServer server;
+    HttpHeadersImpl getRequestHeaders();
 
-    int responseCode = -1;
-    long responseLength;
+    HttpHeadersImpl getResponseHeaders();
 
-    Http2TestExchange(int streamid,
-                      String method,
-                      HttpHeadersImpl reqheaders,
-                      HttpHeadersImpl rspheaders,
-                      URI uri,
-                      InputStream is,
-                      SSLSession sslSession,
-                      BodyOutputStream os,
-                      Http2TestServerConnection conn,
-                      boolean pushAllowed) {
-        this.reqheaders = reqheaders;
-        this.rspheaders = rspheaders;
-        this.uri = uri;
-        this.method = method;
-        this.is = is;
-        this.streamid = streamid;
-        this.os = os;
-        this.sslSession = sslSession;
-        this.pushAllowed = pushAllowed;
-        this.conn = conn;
-        this.server = conn.server;
-    }
+    URI getRequestURI();
 
-    public HttpHeadersImpl getRequestHeaders() {
-        return reqheaders;
-    }
+    String getRequestMethod();
 
-    public HttpHeadersImpl getResponseHeaders() {
-        return rspheaders;
-    }
+    SSLSession getSSLSession();
 
-    public URI getRequestURI() {
-        return uri;
-    }
+    void close();
 
-    public String getRequestMethod() {
-        return method;
-    }
+    InputStream getRequestBody();
 
-    public SSLSession getSSLSession() {
-        return sslSession;
-    }
+    OutputStream getResponseBody();
 
-    public void close() {
-        try {
-            is.close();
-            os.close();
-        } catch (IOException e) {
-            System.err.println("TestServer: HttpExchange.close exception: " + e);
-            e.printStackTrace();
-        }
-    }
+    void sendResponseHeaders(int rCode, long responseLength) throws IOException;
 
-    public InputStream getRequestBody() {
-        return is;
-    }
+    InetSocketAddress getRemoteAddress();
 
-    public OutputStream getResponseBody() {
-        return os;
-    }
+    int getResponseCode();
 
-    public void sendResponseHeaders(int rCode, long responseLength) throws IOException {
-        this.responseLength = responseLength;
-        if (responseLength > 0 || responseLength < 0) {
-                long clen = responseLength > 0 ? responseLength : 0;
-            rspheaders.setHeader("Content-length", Long.toString(clen));
-        }
+    InetSocketAddress getLocalAddress();
 
-        rspheaders.setHeader(":status", Integer.toString(rCode));
+    String getProtocol();
 
-        Http2TestServerConnection.ResponseHeaders response
-                = new Http2TestServerConnection.ResponseHeaders(rspheaders);
-        response.streamid(streamid);
-        response.setFlag(HeaderFrame.END_HEADERS);
-        if (responseLength < 0) {
-            response.setFlag(HeadersFrame.END_STREAM);
-            os.closeInternal();
-        }
-        conn.outputQ.put(response);
-        os.goodToGo();
-        System.err.println("Sent response headers " + rCode);
-    }
+    boolean serverPushAllowed();
 
-    public InetSocketAddress getRemoteAddress() {
-        return (InetSocketAddress) conn.socket.getRemoteSocketAddress();
-    }
+    void serverPush(URI uri, HttpHeadersImpl headers, InputStream content);
 
-    public int getResponseCode() {
-        return responseCode;
-    }
-
-    public InetSocketAddress getLocalAddress() {
-        return server.getAddress();
-    }
-
-    public String getProtocol() {
-        return "HTTP/2";
-    }
-
-    public boolean serverPushAllowed() {
-        return pushAllowed;
-    }
-
-    public void serverPush(URI uri, HttpHeadersImpl headers, InputStream content) {
-        OutgoingPushPromise pp = new OutgoingPushPromise(
-                streamid, uri, headers, content);
-        headers.setHeader(":method", "GET");
-        headers.setHeader(":scheme", uri.getScheme());
-        headers.setHeader(":authority", uri.getAuthority());
-        headers.setHeader(":path", uri.getPath());
-        try {
-            conn.outputQ.put(pp);
-            // writeLoop will spin up thread to read the InputStream
-        } catch (IOException ex) {
-            System.err.println("TestServer: pushPromise exception: " + ex);
-        }
-    }
+    /**
+     * Send a PING on this exchanges connection, and completes the returned CF
+     * with the number of milliseconds it took to get a valid response.
+     * It may also complete exceptionally
+     */
+    CompletableFuture<Long> sendPing();
 }
diff --git a/test/jdk/java/net/httpclient/http2/server/Http2TestExchangeImpl.java b/test/jdk/java/net/httpclient/http2/server/Http2TestExchangeImpl.java
new file mode 100644
index 00000000000..4d0b83f2763
--- /dev/null
+++ b/test/jdk/java/net/httpclient/http2/server/Http2TestExchangeImpl.java
@@ -0,0 +1,192 @@
+/*
+ * Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.IOException;
+import java.net.URI;
+import java.net.InetSocketAddress;
+import java.util.concurrent.CompletableFuture;
+import java.util.function.Consumer;
+import javax.net.ssl.SSLSession;
+import jdk.incubator.http.internal.common.HttpHeadersImpl;
+import jdk.incubator.http.internal.frame.HeaderFrame;
+import jdk.incubator.http.internal.frame.HeadersFrame;
+
+public class Http2TestExchangeImpl implements Http2TestExchange {
+
+    final HttpHeadersImpl reqheaders;
+    final HttpHeadersImpl rspheaders;
+    final URI uri;
+    final String method;
+    final InputStream is;
+    final BodyOutputStream os;
+    final SSLSession sslSession;
+    final int streamid;
+    final boolean pushAllowed;
+    final Http2TestServerConnection conn;
+    final Http2TestServer server;
+
+    int responseCode = -1;
+    long responseLength;
+
+    Http2TestExchangeImpl(int streamid,
+                          String method,
+                          HttpHeadersImpl reqheaders,
+                          HttpHeadersImpl rspheaders,
+                          URI uri,
+                          InputStream is,
+                          SSLSession sslSession,
+                          BodyOutputStream os,
+                          Http2TestServerConnection conn,
+                          boolean pushAllowed) {
+        this.reqheaders = reqheaders;
+        this.rspheaders = rspheaders;
+        this.uri = uri;
+        this.method = method;
+        this.is = is;
+        this.streamid = streamid;
+        this.os = os;
+        this.sslSession = sslSession;
+        this.pushAllowed = pushAllowed;
+        this.conn = conn;
+        this.server = conn.server;
+    }
+
+    @Override
+    public HttpHeadersImpl getRequestHeaders() {
+        return reqheaders;
+    }
+
+    @Override
+    public CompletableFuture<Long> sendPing() {
+        return conn.sendPing();
+    }
+
+    @Override
+    public HttpHeadersImpl getResponseHeaders() {
+        return rspheaders;
+    }
+
+    @Override
+    public URI getRequestURI() {
+        return uri;
+    }
+
+    @Override
+    public String getRequestMethod() {
+        return method;
+    }
+
+    @Override
+    public SSLSession getSSLSession() {
+        return sslSession;
+    }
+
+    @Override
+    public void close() {
+        try {
+            is.close();
+            os.close();
+        } catch (IOException e) {
+            System.err.println("TestServer: HttpExchange.close exception: " + e);
+            e.printStackTrace();
+        }
+    }
+
+    @Override
+    public InputStream getRequestBody() {
+        return is;
+    }
+
+    @Override
+    public OutputStream getResponseBody() {
+        return os;
+    }
+
+    @Override
+    public void sendResponseHeaders(int rCode, long responseLength) throws IOException {
+        this.responseLength = responseLength;
+        if (responseLength > 0 || responseLength < 0) {
+                long clen = responseLength > 0 ? responseLength : 0;
+            rspheaders.setHeader("Content-length", Long.toString(clen));
+        }
+
+        rspheaders.setHeader(":status", Integer.toString(rCode));
+
+        Http2TestServerConnection.ResponseHeaders response
+                = new Http2TestServerConnection.ResponseHeaders(rspheaders);
+        response.streamid(streamid);
+        response.setFlag(HeaderFrame.END_HEADERS);
+
+
+        if (responseLength < 0) {
+            response.setFlag(HeadersFrame.END_STREAM);
+            os.closeInternal();
+        }
+        conn.outputQ.put(response);
+        os.goodToGo();
+        System.err.println("Sent response headers " + rCode);
+    }
+
+    @Override
+    public InetSocketAddress getRemoteAddress() {
+        return (InetSocketAddress) conn.socket.getRemoteSocketAddress();
+    }
+
+    @Override
+    public int getResponseCode() {
+        return responseCode;
+    }
+
+    @Override
+    public InetSocketAddress getLocalAddress() {
+        return server.getAddress();
+    }
+
+    @Override
+    public String getProtocol() {
+        return "HTTP/2";
+    }
+
+    @Override
+    public boolean serverPushAllowed() {
+        return pushAllowed;
+    }
+
+    @Override
+    public void serverPush(URI uri, HttpHeadersImpl headers, InputStream content) {
+        OutgoingPushPromise pp = new OutgoingPushPromise(
+                streamid, uri, headers, content);
+        headers.setHeader(":method", "GET");
+        headers.setHeader(":scheme", uri.getScheme());
+        headers.setHeader(":authority", uri.getAuthority());
+        headers.setHeader(":path", uri.getPath());
+        try {
+            conn.outputQ.put(pp);
+            // writeLoop will spin up thread to read the InputStream
+        } catch (IOException ex) {
+            System.err.println("TestServer: pushPromise exception: " + ex);
+        }
+    }
+}
diff --git a/test/jdk/java/net/httpclient/http2/server/Http2TestExchangeSupplier.java b/test/jdk/java/net/httpclient/http2/server/Http2TestExchangeSupplier.java
new file mode 100644
index 00000000000..d0c3ea127f0
--- /dev/null
+++ b/test/jdk/java/net/httpclient/http2/server/Http2TestExchangeSupplier.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import javax.net.ssl.SSLSession;
+import java.io.InputStream;
+import java.net.URI;
+import jdk.incubator.http.internal.common.HttpHeadersImpl;
+
+/**
+ * A supplier of Http2TestExchanges. If the default Http2TestExchange impl is
+ * not sufficient, then a supplier may be set on an Http2TestServer through its
+ * {@link Http2TestServer#setExchangeSupplier(Http2TestExchangeSupplier)}.
+ *
+ * Useful for testing scenarios where non-standard or specific server behaviour
+ * is required, either direct control over the frames sent, "bad" behaviour, or
+ * something else.
+ */
+public interface Http2TestExchangeSupplier {
+
+    Http2TestExchange get(int streamid,
+                          String method,
+                          HttpHeadersImpl reqheaders,
+                          HttpHeadersImpl rspheaders,
+                          URI uri,
+                          InputStream is,
+                          SSLSession sslSession,
+                          BodyOutputStream os,
+                          Http2TestServerConnection conn,
+                          boolean pushAllowed);
+
+    static Http2TestExchangeSupplier ofDefault() {
+        return Http2TestExchangeImpl::new;
+    }
+}
diff --git a/test/jdk/java/net/httpclient/http2/server/Http2TestServer.java b/test/jdk/java/net/httpclient/http2/server/Http2TestServer.java
index 6889cf7aec8..721cb960d99 100644
--- a/test/jdk/java/net/httpclient/http2/server/Http2TestServer.java
+++ b/test/jdk/java/net/httpclient/http2/server/Http2TestServer.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2017, 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,12 +28,14 @@ import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
 import java.util.concurrent.ThreadFactory;
 import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.Consumer;
 import javax.net.ServerSocketFactory;
 import javax.net.ssl.SSLContext;
 import javax.net.ssl.SSLParameters;
 import javax.net.ssl.SSLServerSocket;
 import javax.net.ssl.SSLServerSocketFactory;
 import javax.net.ssl.SNIServerName;
+import jdk.incubator.http.internal.frame.ErrorFrame;
 
 /**
  * Waits for incoming TCP connections from a client and establishes
@@ -131,6 +133,18 @@ public class Http2TestServer implements AutoCloseable {
         handlers.put(path, handler);
     }
 
+    volatile Http2TestExchangeSupplier exchangeSupplier = Http2TestExchangeSupplier.ofDefault();
+
+    /**
+     * Sets an explicit exchange handler to be used for all future connections.
+     * Useful for testing scenarios where non-standard or specific server
+     * behaviour is required, either direct control over the frames sent, "bad"
+     * behaviour, or something else.
+     */
+    public void setExchangeSupplier(Http2TestExchangeSupplier exchangeSupplier) {
+        this.exchangeSupplier = exchangeSupplier;
+    }
+
     Http2Handler getHandlerFor(String path) {
         if (path == null || path.equals(""))
             path = "/";
@@ -159,8 +173,9 @@ public class Http2TestServer implements AutoCloseable {
     public void stop() {
         // TODO: clean shutdown GoAway
         stopping = true;
+        System.err.printf("Server stopping %d connections\n", connections.size());
         for (Http2TestServerConnection connection : connections.values()) {
-            connection.close();
+            connection.close(ErrorFrame.NO_ERROR);
         }
         try {
             server.close();
@@ -199,23 +214,25 @@ public class Http2TestServer implements AutoCloseable {
                 while (!stopping) {
                     Socket socket = server.accept();
                     InetSocketAddress addr = (InetSocketAddress) socket.getRemoteSocketAddress();
-                    Http2TestServerConnection c = new Http2TestServerConnection(this, socket);
+                    Http2TestServerConnection c =
+                            new Http2TestServerConnection(this, socket, exchangeSupplier);
                     connections.put(addr, c);
                     try {
                         c.run();
-                    } catch(Throwable e) {
+                    } catch (Throwable e) {
                         // we should not reach here, but if we do
                         // the connection might not have been closed
                         // and if so then the client might wait
                         // forever.
                         connections.remove(addr, c);
-                        c.close();
-                        throw e;
+                        c.close(ErrorFrame.PROTOCOL_ERROR);
+                        System.err.println("TestServer: start exception: " + e);
+                        //throw e;
                     }
                 }
             } catch (Throwable e) {
                 if (!stopping) {
-                    System.err.println("TestServer: start exception: " + e);
+                    System.err.println("TestServer: terminating, caught " + e);
                     e.printStackTrace();
                 }
             }
diff --git a/test/jdk/java/net/httpclient/http2/server/Http2TestServerConnection.java b/test/jdk/java/net/httpclient/http2/server/Http2TestServerConnection.java
index 5645c200692..afb3066f76e 100644
--- a/test/jdk/java/net/httpclient/http2/server/Http2TestServerConnection.java
+++ b/test/jdk/java/net/httpclient/http2/server/Http2TestServerConnection.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2017, 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
@@ -36,19 +36,17 @@ import java.net.URISyntaxException;
 import java.nio.ByteBuffer;
 import java.nio.charset.StandardCharsets;
 import java.util.*;
+import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.ExecutorService;
+import java.util.concurrent.ConcurrentLinkedQueue;
 import java.util.function.Consumer;
-
-import jdk.incubator.http.internal.common.ByteBufferReference;
-import jdk.incubator.http.internal.frame.FramesDecoder;
-
-import jdk.incubator.http.internal.common.BufferHandler;
 import jdk.incubator.http.internal.common.HttpHeadersImpl;
-import jdk.incubator.http.internal.common.Queue;
 import jdk.incubator.http.internal.frame.*;
 import jdk.incubator.http.internal.hpack.Decoder;
 import jdk.incubator.http.internal.hpack.DecodingCallback;
 import jdk.incubator.http.internal.hpack.Encoder;
+import sun.net.www.http.ChunkedInputStream;
+import sun.net.www.http.HttpClient;
 import static jdk.incubator.http.internal.frame.SettingsFrame.HEADER_TABLE_SIZE;
 
 /**
@@ -59,10 +57,12 @@ public class Http2TestServerConnection {
     final Http2TestServer server;
     @SuppressWarnings({"rawtypes","unchecked"})
     final Map<Integer, Queue> streams; // input q per stream
+    final Map<Integer, BodyOutputStream> outStreams; // output q per stream
     final HashSet<Integer> pushStreams;
     final Queue<Http2Frame> outputQ;
     volatile int nextstream;
     final Socket socket;
+    final Http2TestExchangeSupplier exchangeSupplier;
     final InputStream is;
     final OutputStream os;
     volatile Encoder hpackOut;
@@ -73,21 +73,66 @@ public class Http2TestServerConnection {
     final boolean secure;
     volatile boolean stopping;
     volatile int nextPushStreamId = 2;
+    ConcurrentLinkedQueue<PingRequest> pings = new ConcurrentLinkedQueue<>();
 
     final static ByteBuffer EMPTY_BUFFER = ByteBuffer.allocate(0);
     final static byte[] EMPTY_BARRAY = new byte[0];
+    final Random random;
 
     final static byte[] clientPreface = "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n".getBytes();
 
-    Http2TestServerConnection(Http2TestServer server, Socket socket) throws IOException {
+    static class Sentinel extends Http2Frame {
+        Sentinel() { super(-1,-1);}
+    }
+
+    static final Sentinel sentinel = new Sentinel();
+
+    class PingRequest {
+        final byte[] pingData;
+        final long pingStamp;
+        final CompletableFuture<Long> response;
+
+        PingRequest() {
+            pingData = new byte[8];
+            random.nextBytes(pingData);
+            pingStamp = System.currentTimeMillis();
+            response = new CompletableFuture<>();
+        }
+
+        PingFrame frame() {
+            return new PingFrame(0, pingData);
+        }
+
+        CompletableFuture<Long> response() {
+            return response;
+        }
+
+        void success() {
+            response.complete(System.currentTimeMillis() - pingStamp);
+        }
+
+        void fail(Throwable t) {
+            response.completeExceptionally(t);
+        }
+    }
+
+    Http2TestServerConnection(Http2TestServer server,
+                              Socket socket,
+                              Http2TestExchangeSupplier exchangeSupplier)
+        throws IOException
+    {
         if (socket instanceof SSLSocket) {
             handshake(server.serverName(), (SSLSocket)socket);
         }
         System.err.println("TestServer: New connection from " + socket);
         this.server = server;
+        this.exchangeSupplier = exchangeSupplier;
         this.streams = Collections.synchronizedMap(new HashMap<>());
-        this.outputQ = new Queue<>();
+        this.outStreams = Collections.synchronizedMap(new HashMap<>());
+        this.outputQ = new Queue<>(sentinel);
+        this.random = new Random();
         this.socket = socket;
+        this.socket.setTcpNoDelay(true);
         this.serverSettings = SettingsFrame.getDefaultSettings();
         this.exec = server.exec;
         this.secure = server.secure;
@@ -96,6 +141,67 @@ public class Http2TestServerConnection {
         os = new BufferedOutputStream(socket.getOutputStream());
     }
 
+    /**
+     * Sends a PING frame on this connection, and completes the returned
+     * CF when the PING ack is received. The CF is given
+     * an integer, whose value is the number of milliseconds
+     * between PING and ACK.
+     */
+    CompletableFuture<Long> sendPing() {
+        PingRequest ping = null;
+        try {
+            ping = new PingRequest();
+            pings.add(ping);
+            outputQ.put(ping.frame());
+        } catch (Throwable t) {
+            ping.fail(t);
+        }
+        return ping.response();
+    }
+
+    void goAway(int error) throws IOException {
+        int laststream = nextstream >= 3 ? nextstream - 2 : 1;
+
+        GoAwayFrame go = new GoAwayFrame(laststream, error);
+        outputQ.put(go);
+    }
+
+    /**
+     * Returns the first PingRequest from Queue
+     */
+    private PingRequest getNextRequest() {
+        return pings.poll();
+    }
+
+    /**
+     * Handles incoming Ping, which could be an ack
+     * or a client originated Ping
+     */
+    void handlePing(PingFrame ping) throws IOException {
+        if (ping.streamid() != 0) {
+            System.err.println("Invalid ping received");
+            close(ErrorFrame.PROTOCOL_ERROR);
+            return;
+        }
+        if (ping.getFlag(PingFrame.ACK)) {
+            // did we send a Ping?
+            PingRequest request = getNextRequest();
+            if (request == null) {
+                System.err.println("Invalid ping ACK received");
+                close(ErrorFrame.PROTOCOL_ERROR);
+                return;
+            } else if (!Arrays.equals(request.pingData, ping.getData())) {
+                request.fail(new RuntimeException("Wrong ping data in ACK"));
+            } else {
+                request.success();
+            }
+        } else {
+            // client originated PING. Just send it back with ACK set
+            ping.setFlag(PingFrame.ACK);
+            outputQ.put(ping);
+        }
+    }
+
     private static boolean compareIPAddrs(InetAddress addr1, String host) {
         try {
             InetAddress addr2 = InetAddress.getByName(host);
@@ -132,15 +238,25 @@ public class Http2TestServerConnection {
         sock.getSession(); // blocks until handshake done
     }
 
-    void close() {
+    void closeIncoming() {
+        close(-1);
+    }
+
+    void close(int error) {
+        if (stopping)
+            return;
         stopping = true;
+        System.err.printf("Server connection to %s stopping. %d streams\n",
+            socket.getRemoteSocketAddress().toString(), streams.size());
         streams.forEach((i, q) -> {
-            q.close();
+            q.orderlyClose();
         });
         try {
+            if (error != -1)
+                goAway(error);
+            outputQ.orderlyClose();
             socket.close();
-            // TODO: put a reset on each stream
-        } catch (IOException e) {
+        } catch (Exception e) {
         }
     }
 
@@ -186,8 +302,8 @@ public class Http2TestServerConnection {
                 new byte[] {0, 0, (byte)payload.length, 4, 0, 0, 0, 0, 0});
         List<Http2Frame> frames = new ArrayList<>();
         FramesDecoder reader = new FramesDecoder(frames::add);
-        reader.decode(ByteBufferReference.of(bb0));
-        reader.decode(ByteBufferReference.of(bb1));
+        reader.decode(bb0);
+        reader.decode(bb1);
         if (frames.size()!=1)
             throw new IOException("Expected 1 frame got "+frames.size()) ;
         Http2Frame frame = frames.get(0);
@@ -222,46 +338,51 @@ public class Http2TestServerConnection {
             nextstream = 3;
         }
 
-        exec.submit(this::readLoop);
-        exec.submit(this::writeLoop);
+        (new ConnectionThread("readLoop", this::readLoop)).start();
+        (new ConnectionThread("writeLoop", this::writeLoop)).start();
     }
 
-    static class BufferPool implements BufferHandler {
-
-        public void setMinBufferSize(int size) {
+    class ConnectionThread extends Thread {
+        final Runnable r;
+        ConnectionThread(String name, Runnable r) {
+            setName(name);
+            setDaemon(true);
+            this.r = r;
         }
 
-        @Override
-        public ByteBuffer getBuffer() {
-            int size = 32 * 1024;
-            return ByteBuffer.allocate(size);
-        }
-
-        @Override
-        public void returnBuffer(ByteBuffer buffer) {
+        public void run() {
+            r.run();
         }
     }
 
     private void writeFrame(Http2Frame frame) throws IOException {
-        ByteBufferReference[] refs = new FramesEncoder().encodeFrame(frame);
+        List<ByteBuffer> bufs = new FramesEncoder().encodeFrame(frame);
         //System.err.println("TestServer: Writing frame " + frame.toString());
         int c = 0;
-        for (ByteBufferReference ref : refs) {
-            ByteBuffer buf = ref.get();
+        for (ByteBuffer buf : bufs) {
             byte[] ba = buf.array();
             int start = buf.arrayOffset() + buf.position();
             c += buf.remaining();
             os.write(ba, start, buf.remaining());
+
+//            System.out.println("writing byte at a time");
+//            while (buf.hasRemaining()) {
+//                byte b = buf.get();
+//                os.write(b);
+//                os.flush();
+//                try {
+//                    Thread.sleep(1);
+//                } catch(InterruptedException e) {
+//                    UncheckedIOException uie = new UncheckedIOException(new IOException(""));
+//                    uie.addSuppressed(e);
+//                    throw uie;
+//                }
+//            }
         }
         os.flush();
         //System.err.printf("TestServer: wrote %d bytes\n", c);
     }
 
-    void handleStreamReset(ResetFrame resetFrame) throws IOException {
-        // TODO: cleanup
-        throw new IOException("Stream reset");
-    }
-
     private void handleCommonFrame(Http2Frame f) throws IOException {
         if (f instanceof SettingsFrame) {
             SettingsFrame sf = (SettingsFrame) f;
@@ -276,9 +397,13 @@ public class Http2TestServerConnection {
             frame.streamid(0);
             outputQ.put(frame);
             return;
-        }
-        //System.err.println("TestServer: Received ---> " + f.toString());
-        throw new UnsupportedOperationException("Not supported yet.");
+        } else if (f instanceof GoAwayFrame) {
+            System.err.println("Closing: "+ f.toString());
+            close(ErrorFrame.NO_ERROR);
+        } else if (f instanceof PingFrame) {
+            handlePing((PingFrame)f);
+        } else
+            throw new UnsupportedOperationException("Not supported yet: " + f.toString());
     }
 
     void sendWindowUpdates(int len, int streamid) throws IOException {
@@ -290,7 +415,7 @@ public class Http2TestServerConnection {
         outputQ.put(wup);
     }
 
-    HttpHeadersImpl decodeHeaders(List<HeaderFrame> frames) {
+    HttpHeadersImpl decodeHeaders(List<HeaderFrame> frames) throws IOException {
         HttpHeadersImpl headers = new HttpHeadersImpl();
 
         DecodingCallback cb = (name, value) -> {
@@ -298,9 +423,9 @@ public class Http2TestServerConnection {
         };
 
         for (HeaderFrame frame : frames) {
-            ByteBufferReference[] buffers = frame.getHeaderBlock();
-            for (ByteBufferReference buffer : buffers) {
-                hpackIn.decode(buffer.get(), false, cb);
+            List<ByteBuffer> buffers = frame.getHeaderBlock();
+            for (ByteBuffer buffer : buffers) {
+                hpackIn.decode(buffer, false, cb);
             }
         }
         hpackIn.decode(EMPTY_BUFFER, true, cb);
@@ -359,7 +484,7 @@ public class Http2TestServerConnection {
         headers.setHeader(":scheme", "http"); // always in this case
         headers.setHeader(":authority", host);
         headers.setHeader(":path", uri.getPath());
-        Queue q = new Queue();
+        Queue q = new Queue(sentinel);
         String body = getRequestBody(request);
         addHeaders(getHeaders(request), headers);
         headers.setHeader("Content-length", Integer.toString(body.length()));
@@ -401,7 +526,7 @@ public class Http2TestServerConnection {
         }
         boolean endStreamReceived = endStream;
         HttpHeadersImpl headers = decodeHeaders(frames);
-        Queue q = new Queue();
+        Queue q = new Queue(sentinel);
         streams.put(streamid, q);
         exec.submit(() -> {
             handleRequest(headers, q, streamid, endStreamReceived);
@@ -428,7 +553,7 @@ public class Http2TestServerConnection {
         System.err.printf("TestServer: %s %s\n", method, path);
         HttpHeadersImpl rspheaders = new HttpHeadersImpl();
         int winsize = clientSettings.getParameter(
-                        SettingsFrame.INITIAL_WINDOW_SIZE);
+                SettingsFrame.INITIAL_WINDOW_SIZE);
         //System.err.println ("Stream window size = " + winsize);
 
         final InputStream bis;
@@ -442,10 +567,11 @@ public class Http2TestServerConnection {
         try (bis;
              BodyOutputStream bos = new BodyOutputStream(streamid, winsize, this))
         {
+            outStreams.put(streamid, bos);
             String us = scheme + "://" + authority + path;
             URI uri = new URI(us);
             boolean pushAllowed = clientSettings.getParameter(SettingsFrame.ENABLE_PUSH) == 1;
-            Http2TestExchange exchange = new Http2TestExchange(streamid, method,
+            Http2TestExchange exchange = exchangeSupplier.get(streamid, method,
                     headers, rspheaders, uri, bis, getSSLSession(),
                     bos, this, pushAllowed);
 
@@ -473,7 +599,11 @@ public class Http2TestServerConnection {
     void readLoop() {
         try {
             while (!stopping) {
-                Http2Frame frame = readFrame();
+                Http2Frame frame = readFrameImpl();
+                if (frame == null) {
+                    closeIncoming();
+                    return;
+                }
                 //System.err.printf("TestServer: received frame %s\n", frame);
                 int stream = frame.streamid();
                 if (stream == 0) {
@@ -497,7 +627,7 @@ public class Http2TestServerConnection {
                     } else {
                         if (q == null && !pushStreams.contains(stream)) {
                             System.err.printf("Non Headers frame received with"+
-                                " non existing stream (%d) ", frame.streamid());
+                                    " non existing stream (%d) ", frame.streamid());
                             System.err.println(frame);
                             continue;
                         }
@@ -507,6 +637,17 @@ public class Http2TestServerConnection {
                                 Consumer<Integer> r = updaters.get(stream);
                                 r.accept(wup.getUpdate());
                             }
+                        } else if (frame.type() == ResetFrame.TYPE) {
+                            // do orderly close on input q
+                            // and close the output q immediately
+                            // This should mean depending on what the
+                            // handler is doing: either an EOF on read
+                            // or an IOException if writing the response.
+                            q.orderlyClose();
+                            BodyOutputStream oq = outStreams.get(stream);
+                            if (oq != null)
+                                oq.closeInternal();
+
                         } else {
                             q.put(frame);
                         }
@@ -518,11 +659,11 @@ public class Http2TestServerConnection {
                 System.err.println("Http server reader thread shutdown");
                 e.printStackTrace();
             }
-            close();
+            close(ErrorFrame.PROTOCOL_ERROR);
         }
     }
 
-    ByteBufferReference[] encodeHeaders(HttpHeadersImpl headers) {
+    List<ByteBuffer> encodeHeaders(HttpHeadersImpl headers) {
         List<ByteBuffer> buffers = new LinkedList<>();
 
         ByteBuffer buf = getBuffer();
@@ -544,7 +685,7 @@ public class Http2TestServerConnection {
         }
         buf.flip();
         buffers.add(buf);
-        return ByteBufferReference.toReferences(buffers.toArray(bbarray));
+        return buffers;
     }
 
     static void closeIgnore(Closeable c) {
@@ -560,6 +701,8 @@ public class Http2TestServerConnection {
                 Http2Frame frame;
                 try {
                     frame = outputQ.take();
+                    if (stopping)
+                        break;
                 } catch(IOException x) {
                     if (stopping && x.getCause() instanceof InterruptedException) {
                         break;
@@ -587,7 +730,11 @@ public class Http2TestServerConnection {
 
     private void handlePush(OutgoingPushPromise op) throws IOException {
         int promisedStreamid = nextPushStreamId;
-        PushPromiseFrame pp = new PushPromiseFrame(op.parentStream, HeaderFrame.END_HEADERS, promisedStreamid, encodeHeaders(op.headers), 0);
+        PushPromiseFrame pp = new PushPromiseFrame(op.parentStream,
+                                                   HeaderFrame.END_HEADERS,
+                                                   promisedStreamid,
+                                                   encodeHeaders(op.headers),
+                                                   0);
         pushStreams.add(promisedStreamid);
         nextPushStreamId += 2;
         pp.streamid(op.parentStream);
@@ -597,6 +744,7 @@ public class Http2TestServerConnection {
                 promisedStreamid,
                 clientSettings.getParameter(
                         SettingsFrame.INITIAL_WINDOW_SIZE), this);
+        outStreams.put(promisedStreamid, oo);
         oo.goodToGo();
         exec.submit(() -> {
             try {
@@ -630,27 +778,46 @@ public class Http2TestServerConnection {
     }
 
     private Http2Frame readFrame() throws IOException {
-        byte[] buf = new byte[9];
-        if (is.readNBytes(buf, 0, 9) != 9)
-            throw new IOException("readFrame: connection closed");
-        int len = 0;
-        for (int i = 0; i < 3; i++) {
-            int n = buf[i] & 0xff;
-            //System.err.println("n = " + n);
-            len = (len << 8) + n;
-        }
-        byte[] rest = new byte[len];
-        int n = is.readNBytes(rest, 0, len);
-        if (n != len)
-            throw new IOException("Error reading frame");
-        List<Http2Frame> frames = new ArrayList<>();
-        FramesDecoder reader = new FramesDecoder(frames::add);
-        reader.decode(ByteBufferReference.of(ByteBuffer.wrap(buf)));
-        reader.decode(ByteBufferReference.of(ByteBuffer.wrap(rest)));
-        if (frames.size()!=1)
-            throw new IOException("Expected 1 frame got "+frames.size()) ;
+        Http2Frame f = readFrameImpl();
+        if (f == null)
+            throw new IOException("connection closed");
+        return f;
+    }
 
-        return frames.get(0);
+    // does not throw an exception for EOF
+    private Http2Frame readFrameImpl() throws IOException {
+        try {
+            byte[] buf = new byte[9];
+            int ret;
+            ret=is.readNBytes(buf, 0, 9);
+            if (ret == 0) {
+                return null;
+            } else if (ret != 9) {
+                throw new IOException("readFrame: connection closed");
+            }
+            int len = 0;
+            for (int i = 0; i < 3; i++) {
+                int n = buf[i] & 0xff;
+                //System.err.println("n = " + n);
+                len = (len << 8) + n;
+            }
+            byte[] rest = new byte[len];
+            int n = is.readNBytes(rest, 0, len);
+            if (n != len)
+                throw new IOException("Error reading frame");
+            List<Http2Frame> frames = new ArrayList<>();
+            FramesDecoder reader = new FramesDecoder(frames::add);
+            reader.decode(ByteBuffer.wrap(buf));
+            reader.decode(ByteBuffer.wrap(rest));
+            if (frames.size()!=1)
+                throw new IOException("Expected 1 frame got "+frames.size()) ;
+
+            return frames.get(0);
+        } catch (IOException ee) {
+            if (stopping)
+                return null;
+            throw ee;
+        }
     }
 
     void sendSettingsFrame() throws IOException {
@@ -721,11 +888,29 @@ public class Http2TestServerConnection {
     String readHttp1Request() throws IOException {
         String headers = readUntil(CRLF + CRLF);
         int clen = getContentLength(headers);
-        // read the content.
-        byte[] buf = new byte[clen];
-        is.readNBytes(buf, 0, clen);
-        String body = new String(buf, StandardCharsets.US_ASCII);
-        return headers + body;
+        String te = getHeader(headers, "Transfer-encoding");
+        byte[] buf = new byte[0];
+        try {
+            if (clen >= 0) {
+                // HTTP/1.1 fixed length content ( may be 0 ), read it
+                buf = new byte[clen];
+                is.readNBytes(buf, 0, clen);
+            } else if ("chunked".equalsIgnoreCase(te)) {
+                //  HTTP/1.1 chunked data, read it
+                buf = readChunkedInputStream(is);
+            }
+            String body = new String(buf, StandardCharsets.US_ASCII);
+            return headers + body;
+        } catch (IOException e) {
+            System.err.println("TestServer: headers read: [ " + headers + " ]");
+            throw e;
+        }
+    }
+
+    // This is a quick hack to get a chunked input stream reader.
+    private static byte[] readChunkedInputStream(InputStream is) throws IOException {
+        ChunkedInputStream cis = new ChunkedInputStream(is, new HttpClient() {}, null);
+        return cis.readAllBytes();
     }
 
     void sendHttp1Response(int code, String msg, String... headers) throws IOException {
@@ -771,7 +956,7 @@ public class Http2TestServerConnection {
     @SuppressWarnings({"rawtypes","unchecked"})
     void addRequestBodyToQueue(String body, Queue q) throws IOException {
         ByteBuffer buf = ByteBuffer.wrap(body.getBytes(StandardCharsets.US_ASCII));
-        DataFrame df = new DataFrame(1, DataFrame.END_STREAM, ByteBufferReference.of(buf));
+        DataFrame df = new DataFrame(1, DataFrame.END_STREAM, buf);
         // only used for primordial stream
         q.put(df);
     }
@@ -795,13 +980,13 @@ public class Http2TestServerConnection {
      * @param amount
      */
     synchronized void obtainConnectionWindow(int amount) throws InterruptedException {
-       while (amount > 0) {
-           int n = Math.min(amount, sendWindow);
-           amount -= n;
-           sendWindow -= n;
-           if (amount > 0)
-               wait();
-       }
+        while (amount > 0) {
+            int n = Math.min(amount, sendWindow);
+            amount -= n;
+            sendWindow -= n;
+            if (amount > 0)
+                wait();
+        }
     }
 
     synchronized void updateConnectionWindow(int amount) {
@@ -823,9 +1008,9 @@ public class Http2TestServerConnection {
     }
 
     static class NullInputStream extends InputStream {
-       static final NullInputStream INSTANCE = new NullInputStream();
-       private NullInputStream() {}
-       public int read()      { return -1; }
-       public int available() { return 0;  }
-   }
+        static final NullInputStream INSTANCE = new NullInputStream();
+        private NullInputStream() {}
+        public int read()      { return -1; }
+        public int available() { return 0;  }
+    }
 }
diff --git a/test/jdk/java/net/httpclient/http2/server/NoBodyHandler.java b/test/jdk/java/net/httpclient/http2/server/NoBodyHandler.java
index b80a398a6bb..6cf7ea3d4ed 100644
--- a/test/jdk/java/net/httpclient/http2/server/NoBodyHandler.java
+++ b/test/jdk/java/net/httpclient/http2/server/NoBodyHandler.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2017, 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
diff --git a/test/jdk/java/net/httpclient/http2/server/OutgoingPushPromise.java b/test/jdk/java/net/httpclient/http2/server/OutgoingPushPromise.java
index 905ee3d4a38..7ef4684e591 100644
--- a/test/jdk/java/net/httpclient/http2/server/OutgoingPushPromise.java
+++ b/test/jdk/java/net/httpclient/http2/server/OutgoingPushPromise.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2017, 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
diff --git a/test/jdk/java/net/httpclient/http2/server/PushHandler.java b/test/jdk/java/net/httpclient/http2/server/PushHandler.java
index e2af22b822d..fcfb4ce7fcd 100644
--- a/test/jdk/java/net/httpclient/http2/server/PushHandler.java
+++ b/test/jdk/java/net/httpclient/http2/server/PushHandler.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2017, 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
diff --git a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/common/Queue.java b/test/jdk/java/net/httpclient/http2/server/Queue.java
similarity index 63%
rename from src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/common/Queue.java
rename to test/jdk/java/net/httpclient/http2/server/Queue.java
index 004c3a4d5e4..cc789272317 100644
--- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/common/Queue.java
+++ b/test/jdk/java/net/httpclient/http2/server/Queue.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2017, 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
@@ -23,37 +23,34 @@
  * questions.
  */
 
-package jdk.incubator.http.internal.common;
-
 import java.io.IOException;
 import java.util.LinkedList;
-import java.util.stream.Stream;
 import java.util.Objects;
+import java.util.stream.Stream;
 
 // Each stream has one of these for input. Each Http2Connection has one
 // for output. Can be used blocking or asynchronously.
 
-public class Queue<T> implements ExceptionallyCloseable  {
+public class Queue<T> implements ExceptionallyCloseable {
 
     private final LinkedList<T> q = new LinkedList<>();
-    private volatile boolean closed = false;
-    private volatile Throwable exception = null;
-    private Runnable callback;
-    private boolean callbackDisabled = false;
+    private boolean closed = false;
+    private boolean closing = false;
+    private Throwable exception = null;
     private int waiters; // true if someone waiting
+    private final T closeSentinel;
+
+    Queue(T closeSentinel) {
+        this.closeSentinel = Objects.requireNonNull(closeSentinel);
+    }
 
     public synchronized int size() {
         return q.size();
     }
 
-    public synchronized boolean tryPut(T obj) throws IOException {
-        if (closed) return false;
-        put(obj);
-        return true;
-    }
-
     public synchronized void put(T obj) throws IOException {
-        if (closed) {
+        Objects.requireNonNull(obj);
+        if (closed || closing) {
             throw new IOException("stream closed");
         }
 
@@ -62,45 +59,27 @@ public class Queue<T> implements ExceptionallyCloseable  {
         if (waiters > 0) {
             notifyAll();
         }
+    }
 
-        if (callbackDisabled) {
+    // Other close() variants are immediate and abortive
+    // This allows whatever is on Q to be processed first.
+
+    public synchronized void orderlyClose() {
+        if (closing || closed)
             return;
-        }
 
-        if (q.size() > 0 && callback != null) {
-            // Note: calling callback while holding the lock is
-            // dangerous and may lead to deadlocks.
-            callback.run();
-        }
-    }
-
-    public synchronized void disableCallback() {
-        callbackDisabled = true;
-    }
-
-    public synchronized void enableCallback() {
-        callbackDisabled = false;
-        while (q.size() > 0) {
-            callback.run();
-        }
-    }
-
-    /**
-     * callback is invoked any time put is called where
-     * the Queue was empty.
-     */
-    public synchronized void registerPutCallback(Runnable callback) {
-        Objects.requireNonNull(callback);
-        this.callback = callback;
-        if (q.size() > 0) {
-            // Note: calling callback while holding the lock is
-            // dangerous and may lead to deadlocks.
-            callback.run();
+        try {
+            put(closeSentinel);
+        } catch (IOException e) {
+            e.printStackTrace();
         }
+        closing = true;
     }
 
     @Override
     public synchronized void close() {
+        if (closed)
+            return;
         closed = true;
         notifyAll();
     }
@@ -133,7 +112,13 @@ public class Queue<T> implements ExceptionallyCloseable  {
                 }
                 waiters--;
             }
-            return q.removeFirst();
+            T item = q.removeFirst();
+            if (item.equals(closeSentinel)) {
+                closed = true;
+                assert q.isEmpty();
+                return null;
+            }
+            return item;
         } catch (InterruptedException ex) {
             throw new IOException(ex);
         }
@@ -147,24 +132,7 @@ public class Queue<T> implements ExceptionallyCloseable  {
         if (q.isEmpty()) {
             return null;
         }
-        T res = q.removeFirst();
-        return res;
-    }
-
-    public synchronized T[] pollAll(T[] type) throws IOException {
-        T[] ret = q.toArray(type);
-        q.clear();
-        return ret;
-    }
-
-    public synchronized void pushback(T v) {
-        q.addFirst(v);
-    }
-
-    public synchronized void pushbackAll(T[] v) {
-        for (int i=v.length-1; i>=0; i--) {
-            q.addFirst(v[i]);
-        }
+        return take();
     }
 
     private IOException newIOException(String msg) {
@@ -174,5 +142,4 @@ public class Queue<T> implements ExceptionallyCloseable  {
             return new IOException(msg, exception);
         }
     }
-
 }
diff --git a/test/jdk/java/net/httpclient/http2/server/TestUtil.java b/test/jdk/java/net/httpclient/http2/server/TestUtil.java
index 2bd85a29a59..a439f0a2596 100644
--- a/test/jdk/java/net/httpclient/http2/server/TestUtil.java
+++ b/test/jdk/java/net/httpclient/http2/server/TestUtil.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2017, 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
diff --git a/test/jdk/java/net/httpclient/security/0.policy b/test/jdk/java/net/httpclient/security/0.policy
index 4eeb7f3a8d2..d9b7a757bdc 100644
--- a/test/jdk/java/net/httpclient/security/0.policy
+++ b/test/jdk/java/net/httpclient/security/0.policy
@@ -1,10 +1,32 @@
+//
+// Copyright (c) 2016, 2017, 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.
+//
+
 // Policy: 0
 
 grant {
     // permissions common to all tests
     permission java.util.PropertyPermission "*", "read";
     permission java.io.FilePermission "${test.classes}${/}-", "read,write,delete";
-    permission java.net.NetPermission "getDefaultHttpClient";
     permission java.lang.RuntimePermission "modifyThread";
     permission java.util.logging.LoggingPermission "control", "";
     permission java.net.SocketPermission "localhost:1024-", "accept,listen";
@@ -40,8 +62,6 @@ grant codeBase "jrt:/jdk.incubator.httpclient" {
 
     permission java.util.PropertyPermission "jdk.httpclient.*","read";
 
-    // ## these permissions do not appear in the NetPermission spec!!! JDK bug?
-    permission java.net.NetPermission "getSSLContext";
-    permission java.net.NetPermission "setSSLContext";
+    permission java.net.NetPermission "getProxySelector";
 };
 
diff --git a/test/jdk/java/net/httpclient/security/1.policy b/test/jdk/java/net/httpclient/security/1.policy
index 6527ba9219f..964e8b643dc 100644
--- a/test/jdk/java/net/httpclient/security/1.policy
+++ b/test/jdk/java/net/httpclient/security/1.policy
@@ -1,9 +1,31 @@
+//
+// Copyright (c) 2016, 2017, 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.
+//
+
 // Policy 1
 grant {
     // permissions common to all tests
     permission java.util.PropertyPermission "*", "read";
     permission java.io.FilePermission "${test.classes}${/}-", "read,write,delete";
-    permission java.net.NetPermission "getDefaultHttpClient";
     permission java.lang.RuntimePermission "modifyThread";
     permission java.util.logging.LoggingPermission "control", "";
     permission java.net.SocketPermission "localhost:1024-", "accept,listen";
@@ -40,9 +62,7 @@ grant codeBase "jrt:/jdk.incubator.httpclient" {
 
     permission java.util.PropertyPermission "jdk.httpclient.*","read";
 
-    // ## these permissions do not appear in the NetPermission spec!!! JDK bug?
-    permission java.net.NetPermission "getSSLContext";
-    permission java.net.NetPermission "setSSLContext";
+    permission java.net.NetPermission "getProxySelector";
 };
 
 
diff --git a/test/jdk/java/net/httpclient/security/10.policy b/test/jdk/java/net/httpclient/security/10.policy
index ae806ff2019..32a985c33f8 100644
--- a/test/jdk/java/net/httpclient/security/10.policy
+++ b/test/jdk/java/net/httpclient/security/10.policy
@@ -1,9 +1,31 @@
+//
+// Copyright (c) 2016, 2017, 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.
+//
+
 // Policy 10
 grant {
     // permissions common to all tests
     permission java.util.PropertyPermission "*", "read";
     permission java.io.FilePermission "${test.classes}${/}-", "read,write,delete";
-    permission java.net.NetPermission "getDefaultHttpClient";
     permission java.lang.RuntimePermission "modifyThread";
     permission java.util.logging.LoggingPermission "control", "";
     permission java.net.SocketPermission "localhost:1024-", "accept,listen";
@@ -39,8 +61,6 @@ grant codeBase "jrt:/jdk.incubator.httpclient" {
 
     permission java.util.PropertyPermission "jdk.httpclient.*","read";
 
-    // ## these permissions do not appear in the NetPermission spec!!! JDK bug?
-    permission java.net.NetPermission "getSSLContext";
-    permission java.net.NetPermission "setSSLContext";
+    permission java.net.NetPermission "getProxySelector";
 };
 
diff --git a/test/jdk/java/net/httpclient/security/11.policy b/test/jdk/java/net/httpclient/security/11.policy
index 28210946243..4db4cfe6e80 100644
--- a/test/jdk/java/net/httpclient/security/11.policy
+++ b/test/jdk/java/net/httpclient/security/11.policy
@@ -1,9 +1,31 @@
+//
+// Copyright (c) 2016, 2017, 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.
+//
+
 // Policy 11
 grant {
     // permissions common to all tests
     permission java.util.PropertyPermission "*", "read";
     permission java.io.FilePermission "${test.classes}${/}-", "read,write,delete";
-    permission java.net.NetPermission "getDefaultHttpClient";
     permission java.lang.RuntimePermission "modifyThread";
     permission java.util.logging.LoggingPermission "control", "";
     permission java.net.SocketPermission "localhost:1024-", "accept,listen";
@@ -41,8 +63,6 @@ grant codeBase "jrt:/jdk.incubator.httpclient" {
 
     permission java.util.PropertyPermission "jdk.httpclient.*","read";
 
-    // ## these permissions do not appear in the NetPermission spec!!! JDK bug?
-    permission java.net.NetPermission "getSSLContext";
-    permission java.net.NetPermission "setSSLContext";
+    permission java.net.NetPermission "getProxySelector";
 };
 
diff --git a/test/jdk/java/net/httpclient/security/12.policy b/test/jdk/java/net/httpclient/security/12.policy
index 28210946243..4db4cfe6e80 100644
--- a/test/jdk/java/net/httpclient/security/12.policy
+++ b/test/jdk/java/net/httpclient/security/12.policy
@@ -1,9 +1,31 @@
+//
+// Copyright (c) 2016, 2017, 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.
+//
+
 // Policy 11
 grant {
     // permissions common to all tests
     permission java.util.PropertyPermission "*", "read";
     permission java.io.FilePermission "${test.classes}${/}-", "read,write,delete";
-    permission java.net.NetPermission "getDefaultHttpClient";
     permission java.lang.RuntimePermission "modifyThread";
     permission java.util.logging.LoggingPermission "control", "";
     permission java.net.SocketPermission "localhost:1024-", "accept,listen";
@@ -41,8 +63,6 @@ grant codeBase "jrt:/jdk.incubator.httpclient" {
 
     permission java.util.PropertyPermission "jdk.httpclient.*","read";
 
-    // ## these permissions do not appear in the NetPermission spec!!! JDK bug?
-    permission java.net.NetPermission "getSSLContext";
-    permission java.net.NetPermission "setSSLContext";
+    permission java.net.NetPermission "getProxySelector";
 };
 
diff --git a/test/jdk/java/net/httpclient/security/14.policy b/test/jdk/java/net/httpclient/security/14.policy
index 859c4f85a4e..5cd778beb87 100644
--- a/test/jdk/java/net/httpclient/security/14.policy
+++ b/test/jdk/java/net/httpclient/security/14.policy
@@ -1,9 +1,31 @@
+//
+// Copyright (c) 2016, 2017, 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.
+//
+
 // Policy 14
 grant {
     // permissions common to all tests
     permission java.util.PropertyPermission "*", "read";
     permission java.io.FilePermission "${test.classes}${/}-", "read,write,delete";
-    permission java.net.NetPermission "getDefaultHttpClient";
     permission java.lang.RuntimePermission "modifyThread";
     permission java.util.logging.LoggingPermission "control", "";
     permission java.net.SocketPermission "localhost:1024-", "accept,listen";
@@ -40,8 +62,6 @@ grant codeBase "jrt:/jdk.incubator.httpclient" {
 
     permission java.util.PropertyPermission "jdk.httpclient.*","read";
 
-    // ## these permissions do not appear in the NetPermission spec!!! JDK bug?
-    permission java.net.NetPermission "getSSLContext";
-    permission java.net.NetPermission "setSSLContext";
+    permission java.net.NetPermission "getProxySelector";
 };
 
diff --git a/test/jdk/java/net/httpclient/security/15.policy b/test/jdk/java/net/httpclient/security/15.policy
index c25941bfc2a..5418bbea054 100644
--- a/test/jdk/java/net/httpclient/security/15.policy
+++ b/test/jdk/java/net/httpclient/security/15.policy
@@ -1,9 +1,31 @@
+//
+// Copyright (c) 2016, 2017, 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.
+//
+
 // Policy 11
 grant {
     // permissions common to all tests
     permission java.util.PropertyPermission "*", "read";
     permission java.io.FilePermission "${test.classes}${/}-", "read,write,delete";
-    permission java.net.NetPermission "getDefaultHttpClient";
     permission java.lang.RuntimePermission "modifyThread";
     permission java.util.logging.LoggingPermission "control", "";
     permission java.net.SocketPermission "localhost:1024-", "accept,listen";
@@ -43,8 +65,6 @@ grant codeBase "jrt:/jdk.incubator.httpclient" {
 
     permission java.util.PropertyPermission "jdk.httpclient.*","read";
 
-    // ## these permissions do not appear in the NetPermission spec!!! JDK bug?
-    permission java.net.NetPermission "getSSLContext";
-    permission java.net.NetPermission "setSSLContext";
+    permission java.net.NetPermission "getProxySelector";
 };
 
diff --git a/test/jdk/java/net/httpclient/security/2.policy b/test/jdk/java/net/httpclient/security/2.policy
index efb4876bae3..2b3e1b9f7ed 100644
--- a/test/jdk/java/net/httpclient/security/2.policy
+++ b/test/jdk/java/net/httpclient/security/2.policy
@@ -1,9 +1,31 @@
+//
+// Copyright (c) 2016, 2017, 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.
+//
+
 // Policy 2
 grant {
     // permissions common to all tests
     permission java.util.PropertyPermission "*", "read";
     permission java.io.FilePermission "${test.classes}${/}-", "read,write,delete";
-    permission java.net.NetPermission "getDefaultHttpClient";
     permission java.lang.RuntimePermission "modifyThread";
     permission java.util.logging.LoggingPermission "control", "";
     permission java.net.SocketPermission "localhost:1024-", "accept,listen";
@@ -40,8 +62,6 @@ grant codeBase "jrt:/jdk.incubator.httpclient" {
 
     permission java.util.PropertyPermission "jdk.httpclient.*","read";
 
-    // ## these permissions do not appear in the NetPermission spec!!! JDK bug?
-    permission java.net.NetPermission "getSSLContext";
-    permission java.net.NetPermission "setSSLContext";
+    permission java.net.NetPermission "getProxySelector";
 };
 
diff --git a/test/jdk/java/net/httpclient/security/3.policy b/test/jdk/java/net/httpclient/security/3.policy
index f64232d1f7d..b876accab84 100644
--- a/test/jdk/java/net/httpclient/security/3.policy
+++ b/test/jdk/java/net/httpclient/security/3.policy
@@ -1,9 +1,31 @@
+//
+// Copyright (c) 2016, 2017, 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.
+//
+
 // Policy 3
 grant {
     // permissions common to all tests
     permission java.util.PropertyPermission "*", "read";
     permission java.io.FilePermission "${test.classes}${/}-", "read,write,delete";
-    permission java.net.NetPermission "getDefaultHttpClient";
     permission java.lang.RuntimePermission "modifyThread";
     permission java.util.logging.LoggingPermission "control", "";
     permission java.net.SocketPermission "localhost:1024-", "accept,listen";
@@ -40,8 +62,6 @@ grant codeBase "jrt:/jdk.incubator.httpclient" {
 
     permission java.util.PropertyPermission "jdk.httpclient.*","read";
 
-    // ## these permissions do not appear in the NetPermission spec!!! JDK bug?
-    permission java.net.NetPermission "getSSLContext";
-    permission java.net.NetPermission "setSSLContext";
+    permission java.net.NetPermission "getProxySelector";
 };
 
diff --git a/test/jdk/java/net/httpclient/security/4.policy b/test/jdk/java/net/httpclient/security/4.policy
index 8f6011743fd..f06f38433bc 100644
--- a/test/jdk/java/net/httpclient/security/4.policy
+++ b/test/jdk/java/net/httpclient/security/4.policy
@@ -1,9 +1,31 @@
+//
+// Copyright (c) 2016, 2017, 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.
+//
+
 // Policy 4
 grant {
     // permissions common to all tests
     permission java.util.PropertyPermission "*", "read";
     permission java.io.FilePermission "${test.classes}${/}-", "read,write,delete";
-    permission java.net.NetPermission "getDefaultHttpClient";
     permission java.lang.RuntimePermission "modifyThread";
     permission java.util.logging.LoggingPermission "control", "";
     permission java.net.SocketPermission "localhost:1024-", "accept,listen";
@@ -41,8 +63,6 @@ grant codeBase "jrt:/jdk.incubator.httpclient" {
 
     permission java.util.PropertyPermission "jdk.httpclient.*","read";
 
-    // ## these permissions do not appear in the NetPermission spec!!! JDK bug?
-    permission java.net.NetPermission "getSSLContext";
-    permission java.net.NetPermission "setSSLContext";
+    permission java.net.NetPermission "getProxySelector";
 };
 
diff --git a/test/jdk/java/net/httpclient/security/5.policy b/test/jdk/java/net/httpclient/security/5.policy
index 0b7d146daf5..3afcb50c676 100644
--- a/test/jdk/java/net/httpclient/security/5.policy
+++ b/test/jdk/java/net/httpclient/security/5.policy
@@ -1,9 +1,31 @@
+//
+// Copyright (c) 2016, 2017, 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.
+//
+
 // Policy 5
 grant {
     // permissions common to all tests
     permission java.util.PropertyPermission "*", "read";
     permission java.io.FilePermission "${test.classes}${/}-", "read,write,delete";
-    permission java.net.NetPermission "getDefaultHttpClient";
     permission java.lang.RuntimePermission "modifyThread";
     permission java.util.logging.LoggingPermission "control", "";
     permission java.net.SocketPermission "localhost:1024-", "accept,listen";
@@ -40,8 +62,6 @@ grant codeBase "jrt:/jdk.incubator.httpclient" {
 
     permission java.util.PropertyPermission "jdk.httpclient.*","read";
 
-    // ## these permissions do not appear in the NetPermission spec!!! JDK bug?
-    permission java.net.NetPermission "getSSLContext";
-    permission java.net.NetPermission "setSSLContext";
+    permission java.net.NetPermission "getProxySelector";
 };
 
diff --git a/test/jdk/java/net/httpclient/security/6.policy b/test/jdk/java/net/httpclient/security/6.policy
index 166f60906f1..b2dffc008c8 100644
--- a/test/jdk/java/net/httpclient/security/6.policy
+++ b/test/jdk/java/net/httpclient/security/6.policy
@@ -1,9 +1,31 @@
+//
+// Copyright (c) 2016, 2017, 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.
+//
+
 // Policy 6
 grant {
     // permissions common to all tests
     permission java.util.PropertyPermission "*", "read";
     permission java.io.FilePermission "${test.classes}${/}-", "read,write,delete";
-    permission java.net.NetPermission "getDefaultHttpClient";
     permission java.lang.RuntimePermission "modifyThread";
     permission java.util.logging.LoggingPermission "control", "";
     permission java.net.SocketPermission "localhost:1024-", "accept,listen";
@@ -40,8 +62,6 @@ grant codeBase "jrt:/jdk.incubator.httpclient" {
 
     permission java.util.PropertyPermission "jdk.httpclient.*","read";
 
-    // ## these permissions do not appear in the NetPermission spec!!! JDK bug?
-    permission java.net.NetPermission "getSSLContext";
-    permission java.net.NetPermission "setSSLContext";
+    permission java.net.NetPermission "getProxySelector";
 };
 
diff --git a/test/jdk/java/net/httpclient/security/7.policy b/test/jdk/java/net/httpclient/security/7.policy
index ea963c3f449..b0f6bc49308 100644
--- a/test/jdk/java/net/httpclient/security/7.policy
+++ b/test/jdk/java/net/httpclient/security/7.policy
@@ -1,9 +1,31 @@
+//
+// Copyright (c) 2016, 2017, 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.
+//
+
 // Policy 7
 grant {
     // permissions common to all tests
     permission java.util.PropertyPermission "*", "read";
     permission java.io.FilePermission "${test.classes}${/}-", "read,write,delete";
-    permission java.net.NetPermission "getDefaultHttpClient";
     permission java.lang.RuntimePermission "modifyThread";
     permission java.util.logging.LoggingPermission "control", "";
     permission java.net.SocketPermission "localhost:1024-", "accept,listen";
@@ -40,8 +62,6 @@ grant codeBase "jrt:/jdk.incubator.httpclient" {
 
     permission java.util.PropertyPermission "jdk.httpclient.*","read";
 
-    // ## these permissions do not appear in the NetPermission spec!!! JDK bug?
-    permission java.net.NetPermission "getSSLContext";
-    permission java.net.NetPermission "setSSLContext";
+    permission java.net.NetPermission "getProxySelector";
 };
 
diff --git a/test/jdk/java/net/httpclient/security/8.policy b/test/jdk/java/net/httpclient/security/8.policy
index 63265f80d6e..b5dbcf2366a 100644
--- a/test/jdk/java/net/httpclient/security/8.policy
+++ b/test/jdk/java/net/httpclient/security/8.policy
@@ -1,9 +1,31 @@
+//
+// Copyright (c) 2016, 2017, 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.
+//
+
 // Policy 8
 grant {
     // permissions common to all tests
     permission java.util.PropertyPermission "*", "read";
     permission java.io.FilePermission "${test.classes}${/}-", "read,write,delete";
-    permission java.net.NetPermission "getDefaultHttpClient";
     permission java.lang.RuntimePermission "modifyThread";
     permission java.util.logging.LoggingPermission "control", "";
     permission java.net.SocketPermission "localhost:1024-", "accept,listen";
@@ -40,8 +62,6 @@ grant codeBase "jrt:/jdk.incubator.httpclient" {
 
     permission java.util.PropertyPermission "jdk.httpclient.*","read";
 
-    // ## these permissions do not appear in the NetPermission spec!!! JDK bug?
-    permission java.net.NetPermission "getSSLContext";
-    permission java.net.NetPermission "setSSLContext";
+    permission java.net.NetPermission "getProxySelector";
 };
 
diff --git a/test/jdk/java/net/httpclient/security/9.policy b/test/jdk/java/net/httpclient/security/9.policy
index 4597c2f7dae..2aa216552b4 100644
--- a/test/jdk/java/net/httpclient/security/9.policy
+++ b/test/jdk/java/net/httpclient/security/9.policy
@@ -1,9 +1,31 @@
+//
+// Copyright (c) 2016, 2017, 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.
+//
+
 // Policy 9
 grant {
     // permissions common to all tests
     permission java.util.PropertyPermission "*", "read";
     permission java.io.FilePermission "${test.classes}${/}-", "read,write,delete";
-    permission java.net.NetPermission "getDefaultHttpClient";
     permission java.lang.RuntimePermission "modifyThread";
     permission java.util.logging.LoggingPermission "control", "";
     permission java.net.SocketPermission "localhost:1024-", "accept,listen";
@@ -40,8 +62,6 @@ grant codeBase "jrt:/jdk.incubator.httpclient" {
 
     permission java.util.PropertyPermission "jdk.httpclient.*","read";
 
-    // ## these permissions do not appear in the NetPermission spec!!! JDK bug?
-    permission java.net.NetPermission "getSSLContext";
-    permission java.net.NetPermission "setSSLContext";
+    permission java.net.NetPermission "getProxySelector";
 };
 
diff --git a/test/jdk/java/net/httpclient/security/Driver.java b/test/jdk/java/net/httpclient/security/Driver.java
index 48d3d78dc27..3ccec62fdb5 100644
--- a/test/jdk/java/net/httpclient/security/Driver.java
+++ b/test/jdk/java/net/httpclient/security/Driver.java
@@ -34,7 +34,7 @@
  * @compile ../ProxyServer.java
  * @build Security
  *
- * @run driver/timeout=90 Driver
+ * @run main/othervm Driver
  */
 
 /**
@@ -52,8 +52,6 @@ import java.io.OutputStream;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.stream.Collectors;
-
-import jdk.testlibrary.OutputAnalyzer;
 import jdk.testlibrary.Utils;
 
 /**
@@ -123,6 +121,8 @@ public class Driver {
         while (retval == 10) {
             List<String> cmd = new ArrayList<>();
             cmd.add(javaCmd);
+            cmd.add("-ea");
+            cmd.add("-esa");
             cmd.add("-Dtest.jdk=" + testJdk);
             cmd.add("-Dtest.src=" + testSrc);
             cmd.add("-Dtest.classes=" + testClasses);
@@ -142,11 +142,15 @@ public class Driver {
                 .redirectErrorStream(true);
 
             String cmdLine = cmd.stream().collect(Collectors.joining(" "));
+            long start = System.currentTimeMillis();
             Process child = processBuilder.start();
             Logger log = new Logger(cmdLine, child, testClasses);
             log.start();
             retval = child.waitFor();
-            System.out.println("retval = " + retval);
+            long elapsed = System.currentTimeMillis() - start;
+            System.out.println("Security " + testnum
+                               + ": retval = " + retval
+                               + ", duration=" + elapsed+" ms");
         }
         if (retval != 0) {
             Thread.sleep(2000);
diff --git a/test/jdk/java/net/httpclient/security/Security.java b/test/jdk/java/net/httpclient/security/Security.java
index 4fa142dfcff..d6b1c623efd 100644
--- a/test/jdk/java/net/httpclient/security/Security.java
+++ b/test/jdk/java/net/httpclient/security/Security.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2017, 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
@@ -82,7 +82,6 @@ import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.CompletionException;
 import java.util.concurrent.CompletionStage;
 import java.util.concurrent.ExecutionException;
-import java.util.concurrent.Executor;
 import java.util.concurrent.Executors;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Flow;
@@ -297,15 +296,15 @@ public class Security {
                 CompletableFuture<HttpResponse<String>> cf =
                     client.sendAsync(request, new HttpResponse.BodyHandler<String>() {
                         @Override
-                        public HttpResponse.BodyProcessor<String> apply(int status, HttpHeaders responseHeaders)  {
-                            final HttpResponse.BodyProcessor<String> stproc = sth.apply(status, responseHeaders);
-                            return new HttpResponse.BodyProcessor<String>() {
+                        public HttpResponse.BodySubscriber<String> apply(int status, HttpHeaders responseHeaders)  {
+                            final HttpResponse.BodySubscriber<String> stproc = sth.apply(status, responseHeaders);
+                            return new HttpResponse.BodySubscriber<String>() {
                                 @Override
                                 public CompletionStage<String> getBody() {
                                     return stproc.getBody();
                                 }
                                 @Override
-                                public void onNext(ByteBuffer item) {
+                                public void onNext(List<ByteBuffer> item) {
                                     SecurityManager sm = System.getSecurityManager();
                                     // should succeed.
                                     sm.checkPermission(new RuntimePermission("foobar"));
@@ -337,6 +336,9 @@ public class Security {
                     Throwable t = e.getCause();
                     if (t instanceof SecurityException)
                         throw (SecurityException)t;
+                    else if ((t instanceof IOException)
+                              && (t.getCause() instanceof SecurityException))
+                        throw ((SecurityException)t.getCause());
                     else
                         throw new RuntimeException(t);
                 }
@@ -379,7 +381,7 @@ public class Security {
             r.execute();
             if (!succeeds) {
                 System.out.println("FAILED: expected security exception");
-                throw new RuntimeException("Failed");
+                throw new RuntimeException("FAILED: expected security exception\"");
             }
             System.out.println (policy + " succeeded as expected");
         } catch (BindException e) {
@@ -419,12 +421,6 @@ public class Security {
         } finally {
             s1.stop(0);
             executor.shutdownNow();
-            for (HttpClient client : clients) {
-                Executor e = client.executor();
-                if (e instanceof ExecutorService) {
-                    ((ExecutorService)e).shutdownNow();
-                }
-            }
         }
     }
 
diff --git a/test/jdk/java/net/httpclient/security/filePerms/FileProcessorPermissionTest.java b/test/jdk/java/net/httpclient/security/filePerms/FileProcessorPermissionTest.java
new file mode 100644
index 00000000000..d8bf837adbc
--- /dev/null
+++ b/test/jdk/java/net/httpclient/security/filePerms/FileProcessorPermissionTest.java
@@ -0,0 +1,138 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @summary Basic checks for SecurityException from body processors APIs
+ * @run testng/othervm/java.security.policy=httpclient.policy FileProcessorPermissionTest
+ */
+
+import java.io.FilePermission;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.security.AccessControlContext;
+import java.security.AccessController;
+import java.security.Permission;
+import java.security.Permissions;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
+import java.security.ProtectionDomain;
+import java.util.List;
+import jdk.incubator.http.HttpRequest;
+import jdk.incubator.http.HttpResponse;
+import org.testng.annotations.Test;
+import static java.nio.file.StandardOpenOption.*;
+import static org.testng.Assert.*;
+
+public class FileProcessorPermissionTest {
+
+    static final String testSrc = System.getProperty("test.src", ".");
+    static final Path fromFilePath = Paths.get(testSrc, "FileProcessorPermissionTest.java");
+    static final Path asFilePath = Paths.get(testSrc, "asFile.txt");
+    static final Path CWD = Paths.get(".");
+    static final Class<SecurityException> SE = SecurityException.class;
+
+    static AccessControlContext withPermissions(Permission... perms) {
+        Permissions p = new Permissions();
+        for (Permission perm : perms) {
+            p.add(perm);
+        }
+        ProtectionDomain pd = new ProtectionDomain(null, p);
+        return new AccessControlContext(new ProtectionDomain[]{ pd });
+    }
+
+    static AccessControlContext noPermissions() {
+        return withPermissions(/*empty*/);
+    }
+
+    @Test
+    public void test() throws Exception {
+        List<PrivilegedExceptionAction<?>> list = List.of(
+                () -> HttpRequest.BodyPublisher.fromFile(fromFilePath),
+
+                () -> HttpResponse.BodyHandler.asFile(asFilePath),
+                () -> HttpResponse.BodyHandler.asFile(asFilePath, CREATE),
+                () -> HttpResponse.BodyHandler.asFile(asFilePath, CREATE, WRITE),
+                () -> HttpResponse.BodyHandler.asFile(asFilePath, CREATE, WRITE, READ),
+                () -> HttpResponse.BodyHandler.asFile(asFilePath, CREATE, WRITE, READ, DELETE_ON_CLOSE),
+
+                () -> HttpResponse.BodyHandler.asFileDownload(CWD),
+                () -> HttpResponse.BodyHandler.asFileDownload(CWD, CREATE),
+                () -> HttpResponse.BodyHandler.asFileDownload(CWD, CREATE, WRITE),
+                () -> HttpResponse.BodyHandler.asFileDownload(CWD, CREATE, WRITE, READ),
+                () -> HttpResponse.BodyHandler.asFileDownload(CWD, CREATE, WRITE, READ, DELETE_ON_CLOSE),
+
+                // TODO: what do these even mean by themselves, maybe ok means nothing?
+                () -> HttpResponse.BodyHandler.asFile(asFilePath, DELETE_ON_CLOSE),
+                () -> HttpResponse.BodyHandler.asFile(asFilePath, READ)
+        );
+
+        // sanity, just run http ( no security manager )
+        System.setSecurityManager(null);
+        try {
+            for (PrivilegedExceptionAction pa : list) {
+                AccessController.doPrivileged(pa);
+            }
+        } finally {
+            System.setSecurityManager(new SecurityManager());
+        }
+
+        // Run with all permissions, i.e. no further restrictions than test's AllPermission
+        for (PrivilegedExceptionAction pa : list) {
+            try {
+                assert System.getSecurityManager() != null;
+                AccessController.doPrivileged(pa, null, new Permission[] { });
+            } catch (PrivilegedActionException pae) {
+                fail("UNEXPECTED Exception:" + pae);
+                pae.printStackTrace();
+            }
+        }
+
+        // Run with limited permissions, i.e. just what is required
+        AccessControlContext minimalACC = withPermissions(
+                new FilePermission(fromFilePath.toString() , "read"),
+                new FilePermission(asFilePath.toString(), "read,write,delete"),
+                new FilePermission(CWD.toString(), "read,write,delete")
+        );
+        for (PrivilegedExceptionAction pa : list) {
+            try {
+                assert System.getSecurityManager() != null;
+                AccessController.doPrivileged(pa, minimalACC);
+            } catch (PrivilegedActionException pae) {
+                fail("UNEXPECTED Exception:" + pae);
+                pae.printStackTrace();
+            }
+        }
+
+        // Run with NO permissions, i.e. expect SecurityException
+        for (PrivilegedExceptionAction pa : list) {
+            try {
+                assert System.getSecurityManager() != null;
+                AccessController.doPrivileged(pa, noPermissions());
+                fail("EXPECTED SecurityException");
+            } catch (SecurityException expected) {
+                System.out.println("Caught expected SE:" + expected);
+            }
+        }
+    }
+}
diff --git a/test/jdk/java/net/httpclient/security/filePerms/httpclient.policy b/test/jdk/java/net/httpclient/security/filePerms/httpclient.policy
new file mode 100644
index 00000000000..8a2c74df9b7
--- /dev/null
+++ b/test/jdk/java/net/httpclient/security/filePerms/httpclient.policy
@@ -0,0 +1,68 @@
+//
+// Copyright (c) 2017, 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.
+//
+
+grant codeBase "jrt:/jdk.incubator.httpclient" {
+    permission java.lang.RuntimePermission "accessClassInPackage.sun.net";
+    permission java.lang.RuntimePermission "accessClassInPackage.sun.net.util";
+    permission java.lang.RuntimePermission "accessClassInPackage.sun.net.www";
+    permission java.lang.RuntimePermission "accessClassInPackage.jdk.internal.misc";
+
+    // ## why is SP not good enough. Check API @throws signatures and impl
+    permission java.net.SocketPermission "*","connect,resolve";
+    permission java.net.URLPermission "http:*","*:*";
+    permission java.net.URLPermission "https:*","*:*";
+    permission java.net.URLPermission "ws:*","*:*";
+    permission java.net.URLPermission "wss:*","*:*";
+    permission java.net.URLPermission "socket:*","CONNECT";  // proxy
+
+    // For request/response body processors, fromFile, asFile
+    permission java.io.FilePermission "<<ALL FILES>>","read,write,delete";
+
+    // ## look at the different property names!
+    permission java.util.PropertyPermission "jdk.httpclient.HttpClient.log","read";  // name!
+    permission java.util.PropertyPermission "jdk.httpclient.auth.retrylimit","read";
+    permission java.util.PropertyPermission "jdk.httpclient.connectionWindowSize","read";
+    permission java.util.PropertyPermission "jdk.httpclient.enablepush","read";
+    permission java.util.PropertyPermission "jdk.httpclient.hpack.maxheadertablesize","read";
+    permission java.util.PropertyPermission "jdk.httpclient.keepalive.timeout","read";
+    permission java.util.PropertyPermission "jdk.httpclient.maxframesize","read";
+    permission java.util.PropertyPermission "jdk.httpclient.maxstreams","read";
+    permission java.util.PropertyPermission "jdk.httpclient.redirects.retrylimit","read";
+    permission java.util.PropertyPermission "jdk.httpclient.windowsize","read";
+    permission java.util.PropertyPermission "jdk.httpclient.bufsize","read";
+    permission java.util.PropertyPermission "jdk.httpclient.internal.selector.timeout","read";
+    permission java.util.PropertyPermission "jdk.internal.httpclient.debug","read";
+    permission java.util.PropertyPermission "jdk.internal.httpclient.hpack.debug","read";
+    permission java.util.PropertyPermission "jdk.internal.httpclient.hpack.log.level","read";
+    permission java.util.PropertyPermission "test.src","read";
+
+    permission java.net.NetPermission "getProxySelector";
+
+    permission java.security.SecurityPermission "createAccessControlContext";
+};
+
+// bootstrap to get the test going, it will do its own restrictions
+grant codeBase "file:${test.classes}/*" {
+    permission java.security.AllPermission;
+};
+
diff --git a/test/jdk/java/net/httpclient/websocket/BuildingWebSocketDriver.java b/test/jdk/java/net/httpclient/websocket/BuildingWebSocketDriver.java
new file mode 100644
index 00000000000..b45e177da5f
--- /dev/null
+++ b/test/jdk/java/net/httpclient/websocket/BuildingWebSocketDriver.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug 8159053
+ * @modules jdk.incubator.httpclient/jdk.incubator.http.internal.websocket:open
+ * @run testng/othervm --add-reads jdk.incubator.httpclient=ALL-UNNAMED jdk.incubator.httpclient/jdk.incubator.http.internal.websocket.BuildingWebSocketTest
+ */
+public final class BuildingWebSocketDriver { }
diff --git a/test/jdk/java/net/httpclient/websocket/ConnectionHandover.java b/test/jdk/java/net/httpclient/websocket/ConnectionHandover.java
index e89ce790404..b62527f4e99 100644
--- a/test/jdk/java/net/httpclient/websocket/ConnectionHandover.java
+++ b/test/jdk/java/net/httpclient/websocket/ConnectionHandover.java
@@ -31,14 +31,10 @@ import java.net.URI;
  * @test
  * @bug 8164625
  * @summary Verifies HttpClient yields the connection to the WebSocket
+ * @build DummyWebSocketServer
  * @run main/othervm -Djdk.httpclient.HttpClient.log=trace ConnectionHandover
  */
 public class ConnectionHandover {
-
-    static {
-        LoggingHelper.setupLogging();
-    }
-
     /*
      * An I/O channel associated with the connection is closed by WebSocket.abort().
      * If this connection is returned to the connection pool, then the second
@@ -52,17 +48,15 @@ public class ConnectionHandover {
             server.open();
             URI uri = server.getURI();
             WebSocket.Builder webSocketBuilder =
-                    HttpClient.newHttpClient().newWebSocketBuilder(uri, new WebSocket.Listener() { });
+                    HttpClient.newHttpClient().newWebSocketBuilder();
 
-            WebSocket ws1 = webSocketBuilder.buildAsync().join();
-            try {
-                ws1.abort();
-            } catch (IOException ignored) { }
+            WebSocket ws1 = webSocketBuilder
+                    .buildAsync(uri, new WebSocket.Listener() { }).join();
+            ws1.abort();
 
-            WebSocket ws2 = webSocketBuilder.buildAsync().join(); // Exception here if the connection was pooled
-            try {
-                ws2.abort();
-            } catch (IOException ignored) { }
+            WebSocket ws2 = webSocketBuilder
+                    .buildAsync(uri, new WebSocket.Listener() { }).join(); // Exception here if the connection was pooled
+            ws2.abort();
         }
     }
 }
diff --git a/test/jdk/java/net/httpclient/websocket/DummyWebSocketServer.java b/test/jdk/java/net/httpclient/websocket/DummyWebSocketServer.java
index 2282e180b10..7e72d52e7f4 100644
--- a/test/jdk/java/net/httpclient/websocket/DummyWebSocketServer.java
+++ b/test/jdk/java/net/httpclient/websocket/DummyWebSocketServer.java
@@ -34,6 +34,7 @@ import java.nio.channels.SocketChannel;
 import java.nio.charset.CharacterCodingException;
 import java.security.MessageDigest;
 import java.security.NoSuchAlgorithmException;
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Base64;
 import java.util.HashMap;
@@ -47,9 +48,7 @@ import java.util.regex.Pattern;
 import java.util.stream.Collectors;
 
 import static java.lang.String.format;
-import static java.lang.System.Logger.Level.ERROR;
-import static java.lang.System.Logger.Level.INFO;
-import static java.lang.System.Logger.Level.TRACE;
+import static java.lang.System.err;
 import static java.nio.charset.StandardCharsets.ISO_8859_1;
 import static java.util.Arrays.asList;
 import static java.util.Objects.requireNonNull;
@@ -83,7 +82,6 @@ import static java.util.Objects.requireNonNull;
  */
 public final class DummyWebSocketServer implements Closeable {
 
-    private final static System.Logger log = System.getLogger(DummyWebSocketServer.class.getName());
     private final AtomicBoolean started = new AtomicBoolean();
     private final Thread thread;
     private volatile ServerSocketChannel ssc;
@@ -98,9 +96,9 @@ public final class DummyWebSocketServer implements Closeable {
         thread = new Thread(() -> {
             try {
                 while (!Thread.currentThread().isInterrupted()) {
-                    log.log(INFO, "Accepting next connection at: " + ssc);
+                    err.println("Accepting next connection at: " + ssc);
                     SocketChannel channel = ssc.accept();
-                    log.log(INFO, "Accepted: " + channel);
+                    err.println("Accepted: " + channel);
                     try {
                         channel.configureBlocking(true);
                         StringBuilder request = new StringBuilder();
@@ -117,18 +115,18 @@ public final class DummyWebSocketServer implements Closeable {
                             b.clear();
                         }
                     } catch (IOException e) {
-                        log.log(TRACE, () -> "Error in connection: " + channel, e);
+                        err.println("Error in connection: " + channel + ", " + e);
                     } finally {
-                        log.log(INFO, "Closed: " + channel);
+                        err.println("Closed: " + channel);
                         close(channel);
                     }
                 }
             } catch (ClosedByInterruptException ignored) {
             } catch (IOException e) {
-                log.log(ERROR, e);
+                err.println(e);
             } finally {
                 close(ssc);
-                log.log(INFO, "Stopped at: " + getURI());
+                err.println("Stopped at: " + getURI());
             }
         });
         thread.setName("DummyWebSocketServer");
@@ -136,7 +134,7 @@ public final class DummyWebSocketServer implements Closeable {
     }
 
     public void open() throws IOException {
-        log.log(INFO, "Starting");
+        err.println("Starting");
         if (!started.compareAndSet(false, true)) {
             throw new IllegalStateException("Already started");
         }
@@ -149,12 +147,12 @@ public final class DummyWebSocketServer implements Closeable {
         } catch (IOException e) {
             close(ssc);
         }
-        log.log(INFO, "Started at: " + getURI());
+        err.println("Started at: " + getURI());
     }
 
     @Override
     public void close() {
-        log.log(INFO, "Stopping: " + getURI());
+        err.println("Stopping: " + getURI());
         thread.interrupt();
         close(ssc);
     }
@@ -210,12 +208,13 @@ public final class DummyWebSocketServer implements Closeable {
             if (!iterator.hasNext()) {
                 throw new IllegalStateException("The request is empty");
             }
-            if (!"GET / HTTP/1.1".equals(iterator.next())) {
+            String statusLine = iterator.next();
+            if (!(statusLine.startsWith("GET /") && statusLine.endsWith(" HTTP/1.1"))) {
                 throw new IllegalStateException
                         ("Unexpected status line: " + request.get(0));
             }
             response.add("HTTP/1.1 101 Switching Protocols");
-            Map<String, String> requestHeaders = new HashMap<>();
+            Map<String, List<String>> requestHeaders = new HashMap<>();
             while (iterator.hasNext()) {
                 String header = iterator.next();
                 String[] split = header.split(": ");
@@ -224,10 +223,8 @@ public final class DummyWebSocketServer implements Closeable {
                             ("Unexpected header: " + header
                                      + ", split=" + Arrays.toString(split));
                 }
-                if (requestHeaders.put(split[0], split[1]) != null) {
-                    throw new IllegalStateException
-                            ("Duplicating headers: " + Arrays.toString(split));
-                }
+                requestHeaders.computeIfAbsent(split[0], k -> new ArrayList<>()).add(split[1]);
+
             }
             if (requestHeaders.containsKey("Sec-WebSocket-Protocol")) {
                 throw new IllegalStateException("Subprotocols are not expected");
@@ -240,17 +237,20 @@ public final class DummyWebSocketServer implements Closeable {
             expectHeader(requestHeaders, "Upgrade", "websocket");
             response.add("Upgrade: websocket");
             expectHeader(requestHeaders, "Sec-WebSocket-Version", "13");
-            String key = requestHeaders.get("Sec-WebSocket-Key");
-            if (key == null) {
+            List<String> key = requestHeaders.get("Sec-WebSocket-Key");
+            if (key == null || key.isEmpty()) {
                 throw new IllegalStateException("Sec-WebSocket-Key is missing");
             }
+            if (key.size() != 1) {
+                throw new IllegalStateException("Sec-WebSocket-Key has too many values : " + key);
+            }
             MessageDigest sha1 = null;
             try {
                 sha1 = MessageDigest.getInstance("SHA-1");
             } catch (NoSuchAlgorithmException e) {
                 throw new InternalError(e);
             }
-            String x = key + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
+            String x = key.get(0) + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
             sha1.update(x.getBytes(ISO_8859_1));
             String v = Base64.getEncoder().encodeToString(sha1.digest());
             response.add("Sec-WebSocket-Accept: " + v);
@@ -258,17 +258,17 @@ public final class DummyWebSocketServer implements Closeable {
         };
     }
 
-    protected static String expectHeader(Map<String, String> headers,
+    protected static String expectHeader(Map<String, List<String>> headers,
                                          String name,
                                          String value) {
-        String v = headers.get(name);
-        if (!value.equals(v)) {
+        List<String> v = headers.get(name);
+        if (!v.contains(value)) {
             throw new IllegalStateException(
                     format("Expected '%s: %s', actual: '%s: %s'",
                            name, value, name, v)
             );
         }
-        return v;
+        return value;
     }
 
     private static void close(AutoCloseable... acs) {
diff --git a/test/jdk/java/net/httpclient/websocket/HeaderWriterDriver.java b/test/jdk/java/net/httpclient/websocket/HeaderWriterDriver.java
new file mode 100644
index 00000000000..f806bd402c1
--- /dev/null
+++ b/test/jdk/java/net/httpclient/websocket/HeaderWriterDriver.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug 8159053
+ * @modules jdk.incubator.httpclient/jdk.incubator.http.internal.websocket:open
+ * @run testng/othervm --add-reads jdk.incubator.httpclient=ALL-UNNAMED jdk.incubator.httpclient/jdk.incubator.http.internal.websocket.HeaderWriterTest
+ */
+public final class HeaderWriterDriver { }
diff --git a/test/jdk/java/net/httpclient/websocket/MaskerDriver.java b/test/jdk/java/net/httpclient/websocket/MaskerDriver.java
new file mode 100644
index 00000000000..facf0a812d5
--- /dev/null
+++ b/test/jdk/java/net/httpclient/websocket/MaskerDriver.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug 8159053
+ * @modules jdk.incubator.httpclient/jdk.incubator.http.internal.websocket:open
+ * @run testng/othervm --add-reads jdk.incubator.httpclient=ALL-UNNAMED jdk.incubator.httpclient/jdk.incubator.http.internal.websocket.MaskerTest
+ */
+public final class MaskerDriver { }
diff --git a/test/jdk/java/net/httpclient/websocket/ReaderDriver.java b/test/jdk/java/net/httpclient/websocket/ReaderDriver.java
new file mode 100644
index 00000000000..d43e48eb088
--- /dev/null
+++ b/test/jdk/java/net/httpclient/websocket/ReaderDriver.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug 8159053
+ * @modules jdk.incubator.httpclient/jdk.incubator.http.internal.websocket:open
+ * @run testng/othervm --add-reads jdk.incubator.httpclient=ALL-UNNAMED jdk.incubator.httpclient/jdk.incubator.http.internal.websocket.ReaderTest
+ */
+public final class ReaderDriver { }
diff --git a/test/jdk/java/net/httpclient/websocket/ReceivingTestDriver.java b/test/jdk/java/net/httpclient/websocket/ReceivingTestDriver.java
new file mode 100644
index 00000000000..e327c9a5d38
--- /dev/null
+++ b/test/jdk/java/net/httpclient/websocket/ReceivingTestDriver.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @modules jdk.incubator.httpclient/jdk.incubator.http.internal.websocket:open
+ * @run testng/othervm --add-reads jdk.incubator.httpclient=ALL-UNNAMED jdk.incubator.httpclient/jdk.incubator.http.internal.websocket.ReceivingTest
+ */
+public class ReceivingTestDriver { }
diff --git a/test/jdk/java/net/httpclient/websocket/SendingTestDriver.java b/test/jdk/java/net/httpclient/websocket/SendingTestDriver.java
new file mode 100644
index 00000000000..85a061a2126
--- /dev/null
+++ b/test/jdk/java/net/httpclient/websocket/SendingTestDriver.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @modules jdk.incubator.httpclient/jdk.incubator.http.internal.websocket:open
+ * @run testng/othervm --add-reads jdk.incubator.httpclient=ALL-UNNAMED jdk.incubator.httpclient/jdk.incubator.http.internal.websocket.SendingTest
+ */
+public class SendingTestDriver { }
diff --git a/test/jdk/java/net/httpclient/websocket/WSDriver.java b/test/jdk/java/net/httpclient/websocket/WSDriver.java
deleted file mode 100644
index b7dcab17b43..00000000000
--- a/test/jdk/java/net/httpclient/websocket/WSDriver.java
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-
-/*
- * @test
- * @bug 8159053
- * @modules jdk.incubator.httpclient/jdk.incubator.http.internal.websocket:open
- * @compile/module=jdk.incubator.httpclient jdk/incubator/http/internal/websocket/TestSupport.java
- *
- * @run testng/othervm --add-reads jdk.incubator.httpclient=ALL-UNNAMED jdk.incubator.httpclient/jdk.incubator.http.internal.websocket.HeaderWriterTest
- * @run testng/othervm --add-reads jdk.incubator.httpclient=ALL-UNNAMED jdk.incubator.httpclient/jdk.incubator.http.internal.websocket.ReaderTest
- * @run testng/othervm --add-reads jdk.incubator.httpclient=ALL-UNNAMED jdk.incubator.httpclient/jdk.incubator.http.internal.websocket.MaskerTest
- * @run testng/othervm --add-reads jdk.incubator.httpclient=ALL-UNNAMED jdk.incubator.httpclient/jdk.incubator.http.internal.websocket.BuildingWebSocketTest
- */
-public final class WSDriver {
-// * @run testng/othervm -XaddReads:jdk.incubator.httpclient=ALL-UNNAMED jdk.incubator.httpclient/jdk.incubator.http.internal.websocket.MockListenerTest
-// * @run testng/othervm -XaddReads:jdk.incubator.httpclient=ALL-UNNAMED jdk.incubator.httpclient/jdk.incubator.http.internal.websocket.MockChannelTest
-// * @run testng/othervm/timeout=1000 -Ddataproviderthreadcount=16 -XaddReads:jdk.incubator.httpclient=ALL-UNNAMED jdk.incubator.httpclient/jdk.incubator.http.internal.websocket.PingTest
-}
-
diff --git a/test/jdk/java/net/httpclient/websocket/WSHandshakeException.java b/test/jdk/java/net/httpclient/websocket/WSHandshakeException.java
new file mode 100644
index 00000000000..eebd38e87fc
--- /dev/null
+++ b/test/jdk/java/net/httpclient/websocket/WSHandshakeException.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @summary Basic test for WebSocketHandshakeException
+ * @library /lib/testlibrary
+ * @build jdk.testlibrary.SimpleSSLContext
+ * @modules jdk.incubator.httpclient
+ *          jdk.httpserver
+ * @run testng/othervm WSHandshakeException
+ */
+;
+import java.net.InetSocketAddress;
+import java.net.URI;
+import java.util.concurrent.CompletionException;
+import java.util.concurrent.Executor;
+import java.util.concurrent.Executors;
+import com.sun.net.httpserver.HttpServer;
+import com.sun.net.httpserver.HttpsConfigurator;
+import com.sun.net.httpserver.HttpsServer;
+import jdk.incubator.http.HttpClient;
+import javax.net.ssl.SSLContext;
+import jdk.incubator.http.WebSocket;
+import jdk.incubator.http.WebSocketHandshakeException;
+import jdk.testlibrary.SimpleSSLContext;
+import org.testng.annotations.AfterTest;
+import org.testng.annotations.BeforeTest;
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.fail;
+import static org.testng.Assert.assertTrue;
+
+public class WSHandshakeException {
+
+    SSLContext sslContext;
+    HttpServer httpTestServer;         // HTTP/1.1    [ 2 servers ]
+    HttpsServer httpsTestServer;       // HTTPS/1.1
+    String httpURI;
+    String httpsURI;
+
+
+    static final int ITERATION_COUNT = 10;
+    // a shared executor helps reduce the amount of threads created by the test
+    static final Executor executor = Executors.newCachedThreadPool();
+
+    @DataProvider(name = "variants")
+    public Object[][] variants() {
+        return new Object[][]{
+                { httpURI,    false },
+                { httpsURI,   false },
+                { httpURI,    true },
+                { httpsURI,   true },
+        };
+    }
+
+    HttpClient newHttpClient() {
+        return HttpClient.newBuilder()
+                         .executor(executor)
+                         .sslContext(sslContext)
+                         .build();
+    }
+
+    @Test(dataProvider = "variants")
+    public void test(String uri, boolean sameClient) throws Exception {
+        HttpClient client = null;
+        for (int i=0; i< ITERATION_COUNT; i++) {
+            if (!sameClient || client == null)
+                client = newHttpClient();
+
+            try {
+                client.newWebSocketBuilder()
+                      .buildAsync(URI.create(uri), new WebSocket.Listener() { })
+                      .join();
+                fail("Expected to throw");
+            } catch (CompletionException ce) {
+                Throwable t = ce.getCause();
+                assertTrue(t instanceof WebSocketHandshakeException);
+                WebSocketHandshakeException wse = (WebSocketHandshakeException) t;
+                assertEquals(wse.getResponse().statusCode(), 404);
+            }
+        }
+    }
+
+
+    @BeforeTest
+    public void setup() throws Exception {
+        sslContext = new SimpleSSLContext().get();
+        if (sslContext == null)
+            throw new AssertionError("Unexpected null sslContext");
+
+        // HTTP/1.1
+        InetSocketAddress sa = new InetSocketAddress("localhost", 0);
+        httpTestServer = HttpServer.create(sa, 0);
+        httpURI = "ws://127.0.0.1:" + httpTestServer.getAddress().getPort() + "/";
+
+        httpsTestServer = HttpsServer.create(sa, 0);
+        httpsTestServer.setHttpsConfigurator(new HttpsConfigurator(sslContext));
+        httpsURI = "wss://127.0.0.1:" + httpsTestServer.getAddress().getPort() + "/";
+
+        httpTestServer.start();
+        httpsTestServer.start();
+    }
+
+    @AfterTest
+    public void teardown() throws Exception {
+        httpTestServer.stop(0);
+        httpsTestServer.stop(0);
+    }
+}
diff --git a/test/jdk/java/net/httpclient/websocket/jdk.incubator.httpclient/jdk/incubator/http/internal/websocket/BuildingWebSocketTest.java b/test/jdk/java/net/httpclient/websocket/jdk.incubator.httpclient/jdk/incubator/http/internal/websocket/BuildingWebSocketTest.java
index 15e70e38fbc..852c903da90 100644
--- a/test/jdk/java/net/httpclient/websocket/jdk.incubator.httpclient/jdk/incubator/http/internal/websocket/BuildingWebSocketTest.java
+++ b/test/jdk/java/net/httpclient/websocket/jdk.incubator.httpclient/jdk/incubator/http/internal/websocket/BuildingWebSocketTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2017, 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
@@ -34,6 +34,7 @@ import java.util.List;
 import java.util.concurrent.CompletionStage;
 import java.util.function.Function;
 import java.util.stream.Collectors;
+import java.util.stream.Stream;
 
 import static jdk.incubator.http.internal.websocket.TestSupport.assertCompletesExceptionally;
 import static jdk.incubator.http.internal.websocket.TestSupport.assertThrows;
@@ -45,85 +46,77 @@ import static jdk.incubator.http.internal.websocket.TestSupport.assertThrows;
  */
 public class BuildingWebSocketTest {
 
+    private final static URI VALID_URI = URI.create("ws://websocket.example.com");
+
     @Test
-    public void nulls() {
+    public void nullArguments() {
         HttpClient c = HttpClient.newHttpClient();
-        URI uri = URI.create("ws://websocket.example.com");
 
         assertThrows(NullPointerException.class,
-                     () -> c.newWebSocketBuilder(null, listener()));
+                     () -> c.newWebSocketBuilder()
+                            .buildAsync(null, listener()));
         assertThrows(NullPointerException.class,
-                     () -> c.newWebSocketBuilder(uri, null));
+                     () -> c.newWebSocketBuilder()
+                            .buildAsync(VALID_URI, null));
         assertThrows(NullPointerException.class,
-                     () -> c.newWebSocketBuilder(null, null));
+                     () -> c.newWebSocketBuilder()
+                            .buildAsync(null, null));
         assertThrows(NullPointerException.class,
-                     () -> c.newWebSocketBuilder(uri, listener())
+                     () -> c.newWebSocketBuilder()
                             .header(null, "value"));
         assertThrows(NullPointerException.class,
-                     () -> c.newWebSocketBuilder(uri, listener())
+                     () -> c.newWebSocketBuilder()
                             .header("name", null));
         assertThrows(NullPointerException.class,
-                     () -> c.newWebSocketBuilder(uri, listener())
+                     () -> c.newWebSocketBuilder()
                             .header(null, null));
         assertThrows(NullPointerException.class,
-                     () -> c.newWebSocketBuilder(uri, listener())
+                     () -> c.newWebSocketBuilder()
                             .subprotocols(null));
         assertThrows(NullPointerException.class,
-                     () -> c.newWebSocketBuilder(uri, listener())
-                            .subprotocols(null, "sub1"));
+                     () -> c.newWebSocketBuilder()
+                            .subprotocols(null, "sub2.example.com"));
         assertThrows(NullPointerException.class,
-                     () -> c.newWebSocketBuilder(uri, listener())
+                     () -> c.newWebSocketBuilder()
                             .subprotocols("sub1.example.com", (String) null));
         assertThrows(NullPointerException.class,
-                     () -> c.newWebSocketBuilder(uri, listener())
+                     () -> c.newWebSocketBuilder()
                             .subprotocols("sub1.example.com", (String[]) null));
         assertThrows(NullPointerException.class,
-                     () -> c.newWebSocketBuilder(uri, listener())
-                            .subprotocols("sub1.example.com",
-                                          "sub2.example.com",
-                                          null));
+                     () -> c.newWebSocketBuilder()
+                            .subprotocols("sub1.example.com", "sub2.example.com", null));
         assertThrows(NullPointerException.class,
-                     () -> c.newWebSocketBuilder(uri, listener())
+                     () -> c.newWebSocketBuilder()
+                             .subprotocols("sub1.example.com", null, "sub3.example.com"));
+        assertThrows(NullPointerException.class,
+                     () -> c.newWebSocketBuilder()
                             .connectTimeout(null));
     }
 
     @Test(dataProvider = "badURIs")
-    void illegalURI(String u) {
-        WebSocket.Builder b = HttpClient.newHttpClient()
-                .newWebSocketBuilder(URI.create(u), listener());
-        assertCompletesExceptionally(IllegalArgumentException.class, b.buildAsync());
+    void illegalURI(URI uri) {
+        WebSocket.Builder b = HttpClient.newHttpClient().newWebSocketBuilder();
+        assertCompletesExceptionally(IllegalArgumentException.class,
+                                     b.buildAsync(uri, listener()));
     }
 
     @Test
     public void illegalHeaders() {
-        List<String> headers = List.of("Authorization",
-                                       "Connection",
-                                       "Cookie",
-                                       "Content-Length",
-                                       "Date",
-                                       "Expect",
-                                       "From",
-                                       "Host",
-                                       "Origin",
-                                       "Proxy-Authorization",
-                                       "Referer",
-                                       "User-agent",
-                                       "Upgrade",
-                                       "Via",
-                                       "Warning",
-                                       "Sec-WebSocket-Accept",
-                                       "Sec-WebSocket-Extensions",
-                                       "Sec-WebSocket-Key",
-                                       "Sec-WebSocket-Protocol",
-                                       "Sec-WebSocket-Version").stream()
-                .map(String::new).collect(Collectors.toList());
+        List<String> headers =
+                List.of("Sec-WebSocket-Accept",
+                        "Sec-WebSocket-Extensions",
+                        "Sec-WebSocket-Key",
+                        "Sec-WebSocket-Protocol",
+                        "Sec-WebSocket-Version")
+                        .stream()
+                        .flatMap(s -> Stream.of(s, new String(s))) // a string and a copy of it
+                        .collect(Collectors.toList());
 
         Function<String, CompletionStage<?>> f =
-                header -> HttpClient
-                        .newHttpClient()
-                        .newWebSocketBuilder(URI.create("ws://websocket.example.com"),
-                                             listener())
-                        .buildAsync();
+                header -> HttpClient.newHttpClient()
+                        .newWebSocketBuilder()
+                        .header(header, "value")
+                        .buildAsync(VALID_URI, listener());
 
         headers.forEach(h -> assertCompletesExceptionally(IllegalArgumentException.class, f.apply(h)));
     }
@@ -134,38 +127,38 @@ public class BuildingWebSocketTest {
     @Test(dataProvider = "badSubprotocols")
     public void illegalSubprotocolsSyntax(String s) {
         WebSocket.Builder b = HttpClient.newHttpClient()
-                .newWebSocketBuilder(URI.create("ws://websocket.example.com"),
-                                     listener());
-        b.subprotocols(s);
-        assertCompletesExceptionally(IllegalArgumentException.class, b.buildAsync());
+                .newWebSocketBuilder()
+                .subprotocols(s);
+        assertCompletesExceptionally(IllegalArgumentException.class,
+                                     b.buildAsync(VALID_URI, listener()));
     }
 
     @Test(dataProvider = "duplicatingSubprotocols")
     public void illegalSubprotocolsDuplicates(String mostPreferred,
                                               String[] lesserPreferred) {
         WebSocket.Builder b = HttpClient.newHttpClient()
-                .newWebSocketBuilder(URI.create("ws://websocket.example.com"),
-                                     listener());
-        b.subprotocols(mostPreferred, lesserPreferred);
-        assertCompletesExceptionally(IllegalArgumentException.class, b.buildAsync());
+                .newWebSocketBuilder()
+                .subprotocols(mostPreferred, lesserPreferred);
+        assertCompletesExceptionally(IllegalArgumentException.class,
+                                     b.buildAsync(VALID_URI, listener()));
     }
 
     @Test(dataProvider = "badConnectTimeouts")
     public void illegalConnectTimeout(Duration d) {
         WebSocket.Builder b = HttpClient.newHttpClient()
-                .newWebSocketBuilder(URI.create("ws://websocket.example.com"),
-                                     listener());
-        b.connectTimeout(d);
-        assertCompletesExceptionally(IllegalArgumentException.class, b.buildAsync());
+                .newWebSocketBuilder()
+                .connectTimeout(d);
+        assertCompletesExceptionally(IllegalArgumentException.class,
+                                     b.buildAsync(VALID_URI, listener()));
     }
 
     @DataProvider
     public Object[][] badURIs() {
         return new Object[][]{
-                {"http://example.com"},
-                {"ftp://example.com"},
-                {"wss://websocket.example.com/hello#fragment"},
-                {"ws://websocket.example.com/hello#fragment"},
+                {URI.create("http://example.com")},
+                {URI.create("ftp://example.com")},
+                {URI.create("wss://websocket.example.com/hello#fragment")},
+                {URI.create("ws://websocket.example.com/hello#fragment")},
         };
     }
 
@@ -193,6 +186,7 @@ public class BuildingWebSocketTest {
     @DataProvider
     public static Object[][] badSubprotocols() {
         return new Object[][]{
+                {""},
                 {new String("")},
                 {"round-brackets("},
                 {"round-brackets)"},
diff --git a/test/jdk/java/net/httpclient/websocket/jdk.incubator.httpclient/jdk/incubator/http/internal/websocket/CloseTest.java b/test/jdk/java/net/httpclient/websocket/jdk.incubator.httpclient/jdk/incubator/http/internal/websocket/CloseTest.java
deleted file mode 100644
index 7e15e794a5d..00000000000
--- a/test/jdk/java/net/httpclient/websocket/jdk.incubator.httpclient/jdk/incubator/http/internal/websocket/CloseTest.java
+++ /dev/null
@@ -1,246 +0,0 @@
-/*
- * Copyright (c) 2016, 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 jdk.incubator.http.internal.websocket;
-
-import org.testng.annotations.DataProvider;
-import org.testng.annotations.Test;
-
-import jdk.incubator.http.WebSocket;
-
-import java.net.URI;
-import java.nio.ByteBuffer;
-import java.nio.CharBuffer;
-import java.nio.charset.CharacterCodingException;
-import java.nio.charset.StandardCharsets;
-import java.util.Iterator;
-import java.util.List;
-import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.Executor;
-
-import static jdk.incubator.http.internal.websocket.TestSupport.Expectation.ifExpect;
-import static jdk.incubator.http.internal.websocket.TestSupport.cartesianIterator;
-import static java.util.Arrays.asList;
-import static java.util.List.of;
-
-/*
- * Tests for Close message handling: examines sendClose/onClose contracts.
- */
-public final class CloseTest {
-
-    /*
-     * Verifies the domain of the arguments of sendClose(code, reason).
-     */
-    @Test(dataProvider = "sendClose")
-    public void testSendCloseArguments(int code, String reason) {
-        WebSocket ws = newWebSocket();
-        ifExpect(
-                reason == null,
-                NullPointerException.class::isInstance)
-        .orExpect(
-                !isOutgoingCodeLegal(code),
-                IllegalArgumentException.class::isInstance)
-        .orExpect(
-                !isReasonLegal(reason),
-                IllegalArgumentException.class::isInstance)
-        .assertThrows(() -> ws.sendClose(code, reason));
-    }
-
-    /*
-     * After sendClose(code, reason) has returned normally or exceptionally, no
-     * more messages can be sent. However, if the invocation has thrown IAE/NPE
-     * (i.e. programming error) messages can still be sent (failure atomicity).
-     */
-    public void testSendClose(int code, String reason) {
-        newWebSocket().sendClose(10, "");
-    }
-
-    /*
-     * After sendClose() has been invoked, no more messages can be sent.
-     */
-    public void testSendClose() {
-        WebSocket ws = newWebSocket();
-        CompletableFuture<WebSocket> cf = ws.sendClose();
-    }
-
-    // TODO: sendClose can be invoked whenever is suitable without ISE
-    // + idempotency
-
-    /*
-     * An invocation of sendClose(code, reason) will cause a Close message with
-     * the same code and the reason to appear on the wire.
-     */
-    public void testSendCloseWysiwyg(int code, String reason) {
-
-    }
-
-    /*
-     * An invocation of sendClose() will cause an empty Close message to appear
-     * on the wire.
-     */
-    public void testSendCloseWysiwyg() {
-
-    }
-
-    /*
-     * Automatic Closing handshake. Listener receives onClose() and returns from
-     * it. WebSocket closes in accordance to the returned value.
-     */
-    public void testClosingHandshake1() {
-        // TODO: closed if observed shortly after the returned CS completes
-    }
-
-    /*
-     * sendClose is invoked from within onClose. After sendClose has returned,
-     * isClosed() reports true.
-     */
-    public void testClosingHandshake2() {
-        // 1. newWebSocket().sendClose();
-        // 2. onClose return null
-        // 3. isClosed() == true
-    }
-
-    /*
-     * sendClose has been invoked, then onClose. Shortly after onClose has
-     * returned, isClosed reports true.
-     */
-    public void testClosingHandshake3() {
-    }
-
-    /*
-     * Return from onClose with nevercompleting CS then sendClose().
-     */
-    public void testClosingHandshake4() {
-
-    }
-
-    /*
-     * Exceptions thrown from onClose and exceptions a CS returned from onClose
-     * "completes exceptionally" with are ignored. In other words, they are
-     * never reported to onError().
-     */
-    public void testOnCloseExceptions() {
-
-    }
-
-    /*
-     * An incoming Close message on the wire will cause an invocation of onClose
-     * with appropriate values. However, if this message violates the WebSocket
-     * Protocol, onError is invoked instead.
-     *
-     * // TODO: automatic close (if error) AND isClose returns true from onError
-     */
-    public void testOnCloseWysiwyg() {
-
-    }
-
-    /*
-     * Data is read off the wire. An end-of-stream has been reached while
-     * reading a frame.
-     *
-     * onError is invoked with java.net.ProtocolException and the WebSocket this
-     * listener has been attached to
-     */
-    public void testUnexpectedEOS() {
-
-    }
-
-    /*
-     * Data is read off the wire. An end-of-stream has been reached in between
-     * frames, and no Close frame has been received yet.
-     *
-     * onClose is invoked with the status code 1006 and the WebSocket this
-     * listener has been attached to
-     */
-    public void testEOS() {
-
-    }
-
-    // TODO: check buffers for change
-
-    @DataProvider(name = "sendClose")
-    public Iterator<Object[]> createData() {
-        List<Integer> codes = asList(
-                Integer.MIN_VALUE, -1, 0, 1, 500, 998, 999, 1000, 1001, 1002,
-                1003, 1004, 1005, 1006, 1007, 1008, 1009, 1010, 1011, 1012,
-                1013, 1014, 1015, 1016, 2998, 2999, 3000, 3001, 3998, 3999,
-                4000, 4001, 4998, 4999, 5000, 5001, 32768, 65535, 65536,
-                Integer.MAX_VALUE);
-        String longReason1 = "This is a reason string. Nothing special except " +
-                "its UTF-8 representation is a bit " +
-                "longer than one hundred and twenty three bytes.";
-        assert longReason1.getBytes(StandardCharsets.UTF_8).length > 123;
-
-        // Russian alphabet repeated cyclically until it's enough to pass "123"
-        // bytes length
-        StringBuilder b = new StringBuilder();
-        char c = '\u0410';
-        for (int i = 0; i < 62; i++) {
-            b.append(c);
-            if (++c > '\u042F') {
-                c = '\u0410';
-            }
-        }
-        String longReason2 = b.toString();
-        assert longReason2.length() <= 123
-                && longReason2.getBytes(StandardCharsets.UTF_8).length > 123;
-
-        String malformedReason = new String(new char[]{0xDC00, 0xD800});
-
-        List<String> reasons = asList
-                (null, "", "abc", longReason1, longReason2, malformedReason);
-
-        return cartesianIterator(of(codes, reasons), args -> args);
-    }
-
-    private boolean isReasonLegal(String reason) {
-        if (reason == null) {
-            return false;
-        }
-        ByteBuffer result;
-        try {
-            result = StandardCharsets.UTF_8.newEncoder().encode(CharBuffer.wrap(reason));
-        } catch (CharacterCodingException e) {
-            return false;
-        }
-        return result.remaining() <= 123;
-    }
-
-    private static boolean isOutgoingCodeLegal(int code) {
-        if (code < 1000 || code > 4999) {
-            return false;
-        }
-        if (code < 1016) {
-            return code == 1000 || code == 1001 || code == 1008 || code == 1011;
-        }
-        return code >= 3000;
-    }
-
-    private WebSocket newWebSocket() {
-        WebSocket.Listener l = new WebSocket.Listener() { };
-        return new WebSocketImpl(URI.create("ws://example.com"),
-                                 "",
-                                 new MockChannel.Builder().build(),
-                                 l);
-    }
-}
diff --git a/test/jdk/java/net/httpclient/websocket/jdk.incubator.httpclient/jdk/incubator/http/internal/websocket/DataProviders.java b/test/jdk/java/net/httpclient/websocket/jdk.incubator.httpclient/jdk/incubator/http/internal/websocket/DataProviders.java
deleted file mode 100644
index a0b49e67c50..00000000000
--- a/test/jdk/java/net/httpclient/websocket/jdk.incubator.httpclient/jdk/incubator/http/internal/websocket/DataProviders.java
+++ /dev/null
@@ -1,107 +0,0 @@
-/*
- * Copyright (c) 2016, 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 jdk.incubator.http.internal.websocket;
-
-import org.testng.annotations.DataProvider;
-
-import jdk.incubator.http.internal.websocket.TestSupport.F5;
-import java.nio.ByteBuffer;
-import java.util.Iterator;
-import java.util.List;
-import java.util.stream.Stream;
-
-import static jdk.incubator.http.internal.websocket.TestSupport.cartesianIterator;
-import static jdk.incubator.http.internal.websocket.TestSupport.concat;
-import static jdk.incubator.http.internal.websocket.TestSupport.iteratorOf;
-import static jdk.incubator.http.internal.websocket.TestSupport.iteratorOf1;
-import static java.util.List.of;
-
-/*
- * Data providers for WebSocket tests
- */
-public final class DataProviders {
-
-    /*
-     * Various ByteBuffer-s to be passed to sendPing/sendPong.
-     *
-     * Actual data is put in the middle of the buffer to make sure the code under
-     * test relies on position/limit rather than on 0 and capacity.
-     *
-     *     +-------------------+-------~ ~-------------+--------------+
-     *     |<---- leading ---->|<------~ ~--- data --->|<- trailing ->|
-     *     +-------------------+-------~ ~-------------+--------------+
-     *     ^0                   ^position               ^limit         ^capacity
-     */
-    @DataProvider(name = "outgoingData", parallel = true)
-    public static Iterator<Object[]> outgoingData() {
-        List<Integer> leading  = of(0, 1, 17, 125);
-        List<Integer> trailing = of(0, 1, 19, 123);
-        List<Integer> sizes    = of(0, 1, 2, 17, 32, 64, 122, 123, 124, 125, 126, 127, 128, 256);
-        List<Boolean> direct   = of(true, false);
-        List<Boolean> readonly = of(false); // TODO: return readonly (true)
-        F5<Integer, Integer, Integer, Boolean, Boolean, Object[]> f =
-                (l, t, s, d, r) -> {
-                    ByteBuffer b;
-                    if (d) {
-                        b = ByteBuffer.allocateDirect(l + t + s);
-                    } else {
-                        b = ByteBuffer.allocate(l + t + s);
-                    }
-                    fill(b);
-                    if (r) {
-                        b = b.asReadOnlyBuffer();
-                    }
-                    b.position(l).limit(l + s);
-                    return new ByteBuffer[]{b};
-                };
-        Iterator<Object[]> product = cartesianIterator(leading, trailing, sizes, direct, readonly, f);
-        Iterator<Object[]> i = iteratorOf1(new Object[]{null});
-        return concat(iteratorOf(i, product));
-    }
-
-    @DataProvider(name = "incomingData", parallel = true)
-    public static Iterator<Object[]> incomingData() {
-        return Stream.of(0, 1, 2, 17, 63, 125)
-                .map(i -> new Object[]{fill(ByteBuffer.allocate(i))})
-                .iterator();
-    }
-
-    @DataProvider(name = "incorrectFrame")
-    public static Iterator<Object[]> incorrectFrame() {
-        List<Boolean> fin   = of(true, false );
-        List<Boolean> rsv1  = of(true, false );
-        List<Boolean> rsv2  = of(true, false );
-        List<Boolean> rsv3  = of(true, false );
-        List<Integer> sizes = of(0, 126, 1024);
-        return cartesianIterator(fin, rsv1, rsv2, rsv3, sizes,
-                (a, b, c, d, e) -> new Object[]{a, b, c, d, ByteBuffer.allocate(e)});
-    }
-
-    private static ByteBuffer fill(ByteBuffer b) {
-        int i = 0;
-        while (b.hasRemaining()) {
-            b.put((byte) (++i & 0xff));
-        }
-        return b;
-    }
-}
diff --git a/test/jdk/java/net/httpclient/websocket/jdk.incubator.httpclient/jdk/incubator/http/internal/websocket/HeaderWriterTest.java b/test/jdk/java/net/httpclient/websocket/jdk.incubator.httpclient/jdk/incubator/http/internal/websocket/HeaderWriterTest.java
index 0ad2f06940c..62f550b29f3 100644
--- a/test/jdk/java/net/httpclient/websocket/jdk.incubator.httpclient/jdk/incubator/http/internal/websocket/HeaderWriterTest.java
+++ b/test/jdk/java/net/httpclient/websocket/jdk.incubator.httpclient/jdk/incubator/http/internal/websocket/HeaderWriterTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2017, 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
diff --git a/test/jdk/java/net/httpclient/websocket/jdk.incubator.httpclient/jdk/incubator/http/internal/websocket/MaskerTest.java b/test/jdk/java/net/httpclient/websocket/jdk.incubator.httpclient/jdk/incubator/http/internal/websocket/MaskerTest.java
index a5fc20a7303..7d6e03f9314 100644
--- a/test/jdk/java/net/httpclient/websocket/jdk.incubator.httpclient/jdk/incubator/http/internal/websocket/MaskerTest.java
+++ b/test/jdk/java/net/httpclient/websocket/jdk.incubator.httpclient/jdk/incubator/http/internal/websocket/MaskerTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2017, 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
diff --git a/test/jdk/java/net/httpclient/websocket/jdk.incubator.httpclient/jdk/incubator/http/internal/websocket/MockChannel.java b/test/jdk/java/net/httpclient/websocket/jdk.incubator.httpclient/jdk/incubator/http/internal/websocket/MockChannel.java
deleted file mode 100644
index 03a94a0a77d..00000000000
--- a/test/jdk/java/net/httpclient/websocket/jdk.incubator.httpclient/jdk/incubator/http/internal/websocket/MockChannel.java
+++ /dev/null
@@ -1,415 +0,0 @@
-/*
- * Copyright (c) 2016, 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 jdk.incubator.http.internal.websocket;
-
-import jdk.incubator.http.WebSocket.MessagePart;
-import jdk.incubator.http.internal.websocket.Frame.Opcode;
-import jdk.incubator.http.internal.websocket.TestSupport.F1;
-import jdk.incubator.http.internal.websocket.TestSupport.F2;
-import jdk.incubator.http.internal.websocket.TestSupport.InvocationChecker;
-import jdk.incubator.http.internal.websocket.TestSupport.InvocationExpectation;
-import jdk.incubator.http.internal.websocket.TestSupport.Mock;
-
-import java.io.IOException;
-import java.nio.ByteBuffer;
-import java.nio.channels.ClosedChannelException;
-import java.nio.channels.SelectionKey;
-import java.util.Iterator;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.OptionalInt;
-import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.CompletionStage;
-import java.util.concurrent.Executor;
-import java.util.concurrent.Executors;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.function.Supplier;
-
-import static jdk.incubator.http.internal.websocket.Frame.MAX_HEADER_SIZE_BYTES;
-
-final class MockChannel implements RawChannel, Mock {
-
-    /* Reads and writes must be able to be served concurrently, thus 2 threads */ // TODO: test this
-    private final Executor executor = Executors.newFixedThreadPool(2);
-    private final Object stateLock = new Object();
-    private final Object readLock = new Object();
-    private final Object writeLock = new Object();
-    private volatile boolean closed;
-    private boolean isInputOpen = true;
-    private boolean isOutputOpen = true;
-    private final Frame.Reader reader = new Frame.Reader();
-    private final MockFrameConsumer delegate;
-    private final Iterator<ReadRule> readScenario;
-    private ReadRule currentRule;
-    private final AtomicBoolean handedOver = new AtomicBoolean();
-
-    private MockChannel(Iterable<ReadRule> scenario,
-                        Iterable<InvocationExpectation> expectations) {
-        Iterator<ReadRule> iterator = scenario.iterator();
-        if (!iterator.hasNext()) {
-            throw new RuntimeException();
-        }
-        this.readScenario = iterator;
-        this.currentRule = iterator.next();
-        this.delegate = new MockFrameConsumer(expectations);
-    }
-
-    @Override
-    public void registerEvent(RawEvent event) throws IOException {
-        int ops = event.interestOps();
-        if ((ops & SelectionKey.OP_WRITE) != 0) {
-            synchronized (stateLock) {
-                checkOpen();
-                executor.execute(event::handle);
-            }
-        } else if ((ops & SelectionKey.OP_READ) != 0) {
-            CompletionStage<?> cs;
-            synchronized (readLock) {
-                cs = currentRule().whenReady();
-                synchronized (stateLock) {
-                    checkOpen();
-                    cs.thenRun(() -> executor.execute(event::handle));
-                }
-            }
-        } else {
-            throw new RuntimeException("Unexpected registration: " + ops);
-        }
-    }
-
-    @Override
-    public ByteBuffer initialByteBuffer() throws IllegalStateException {
-        if (!handedOver.compareAndSet(false, true)) {
-            throw new IllegalStateException();
-        }
-        return ByteBuffer.allocate(0);
-    }
-
-    @Override
-    public ByteBuffer read() throws IOException {
-        synchronized (readLock) {
-            checkOpen();
-            synchronized (stateLock) {
-                if (!isInputOpen) {
-                    return null;
-                }
-            }
-            ByteBuffer r = currentRule().read();
-            checkOpen();
-            return r;
-        }
-    }
-
-    @Override
-    public long write(ByteBuffer[] src, int offset, int len) throws IOException {
-        synchronized (writeLock) {
-            checkOpen();
-            synchronized (stateLock) {
-                if (!isOutputOpen) {
-                    throw new ClosedChannelException();
-                }
-            }
-            long n = 0;
-            for (int i = offset; i < offset + len && isOpen(); i++) {
-                ByteBuffer b = src[i];
-                int rem = src[i].remaining();
-                while (b.hasRemaining() && isOpen()) {
-                    reader.readFrame(b, delegate);
-                }
-                n += rem;
-            }
-            checkOpen();
-            return n;
-        }
-    }
-
-    public boolean isOpen() {
-        return !closed;
-    }
-
-    @Override
-    public void shutdownInput() throws IOException {
-        synchronized (stateLock) {
-            if (!isOpen()) {
-                throw new ClosedChannelException();
-            }
-            isInputOpen = false;
-        }
-    }
-
-    @Override
-    public void shutdownOutput() throws IOException {
-        synchronized (stateLock) {
-            if (!isOpen()) {
-                throw new ClosedChannelException();
-            }
-            isOutputOpen = false;
-        }
-    }
-
-    @Override
-    public void close() {
-        synchronized (stateLock) {
-            closed = true;
-        }
-    }
-
-    @Override
-    public String toString() {
-        return super.toString() + "[" + (closed ? "closed" : "open") + "]";
-    }
-
-    private ReadRule currentRule() {
-        assert Thread.holdsLock(readLock);
-        while (!currentRule.applies()) { // There should be the terminal rule which always applies
-            currentRule = readScenario.next();
-        }
-        return currentRule;
-    }
-
-    private void checkOpen() throws ClosedChannelException {
-        if (!isOpen()) {
-            throw new ClosedChannelException();
-        }
-    }
-
-    @Override
-    public CompletableFuture<Void> expectations(long timeout, TimeUnit unit) {
-        return delegate.expectations(timeout, unit);
-    }
-
-    private static class MockFrameConsumer extends FrameConsumer implements Mock {
-
-        private final Frame.Masker masker = new Frame.Masker();
-
-        MockFrameConsumer(Iterable<InvocationExpectation> expectations) {
-            super(new MockMessageStreamConsumer(expectations));
-        }
-
-        @Override
-        public void mask(boolean value) {
-        }
-
-        @Override
-        public void maskingKey(int value) {
-            masker.mask(value);
-        }
-
-        @Override
-        public void payloadData(ByteBuffer data) {
-            int p = data.position();
-            int l = data.limit();
-            masker.transferMasking(data, data);
-//            select(p, l, data); FIXME
-            super.payloadData(data);
-        }
-
-        @Override
-        public CompletableFuture<Void> expectations(long timeout, TimeUnit unit) {
-            return ((Mock) getOutput()).expectations(timeout, unit);
-        }
-    }
-
-    private static final class MockMessageStreamConsumer implements MessageStreamConsumer, Mock {
-
-        private final InvocationChecker checker;
-
-        MockMessageStreamConsumer(Iterable<InvocationExpectation> expectations) {
-            checker = new InvocationChecker(expectations);
-        }
-
-        @Override
-        public void onText(MessagePart part, CharSequence data) {
-            checker.checkInvocation("onText", part, data);
-        }
-
-        @Override
-        public void onBinary(MessagePart part, ByteBuffer data) {
-            checker.checkInvocation("onBinary", part, data);
-        }
-
-        @Override
-        public void onPing(ByteBuffer data) {
-            checker.checkInvocation("onPing", data);
-        }
-
-        @Override
-        public void onPong(ByteBuffer data) {
-            checker.checkInvocation("onPong", data);
-        }
-
-        @Override
-        public void onClose(OptionalInt statusCode, CharSequence reason) {
-            checker.checkInvocation("onClose", statusCode, reason);
-        }
-
-        @Override
-        public void onError(Exception e) {
-            checker.checkInvocation("onError", e);
-        }
-
-        @Override
-        public void onComplete() {
-            checker.checkInvocation("onComplete");
-        }
-
-        @Override
-        public CompletableFuture<Void> expectations(long timeout, TimeUnit unit) {
-            return checker.expectations(timeout, unit);
-        }
-    }
-
-    public static final class Builder {
-
-        private final Frame.HeaderWriter b = new Frame.HeaderWriter();
-        private final List<InvocationExpectation> expectations = new LinkedList<>();
-        private final List<ReadRule> scenario = new LinkedList<>();
-
-        Builder expectPing(F1<? super ByteBuffer, Boolean> predicate) {
-            InvocationExpectation e = new InvocationExpectation("onPing",
-                    args -> predicate.apply((ByteBuffer) args[0]));
-            expectations.add(e);
-            return this;
-        }
-
-        Builder expectPong(F1<? super ByteBuffer, Boolean> predicate) {
-            InvocationExpectation e = new InvocationExpectation("onPong",
-                    args -> predicate.apply((ByteBuffer) args[0]));
-            expectations.add(e);
-            return this;
-        }
-
-        Builder expectClose(F2<? super Integer, ? super String, Boolean> predicate) {
-            InvocationExpectation e = new InvocationExpectation("onClose",
-                    args -> predicate.apply((Integer) args[0], (String) args[1]));
-            expectations.add(e);
-            return this;
-        }
-
-        Builder provideFrame(boolean fin, boolean rsv1, boolean rsv2,
-                             boolean rsv3, Opcode opcode, ByteBuffer data) {
-
-            ByteBuffer b = ByteBuffer.allocate(MAX_HEADER_SIZE_BYTES + data.remaining());
-            this.b.fin(fin).rsv1(rsv1).rsv2(rsv2).rsv3(rsv3).opcode(opcode).noMask()
-                    .payloadLen(data.remaining()).write(b);
-
-            int p = data.position();
-            int l = data.limit();
-            b.put(data);
-            b.flip();
-//            select(p, l, data); FIXME
-
-            ReadRule r = new ReadRule() {
-
-                private volatile boolean provided;
-
-                @Override
-                public CompletionStage<?> whenReady() {
-                    return NOW;
-                }
-
-                @Override
-                public ByteBuffer read() throws IOException {
-                    provided = true;
-                    return data;
-                }
-
-                @Override
-                public boolean applies() {
-                    return !provided;
-                }
-            };
-            scenario.add(r);
-            return this;
-        }
-
-        Builder provideEos() {
-            ReadRule r = new ReadRule() {
-
-                @Override
-                public CompletionStage<?> whenReady() {
-                    return NOW;
-                }
-
-                @Override
-                public ByteBuffer read() throws IOException {
-                    return null;
-                }
-
-                @Override
-                public boolean applies() {
-                    return true;
-                }
-            };
-            scenario.add(r);
-            return this;
-        }
-
-        Builder provideException(Supplier<? extends IOException> s) {
-            return this;
-        }
-
-        MockChannel build() {
-            LinkedList<ReadRule> scenario = new LinkedList<>(this.scenario);
-            scenario.add(new Terminator());
-            return new MockChannel(scenario, new LinkedList<>(expectations));
-        }
-    }
-
-    private interface ReadRule {
-
-        /*
-         * Returns a CS which when completed means `read(ByteBuffer dst)` can be
-         * invoked
-         */
-        CompletionStage<?> whenReady();
-
-        ByteBuffer read() throws IOException;
-
-        /*
-         * Returns true if this rule still applies, otherwise returns false
-         */
-        boolean applies();
-    }
-
-    public static final class Terminator implements ReadRule {
-
-        @Override
-        public CompletionStage<?> whenReady() {
-            return NEVER;
-        }
-
-        @Override
-        public ByteBuffer read() {
-            return ByteBuffer.allocate(0);
-        }
-
-        @Override
-        public boolean applies() {
-            return true;
-        }
-    }
-
-    private static final CompletionStage<?> NOW = CompletableFuture.completedStage(null);
-    private static final CompletionStage<?> NEVER = new CompletableFuture();
-}
diff --git a/test/jdk/java/net/httpclient/websocket/jdk.incubator.httpclient/jdk/incubator/http/internal/websocket/MockChannelTest.java b/test/jdk/java/net/httpclient/websocket/jdk.incubator.httpclient/jdk/incubator/http/internal/websocket/MockChannelTest.java
deleted file mode 100644
index 5b64ab64552..00000000000
--- a/test/jdk/java/net/httpclient/websocket/jdk.incubator.httpclient/jdk/incubator/http/internal/websocket/MockChannelTest.java
+++ /dev/null
@@ -1,102 +0,0 @@
-/*
- * Copyright (c) 2016, 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 jdk.incubator.http.internal.websocket;
-
-import org.testng.annotations.Test;
-import jdk.incubator.http.internal.websocket.Frame.Opcode;
-
-import java.io.IOException;
-import jdk.incubator.http.internal.websocket.TestSupport.AssertionFailedException;
-
-import java.nio.ByteBuffer;
-import java.util.concurrent.TimeUnit;
-
-import static jdk.incubator.http.internal.websocket.TestSupport.assertThrows;
-import static jdk.incubator.http.internal.websocket.TestSupport.checkExpectations;
-import static jdk.incubator.http.internal.websocket.Frame.MAX_HEADER_SIZE_BYTES;
-
-public final class MockChannelTest {
-
-    // TODO: tests for read (stubbing)
-
-    @Test
-    public void testPass01() {
-        MockChannel ch = new MockChannel.Builder().build();
-        checkExpectations(1, TimeUnit.SECONDS, ch);
-    }
-
-    @Test
-    public void testPass02() throws IOException {
-        int len = 8;
-        ByteBuffer header = ByteBuffer.allocate(MAX_HEADER_SIZE_BYTES);
-        ByteBuffer data = ByteBuffer.allocate(len);
-        new Frame.HeaderWriter()
-                .fin(true).opcode(Opcode.PONG).payloadLen(len).mask(0x12345678)
-                .write(header);
-        header.flip();
-        MockChannel ch = new MockChannel.Builder()
-                .expectPong(bb -> bb.remaining() == len)
-                .build();
-        ch.write(new ByteBuffer[]{header, data}, 0, 2);
-        checkExpectations(1, TimeUnit.SECONDS, ch);
-    }
-
-    @Test
-    public void testPass03() throws IOException {
-        int len = 8;
-        ByteBuffer header = ByteBuffer.allocate(MAX_HEADER_SIZE_BYTES);
-        ByteBuffer data = ByteBuffer.allocate(len - 2); // not all data is written
-        new Frame.HeaderWriter()
-                .fin(true).opcode(Opcode.PONG).payloadLen(len).mask(0x12345678)
-                .write(header);
-        header.flip();
-        MockChannel ch = new MockChannel.Builder().build(); // expected no invocations
-        ch.write(new ByteBuffer[]{header, data}, 0, 2);
-        checkExpectations(1, TimeUnit.SECONDS, ch);
-    }
-
-    @Test
-    public void testFail01() {
-        MockChannel ch = new MockChannel.Builder()
-                .expectClose((code, reason) -> code == 1002 && reason.isEmpty())
-                .build();
-        assertThrows(AssertionFailedException.class,
-                () -> checkExpectations(1, TimeUnit.SECONDS, ch));
-    }
-
-    @Test
-    public void testFail02() throws IOException {
-        ByteBuffer header = ByteBuffer.allocate(MAX_HEADER_SIZE_BYTES);
-        new Frame.HeaderWriter()
-                .fin(true).opcode(Opcode.CLOSE).payloadLen(2).mask(0x12345678)
-                .write(header);
-        header.flip();
-        ByteBuffer data = ByteBuffer.allocate(2).putChar((char) 1004).flip();
-        MockChannel ch = new MockChannel.Builder()
-                .expectClose((code, reason) -> code == 1002 && reason.isEmpty())
-                .build();
-        ch.write(new ByteBuffer[]{header, data}, 0, 2);
-        assertThrows(AssertionFailedException.class,
-                () -> checkExpectations(1, TimeUnit.SECONDS, ch));
-    }
-}
diff --git a/test/jdk/java/net/httpclient/websocket/jdk.incubator.httpclient/jdk/incubator/http/internal/websocket/MockListener.java b/test/jdk/java/net/httpclient/websocket/jdk.incubator.httpclient/jdk/incubator/http/internal/websocket/MockListener.java
index 0d09ac6abcc..9147304573b 100644
--- a/test/jdk/java/net/httpclient/websocket/jdk.incubator.httpclient/jdk/incubator/http/internal/websocket/MockListener.java
+++ b/test/jdk/java/net/httpclient/websocket/jdk.incubator.httpclient/jdk/incubator/http/internal/websocket/MockListener.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2017, 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
@@ -20,112 +20,383 @@
  * or visit www.oracle.com if you need additional information or have any
  * questions.
  */
+
 package jdk.incubator.http.internal.websocket;
 
-import jdk.incubator.http.internal.websocket.TestSupport.F1;
-import jdk.incubator.http.internal.websocket.TestSupport.F2;
-import jdk.incubator.http.internal.websocket.TestSupport.F3;
-import jdk.incubator.http.internal.websocket.TestSupport.InvocationChecker;
-import jdk.incubator.http.internal.websocket.TestSupport.InvocationExpectation;
-import jdk.incubator.http.internal.websocket.TestSupport.Mock;
 import jdk.incubator.http.WebSocket;
-import jdk.incubator.http.WebSocket.Listener;
 import jdk.incubator.http.WebSocket.MessagePart;
+
 import java.nio.ByteBuffer;
-import java.util.LinkedList;
+import java.util.ArrayList;
 import java.util.List;
+import java.util.Objects;
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.CompletionStage;
-import java.util.concurrent.TimeUnit;
 
-final class MockListener implements Listener, Mock {
+import static jdk.incubator.http.internal.websocket.TestSupport.fullCopy;
 
-    private final InvocationChecker checker;
+public class MockListener implements WebSocket.Listener {
 
-    @Override
-    public CompletableFuture<Void> expectations(long timeout, TimeUnit unit) {
-        return checker.expectations(timeout, unit);
-    }
+    private final long bufferSize;
+    private long count;
+    private final List<ListenerInvocation> invocations = new ArrayList<>();
+    private final CompletableFuture<?> lastCall = new CompletableFuture<>();
 
-    public static final class Builder {
-
-        private final List<InvocationExpectation> expectations = new LinkedList<>();
-
-        Builder expectOnOpen(F1<? super WebSocket, Boolean> predicate) {
-            InvocationExpectation e = new InvocationExpectation("onOpen",
-                    args -> predicate.apply((WebSocket) args[0]));
-            expectations.add(e);
-            return this;
+    /*
+     * Typical buffer sizes: 1, n, Long.MAX_VALUE
+     */
+    public MockListener(long bufferSize) {
+        if (bufferSize < 1) {
+            throw new IllegalArgumentException();
         }
-
-        Builder expectOnPing(F2<? super WebSocket, ? super ByteBuffer, Boolean> predicate) {
-            InvocationExpectation e = new InvocationExpectation("onPing",
-                    args -> predicate.apply((WebSocket) args[0], (ByteBuffer) args[1]));
-            expectations.add(e);
-            return this;
-        }
-
-        Builder expectOnClose(F3<? super WebSocket, ? super Integer, ? super String, Boolean> predicate) {
-            expectations.add(new InvocationExpectation("onClose",
-                    args -> predicate.apply((WebSocket) args[0], (Integer) args[1], (String) args[2])));
-            return this;
-        }
-
-        Builder expectOnError(F2<? super WebSocket, ? super Throwable, Boolean> predicate) {
-            expectations.add(new InvocationExpectation("onError",
-                    args -> predicate.apply((WebSocket) args[0], (Throwable) args[1])));
-            return this;
-        }
-
-        MockListener build() {
-            return new MockListener(new LinkedList<>(expectations));
-        }
-    }
-
-    private MockListener(List<InvocationExpectation> expectations) {
-        this.checker = new InvocationChecker(expectations);
+        this.bufferSize = bufferSize;
     }
 
     @Override
     public void onOpen(WebSocket webSocket) {
-        checker.checkInvocation("onOpen", webSocket);
+        System.out.printf("onOpen(%s)%n", webSocket);
+        invocations.add(new OnOpen(webSocket));
+        onOpen0(webSocket);
+    }
+
+    protected void onOpen0(WebSocket webSocket) {
+        replenish(webSocket);
     }
 
     @Override
-    public CompletionStage<?> onText(WebSocket webSocket, CharSequence message,
+    public CompletionStage<?> onText(WebSocket webSocket,
+                                     CharSequence message,
                                      MessagePart part) {
-        checker.checkInvocation("onText", webSocket, message, part);
+        System.out.printf("onText(%s, %s, %s)%n", webSocket, message, part);
+        invocations.add(new OnText(webSocket, message.toString(), part));
+        return onText0(webSocket, message, part);
+    }
+
+    protected CompletionStage<?> onText0(WebSocket webSocket,
+                                         CharSequence message,
+                                         MessagePart part) {
+        replenish(webSocket);
         return null;
     }
 
     @Override
-    public CompletionStage<?> onBinary(WebSocket webSocket, ByteBuffer message,
+    public CompletionStage<?> onBinary(WebSocket webSocket,
+                                       ByteBuffer message,
                                        MessagePart part) {
-        checker.checkInvocation("onBinary", webSocket, message, part);
+        System.out.printf("onBinary(%s, %s, %s)%n", webSocket, message, part);
+        invocations.add(new OnBinary(webSocket, fullCopy(message), part));
+        return onBinary0(webSocket, message, part);
+    }
+
+    protected CompletionStage<?> onBinary0(WebSocket webSocket,
+                                           ByteBuffer message,
+                                           MessagePart part) {
+        replenish(webSocket);
         return null;
     }
 
     @Override
     public CompletionStage<?> onPing(WebSocket webSocket, ByteBuffer message) {
-        checker.checkInvocation("onPing", webSocket, message);
+        System.out.printf("onPing(%s, %s)%n", webSocket, message);
+        invocations.add(new OnPing(webSocket, fullCopy(message)));
+        return onPing0(webSocket, message);
+    }
+
+    protected CompletionStage<?> onPing0(WebSocket webSocket, ByteBuffer message) {
+        replenish(webSocket);
         return null;
     }
 
     @Override
     public CompletionStage<?> onPong(WebSocket webSocket, ByteBuffer message) {
-        checker.checkInvocation("onPong", webSocket, message);
+        System.out.printf("onPong(%s, %s)%n", webSocket, message);
+        invocations.add(new OnPong(webSocket, fullCopy(message)));
+        return onPong0(webSocket, message);
+    }
+
+    protected CompletionStage<?> onPong0(WebSocket webSocket, ByteBuffer message) {
+        replenish(webSocket);
         return null;
     }
 
     @Override
-    public CompletionStage<?> onClose(WebSocket webSocket, int statusCode,
+    public CompletionStage<?> onClose(WebSocket webSocket,
+                                      int statusCode,
                                       String reason) {
-        checker.checkInvocation("onClose", webSocket, statusCode, reason);
+        System.out.printf("onClose(%s, %s, %s)%n", webSocket, statusCode, reason);
+        invocations.add(new OnClose(webSocket, statusCode, reason));
+        lastCall.complete(null);
         return null;
     }
 
     @Override
     public void onError(WebSocket webSocket, Throwable error) {
-        checker.checkInvocation("onError", webSocket, error);
+        System.out.printf("onError(%s, %s)%n", webSocket, error);
+        invocations.add(new OnError(webSocket, error == null ? null : error.getClass()));
+        lastCall.complete(null);
+    }
+
+    public CompletableFuture<?> onCloseOrOnErrorCalled() {
+        return lastCall.copy();
+    }
+
+    protected void replenish(WebSocket webSocket) {
+        if (--count <= 0) {
+            count = bufferSize - bufferSize / 2;
+        }
+        webSocket.request(count);
+    }
+
+    public List<ListenerInvocation> invocations() {
+        return new ArrayList<>(invocations);
+    }
+
+    public abstract static class ListenerInvocation {
+
+        public static OnOpen onOpen(WebSocket webSocket) {
+            return new OnOpen(webSocket);
+        }
+
+        public static OnText onText(WebSocket webSocket,
+                                    String text,
+                                    MessagePart part) {
+            return new OnText(webSocket, text, part);
+        }
+
+        public static OnBinary onBinary(WebSocket webSocket,
+                                        ByteBuffer data,
+                                        MessagePart part) {
+            return new OnBinary(webSocket, data, part);
+        }
+
+        public static OnPing onPing(WebSocket webSocket,
+                                    ByteBuffer data) {
+            return new OnPing(webSocket, data);
+        }
+
+        public static OnPong onPong(WebSocket webSocket,
+                                    ByteBuffer data) {
+            return new OnPong(webSocket, data);
+        }
+
+        public static OnClose onClose(WebSocket webSocket,
+                                      int statusCode,
+                                      String reason) {
+            return new OnClose(webSocket, statusCode, reason);
+        }
+
+        public static OnError onError(WebSocket webSocket,
+                                      Class<? extends Throwable> clazz) {
+            return new OnError(webSocket, clazz);
+        }
+
+        final WebSocket webSocket;
+
+        private ListenerInvocation(WebSocket webSocket) {
+            this.webSocket = webSocket;
+        }
+    }
+
+    public static final class OnOpen extends ListenerInvocation {
+
+        public OnOpen(WebSocket webSocket) {
+            super(webSocket);
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) return true;
+            if (o == null || getClass() != o.getClass()) return false;
+            ListenerInvocation that = (ListenerInvocation) o;
+            return Objects.equals(webSocket, that.webSocket);
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hashCode(webSocket);
+        }
+    }
+
+    public static final class OnText extends ListenerInvocation {
+
+        final String text;
+        final MessagePart part;
+
+        public OnText(WebSocket webSocket, String text, MessagePart part) {
+            super(webSocket);
+            this.text = text;
+            this.part = part;
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) return true;
+            if (o == null || getClass() != o.getClass()) return false;
+            OnText onText = (OnText) o;
+            return Objects.equals(text, onText.text) &&
+                    part == onText.part &&
+                    Objects.equals(webSocket, onText.webSocket);
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(text, part, webSocket);
+        }
+
+        @Override
+        public String toString() {
+            return String.format("onText(%s, %s, %s)", webSocket, text, part);
+        }
+    }
+
+    public static final class OnBinary extends ListenerInvocation {
+
+        final ByteBuffer data;
+        final MessagePart part;
+
+        public OnBinary(WebSocket webSocket, ByteBuffer data, MessagePart part) {
+            super(webSocket);
+            this.data = data;
+            this.part = part;
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) return true;
+            if (o == null || getClass() != o.getClass()) return false;
+            OnBinary onBinary = (OnBinary) o;
+            return Objects.equals(data, onBinary.data) &&
+                    part == onBinary.part &&
+                    Objects.equals(webSocket, onBinary.webSocket);
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(data, part, webSocket);
+        }
+
+        @Override
+        public String toString() {
+            return String.format("onBinary(%s, %s, %s)", webSocket, data, part);
+        }
+    }
+
+    public static final class OnPing extends ListenerInvocation {
+
+        final ByteBuffer data;
+
+        public OnPing(WebSocket webSocket, ByteBuffer data) {
+            super(webSocket);
+            this.data = data;
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) return true;
+            if (o == null || getClass() != o.getClass()) return false;
+            OnPing onPing = (OnPing) o;
+            return Objects.equals(data, onPing.data) &&
+                    Objects.equals(webSocket, onPing.webSocket);
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(data, webSocket);
+        }
+
+        @Override
+        public String toString() {
+            return String.format("onPing(%s, %s)", webSocket, data);
+        }
+    }
+
+    public static final class OnPong extends ListenerInvocation {
+
+        final ByteBuffer data;
+
+        public OnPong(WebSocket webSocket, ByteBuffer data) {
+            super(webSocket);
+            this.data = data;
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) return true;
+            if (o == null || getClass() != o.getClass()) return false;
+            OnPong onPong = (OnPong) o;
+            return Objects.equals(data, onPong.data) &&
+                    Objects.equals(webSocket, onPong.webSocket);
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(data, webSocket);
+        }
+
+        @Override
+        public String toString() {
+            return String.format("onPong(%s, %s)", webSocket, data);
+        }
+    }
+
+    public static final class OnClose extends ListenerInvocation {
+
+        final int statusCode;
+        final String reason;
+
+        public OnClose(WebSocket webSocket, int statusCode, String reason) {
+            super(webSocket);
+            this.statusCode = statusCode;
+            this.reason = reason;
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) return true;
+            if (o == null || getClass() != o.getClass()) return false;
+            OnClose onClose = (OnClose) o;
+            return statusCode == onClose.statusCode &&
+                    Objects.equals(reason, onClose.reason) &&
+                    Objects.equals(webSocket, onClose.webSocket);
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(statusCode, reason, webSocket);
+        }
+
+        @Override
+        public String toString() {
+            return String.format("onClose(%s, %s, %s)", webSocket, statusCode, reason);
+        }
+    }
+
+    public static final class OnError extends ListenerInvocation {
+
+        final Class<? extends Throwable> clazz;
+
+        public OnError(WebSocket webSocket, Class<? extends Throwable> clazz) {
+            super(webSocket);
+            this.clazz = clazz;
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) return true;
+            if (o == null || getClass() != o.getClass()) return false;
+            OnError onError = (OnError) o;
+            return Objects.equals(clazz, onError.clazz) &&
+                    Objects.equals(webSocket, onError.webSocket);
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(clazz, webSocket);
+        }
+
+        @Override
+        public String toString() {
+            return String.format("onError(%s, %s)", webSocket, clazz);
+        }
     }
 }
diff --git a/test/jdk/java/net/httpclient/websocket/jdk.incubator.httpclient/jdk/incubator/http/internal/websocket/MockListenerTest.java b/test/jdk/java/net/httpclient/websocket/jdk.incubator.httpclient/jdk/incubator/http/internal/websocket/MockListenerTest.java
deleted file mode 100644
index 33a2a1ff600..00000000000
--- a/test/jdk/java/net/httpclient/websocket/jdk.incubator.httpclient/jdk/incubator/http/internal/websocket/MockListenerTest.java
+++ /dev/null
@@ -1,99 +0,0 @@
-/*
- * Copyright (c) 2016, 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 jdk.incubator.http.internal.websocket;
-
-import org.testng.annotations.Test;
-
-import jdk.incubator.http.internal.websocket.TestSupport.AssertionFailedException;
-import java.util.concurrent.TimeUnit;
-
-import static jdk.incubator.http.internal.websocket.TestSupport.assertThrows;
-import static jdk.incubator.http.internal.websocket.TestSupport.checkExpectations;
-
-public class MockListenerTest {
-
-    @Test
-    public void testPass01() {
-        MockListener l = new MockListener.Builder().build();
-        checkExpectations(1, TimeUnit.SECONDS, l);
-    }
-
-    @Test
-    public void testPass02() {
-        MockListener l = new MockListener.Builder()
-                .expectOnOpen(ws -> ws == null)
-                .build();
-        l.onOpen(null);
-        checkExpectations(1, TimeUnit.SECONDS, l);
-    }
-
-    @Test
-    public void testPass03() {
-        MockListener l = new MockListener.Builder()
-                .expectOnOpen(ws -> ws == null)
-                .expectOnClose((ws, code, reason) ->
-                        ws == null && code == 1002 && "blah".equals(reason))
-                .build();
-        l.onOpen(null);
-        l.onClose(null, 1002, "blah");
-        checkExpectations(1, TimeUnit.SECONDS, l);
-    }
-
-    @Test
-    public void testFail01() {
-        MockListener l = new MockListener.Builder()
-                .expectOnOpen(ws -> ws != null)
-                .build();
-        l.onOpen(null);
-        assertThrows(AssertionFailedException.class,
-                () -> checkExpectations(1, TimeUnit.SECONDS, l));
-    }
-
-    @Test
-    public void testFail02() {
-        MockListener l = new MockListener.Builder()
-                .expectOnOpen(ws -> true)
-                .build();
-        assertThrows(AssertionFailedException.class,
-                () -> checkExpectations(1, TimeUnit.SECONDS, l));
-    }
-
-    @Test
-    public void testFail03() {
-        MockListener l = new MockListener.Builder()
-                .expectOnOpen(ws -> true)
-                .build();
-        l.onOpen(null);
-        l.onClose(null, 1002, "");
-        assertThrows(AssertionFailedException.class,
-                () -> checkExpectations(1, TimeUnit.SECONDS, l));
-    }
-
-    @Test
-    public void testFail04() {
-        MockListener l = new MockListener.Builder().build();
-        l.onClose(null, 1002, "");
-        assertThrows(AssertionFailedException.class,
-                () -> checkExpectations(1, TimeUnit.SECONDS, l));
-    }
-}
diff --git a/test/jdk/java/net/httpclient/websocket/jdk.incubator.httpclient/jdk/incubator/http/internal/websocket/MockReceiver.java b/test/jdk/java/net/httpclient/websocket/jdk.incubator.httpclient/jdk/incubator/http/internal/websocket/MockReceiver.java
new file mode 100644
index 00000000000..5129847d2dd
--- /dev/null
+++ b/test/jdk/java/net/httpclient/websocket/jdk.incubator.httpclient/jdk/incubator/http/internal/websocket/MockReceiver.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 2017, 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 jdk.incubator.http.internal.websocket;
+
+import jdk.incubator.http.internal.common.Pair;
+import jdk.incubator.http.internal.common.SequentialScheduler;
+import jdk.incubator.http.internal.common.SequentialScheduler.DeferredCompleter;
+
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.CompletionStage;
+import java.util.function.Consumer;
+
+public class MockReceiver extends Receiver {
+
+    private final Iterator<Pair<CompletionStage<?>, Consumer<MessageStreamConsumer>>> iterator;
+    private final MessageStreamConsumer consumer;
+
+    public MockReceiver(MessageStreamConsumer consumer, RawChannel channel,
+                        Pair<CompletionStage<?>, Consumer<MessageStreamConsumer>>... pairs) {
+        super(consumer, channel);
+        this.consumer = consumer;
+        iterator = Arrays.asList(pairs).iterator();
+    }
+
+    @Override
+    protected SequentialScheduler createScheduler() {
+        class X { // Class is hack needed to allow the task to refer to the scheduler
+            SequentialScheduler scheduler = new SequentialScheduler(task());
+
+            SequentialScheduler.RestartableTask task() {
+                return new SequentialScheduler.RestartableTask() {
+                    @Override
+                    public void run(DeferredCompleter taskCompleter) {
+                        if (!scheduler.isStopped() && !demand.isFulfilled()) {
+                            if (!iterator.hasNext()) {
+                                taskCompleter.complete();
+                                return;
+                            }
+                            Pair<CompletionStage<?>, Consumer<MessageStreamConsumer>> p = iterator.next();
+                            CompletableFuture<?> cf = p.first.toCompletableFuture();
+                            if (cf.isDone()) { // Forcing synchronous execution
+                                p.second.accept(consumer);
+                                repeat(taskCompleter);
+                            } else {
+                                cf.whenCompleteAsync((r, e) -> {
+                                    p.second.accept(consumer);
+                                    repeat(taskCompleter);
+                                });
+                            }
+                        } else {
+                            taskCompleter.complete();
+                        }
+                    }
+
+                    private void repeat(DeferredCompleter taskCompleter) {
+                        taskCompleter.complete();
+                        scheduler.runOrSchedule();
+                    }
+                };
+            }
+        }
+        return new X().scheduler;
+    }
+}
diff --git a/test/jdk/java/net/httpclient/websocket/jdk.incubator.httpclient/jdk/incubator/http/internal/websocket/MockTransmitter.java b/test/jdk/java/net/httpclient/websocket/jdk.incubator.httpclient/jdk/incubator/http/internal/websocket/MockTransmitter.java
new file mode 100644
index 00000000000..c65ec585dca
--- /dev/null
+++ b/test/jdk/java/net/httpclient/websocket/jdk.incubator.httpclient/jdk/incubator/http/internal/websocket/MockTransmitter.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2017, 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 jdk.incubator.http.internal.websocket;
+
+import java.util.Queue;
+import java.util.concurrent.CompletionStage;
+import java.util.concurrent.ConcurrentLinkedQueue;
+import java.util.function.Consumer;
+
+public abstract class MockTransmitter extends Transmitter {
+
+    private final long startTime = System.currentTimeMillis();
+
+    private final Queue<OutgoingMessage> messages = new ConcurrentLinkedQueue<>();
+
+    public MockTransmitter() {
+        super(null);
+    }
+
+    @Override
+    public void send(OutgoingMessage message,
+                     Consumer<Exception> completionHandler) {
+        System.out.printf("[%6s ms.] begin send(%s)%n",
+                          System.currentTimeMillis() - startTime,
+                          message);
+        messages.add(message);
+        whenSent().whenComplete((r, e) -> {
+            System.out.printf("[%6s ms.] complete send(%s)%n",
+                              System.currentTimeMillis() - startTime,
+                              message);
+            if (e != null) {
+                completionHandler.accept((Exception) e);
+            } else {
+                completionHandler.accept(null);
+            }
+        });
+        System.out.printf("[%6s ms.] end send(%s)%n",
+                          System.currentTimeMillis() - startTime,
+                          message);
+    }
+
+    @Override
+    public void close() { }
+
+    protected abstract CompletionStage<?> whenSent();
+
+    public Queue<OutgoingMessage> queue() {
+        return messages;
+    }
+}
diff --git a/test/jdk/java/net/httpclient/websocket/jdk.incubator.httpclient/jdk/incubator/http/internal/websocket/MockTransport.java b/test/jdk/java/net/httpclient/websocket/jdk.incubator.httpclient/jdk/incubator/http/internal/websocket/MockTransport.java
new file mode 100644
index 00000000000..bdbf8b1ac27
--- /dev/null
+++ b/test/jdk/java/net/httpclient/websocket/jdk.incubator.httpclient/jdk/incubator/http/internal/websocket/MockTransport.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2017, 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 jdk.incubator.http.internal.websocket;
+
+import java.nio.ByteBuffer;
+
+public class MockTransport extends TransportSupplier {
+
+    public MockTransport() {
+        super(new NullRawChannel());
+    }
+
+    public static class NullRawChannel implements RawChannel {
+
+        @Override
+        public void registerEvent(RawEvent event) {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public ByteBuffer initialByteBuffer() {
+            return ByteBuffer.allocate(0);
+        }
+
+        @Override
+        public ByteBuffer read() {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public long write(ByteBuffer[] srcs, int offset, int length) {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public void shutdownInput() {
+        }
+
+        @Override
+        public void shutdownOutput() {
+        }
+
+        @Override
+        public void close() {
+        }
+    }
+}
diff --git a/test/jdk/java/net/httpclient/websocket/jdk.incubator.httpclient/jdk/incubator/http/internal/websocket/PingTest.java b/test/jdk/java/net/httpclient/websocket/jdk.incubator.httpclient/jdk/incubator/http/internal/websocket/PingTest.java
deleted file mode 100644
index 183ebb8d9c5..00000000000
--- a/test/jdk/java/net/httpclient/websocket/jdk.incubator.httpclient/jdk/incubator/http/internal/websocket/PingTest.java
+++ /dev/null
@@ -1,195 +0,0 @@
-/*
- * Copyright (c) 2016, 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 jdk.incubator.http.internal.websocket;
-
-import org.testng.SkipException;
-import org.testng.annotations.Test;
-import jdk.incubator.http.internal.websocket.Frame.Opcode;
-
-import java.io.IOException;
-import java.net.ProtocolException;
-import jdk.incubator.http.WebSocket;
-import java.nio.ByteBuffer;
-import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.Executors;
-import java.util.concurrent.TimeUnit;
-
-import static jdk.incubator.http.internal.websocket.TestSupport.Expectation.ifExpect;
-import static jdk.incubator.http.internal.websocket.TestSupport.assertCompletesExceptionally;
-import static jdk.incubator.http.internal.websocket.TestSupport.checkExpectations;
-import static org.testng.Assert.assertSame;
-
-/*
- * Examines sendPing/onPing contracts
- */
-public final class PingTest {
-
-    /*
-     * sendPing(message) is invoked. If the `message` argument is illegal, then
-     * the method must throw an appropriate exception. Otherwise no exception
-     * must be thrown.
-     */
-//    @Test(dataProvider = "outgoingData", dataProviderClass = DataProviders.class)
-    public void testSendPingArguments(ByteBuffer message) {
-        WebSocket ws = newWebSocket();
-        ifExpect(
-                message == null,
-                NullPointerException.class::isInstance)
-        .orExpect(
-                message != null && message.remaining() > 125,
-                IllegalArgumentException.class::isInstance)
-        .assertThrows(
-                () -> ws.sendPing(message)
-        );
-    }
-
-    /*
-     * sendPing(message) with a legal argument has been invoked, then:
-     *
-     * 1. A Ping message with the same payload appears on the wire
-     * 2. The CF returned from the method completes normally with the same
-     *    WebSocket that sendPing has been called on
-     */
-    @Test(dataProvider = "outgoingData", dataProviderClass = DataProviders.class)
-    public void testSendPingWysiwyg(ByteBuffer message) throws ExecutionException, InterruptedException {
-        if (message == null || message.remaining() > 125) {
-            return;
-        }
-        ByteBuffer snapshot = copy(message);
-        MockChannel channel = new MockChannel.Builder()
-                .expectPing(snapshot::equals)
-                .build();
-        WebSocket ws = newWebSocket(channel);
-        CompletableFuture<WebSocket> cf = ws.sendPing(message);
-        WebSocket ws1 = cf.join();
-        assertSame(ws1, ws); // (2)
-        checkExpectations(channel); // (1)
-    }
-
-    /*
-     * If an I/O error occurs while Ping messages is being sent, then:
-     *
-     * 1. The CF returned from sendPing completes exceptionally with this I/O
-     *    error as the cause
-     */
-//    @Test
-    public void testSendPingIOException() {
-        MockChannel ch = new MockChannel.Builder()
-//                .provideWriteException(IOException::new)
-                .build();
-        WebSocket ws = newWebSocket(ch);
-        CompletableFuture<WebSocket> cf = ws.sendPing(ByteBuffer.allocate(16));
-        assertCompletesExceptionally(IOException.class, cf);
-    }
-
-    /*
-     * If an incorrect Ping frame appears on the wire, then:
-     *
-     * 1. onError with the java.net.ProtocolException is invoked
-     * 1. A Close frame with status code 1002 appears on the wire
-     */
-//    @Test(dataProvider = "incorrectFrame", dataProviderClass = DataProviders.class)
-    public void testOnPingIncorrect(boolean fin, boolean rsv1, boolean rsv2,
-                                    boolean rsv3, ByteBuffer data) {
-        if (fin && !rsv1 && !rsv2 && !rsv3 && data.remaining() <= 125) {
-            throw new SkipException("Correct frame");
-        }
-        CompletableFuture<WebSocket> webSocket = new CompletableFuture<>();
-        MockChannel channel = new MockChannel.Builder()
-                .provideFrame(fin, rsv1, rsv2, rsv3, Opcode.PING, data)
-                .expectClose((code, reason) ->
-                        Integer.valueOf(1002).equals(code) && "".equals(reason))
-                .build();
-        MockListener listener = new MockListener.Builder()
-                .expectOnOpen((ws) -> true)
-                .expectOnError((ws, error) -> error instanceof ProtocolException)
-                .build();
-        webSocket.complete(newWebSocket(channel, listener));
-        checkExpectations(500, TimeUnit.MILLISECONDS, channel, listener);
-    }
-
-    /*
-     * If a Ping message has been read off the wire, then:
-     *
-     * 1. onPing is invoked with the data and the WebSocket the listener has
-     *    been attached to
-     * 2. A Pong message with the same contents will be sent in reply
-     */
-    @Test(dataProvider = "incomingData", dataProviderClass = DataProviders.class)
-    public void testOnPingReply(ByteBuffer data) {
-        CompletableFuture<WebSocket> webSocket = new CompletableFuture<>();
-        MockChannel channel = new MockChannel.Builder()
-                .provideFrame(true, false, false, false, Opcode.PING, data)
-                .expectPong(data::equals)
-                .build();
-        MockListener listener = new MockListener.Builder()
-                .expectOnOpen((ws) -> true) // maybe should capture with a CF?
-                .expectOnPing((ws, bb) -> data.equals(bb))
-                .build();
-        webSocket.complete(newWebSocket(channel, listener));
-        checkExpectations(500, TimeUnit.MILLISECONDS, channel, listener);
-    }
-
-    /*
-     * If onPing throws an exception or CS returned from it completes
-     * exceptionally, then:
-     *
-     * 1. onError is invoked with this particular exception as the cause and the
-     *    WebSocket the listener has been attached to
-     */
-    public void testOnPingExceptions() {
-    }
-
-    /*
-     * If a Ping message has been read off the wire and an I/O error occurs
-     * while WebSocket sends a Pong reply to it, then:
-     *
-     * 1. onError is invoked with this error as the cause and the WebSocket this
-     *    listener has been attached to
-     */
-    public void testOnPingReplyIOException() {
-    }
-
-    private WebSocket newWebSocket() {
-        return newWebSocket(new MockChannel.Builder().build());
-    }
-
-    private WebSocket newWebSocket(RawChannel ch) {
-        return newWebSocket(ch, new WebSocket.Listener() { });
-    }
-
-    private WebSocket newWebSocket(RawChannel ch, WebSocket.Listener l) {
-//        WebSocketImpl ws = new WebSocketImpl("", ch, l, Executors.newCachedThreadPool());
-//        ws.();
-//        ws.request(Long.MAX_VALUE);
-        return null; // FIXME
-    }
-
-    public static ByteBuffer copy(ByteBuffer src) {
-        int pos = src.position();
-        ByteBuffer b = ByteBuffer.allocate(src.remaining()).put(src).flip();
-        src.position(pos);
-        return b;
-    }
-}
diff --git a/test/jdk/java/net/httpclient/websocket/jdk.incubator.httpclient/jdk/incubator/http/internal/websocket/ReaderTest.java b/test/jdk/java/net/httpclient/websocket/jdk.incubator.httpclient/jdk/incubator/http/internal/websocket/ReaderTest.java
index 0602860e39b..97cfe962137 100644
--- a/test/jdk/java/net/httpclient/websocket/jdk.incubator.httpclient/jdk/incubator/http/internal/websocket/ReaderTest.java
+++ b/test/jdk/java/net/httpclient/websocket/jdk.incubator.httpclient/jdk/incubator/http/internal/websocket/ReaderTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2017, 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
diff --git a/test/jdk/java/net/httpclient/websocket/jdk.incubator.httpclient/jdk/incubator/http/internal/websocket/ReceivingTest.java b/test/jdk/java/net/httpclient/websocket/jdk.incubator.httpclient/jdk/incubator/http/internal/websocket/ReceivingTest.java
new file mode 100644
index 00000000000..4a4858b651a
--- /dev/null
+++ b/test/jdk/java/net/httpclient/websocket/jdk.incubator.httpclient/jdk/incubator/http/internal/websocket/ReceivingTest.java
@@ -0,0 +1,206 @@
+/*
+ * Copyright (c) 2017, 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 jdk.incubator.http.internal.websocket;
+
+import jdk.incubator.http.WebSocket;
+import org.testng.annotations.Test;
+
+import java.net.URI;
+import java.nio.ByteBuffer;
+import java.util.List;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.CompletionStage;
+import java.util.concurrent.TimeUnit;
+
+import static java.util.concurrent.CompletableFuture.completedStage;
+import static jdk.incubator.http.WebSocket.MessagePart.FIRST;
+import static jdk.incubator.http.WebSocket.MessagePart.LAST;
+import static jdk.incubator.http.WebSocket.MessagePart.PART;
+import static jdk.incubator.http.WebSocket.MessagePart.WHOLE;
+import static jdk.incubator.http.WebSocket.NORMAL_CLOSURE;
+import static jdk.incubator.http.internal.common.Pair.pair;
+import static jdk.incubator.http.internal.websocket.MockListener.ListenerInvocation.onClose;
+import static jdk.incubator.http.internal.websocket.MockListener.ListenerInvocation.onError;
+import static jdk.incubator.http.internal.websocket.MockListener.ListenerInvocation.onOpen;
+import static jdk.incubator.http.internal.websocket.MockListener.ListenerInvocation.onPing;
+import static jdk.incubator.http.internal.websocket.MockListener.ListenerInvocation.onPong;
+import static jdk.incubator.http.internal.websocket.MockListener.ListenerInvocation.onText;
+import static org.testng.Assert.assertEquals;
+
+public class ReceivingTest {
+
+    // TODO: request in onClose/onError
+    // TODO: throw exception in onClose/onError
+    // TODO: exception is thrown from request()
+
+    @Test
+    public void testNonPositiveRequest() throws Exception {
+        MockListener listener = new MockListener(Long.MAX_VALUE) {
+            @Override
+            protected void onOpen0(WebSocket webSocket) {
+                webSocket.request(0);
+            }
+        };
+        MockTransport transport = new MockTransport() {
+            @Override
+            protected Receiver newReceiver(MessageStreamConsumer consumer) {
+                return new MockReceiver(consumer, channel, pair(now(), m -> m.onText("1", WHOLE)));
+            }
+        };
+        WebSocket ws = newInstance(listener, transport);
+        listener.onCloseOrOnErrorCalled().get(10, TimeUnit.SECONDS);
+        List<MockListener.ListenerInvocation> invocations = listener.invocations();
+        assertEquals(invocations, List.of(onOpen(ws), onError(ws, IllegalArgumentException.class)));
+    }
+
+    @Test
+    public void testText1() throws Exception {
+        MockListener listener = new MockListener(Long.MAX_VALUE);
+        MockTransport transport = new MockTransport() {
+            @Override
+            protected Receiver newReceiver(MessageStreamConsumer consumer) {
+                return new MockReceiver(consumer, channel,
+                                        pair(now(), m -> m.onText("1", FIRST)),
+                                        pair(now(), m -> m.onText("2", PART)),
+                                        pair(now(), m -> m.onText("3", LAST)),
+                                        pair(now(), m -> m.onClose(NORMAL_CLOSURE, "no reason")));
+            }
+        };
+        WebSocket ws = newInstance(listener, transport);
+        listener.onCloseOrOnErrorCalled().get(10, TimeUnit.SECONDS);
+        List<MockListener.ListenerInvocation> invocations = listener.invocations();
+        assertEquals(invocations, List.of(onOpen(ws),
+                                          onText(ws, "1", FIRST),
+                                          onText(ws, "2", PART),
+                                          onText(ws, "3", LAST),
+                                          onClose(ws, NORMAL_CLOSURE, "no reason")));
+    }
+
+    @Test
+    public void testText2() throws Exception {
+        MockListener listener = new MockListener(Long.MAX_VALUE);
+        MockTransport transport = new MockTransport() {
+            @Override
+            protected Receiver newReceiver(MessageStreamConsumer consumer) {
+                return new MockReceiver(consumer, channel,
+                                        pair(now(),      m -> m.onText("1", FIRST)),
+                                        pair(seconds(1), m -> m.onText("2", PART)),
+                                        pair(now(),      m -> m.onText("3", LAST)),
+                                        pair(seconds(1), m -> m.onClose(NORMAL_CLOSURE, "no reason")));
+            }
+        };
+        WebSocket ws = newInstance(listener, transport);
+        listener.onCloseOrOnErrorCalled().get(10, TimeUnit.SECONDS);
+        List<MockListener.ListenerInvocation> invocations = listener.invocations();
+        assertEquals(invocations, List.of(onOpen(ws),
+                                          onText(ws, "1", FIRST),
+                                          onText(ws, "2", PART),
+                                          onText(ws, "3", LAST),
+                                          onClose(ws, NORMAL_CLOSURE, "no reason")));
+    }
+
+    @Test
+    public void testTextIntermixedWithPongs() throws Exception {
+        MockListener listener = new MockListener(Long.MAX_VALUE);
+        MockTransport transport = new MockTransport() {
+            @Override
+            protected Receiver newReceiver(MessageStreamConsumer consumer) {
+                return new MockReceiver(consumer, channel,
+                                        pair(now(),      m -> m.onText("1", FIRST)),
+                                        pair(now(),      m -> m.onText("2", PART)),
+                                        pair(now(),      m -> m.onPong(ByteBuffer.allocate(16))),
+                                        pair(seconds(1), m -> m.onPong(ByteBuffer.allocate(32))),
+                                        pair(now(),      m -> m.onText("3", LAST)),
+                                        pair(now(),      m -> m.onPong(ByteBuffer.allocate(64))),
+                                        pair(now(),      m -> m.onClose(NORMAL_CLOSURE, "no reason")));
+            }
+        };
+        WebSocket ws = newInstance(listener, transport);
+        listener.onCloseOrOnErrorCalled().get(10, TimeUnit.SECONDS);
+        List<MockListener.ListenerInvocation> invocations = listener.invocations();
+        assertEquals(invocations, List.of(onOpen(ws),
+                                          onText(ws, "1", FIRST),
+                                          onText(ws, "2", PART),
+                                          onPong(ws, ByteBuffer.allocate(16)),
+                                          onPong(ws, ByteBuffer.allocate(32)),
+                                          onText(ws, "3", LAST),
+                                          onPong(ws, ByteBuffer.allocate(64)),
+                                          onClose(ws, NORMAL_CLOSURE, "no reason")));
+    }
+
+    @Test
+    public void testTextIntermixedWithPings() throws Exception {
+        MockListener listener = new MockListener(Long.MAX_VALUE);
+        MockTransport transport = new MockTransport() {
+            @Override
+            protected Receiver newReceiver(MessageStreamConsumer consumer) {
+                return new MockReceiver(consumer, channel,
+                                        pair(now(),      m -> m.onText("1", FIRST)),
+                                        pair(now(),      m -> m.onText("2", PART)),
+                                        pair(now(),      m -> m.onPing(ByteBuffer.allocate(16))),
+                                        pair(seconds(1), m -> m.onPing(ByteBuffer.allocate(32))),
+                                        pair(now(),      m -> m.onText("3", LAST)),
+                                        pair(now(),      m -> m.onPing(ByteBuffer.allocate(64))),
+                                        pair(now(),      m -> m.onClose(NORMAL_CLOSURE, "no reason")));
+            }
+
+            @Override
+            protected Transmitter newTransmitter() {
+                return new MockTransmitter() {
+                    @Override
+                    protected CompletionStage<?> whenSent() {
+                        return now();
+                    }
+                };
+            }
+        };
+        WebSocket ws = newInstance(listener, transport);
+        listener.onCloseOrOnErrorCalled().get(10, TimeUnit.SECONDS);
+        List<MockListener.ListenerInvocation> invocations = listener.invocations();
+        System.out.println(invocations);
+        assertEquals(invocations, List.of(onOpen(ws),
+                                          onText(ws, "1", FIRST),
+                                          onText(ws, "2", PART),
+                                          onPing(ws, ByteBuffer.allocate(16)),
+                                          onPing(ws, ByteBuffer.allocate(32)),
+                                          onText(ws, "3", LAST),
+                                          onPing(ws, ByteBuffer.allocate(64)),
+                                          onClose(ws, NORMAL_CLOSURE, "no reason")));
+    }
+
+    private static CompletionStage<?> seconds(long s) {
+        return new CompletableFuture<>().completeOnTimeout(null, s, TimeUnit.SECONDS);
+    }
+
+    private static CompletionStage<?> now() {
+        return completedStage(null);
+    }
+
+    private static WebSocket newInstance(WebSocket.Listener listener,
+                                         TransportSupplier transport) {
+        URI uri = URI.create("ws://localhost");
+        String subprotocol = "";
+        return WebSocketImpl.newInstance(uri, subprotocol, listener, transport);
+    }
+}
diff --git a/test/jdk/java/net/httpclient/websocket/jdk.incubator.httpclient/jdk/incubator/http/internal/websocket/SendingTest.java b/test/jdk/java/net/httpclient/websocket/jdk.incubator.httpclient/jdk/incubator/http/internal/websocket/SendingTest.java
new file mode 100644
index 00000000000..4150a87bbfe
--- /dev/null
+++ b/test/jdk/java/net/httpclient/websocket/jdk.incubator.httpclient/jdk/incubator/http/internal/websocket/SendingTest.java
@@ -0,0 +1,164 @@
+/*
+ * Copyright (c) 2017, 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 jdk.incubator.http.internal.websocket;
+
+import jdk.incubator.http.WebSocket;
+import org.testng.annotations.Test;
+
+import java.net.URI;
+import java.nio.ByteBuffer;
+import java.util.Random;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.CompletionStage;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import static jdk.incubator.http.WebSocket.NORMAL_CLOSURE;
+import static jdk.incubator.http.internal.websocket.TestSupport.assertCompletesExceptionally;
+import static jdk.incubator.http.internal.websocket.WebSocketImpl.newInstance;
+import static org.testng.Assert.assertEquals;
+
+public class SendingTest {
+
+    @Test
+    public void sendTextImmediately() {
+        MockTransmitter t = new MockTransmitter() {
+            @Override
+            protected CompletionStage<?> whenSent() {
+                return CompletableFuture.completedFuture(null);
+            }
+        };
+        WebSocket ws = newWebSocket(t);
+        CompletableFuture.completedFuture(ws)
+                .thenCompose(w -> w.sendText("1", true))
+                .thenCompose(w -> w.sendText("2", true))
+                .thenCompose(w -> w.sendText("3", true))
+                .join();
+
+        assertEquals(t.queue().size(), 3);
+    }
+
+    @Test
+    public void sendTextWithDelay() {
+        MockTransmitter t = new MockTransmitter() {
+            @Override
+            protected CompletionStage<?> whenSent() {
+                return new CompletableFuture<>()
+                        .completeOnTimeout(null, 1, TimeUnit.SECONDS);
+            }
+        };
+        WebSocket ws = newWebSocket(t);
+        CompletableFuture.completedFuture(ws)
+                .thenCompose(w -> w.sendText("1", true))
+                .thenCompose(w -> w.sendText("2", true))
+                .thenCompose(w -> w.sendText("3", true))
+                .join();
+
+        assertEquals(t.queue().size(), 3);
+    }
+
+    @Test
+    public void sendTextMixedDelay() {
+        Random r = new Random();
+
+        MockTransmitter t = new MockTransmitter() {
+            @Override
+            protected CompletionStage<?> whenSent() {
+                return r.nextBoolean() ?
+                        new CompletableFuture<>().completeOnTimeout(null, 1, TimeUnit.SECONDS) :
+                        CompletableFuture.completedFuture(null);
+            }
+        };
+        WebSocket ws = newWebSocket(t);
+        CompletableFuture.completedFuture(ws)
+                .thenCompose(w -> w.sendText("1", true))
+                .thenCompose(w -> w.sendText("2", true))
+                .thenCompose(w -> w.sendText("3", true))
+                .thenCompose(w -> w.sendText("4", true))
+                .thenCompose(w -> w.sendText("5", true))
+                .thenCompose(w -> w.sendText("6", true))
+                .thenCompose(w -> w.sendText("7", true))
+                .thenCompose(w -> w.sendText("8", true))
+                .thenCompose(w -> w.sendText("9", true))
+                .join();
+
+        assertEquals(t.queue().size(), 9);
+    }
+
+    @Test
+    public void sendControlMessagesConcurrently() {
+
+        CompletableFuture<?> first = new CompletableFuture<>();
+
+        MockTransmitter t = new MockTransmitter() {
+
+            final AtomicInteger i = new AtomicInteger();
+
+            @Override
+            protected CompletionStage<?> whenSent() {
+                if (i.incrementAndGet() == 1) {
+                    return first;
+                } else {
+                    return CompletableFuture.completedFuture(null);
+                }
+            }
+        };
+        WebSocket ws = newWebSocket(t);
+
+        CompletableFuture<?> cf1 = ws.sendPing(ByteBuffer.allocate(0));
+        CompletableFuture<?> cf2 = ws.sendPong(ByteBuffer.allocate(0));
+        CompletableFuture<?> cf3 = ws.sendClose(NORMAL_CLOSURE, "");
+        CompletableFuture<?> cf4 = ws.sendClose(NORMAL_CLOSURE, "");
+        CompletableFuture<?> cf5 = ws.sendPing(ByteBuffer.allocate(0));
+        CompletableFuture<?> cf6 = ws.sendPong(ByteBuffer.allocate(0));
+
+        first.complete(null);
+        // Don't care about exceptional completion, only that all of them have
+        // completed
+        CompletableFuture.allOf(cf1, cf2, cf3, cf4, cf5, cf6)
+                .handle((v, e) -> null).join();
+
+        cf3.join(); /* Check that sendClose has completed normally */
+        cf4.join(); /* Check that repeated sendClose has completed normally */
+        assertCompletesExceptionally(IllegalStateException.class, cf5);
+        assertCompletesExceptionally(IllegalStateException.class, cf6);
+
+        assertEquals(t.queue().size(), 3); // 6 minus 3 that were not accepted
+    }
+
+    private static WebSocket newWebSocket(Transmitter transmitter) {
+        URI uri = URI.create("ws://localhost");
+        String subprotocol = "";
+        TransportSupplier transport = new MockTransport() {
+            @Override
+            public Transmitter transmitter() {
+                return transmitter;
+            }
+        };
+        return newInstance(uri,
+                           subprotocol,
+                           new MockListener(Long.MAX_VALUE),
+                           transport);
+    }
+}
diff --git a/test/jdk/java/net/httpclient/websocket/jdk.incubator.httpclient/jdk/incubator/http/internal/websocket/TestSupport.java b/test/jdk/java/net/httpclient/websocket/jdk.incubator.httpclient/jdk/incubator/http/internal/websocket/TestSupport.java
index 3c9bf63bcee..d74fc8dbe1d 100644
--- a/test/jdk/java/net/httpclient/websocket/jdk.incubator.httpclient/jdk/incubator/http/internal/websocket/TestSupport.java
+++ b/test/jdk/java/net/httpclient/websocket/jdk.incubator.httpclient/jdk/incubator/http/internal/websocket/TestSupport.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2017, 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
@@ -23,13 +23,18 @@
 package jdk.incubator.http.internal.websocket;
 
 import java.nio.ByteBuffer;
-import java.util.*;
-import java.util.concurrent.*;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.NoSuchElementException;
+import java.util.Stack;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.CompletionStage;
 import java.util.function.Consumer;
 import java.util.function.Predicate;
 import java.util.regex.Pattern;
-import java.util.stream.Collectors;
-import java.util.stream.Stream;
 
 import static java.util.List.of;
 import static java.util.Objects.requireNonNull;
@@ -188,43 +193,6 @@ final class TestSupport {
         };
     }
 
-//    static <T> Iterator<T> filter(Iterator<? extends T> source,
-//                                  Predicate<? super T> predicate) {
-//        return new Iterator<>() {
-//
-//            { findNext(); }
-//
-//            T next;
-//            boolean hasNext;
-//
-//            @Override
-//            public boolean hasNext() {
-//                return hasNext;
-//            }
-//
-//            @Override
-//            public T next() {
-//                if (!hasNext) {
-//                    throw new NoSuchElementException();
-//                }
-//                T n = this.next;
-//                findNext();
-//                return n;
-//            }
-//
-//            void findNext() {
-//                while (source.hasNext()) {
-//                    T n = source.next();
-//                    if (predicate.test(n)) {
-//                        hasNext = true;
-//                        next = n;
-//                        break;
-//                    }
-//                }
-//            }
-//        };
-//    }
-
     static ByteBuffer fullCopy(ByteBuffer src) {
         ByteBuffer copy = ByteBuffer.allocate(src.capacity());
         int p = src.position();
@@ -297,141 +265,6 @@ final class TestSupport {
         a[j] = x;
     }
 
-    static <T> Iterator<T> concat(Iterator<? extends Iterator<? extends T>> iterators) {
-        requireNonNull(iterators);
-        return new Iterator<>() {
-
-            private Iterator<? extends T> current = Collections.emptyIterator();
-
-            @Override
-            public boolean hasNext() {
-                while (!current.hasNext()) {
-                    if (!iterators.hasNext()) {
-                        return false;
-                    } else {
-                        current = iterators.next();
-                    }
-                }
-                return true;
-            }
-
-            @Override
-            public T next() {
-                if (!hasNext()) {
-                    throw new NoSuchElementException();
-                }
-                return current.next();
-            }
-        };
-    }
-
-    interface Mock {
-
-        /*
-         * Completes exceptionally if there are any expectations that haven't
-         * been met within the given time period, otherwise completes normally
-         */
-        CompletableFuture<Void> expectations(long timeout, TimeUnit unit);
-    }
-
-    static final class InvocationChecker {
-
-        private final Object lock = new Object();
-        private final Iterator<InvocationExpectation> expectations;
-        private final CompletableFuture<Void> expectationsViolation
-                = new CompletableFuture<>();
-
-        InvocationChecker(Iterable<InvocationExpectation> expectations) {
-            this.expectations = requireNonNull(expectations).iterator();
-        }
-
-        /*
-         * Completes exceptionally if there are any expectations that haven't
-         * been met within the given time period, otherwise completes normally
-         */
-        CompletableFuture<Void> expectations(long timeout, TimeUnit unit) {
-            return expectationsViolation
-                    .orTimeout(timeout, unit)
-                    .handle((v, t) -> {
-                        if (t == null) {
-                            throw new InternalError(
-                                    "Unexpected normal completion: " + v);
-                        } else if (t instanceof TimeoutException) {
-                            synchronized (lock) {
-                                if (!expectations.hasNext()) {
-                                    return null;
-                                } else {
-                                    throw new AssertionFailedException(
-                                            "More invocations were expected");
-                                }
-                            }
-                        } else if (t instanceof AssertionFailedException) {
-                            throw (AssertionFailedException) t;
-                        } else {
-                            throw new RuntimeException(t);
-                        }
-                    });
-        }
-
-        void checkInvocation(String name, Object... args) {
-            synchronized (lock) {
-                if (!expectations.hasNext()) {
-                    expectationsViolation.completeExceptionally(
-                            new AssertionFailedException(
-                                    "Less invocations were expected: " + name));
-                    return;
-                }
-                InvocationExpectation next = expectations.next();
-                if (!next.name.equals(name)) {
-                    expectationsViolation.completeExceptionally(
-                            new AssertionFailedException(
-                                    "A different invocation was expected: " + name)
-                    );
-                    return;
-                }
-                if (!next.predicate.apply(args)) {
-                    expectationsViolation.completeExceptionally(
-                            new AssertionFailedException(
-                                    "Invocation doesn't match the predicate: "
-                                            + name + ", " + Arrays.toString(args))
-                    );
-                }
-            }
-        }
-    }
-
-    static final class InvocationExpectation {
-
-        final String name;
-        final F<Boolean> predicate;
-
-        InvocationExpectation(String name, F<Boolean> predicate) {
-            this.name = requireNonNull(name);
-            this.predicate = requireNonNull(predicate);
-        }
-    }
-
-    static void checkExpectations(Mock... mocks) {
-        checkExpectations(0, TimeUnit.SECONDS, mocks);
-    }
-
-    static void checkExpectations(long timeout, TimeUnit unit, Mock... mocks) {
-        CompletableFuture<?>[] completableFutures = Stream.of(mocks)
-                .map(m -> m.expectations(timeout, unit))
-                .collect(Collectors.toList()).toArray(new CompletableFuture<?>[0]);
-        CompletableFuture<Void> cf = CompletableFuture.allOf(completableFutures);
-        try {
-            cf.join();
-        } catch (CompletionException e) {
-            Throwable cause = e.getCause();
-            if (cause instanceof AssertionFailedException) {
-                throw (AssertionFailedException) cause;
-            } else {
-                throw e;
-            }
-        }
-    }
-
     public static <T extends Throwable> T assertThrows(Class<? extends T> clazz,
                                                        ThrowingProcedure code) {
         @SuppressWarnings("unchecked")
@@ -465,6 +298,7 @@ final class TestSupport {
             caught = t;
         }
         if (predicate.test(caught)) {
+            System.out.println("Got expected exception: " + caught);
             return caught;
         }
         if (caught == null) {
@@ -480,46 +314,13 @@ final class TestSupport {
                                                   CompletionStage<?> stage) {
         CompletableFuture<?> cf =
                 CompletableFuture.completedFuture(null).thenCompose(x -> stage);
-        return assertThrows(t -> clazz.isInstance(t.getCause()), cf::get);
+        return assertThrows(t -> clazz == t.getCause().getClass(), cf::get);
     }
 
     interface ThrowingProcedure {
         void run() throws Throwable;
     }
 
-    static final class Expectation {
-
-        private final List<Predicate<? super Throwable>> list = new LinkedList<>();
-
-        static Expectation ifExpect(boolean condition,
-                                    Predicate<? super Throwable> predicate) {
-            return addPredicate(new Expectation(), condition, predicate);
-        }
-
-        Expectation orExpect(boolean condition,
-                             Predicate<? super Throwable> predicate) {
-            return addPredicate(this, condition, predicate);
-        }
-
-        static Expectation addPredicate(Expectation e, boolean condition,
-                                        Predicate<? super Throwable> predicate) {
-            if (condition) {
-                e.list.add(requireNonNull(predicate));
-            }
-            return e;
-        }
-
-        public Throwable assertThrows(ThrowingProcedure code) {
-            Predicate<Throwable> p;
-            if (list.isEmpty()) {
-                p = Objects::isNull;
-            } else {
-                p = e -> list.stream().anyMatch(x -> x.test(e));
-            }
-            return TestSupport.assertThrows(p, code);
-        }
-    }
-
     static final class AssertionFailedException extends RuntimeException {
 
         private static final long serialVersionUID = 1L;
diff --git a/test/jdk/java/net/httpclient/websocket/security/WSURLPermissionTest.java b/test/jdk/java/net/httpclient/websocket/security/WSURLPermissionTest.java
new file mode 100644
index 00000000000..ebca6852c42
--- /dev/null
+++ b/test/jdk/java/net/httpclient/websocket/security/WSURLPermissionTest.java
@@ -0,0 +1,579 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @summary Basic security checks for WebSocket URI from the Builder
+ * @compile ../DummyWebSocketServer.java ../../ProxyServer.java
+ * @run testng/othervm/java.security.policy=httpclient.policy WSURLPermissionTest
+ */
+
+import java.io.IOException;
+import java.net.InetSocketAddress;
+import java.net.Proxy;
+import java.net.ProxySelector;
+import java.net.SocketAddress;
+import java.net.URI;
+import java.net.URLPermission;
+import java.security.AccessControlContext;
+import java.security.AccessController;
+import java.security.Permission;
+import java.security.Permissions;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
+import java.security.ProtectionDomain;
+import java.util.List;
+import java.util.concurrent.ExecutionException;
+import jdk.incubator.http.HttpClient;
+import jdk.incubator.http.WebSocket;
+import org.testng.annotations.AfterTest;
+import org.testng.annotations.BeforeTest;
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+import static org.testng.Assert.*;
+
+public class WSURLPermissionTest {
+
+    static AccessControlContext withPermissions(Permission... perms) {
+        Permissions p = new Permissions();
+        for (Permission perm : perms) {
+            p.add(perm);
+        }
+        ProtectionDomain pd = new ProtectionDomain(null, p);
+        return new AccessControlContext(new ProtectionDomain[]{ pd });
+    }
+
+    static AccessControlContext noPermissions() {
+        return withPermissions(/*empty*/);
+    }
+
+    URI wsURI;
+    DummyWebSocketServer webSocketServer;
+    InetSocketAddress proxyAddress;
+
+    @BeforeTest
+    public void setup() throws Exception {
+        ProxyServer proxyServer = new ProxyServer(0, true);
+        proxyAddress = new InetSocketAddress("127.0.0.1", proxyServer.getPort());
+        webSocketServer = new DummyWebSocketServer();
+        webSocketServer.open();
+        wsURI = webSocketServer.getURI();
+
+        System.out.println("Proxy Server: " + proxyAddress);
+        System.out.println("DummyWebSocketServer: " + wsURI);
+    }
+
+    @AfterTest
+    public void teardown() {
+        webSocketServer.close();
+    }
+
+    static class NoOpListener implements WebSocket.Listener {}
+    static final WebSocket.Listener noOpListener = new NoOpListener();
+
+    @DataProvider(name = "passingScenarios")
+    public Object[][] passingScenarios() {
+        HttpClient noProxyClient = HttpClient.newHttpClient();
+        return new Object[][]{
+            { (PrivilegedExceptionAction<?>)() -> {
+                 noProxyClient.newWebSocketBuilder()
+                              .buildAsync(wsURI, noOpListener).get().abort();
+                 return null; },                                       // no actions
+              new URLPermission[] { new URLPermission(wsURI.toString()) },
+              "0"  /* for log file identification */ },
+
+            { (PrivilegedExceptionAction<?>)() -> {
+                 noProxyClient.newWebSocketBuilder()
+                              .buildAsync(wsURI, noOpListener).get().abort();
+                 return null; },                                       // scheme wildcard
+              new URLPermission[] { new URLPermission("ws://*") },
+              "0.1" },
+
+            { (PrivilegedExceptionAction<?>)() -> {
+                 noProxyClient.newWebSocketBuilder()
+                              .buildAsync(wsURI, noOpListener).get().abort();
+                 return null; },                                       // port wildcard
+              new URLPermission[] { new URLPermission("ws://"+wsURI.getHost()+":*") },
+              "0.2" },
+
+            { (PrivilegedExceptionAction<?>)() -> {
+                 noProxyClient.newWebSocketBuilder()
+                              .buildAsync(wsURI, noOpListener).get().abort();
+                 return null; },                                        // empty actions
+              new URLPermission[] { new URLPermission(wsURI.toString(), "") },
+              "1" },
+
+            { (PrivilegedExceptionAction<?>)() -> {
+                 noProxyClient.newWebSocketBuilder()
+                              .buildAsync(wsURI, noOpListener).get().abort();
+                 return null; },                                         // colon
+              new URLPermission[] { new URLPermission(wsURI.toString(), ":") },
+              "2" },
+
+            { (PrivilegedExceptionAction<?>)() -> {
+                 noProxyClient.newWebSocketBuilder()
+                              .buildAsync(wsURI, noOpListener).get().abort();
+                 return null; },                                        // wildcard
+              new URLPermission[] { new URLPermission(wsURI.toString(), "*:*") },
+              "3" },
+
+            // WS permission checking is agnostic of method, any/none will do
+            { (PrivilegedExceptionAction<?>)() -> {
+                 noProxyClient.newWebSocketBuilder()
+                              .buildAsync(wsURI, noOpListener).get().abort();
+                 return null; },                                        // specific method
+              new URLPermission[] { new URLPermission(wsURI.toString(), "GET") },
+              "3.1" },
+
+            { (PrivilegedExceptionAction<?>)() -> {
+                 noProxyClient.newWebSocketBuilder()
+                              .buildAsync(wsURI, noOpListener).get().abort();
+                 return null; },                                        // specific method
+              new URLPermission[] { new URLPermission(wsURI.toString(), "POST") },
+              "3.2" },
+
+            { (PrivilegedExceptionAction<?>)() -> {
+                URI uriWithPath = wsURI.resolve("/path/x");
+                 noProxyClient.newWebSocketBuilder()
+                              .buildAsync(uriWithPath, noOpListener).get().abort();
+                 return null; },                                       // path
+              new URLPermission[] { new URLPermission(wsURI.resolve("/path/x").toString()) },
+              "4" },
+
+            { (PrivilegedExceptionAction<?>)() -> {
+                URI uriWithPath = wsURI.resolve("/path/x");
+                 noProxyClient.newWebSocketBuilder()
+                              .buildAsync(uriWithPath, noOpListener).get().abort();
+                 return null; },                                       // same dir wildcard
+              new URLPermission[] { new URLPermission(wsURI.resolve("/path/*").toString()) },
+              "5" },
+
+            { (PrivilegedExceptionAction<?>)() -> {
+                URI uriWithPath = wsURI.resolve("/path/x");
+                 noProxyClient.newWebSocketBuilder()
+                              .buildAsync(uriWithPath, noOpListener).get().abort();
+                 return null; },                                       // recursive
+              new URLPermission[] { new URLPermission(wsURI.resolve("/path/-").toString()) },
+              "6" },
+
+            { (PrivilegedExceptionAction<?>)() -> {
+                URI uriWithPath = wsURI.resolve("/path/x");
+                 noProxyClient.newWebSocketBuilder()
+                              .buildAsync(uriWithPath, noOpListener).get().abort();
+                 return null; },                                       // recursive top
+              new URLPermission[] { new URLPermission(wsURI.resolve("/-").toString()) },
+              "7" },
+
+            { (PrivilegedExceptionAction<?>)() -> {
+                 noProxyClient.newWebSocketBuilder()
+                              .header("A-Header", "A-Value")  // header
+                              .buildAsync(wsURI, noOpListener).get().abort();
+                 return null; },
+              new URLPermission[] { new URLPermission(wsURI.toString(), ":A-Header") },
+              "8" },
+
+            { (PrivilegedExceptionAction<?>)() -> {
+                 noProxyClient.newWebSocketBuilder()
+                              .header("A-Header", "A-Value")  // header
+                              .buildAsync(wsURI, noOpListener).get().abort();
+                 return null; },                                        // wildcard
+              new URLPermission[] { new URLPermission(wsURI.toString(), ":*") },
+              "9" },
+
+            { (PrivilegedExceptionAction<?>)() -> {
+                 noProxyClient.newWebSocketBuilder()
+                              .header("A-Header", "A-Value")  // headers
+                              .header("B-Header", "B-Value")  // headers
+                              .buildAsync(wsURI, noOpListener).get().abort();
+                 return null; },
+              new URLPermission[] { new URLPermission(wsURI.toString(), ":A-Header,B-Header") },
+              "10" },
+
+            { (PrivilegedExceptionAction<?>)() -> {
+                 noProxyClient.newWebSocketBuilder()
+                              .header("A-Header", "A-Value")  // headers
+                              .header("B-Header", "B-Value")  // headers
+                              .buildAsync(wsURI, noOpListener).get().abort();
+                 return null; },                                        // wildcard
+              new URLPermission[] { new URLPermission(wsURI.toString(), ":*") },
+              "11" },
+
+            { (PrivilegedExceptionAction<?>)() -> {
+                 noProxyClient.newWebSocketBuilder()
+                              .header("A-Header", "A-Value")  // headers
+                              .header("B-Header", "B-Value")  // headers
+                              .buildAsync(wsURI, noOpListener).get().abort();
+                 return null; },                                        // wildcards
+              new URLPermission[] { new URLPermission(wsURI.toString(), "*:*") },
+              "12" },
+
+            { (PrivilegedExceptionAction<?>)() -> {
+                 noProxyClient.newWebSocketBuilder()
+                              .header("A-Header", "A-Value")  // multi-value
+                              .header("A-Header", "B-Value")  // headers
+                              .buildAsync(wsURI, noOpListener).get().abort();
+                 return null; },                                        // wildcard
+              new URLPermission[] { new URLPermission(wsURI.toString(), ":*") },
+              "13" },
+
+            { (PrivilegedExceptionAction<?>)() -> {
+                 noProxyClient.newWebSocketBuilder()
+                              .header("A-Header", "A-Value")  // multi-value
+                              .header("A-Header", "B-Value")  // headers
+                              .buildAsync(wsURI, noOpListener).get().abort();
+                 return null; },                                        // single grant
+              new URLPermission[] { new URLPermission(wsURI.toString(), ":A-Header") },
+              "14" },
+
+            // client with a DIRECT proxy
+            { (PrivilegedExceptionAction<?>)() -> {
+                 ProxySelector ps = ProxySelector.of(null);
+                 HttpClient client = HttpClient.newBuilder().proxy(ps).build();
+                 client.newWebSocketBuilder()
+                       .buildAsync(wsURI, noOpListener).get().abort();
+                 return null; },
+              new URLPermission[] { new URLPermission(wsURI.toString()) },
+              "15" },
+
+            // client with a SOCKS proxy! ( expect implementation to ignore SOCKS )
+            { (PrivilegedExceptionAction<?>)() -> {
+                 ProxySelector ps = new ProxySelector() {
+                     @Override public List<Proxy> select(URI uri) {
+                         return List.of(new Proxy(Proxy.Type.SOCKS, proxyAddress)); }
+                     @Override
+                     public void connectFailed(URI uri, SocketAddress sa, IOException ioe) { }
+                 };
+                 HttpClient client = HttpClient.newBuilder().proxy(ps).build();
+                 client.newWebSocketBuilder()
+                       .buildAsync(wsURI, noOpListener).get().abort();
+                 return null; },
+              new URLPermission[] { new URLPermission(wsURI.toString()) },
+              "16" },
+
+            // client with a HTTP/HTTPS proxy
+            { (PrivilegedExceptionAction<?>)() -> {
+                 assert proxyAddress != null;
+                 ProxySelector ps = ProxySelector.of(proxyAddress);
+                 HttpClient client = HttpClient.newBuilder().proxy(ps).build();
+                 client.newWebSocketBuilder()
+                       .buildAsync(wsURI, noOpListener).get().abort();
+                 return null; },
+              new URLPermission[] {
+                    new URLPermission(wsURI.toString()),            // CONNECT action string
+                    new URLPermission("socket://"+proxyAddress.getHostName()
+                                      +":"+proxyAddress.getPort(), "CONNECT")},
+              "17" },
+
+            { (PrivilegedExceptionAction<?>)() -> {
+                 assert proxyAddress != null;
+                 ProxySelector ps = ProxySelector.of(proxyAddress);
+                 HttpClient client = HttpClient.newBuilder().proxy(ps).build();
+                 client.newWebSocketBuilder()
+                       .buildAsync(wsURI, noOpListener).get().abort();
+                 return null; },
+              new URLPermission[] {
+                    new URLPermission(wsURI.toString()),            // no action string
+                    new URLPermission("socket://"+proxyAddress.getHostName()
+                                      +":"+proxyAddress.getPort())},
+              "18" },
+
+            { (PrivilegedExceptionAction<?>)() -> {
+                 assert proxyAddress != null;
+                 ProxySelector ps = ProxySelector.of(proxyAddress);
+                 HttpClient client = HttpClient.newBuilder().proxy(ps).build();
+                 client.newWebSocketBuilder()
+                       .buildAsync(wsURI, noOpListener).get().abort();
+                 return null; },
+              new URLPermission[] {
+                    new URLPermission(wsURI.toString()),            // wildcard headers
+                    new URLPermission("socket://"+proxyAddress.getHostName()
+                                      +":"+proxyAddress.getPort(), "CONNECT:*")},
+              "19" },
+
+            { (PrivilegedExceptionAction<?>)() -> {
+                 assert proxyAddress != null;
+                 CountingProxySelector ps = CountingProxySelector.of(proxyAddress);
+                 HttpClient client = HttpClient.newBuilder().proxy(ps).build();
+                 client.newWebSocketBuilder()
+                       .buildAsync(wsURI, noOpListener).get().abort();
+                 assertEquals(ps.count(), 1);  // ps.select only invoked once
+                 return null; },
+              new URLPermission[] {
+                    new URLPermission(wsURI.toString()),            // empty headers
+                    new URLPermission("socket://"+proxyAddress.getHostName()
+                                      +":"+proxyAddress.getPort(), "CONNECT:")},
+              "20" },
+
+            { (PrivilegedExceptionAction<?>)() -> {
+                 assert proxyAddress != null;
+                 ProxySelector ps = ProxySelector.of(proxyAddress);
+                 HttpClient client = HttpClient.newBuilder().proxy(ps).build();
+                 client.newWebSocketBuilder()
+                       .buildAsync(wsURI, noOpListener).get().abort();
+                 return null; },
+              new URLPermission[] {
+                    new URLPermission(wsURI.toString()),
+                    new URLPermission("socket://*")},               // wildcard socket URL
+              "21" },
+
+            { (PrivilegedExceptionAction<?>)() -> {
+                 assert proxyAddress != null;
+                 ProxySelector ps = ProxySelector.of(proxyAddress);
+                 HttpClient client = HttpClient.newBuilder().proxy(ps).build();
+                 client.newWebSocketBuilder()
+                       .buildAsync(wsURI, noOpListener).get().abort();
+                 return null; },
+              new URLPermission[] {
+                    new URLPermission("ws://*"),                    // wildcard ws URL
+                    new URLPermission("socket://*")},               // wildcard socket URL
+              "22" },
+
+        };
+    }
+
+    @Test(dataProvider = "passingScenarios")
+    public void testWithNoSecurityManager(PrivilegedExceptionAction<?> action,
+                                          URLPermission[] unused,
+                                          String dataProviderId)
+        throws Exception
+    {
+        // sanity ( no security manager )
+        System.setSecurityManager(null);
+        try {
+            AccessController.doPrivileged(action);
+        } finally {
+            System.setSecurityManager(new SecurityManager());
+        }
+    }
+
+    @Test(dataProvider = "passingScenarios")
+    public void testWithAllPermissions(PrivilegedExceptionAction<?> action,
+                                       URLPermission[] unused,
+                                       String dataProviderId)
+        throws Exception
+    {
+        // Run with all permissions, i.e. no further restrictions than test's AllPermission
+        assert System.getSecurityManager() != null;
+        AccessController.doPrivileged(action);
+    }
+
+    @Test(dataProvider = "passingScenarios")
+    public void testWithMinimalPermissions(PrivilegedExceptionAction<?> action,
+                                           URLPermission[] perms,
+                                           String dataProviderId)
+        throws Exception
+    {
+        // Run with minimal permissions, i.e. just what is required
+        assert System.getSecurityManager() != null;
+        AccessControlContext minimalACC = withPermissions(perms);
+        AccessController.doPrivileged(action, minimalACC);
+    }
+
+    @Test(dataProvider = "passingScenarios")
+    public void testWithNoPermissions(PrivilegedExceptionAction<?> action,
+                                      URLPermission[] unused,
+                                      String dataProviderId)
+        throws Exception
+    {
+        // Run with NO permissions, i.e. expect SecurityException
+        assert System.getSecurityManager() != null;
+        try {
+            AccessController.doPrivileged(action, noPermissions());
+            fail("EXPECTED SecurityException");
+        } catch (PrivilegedActionException expected) {
+            Throwable t = expected.getCause();
+            if (t instanceof ExecutionException)
+                t = t.getCause();
+
+            if (t instanceof SecurityException)
+                System.out.println("Caught expected SE:" + expected);
+            else
+                fail("Expected SecurityException, but got: " + t);
+        }
+    }
+
+    // --- Negative tests ---
+
+    @DataProvider(name = "failingScenarios")
+    public Object[][] failingScenarios() {
+        HttpClient noProxyClient = HttpClient.newHttpClient();
+        return new Object[][]{
+            { (PrivilegedExceptionAction<?>) () -> {
+                 noProxyClient.newWebSocketBuilder()
+                              .buildAsync(wsURI, noOpListener).get().abort();
+                 return null;
+              },
+              new URLPermission[]{ /* no permissions */ },
+              "50"  /* for log file identification */},
+
+            { (PrivilegedExceptionAction<?>) () -> {
+                 noProxyClient.newWebSocketBuilder()
+                              .buildAsync(wsURI, noOpListener).get().abort();
+                 return null;
+              },                                        // wrong scheme
+              new URLPermission[]{ new URLPermission("http://*") },
+              "51" },
+
+            { (PrivilegedExceptionAction<?>) () -> {
+                 noProxyClient.newWebSocketBuilder()
+                              .buildAsync(wsURI, noOpListener).get().abort();
+                 return null;
+              },                                        // wrong scheme
+              new URLPermission[]{ new URLPermission("socket://*") },
+              "52" },
+
+            { (PrivilegedExceptionAction<?>) () -> {
+                 noProxyClient.newWebSocketBuilder()
+                              .buildAsync(wsURI, noOpListener).get().abort();
+                 return null;
+              },                                        // wrong host
+              new URLPermission[]{ new URLPermission("ws://foo.com/") },
+              "53" },
+
+            { (PrivilegedExceptionAction<?>) () -> {
+                 noProxyClient.newWebSocketBuilder()
+                              .buildAsync(wsURI, noOpListener).get().abort();
+                 return null;
+              },                                        // wrong port
+              new URLPermission[]{ new URLPermission("ws://"+ wsURI.getHost()+":5") },
+              "54" },
+
+            { (PrivilegedExceptionAction<?>) () -> {
+                  noProxyClient.newWebSocketBuilder()
+                               .header("A-Header", "A-Value")
+                               .buildAsync(wsURI, noOpListener).get().abort();
+                  return null;
+              },                                                    // only perm to set B not A
+              new URLPermission[] { new URLPermission(wsURI.toString(), "*:B-Header") },
+              "55" },
+
+            { (PrivilegedExceptionAction<?>) () -> {
+                  noProxyClient.newWebSocketBuilder()
+                               .header("A-Header", "A-Value")
+                               .header("B-Header", "B-Value")
+                               .buildAsync(wsURI, noOpListener).get().abort();
+                  return null;
+              },                                                    // only perm to set B not A
+              new URLPermission[] { new URLPermission(wsURI.toString(), "*:B-Header") },
+              "56" },
+
+            { (PrivilegedExceptionAction<?>)() -> {
+                URI uriWithPath = wsURI.resolve("/path/x");
+                 noProxyClient.newWebSocketBuilder()
+                              .buildAsync(uriWithPath, noOpListener).get().abort();
+                 return null; },                                    // wrong path
+              new URLPermission[] { new URLPermission(wsURI.resolve("/aDiffPath/").toString()) },
+              "57" },
+
+            { (PrivilegedExceptionAction<?>)() -> {
+                URI uriWithPath = wsURI.resolve("/path/x");
+                 noProxyClient.newWebSocketBuilder()
+                              .buildAsync(uriWithPath, noOpListener).get().abort();
+                 return null; },                                    // more specific path
+              new URLPermission[] { new URLPermission(wsURI.resolve("/path/x/y").toString()) },
+              "58" },
+
+            // client with a HTTP/HTTPS proxy
+            { (PrivilegedExceptionAction<?>)() -> {
+                 assert proxyAddress != null;
+                 ProxySelector ps = ProxySelector.of(proxyAddress);
+                 HttpClient client = HttpClient.newBuilder().proxy(ps).build();
+                 client.newWebSocketBuilder()
+                       .buildAsync(wsURI, noOpListener).get().abort();
+                 return null; },                                    // missing proxy perm
+              new URLPermission[] { new URLPermission(wsURI.toString()) },
+              "100" },
+
+            // client with a HTTP/HTTPS proxy
+            { (PrivilegedExceptionAction<?>)() -> {
+                 assert proxyAddress != null;
+                 ProxySelector ps = ProxySelector.of(proxyAddress);
+                 HttpClient client = HttpClient.newBuilder().proxy(ps).build();
+                 client.newWebSocketBuilder()
+                       .buildAsync(wsURI, noOpListener).get().abort();
+                 return null; },
+              new URLPermission[] {
+                    new URLPermission(wsURI.toString()),            // missing proxy CONNECT
+                    new URLPermission("socket://*", "GET") },
+              "101" },
+        };
+    }
+
+    @Test(dataProvider = "failingScenarios")
+    public void testWithoutEnoughPermissions(PrivilegedExceptionAction<?> action,
+                                             URLPermission[] perms,
+                                             String dataProviderId)
+        throws Exception
+    {
+        // Run without Enough permissions, i.e. expect SecurityException
+        assert System.getSecurityManager() != null;
+        AccessControlContext notEnoughPermsACC = withPermissions(perms);
+        try {
+            AccessController.doPrivileged(action, notEnoughPermsACC);
+            fail("EXPECTED SecurityException");
+        } catch (PrivilegedActionException expected) {
+            Throwable t = expected.getCause();
+            if (t instanceof ExecutionException)
+                t = t.getCause();
+
+            if (t instanceof SecurityException)
+                System.out.println("Caught expected SE:" + expected);
+            else
+                fail("Expected SecurityException, but got: " + t);
+        }
+    }
+
+    /**
+     * A Proxy Selector that wraps a ProxySelector.of(), and counts the number
+     * of times its select method has been invoked. This can be used to ensure
+     * that the Proxy Selector is invoked only once per WebSocket.Builder::buildAsync
+     * invocation.
+     */
+    static class CountingProxySelector extends ProxySelector {
+        private final ProxySelector proxySelector;
+        private volatile int count; // 0
+        private CountingProxySelector(InetSocketAddress proxyAddress) {
+            proxySelector = ProxySelector.of(proxyAddress);
+        }
+
+        public static CountingProxySelector of(InetSocketAddress proxyAddress) {
+            return new CountingProxySelector(proxyAddress);
+        }
+
+        int count() { return count; }
+
+        @Override
+        public List<Proxy> select(URI uri) {
+            System.out.println("PS: uri");
+            Throwable t = new Throwable();
+            t.printStackTrace(System.out);
+            count++;
+            return proxySelector.select(uri);
+        }
+
+        @Override
+        public void connectFailed(URI uri, SocketAddress sa, IOException ioe) {
+            proxySelector.connectFailed(uri, sa, ioe);
+        }
+    }
+}
diff --git a/test/jdk/java/net/httpclient/websocket/security/httpclient.policy b/test/jdk/java/net/httpclient/websocket/security/httpclient.policy
new file mode 100644
index 00000000000..8a2c74df9b7
--- /dev/null
+++ b/test/jdk/java/net/httpclient/websocket/security/httpclient.policy
@@ -0,0 +1,68 @@
+//
+// Copyright (c) 2017, 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.
+//
+
+grant codeBase "jrt:/jdk.incubator.httpclient" {
+    permission java.lang.RuntimePermission "accessClassInPackage.sun.net";
+    permission java.lang.RuntimePermission "accessClassInPackage.sun.net.util";
+    permission java.lang.RuntimePermission "accessClassInPackage.sun.net.www";
+    permission java.lang.RuntimePermission "accessClassInPackage.jdk.internal.misc";
+
+    // ## why is SP not good enough. Check API @throws signatures and impl
+    permission java.net.SocketPermission "*","connect,resolve";
+    permission java.net.URLPermission "http:*","*:*";
+    permission java.net.URLPermission "https:*","*:*";
+    permission java.net.URLPermission "ws:*","*:*";
+    permission java.net.URLPermission "wss:*","*:*";
+    permission java.net.URLPermission "socket:*","CONNECT";  // proxy
+
+    // For request/response body processors, fromFile, asFile
+    permission java.io.FilePermission "<<ALL FILES>>","read,write,delete";
+
+    // ## look at the different property names!
+    permission java.util.PropertyPermission "jdk.httpclient.HttpClient.log","read";  // name!
+    permission java.util.PropertyPermission "jdk.httpclient.auth.retrylimit","read";
+    permission java.util.PropertyPermission "jdk.httpclient.connectionWindowSize","read";
+    permission java.util.PropertyPermission "jdk.httpclient.enablepush","read";
+    permission java.util.PropertyPermission "jdk.httpclient.hpack.maxheadertablesize","read";
+    permission java.util.PropertyPermission "jdk.httpclient.keepalive.timeout","read";
+    permission java.util.PropertyPermission "jdk.httpclient.maxframesize","read";
+    permission java.util.PropertyPermission "jdk.httpclient.maxstreams","read";
+    permission java.util.PropertyPermission "jdk.httpclient.redirects.retrylimit","read";
+    permission java.util.PropertyPermission "jdk.httpclient.windowsize","read";
+    permission java.util.PropertyPermission "jdk.httpclient.bufsize","read";
+    permission java.util.PropertyPermission "jdk.httpclient.internal.selector.timeout","read";
+    permission java.util.PropertyPermission "jdk.internal.httpclient.debug","read";
+    permission java.util.PropertyPermission "jdk.internal.httpclient.hpack.debug","read";
+    permission java.util.PropertyPermission "jdk.internal.httpclient.hpack.log.level","read";
+    permission java.util.PropertyPermission "test.src","read";
+
+    permission java.net.NetPermission "getProxySelector";
+
+    permission java.security.SecurityPermission "createAccessControlContext";
+};
+
+// bootstrap to get the test going, it will do its own restrictions
+grant codeBase "file:${test.classes}/*" {
+    permission java.security.AllPermission;
+};
+
diff --git a/test/jdk/java/net/httpclient/whitebox/ConnectionPoolTestDriver.java b/test/jdk/java/net/httpclient/whitebox/ConnectionPoolTestDriver.java
new file mode 100644
index 00000000000..91fb1e02bac
--- /dev/null
+++ b/test/jdk/java/net/httpclient/whitebox/ConnectionPoolTestDriver.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug 8187044 8187111
+ * @summary Verifies that the ConnectionPool correctly handle
+ *          connection deadlines and purges the right connections
+ *          from the cache.
+ * @modules jdk.incubator.httpclient java.management
+ * @run main/othervm --add-reads jdk.incubator.httpclient=java.management jdk.incubator.httpclient/jdk.incubator.http.ConnectionPoolTest
+ */
diff --git a/test/jdk/java/net/httpclient/whitebox/DemandTestDriver.java b/test/jdk/java/net/httpclient/whitebox/DemandTestDriver.java
new file mode 100644
index 00000000000..c2749dc6dc9
--- /dev/null
+++ b/test/jdk/java/net/httpclient/whitebox/DemandTestDriver.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @modules jdk.incubator.httpclient/jdk.incubator.http.internal.common
+ * @run testng jdk.incubator.httpclient/jdk.incubator.http.internal.common.DemandTest
+ */
diff --git a/test/jdk/java/net/httpclient/whitebox/Driver.java b/test/jdk/java/net/httpclient/whitebox/Driver.java
index 00f5b12bf94..d69f7bf5244 100644
--- a/test/jdk/java/net/httpclient/whitebox/Driver.java
+++ b/test/jdk/java/net/httpclient/whitebox/Driver.java
@@ -23,10 +23,8 @@
 
 /*
  * @test
- * @bug 8151299 8164704 8187044
- * @modules jdk.incubator.httpclient java.management
+ * @bug 8151299 8164704
+ * @modules jdk.incubator.httpclient
  * @run testng jdk.incubator.httpclient/jdk.incubator.http.SelectorTest
  * @run testng jdk.incubator.httpclient/jdk.incubator.http.RawChannelTest
- * @run testng jdk.incubator.httpclient/jdk.incubator.http.ResponseHeadersTest
- * @run main/othervm --add-reads jdk.incubator.httpclient=java.management jdk.incubator.httpclient/jdk.incubator.http.ConnectionPoolTest
  */
diff --git a/test/jdk/java/net/httpclient/whitebox/FlowTestDriver.java b/test/jdk/java/net/httpclient/whitebox/FlowTestDriver.java
new file mode 100644
index 00000000000..cddf365700c
--- /dev/null
+++ b/test/jdk/java/net/httpclient/whitebox/FlowTestDriver.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @modules jdk.incubator.httpclient
+ * @run testng jdk.incubator.httpclient/jdk.incubator.http.FlowTest
+ */
diff --git a/test/jdk/java/net/httpclient/whitebox/Http1HeaderParserTestDriver.java b/test/jdk/java/net/httpclient/whitebox/Http1HeaderParserTestDriver.java
new file mode 100644
index 00000000000..44844e2ba6b
--- /dev/null
+++ b/test/jdk/java/net/httpclient/whitebox/Http1HeaderParserTestDriver.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @modules jdk.incubator.httpclient
+ * @run testng jdk.incubator.httpclient/jdk.incubator.http.Http1HeaderParserTest
+ */
diff --git a/test/jdk/java/net/httpclient/whitebox/SSLEchoTubeTestDriver.java b/test/jdk/java/net/httpclient/whitebox/SSLEchoTubeTestDriver.java
new file mode 100644
index 00000000000..7febdff3b9c
--- /dev/null
+++ b/test/jdk/java/net/httpclient/whitebox/SSLEchoTubeTestDriver.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @modules jdk.incubator.httpclient
+ * @run testng/othervm -Djdk.internal.httpclient.debug=true jdk.incubator.httpclient/jdk.incubator.http.SSLEchoTubeTest
+ */
diff --git a/test/jdk/java/net/httpclient/whitebox/SSLTubeTestDriver.java b/test/jdk/java/net/httpclient/whitebox/SSLTubeTestDriver.java
new file mode 100644
index 00000000000..fbd25f3abfb
--- /dev/null
+++ b/test/jdk/java/net/httpclient/whitebox/SSLTubeTestDriver.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @modules jdk.incubator.httpclient
+ * @run testng/othervm -Djdk.internal.httpclient.debug=true jdk.incubator.httpclient/jdk.incubator.http.SSLTubeTest
+ */
diff --git a/test/jdk/java/net/httpclient/whitebox/WrapperTestDriver.java b/test/jdk/java/net/httpclient/whitebox/WrapperTestDriver.java
new file mode 100644
index 00000000000..23b8403f680
--- /dev/null
+++ b/test/jdk/java/net/httpclient/whitebox/WrapperTestDriver.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @modules jdk.incubator.httpclient
+ * @run testng jdk.incubator.httpclient/jdk.incubator.http.WrapperTest
+ */
diff --git a/test/jdk/java/net/httpclient/whitebox/jdk.incubator.httpclient/jdk/incubator/http/AbstractRandomTest.java b/test/jdk/java/net/httpclient/whitebox/jdk.incubator.httpclient/jdk/incubator/http/AbstractRandomTest.java
new file mode 100644
index 00000000000..d90847cc07c
--- /dev/null
+++ b/test/jdk/java/net/httpclient/whitebox/jdk.incubator.httpclient/jdk/incubator/http/AbstractRandomTest.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2017, 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 jdk.incubator.http;
+
+import java.util.Random;
+
+/** Abstract supertype for tests that need random numbers within a given range. */
+public class AbstractRandomTest {
+
+    private static Long getSystemSeed() {
+        Long seed = null;
+        try {
+            // note that Long.valueOf(null) also throws a NumberFormatException
+            // so if the property is undefined this will still work correctly
+            seed = Long.valueOf(System.getProperty("seed"));
+        } catch (NumberFormatException e) {
+            // do nothing: seed is still null
+        }
+        return seed;
+    }
+
+    private static long getSeed() {
+        Long seed = getSystemSeed();
+        if (seed == null) {
+            seed = (new Random()).nextLong();
+        }
+        System.out.println("Seed from AbstractRandomTest.getSeed = "+seed+"L");
+        return seed;
+    }
+
+    private static Random random = new Random(getSeed());
+
+    protected static int randomRange(int lower, int upper) {
+        if (lower > upper)
+            throw new IllegalArgumentException("lower > upper");
+        int diff = upper - lower;
+        int r = lower + random.nextInt(diff);
+        return r - (r % 8); // round down to multiple of 8 (align for longs)
+    }
+}
diff --git a/test/jdk/java/net/httpclient/whitebox/jdk.incubator.httpclient/jdk/incubator/http/AbstractSSLTubeTest.java b/test/jdk/java/net/httpclient/whitebox/jdk.incubator.httpclient/jdk/incubator/http/AbstractSSLTubeTest.java
new file mode 100644
index 00000000000..f24e84f8c58
--- /dev/null
+++ b/test/jdk/java/net/httpclient/whitebox/jdk.incubator.httpclient/jdk/incubator/http/AbstractSSLTubeTest.java
@@ -0,0 +1,320 @@
+/*
+ * Copyright (c) 2017, 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 jdk.incubator.http;
+
+import jdk.incubator.http.internal.common.FlowTube;
+import jdk.incubator.http.internal.common.SSLTube;
+import jdk.incubator.http.internal.common.Utils;
+import org.testng.annotations.Test;
+
+import javax.net.ssl.KeyManagerFactory;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLEngine;
+import javax.net.ssl.SSLParameters;
+import javax.net.ssl.TrustManagerFactory;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.ByteBuffer;
+import java.security.KeyManagementException;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.UnrecoverableKeyException;
+import java.security.cert.CertificateException;
+import java.util.List;
+import java.util.Random;
+import java.util.StringTokenizer;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Flow;
+import java.util.concurrent.ForkJoinPool;
+import java.util.concurrent.SubmissionPublisher;
+import java.util.concurrent.atomic.AtomicLong;
+
+public class AbstractSSLTubeTest extends AbstractRandomTest {
+
+    public static final long COUNTER = 600;
+    public static final int LONGS_PER_BUF = 800;
+    public static final long TOTAL_LONGS = COUNTER * LONGS_PER_BUF;
+    public static final ByteBuffer SENTINEL = ByteBuffer.allocate(0);
+    // This is a hack to work around an issue with SubmissionPublisher.
+    // SubmissionPublisher will call onComplete immediately without forwarding
+    // remaining pending data if SubmissionPublisher.close() is called when
+    // there is no demand. In other words, it doesn't wait for the subscriber
+    // to pull all the data before calling onComplete.
+    // We use a CountDownLatch to figure out when it is safe to call close().
+    // This may cause the test to hang if data are buffered.
+    protected final CountDownLatch allBytesReceived = new CountDownLatch(1);
+
+
+    protected static ByteBuffer getBuffer(long startingAt) {
+        ByteBuffer buf = ByteBuffer.allocate(LONGS_PER_BUF * 8);
+        for (int j = 0; j < LONGS_PER_BUF; j++) {
+            buf.putLong(startingAt++);
+        }
+        buf.flip();
+        return buf;
+    }
+
+    protected void run(FlowTube server,
+                       ExecutorService sslExecutor,
+                       CountDownLatch allBytesReceived) throws IOException {
+        FlowTube client = new SSLTube(createSSLEngine(true),
+                                      sslExecutor,
+                                      server);
+        SubmissionPublisher<List<ByteBuffer>> p =
+                new SubmissionPublisher<>(ForkJoinPool.commonPool(),
+                                          Integer.MAX_VALUE);
+        FlowTube.TubePublisher begin = p::subscribe;
+        CompletableFuture<Void> completion = new CompletableFuture<>();
+        EndSubscriber end = new EndSubscriber(TOTAL_LONGS, completion, allBytesReceived);
+        client.connectFlows(begin, end);
+        /* End of wiring */
+
+        long count = 0;
+        System.out.printf("Submitting %d buffer arrays\n", COUNTER);
+        System.out.printf("LoopCount should be %d\n", TOTAL_LONGS);
+        for (long i = 0; i < COUNTER; i++) {
+            ByteBuffer b = getBuffer(count);
+            count += LONGS_PER_BUF;
+            p.submit(List.of(b));
+        }
+        System.out.println("Finished submission. Waiting for loopback");
+        completion.whenComplete((r,t) -> allBytesReceived.countDown());
+        try {
+            allBytesReceived.await();
+        } catch (InterruptedException e) {
+            throw new IOException(e);
+        }
+        p.close();
+        System.out.println("All bytes received: calling publisher.close()");
+        try {
+            completion.join();
+            System.out.println("OK");
+        } finally {
+            sslExecutor.shutdownNow();
+        }
+    }
+
+    protected static void sleep(long millis) {
+        try {
+            Thread.sleep(millis);
+        } catch (InterruptedException e) {
+
+        }
+    }
+
+    /**
+     * The final subscriber which receives the decrypted looped-back data. Just
+     * needs to compare the data with what was sent. The given CF is either
+     * completed exceptionally with an error or normally on success.
+     */
+    protected static class EndSubscriber implements FlowTube.TubeSubscriber {
+
+        private static final int REQUEST_WINDOW = 13;
+
+        private final long nbytes;
+        private final AtomicLong counter = new AtomicLong();
+        private final CompletableFuture<?> completion;
+        private final CountDownLatch allBytesReceived;
+        private volatile Flow.Subscription subscription;
+        private long unfulfilled;
+
+        EndSubscriber(long nbytes, CompletableFuture<?> completion,
+                      CountDownLatch allBytesReceived) {
+            this.nbytes = nbytes;
+            this.completion = completion;
+            this.allBytesReceived = allBytesReceived;
+        }
+
+        @Override
+        public void onSubscribe(Flow.Subscription subscription) {
+            this.subscription = subscription;
+            unfulfilled = REQUEST_WINDOW;
+            System.out.println("EndSubscriber request " + REQUEST_WINDOW);
+            subscription.request(REQUEST_WINDOW);
+        }
+
+        public static String info(List<ByteBuffer> i) {
+            StringBuilder sb = new StringBuilder();
+            sb.append("size: ").append(Integer.toString(i.size()));
+            int x = 0;
+            for (ByteBuffer b : i)
+                x += b.remaining();
+            sb.append(" bytes: ").append(x);
+            return sb.toString();
+        }
+
+        @Override
+        public void onNext(List<ByteBuffer> buffers) {
+            if (--unfulfilled == (REQUEST_WINDOW / 2)) {
+                long req = REQUEST_WINDOW - unfulfilled;
+                System.out.println("EndSubscriber request " + req);
+                unfulfilled = REQUEST_WINDOW;
+                subscription.request(req);
+            }
+
+            long currval = counter.get();
+            if (currval % 500 == 0) {
+                System.out.println("EndSubscriber: " + currval);
+            }
+            System.out.println("EndSubscriber onNext " + Utils.remaining(buffers));
+
+            for (ByteBuffer buf : buffers) {
+                while (buf.hasRemaining()) {
+                    long n = buf.getLong();
+                    if (currval > (TOTAL_LONGS - 50)) {
+                        System.out.println("End: " + currval);
+                    }
+                    if (n != currval++) {
+                        System.out.println("ERROR at " + n + " != " + (currval - 1));
+                        completion.completeExceptionally(new RuntimeException("ERROR"));
+                        subscription.cancel();
+                        return;
+                    }
+                }
+            }
+
+            counter.set(currval);
+            if (currval >= TOTAL_LONGS) {
+                allBytesReceived.countDown();
+            }
+        }
+
+        @Override
+        public void onError(Throwable throwable) {
+            System.out.println("EndSubscriber onError " + throwable);
+            completion.completeExceptionally(throwable);
+            allBytesReceived.countDown();
+        }
+
+        @Override
+        public void onComplete() {
+            long n = counter.get();
+            if (n != nbytes) {
+                System.out.printf("nbytes=%d n=%d\n", nbytes, n);
+                completion.completeExceptionally(new RuntimeException("ERROR AT END"));
+            } else {
+                System.out.println("DONE OK");
+                completion.complete(null);
+            }
+            allBytesReceived.countDown();
+        }
+
+        @Override
+        public String toString() {
+            return "EndSubscriber";
+        }
+    }
+
+    protected static SSLEngine createSSLEngine(boolean client) throws IOException {
+        SSLContext context = (new SimpleSSLContext()).get();
+        SSLEngine engine = context.createSSLEngine();
+        SSLParameters params = context.getSupportedSSLParameters();
+        params.setProtocols(new String[]{"TLSv1.2"}); // TODO: This is essential. Needs to be protocol impl
+        if (client) {
+            params.setApplicationProtocols(new String[]{"proto1", "proto2"}); // server will choose proto2
+        } else {
+            params.setApplicationProtocols(new String[]{"proto2"}); // server will choose proto2
+        }
+        engine.setSSLParameters(params);
+        engine.setUseClientMode(client);
+        return engine;
+    }
+
+    /**
+     * Creates a simple usable SSLContext for SSLSocketFactory or a HttpsServer
+     * using either a given keystore or a default one in the test tree.
+     *
+     * Using this class with a security manager requires the following
+     * permissions to be granted:
+     *
+     * permission "java.util.PropertyPermission" "test.src.path", "read";
+     * permission java.io.FilePermission "${test.src}/../../../../lib/testlibrary/jdk/testlibrary/testkeys",
+     * "read"; The exact path above depends on the location of the test.
+     */
+    protected static class SimpleSSLContext {
+
+        private final SSLContext ssl;
+
+        /**
+         * Loads default keystore from SimpleSSLContext source directory
+         */
+        public SimpleSSLContext() throws IOException {
+            String paths = System.getProperty("test.src.path");
+            StringTokenizer st = new StringTokenizer(paths, File.pathSeparator);
+            boolean securityExceptions = false;
+            SSLContext sslContext = null;
+            while (st.hasMoreTokens()) {
+                String path = st.nextToken();
+                try {
+                    File f = new File(path, "../../../../lib/testlibrary/jdk/testlibrary/testkeys");
+                    if (f.exists()) {
+                        try (FileInputStream fis = new FileInputStream(f)) {
+                            sslContext = init(fis);
+                            break;
+                        }
+                    }
+                } catch (SecurityException e) {
+                    // catch and ignore because permission only required
+                    // for one entry on path (at most)
+                    securityExceptions = true;
+                }
+            }
+            if (securityExceptions) {
+                System.err.println("SecurityExceptions thrown on loading testkeys");
+            }
+            ssl = sslContext;
+        }
+
+        private SSLContext init(InputStream i) throws IOException {
+            try {
+                char[] passphrase = "passphrase".toCharArray();
+                KeyStore ks = KeyStore.getInstance("JKS");
+                ks.load(i, passphrase);
+
+                KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
+                kmf.init(ks, passphrase);
+
+                TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
+                tmf.init(ks);
+
+                SSLContext ssl = SSLContext.getInstance("TLS");
+                ssl.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
+                return ssl;
+            } catch (KeyManagementException | KeyStoreException |
+                    UnrecoverableKeyException | CertificateException |
+                    NoSuchAlgorithmException e) {
+                throw new RuntimeException(e.getMessage());
+            }
+        }
+
+        public SSLContext get() {
+            return ssl;
+        }
+    }
+}
diff --git a/test/jdk/java/net/httpclient/whitebox/jdk.incubator.httpclient/jdk/incubator/http/ConnectionPoolTest.java b/test/jdk/java/net/httpclient/whitebox/jdk.incubator.httpclient/jdk/incubator/http/ConnectionPoolTest.java
index 19e815fa286..0f99fb1cebe 100644
--- a/test/jdk/java/net/httpclient/whitebox/jdk.incubator.httpclient/jdk/incubator/http/ConnectionPoolTest.java
+++ b/test/jdk/java/net/httpclient/whitebox/jdk.incubator.httpclient/jdk/incubator/http/ConnectionPoolTest.java
@@ -25,27 +25,30 @@ package jdk.incubator.http;
 
 import java.io.IOException;
 import java.lang.management.ManagementFactory;
-import java.lang.ref.Reference;
-import java.lang.ref.ReferenceQueue;
-import java.lang.ref.WeakReference;
 import java.net.Authenticator;
-import java.net.CookieManager;
+import java.net.CookieHandler;
 import java.net.InetSocketAddress;
 import java.net.ProxySelector;
 import java.nio.ByteBuffer;
 import java.nio.channels.SocketChannel;
+import java.util.List;
 import java.util.Optional;
+import java.util.Random;
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.Executor;
+import java.util.concurrent.Flow;
+import java.util.stream.IntStream;
+import java.time.Instant;
+import java.time.temporal.ChronoUnit;
 import javax.net.ssl.SSLContext;
 import javax.net.ssl.SSLParameters;
-import jdk.incubator.http.internal.common.ByteBufferReference;
+import jdk.incubator.http.internal.common.FlowTube;
 
 /**
- * @summary Verifies that the ConnectionPool won't prevent an HttpClient
- *          from being GC'ed. Verifies that the ConnectionPool has at most
- *          one CacheCleaner thread running.
- * @bug 8187044
+ * @summary Verifies that the ConnectionPool correctly handle
+ *          connection deadlines and purges the right connections
+ *          from the cache.
+ * @bug 8187044 8187111
  * @author danielfuchs
  */
 public class ConnectionPoolTest {
@@ -65,100 +68,99 @@ public class ConnectionPoolTest {
     }
 
     public static void testCacheCleaners() throws Exception {
-        ConnectionPool pool = new ConnectionPool();
+        ConnectionPool pool = new ConnectionPool(666);
         HttpClient client = new HttpClientStub(pool);
         InetSocketAddress proxy = InetSocketAddress.createUnresolved("bar", 80);
         System.out.println("Adding 10 connections to pool");
-        for (int i=0; i<10; i++) {
-            InetSocketAddress addr = InetSocketAddress.createUnresolved("foo"+i, 80);
-            HttpConnection c1 = new HttpConnectionStub(client, addr, proxy, true);
-            pool.returnToPool(c1);
-        }
-        while (getActiveCleaners() == 0) {
-            System.out.println("Waiting for cleaner to start");
-            Thread.sleep(10);
-        }
-        System.out.println("Active CacheCleaners: " + getActiveCleaners());
-        if (getActiveCleaners() > 1) {
-            throw new RuntimeException("Too many CacheCleaner active: "
-                    + getActiveCleaners());
-        }
-        System.out.println("Removing 9 connections from pool");
-        for (int i=0; i<9; i++) {
-            InetSocketAddress addr = InetSocketAddress.createUnresolved("foo"+i, 80);
-            HttpConnection c2 = pool.getConnection(true, addr, proxy);
-            if (c2 == null) {
-                throw new RuntimeException("connection not found for " + addr);
-            }
-        }
-        System.out.println("Active CacheCleaners: " + getActiveCleaners());
-        if (getActiveCleaners() != 1) {
-            throw new RuntimeException("Wrong number of CacheCleaner active: "
-                    + getActiveCleaners());
-        }
-        System.out.println("Removing last connection from pool");
-        for (int i=9; i<10; i++) {
-            InetSocketAddress addr = InetSocketAddress.createUnresolved("foo"+i, 80);
-            HttpConnection c2 = pool.getConnection(true, addr, proxy);
-            if (c2 == null) {
-                throw new RuntimeException("connection not found for " + addr);
-            }
-        }
-        System.out.println("Active CacheCleaners: " + getActiveCleaners()
-                + " (may be 0 or may still be 1)");
-        if (getActiveCleaners() > 1) {
-            throw new RuntimeException("Too many CacheCleaner active: "
-                    + getActiveCleaners());
-        }
-        InetSocketAddress addr = InetSocketAddress.createUnresolved("foo", 80);
-        HttpConnection c = new HttpConnectionStub(client, addr, proxy, true);
-        System.out.println("Adding/Removing one connection from pool 20 times in a loop");
-        for (int i=0; i<20; i++) {
-            pool.returnToPool(c);
-            HttpConnection c2 = pool.getConnection(true, addr, proxy);
-            if (c2 == null) {
-                throw new RuntimeException("connection not found for " + addr);
-            }
-            if (c2 != c) {
-                throw new RuntimeException("wrong connection found for " + addr);
-            }
-        }
-        if (getActiveCleaners() > 1) {
-            throw new RuntimeException("Too many CacheCleaner active: "
-                    + getActiveCleaners());
-        }
-        ReferenceQueue<HttpClient> queue = new ReferenceQueue<>();
-        WeakReference<HttpClient> weak = new WeakReference<>(client, queue);
-        System.gc();
-        Reference.reachabilityFence(pool);
-        client = null; pool = null; c = null;
-        while (true) {
-            long cleaners = getActiveCleaners();
-            System.out.println("Waiting for GC to release stub HttpClient;"
-                    + " active cache cleaners: " + cleaners);
-            System.gc();
-            Reference<?> ref = queue.remove(1000);
-            if (ref == weak) {
-                System.out.println("Stub HttpClient GC'ed");
-                break;
-            }
-        }
-        while (getActiveCleaners() > 0) {
-            System.out.println("Waiting for CacheCleaner to stop");
-            Thread.sleep(1000);
-        }
-        System.out.println("Active CacheCleaners: "
-                + getActiveCleaners());
+        Random random = new Random();
 
-        if (getActiveCleaners() > 0) {
-            throw new RuntimeException("Too many CacheCleaner active: "
-                    + getActiveCleaners());
+        final int count = 20;
+        Instant now = Instant.now().truncatedTo(ChronoUnit.SECONDS);
+        int[] keepAlives = new int[count];
+        HttpConnectionStub[] connections = new HttpConnectionStub[count];
+        long purge = pool.purgeExpiredConnectionsAndReturnNextDeadline(now);
+        long expected = 0;
+        if (purge != expected) {
+            throw new RuntimeException("Bad purge delay: " + purge
+                                        + ", expected " + expected);
+        }
+        expected = Long.MAX_VALUE;
+        for (int i=0; i<count; i++) {
+            InetSocketAddress addr = InetSocketAddress.createUnresolved("foo"+i, 80);
+            keepAlives[i] = random.nextInt(10) * 10  + 10;
+            connections[i] = new HttpConnectionStub(client, addr, proxy, true);
+            System.out.println("Adding connection: " + now
+                                + " keepAlive: " + keepAlives[i]
+                                + " /" + connections[i]);
+            pool.returnToPool(connections[i], now, keepAlives[i]);
+            expected = Math.min(expected, keepAlives[i] * 1000);
+            purge = pool.purgeExpiredConnectionsAndReturnNextDeadline(now);
+            if (purge != expected) {
+                throw new RuntimeException("Bad purge delay: " + purge
+                                        + ", expected " + expected);
+            }
+        }
+        int min = IntStream.of(keepAlives).min().getAsInt();
+        int max = IntStream.of(keepAlives).max().getAsInt();
+        int mean = (min + max)/2;
+        System.out.println("min=" + min + ", max=" + max + ", mean=" + mean);
+        purge = pool.purgeExpiredConnectionsAndReturnNextDeadline(now);
+        System.out.println("first purge would be in " + purge + " ms");
+        if (Math.abs(purge/1000 - min) > 0) {
+            throw new RuntimeException("expected " + min + " got " + purge/1000);
+        }
+        long opened = java.util.stream.Stream.of(connections)
+                     .filter(HttpConnectionStub::connected).count();
+        if (opened != count) {
+            throw new RuntimeException("Opened: expected "
+                                       + count + " got " + opened);
+        }
+        purge = mean * 1000;
+        System.out.println("start purging at " + purge + " ms");
+        Instant next = now;
+        do {
+           System.out.println("next purge is in " + purge + " ms");
+           next = next.plus(purge, ChronoUnit.MILLIS);
+           purge = pool.purgeExpiredConnectionsAndReturnNextDeadline(next);
+           long k = now.until(next, ChronoUnit.SECONDS);
+           System.out.println("now is " + k + "s from start");
+           for (int i=0; i<count; i++) {
+               if (connections[i].connected() != (k < keepAlives[i])) {
+                   throw new RuntimeException("Bad connection state for "
+                             + i
+                             + "\n\t connected=" + connections[i].connected()
+                             + "\n\t keepAlive=" + keepAlives[i]
+                             + "\n\t elapsed=" + k);
+               }
+           }
+        } while (purge > 0);
+        opened = java.util.stream.Stream.of(connections)
+                     .filter(HttpConnectionStub::connected).count();
+        if (opened != 0) {
+           throw new RuntimeException("Closed: expected "
+                                       + count + " got "
+                                       + (count-opened));
         }
     }
+
     static <T> T error() {
         throw new InternalError("Should not reach here: wrong test assumptions!");
     }
 
+    static class FlowTubeStub implements FlowTube {
+        final HttpConnectionStub conn;
+        FlowTubeStub(HttpConnectionStub conn) { this.conn = conn; }
+        @Override
+        public void onSubscribe(Flow.Subscription subscription) { }
+        @Override public void onError(Throwable error) { error(); }
+        @Override public void onComplete() { error(); }
+        @Override public void onNext(List<ByteBuffer> item) { error();}
+        @Override
+        public void subscribe(Flow.Subscriber<? super List<ByteBuffer>> subscriber) {
+        }
+        @Override public boolean isFinished() { return conn.closed; }
+    }
+
     // Emulates an HttpConnection that has a strong reference to its HttpClient.
     static class HttpConnectionStub extends HttpConnection {
 
@@ -172,50 +174,44 @@ public class ConnectionPoolTest {
             this.proxy = proxy;
             this.secured = secured;
             this.client = client;
+            this.flow = new FlowTubeStub(this);
         }
 
-        InetSocketAddress proxy;
-        InetSocketAddress address;
-        boolean secured;
-        ConnectionPool.CacheKey key;
-        HttpClient client;
+        final InetSocketAddress proxy;
+        final InetSocketAddress address;
+        final boolean secured;
+        final ConnectionPool.CacheKey key;
+        final HttpClient client;
+        final FlowTubeStub flow;
+        volatile boolean closed;
 
         // All these return something
-        @Override boolean connected() {return true;}
+        @Override boolean connected() {return !closed;}
         @Override boolean isSecure() {return secured;}
         @Override boolean isProxied() {return proxy!=null;}
         @Override ConnectionPool.CacheKey cacheKey() {return key;}
-        @Override public void close() {}
         @Override void shutdownInput() throws IOException {}
         @Override void shutdownOutput() throws IOException {}
+        @Override
+        public void close() {
+            closed=true;
+            System.out.println("closed: " + this);
+        }
+        @Override
         public String toString() {
             return "HttpConnectionStub: " + address + " proxy: " + proxy;
         }
 
         // All these throw errors
-        @Override
-        public void connect() throws IOException, InterruptedException {error();}
+        @Override public HttpPublisher publisher() {return error();}
         @Override public CompletableFuture<Void> connectAsync() {return error();}
         @Override SocketChannel channel() {return error();}
-        @Override void flushAsync() throws IOException {error();}
         @Override
-        protected ByteBuffer readImpl() throws IOException {return error();}
-        @Override CompletableFuture<Void> whenReceivingResponse() {return error();}
-        @Override
-        long write(ByteBuffer[] buffers, int start, int number) throws IOException {
-            throw (Error)error();
+        HttpConnection.DetachedConnectionChannel detachChannel() {
+            return error();
         }
         @Override
-        long write(ByteBuffer buffer) throws IOException {throw (Error)error();}
-        @Override
-        void writeAsync(ByteBufferReference[] buffers) throws IOException {
-            error();
-        }
-        @Override
-        void writeAsyncUnordered(ByteBufferReference[] buffers)
-                throws IOException {
-            error();
-        }
+        FlowTube getConnectionFlow() {return flow;}
     }
     // Emulates an HttpClient that has a strong reference to its connection pool.
     static class HttpClientStub extends HttpClient {
@@ -223,14 +219,14 @@ public class ConnectionPoolTest {
             this.pool = pool;
         }
         final ConnectionPool pool;
-        @Override public Optional<CookieManager> cookieManager() {return error();}
+        @Override public Optional<CookieHandler> cookieHandler() {return error();}
         @Override public HttpClient.Redirect followRedirects() {return error();}
         @Override public Optional<ProxySelector> proxy() {return error();}
         @Override public SSLContext sslContext() {return error();}
-        @Override public Optional<SSLParameters> sslParameters() {return error();}
+        @Override public SSLParameters sslParameters() {return error();}
         @Override public Optional<Authenticator> authenticator() {return error();}
         @Override public HttpClient.Version version() {return HttpClient.Version.HTTP_1_1;}
-        @Override public Executor executor() {return error();}
+        @Override public Optional<Executor> executor() {return error();}
         @Override
         public <T> HttpResponse<T> send(HttpRequest req,
                 HttpResponse.BodyHandler<T> responseBodyHandler)
@@ -244,7 +240,7 @@ public class ConnectionPoolTest {
         }
         @Override
         public <U, T> CompletableFuture<U> sendAsync(HttpRequest req,
-                HttpResponse.MultiProcessor<U, T> multiProcessor) {
+                HttpResponse.MultiSubscriber<U, T> multiSubscriber) {
             return error();
         }
     }
diff --git a/test/jdk/java/net/httpclient/whitebox/jdk.incubator.httpclient/jdk/incubator/http/FlowTest.java b/test/jdk/java/net/httpclient/whitebox/jdk.incubator.httpclient/jdk/incubator/http/FlowTest.java
new file mode 100644
index 00000000000..5f4cc7ef147
--- /dev/null
+++ b/test/jdk/java/net/httpclient/whitebox/jdk.incubator.httpclient/jdk/incubator/http/FlowTest.java
@@ -0,0 +1,547 @@
+/*
+ * Copyright (c) 2017, 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 jdk.incubator.http;
+
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.Socket;
+import java.nio.ByteBuffer;
+import java.security.KeyManagementException;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.UnrecoverableKeyException;
+import java.security.cert.CertificateException;
+import java.util.List;
+import java.util.Random;
+import java.util.StringTokenizer;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Flow;
+import java.util.concurrent.Flow.Subscriber;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.SubmissionPublisher;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicLong;
+import javax.net.ssl.KeyManagerFactory;
+import javax.net.ssl.*;
+import javax.net.ssl.TrustManagerFactory;
+import jdk.incubator.http.internal.common.Utils;
+import org.testng.annotations.Test;
+import jdk.incubator.http.internal.common.SSLFlowDelegate;
+
+@Test
+public class FlowTest extends AbstractRandomTest {
+
+    private final SubmissionPublisher<List<ByteBuffer>> srcPublisher;
+    private final ExecutorService executor;
+    private static final long COUNTER = 3000;
+    private static final int LONGS_PER_BUF = 800;
+    static final long TOTAL_LONGS = COUNTER * LONGS_PER_BUF;
+    public static final ByteBuffer SENTINEL = ByteBuffer.allocate(0);
+    static volatile String alpn;
+
+    // This is a hack to work around an issue with SubmissionPublisher.
+    // SubmissionPublisher will call onComplete immediately without forwarding
+    // remaining pending data if SubmissionPublisher.close() is called when
+    // there is no demand. In other words, it doesn't wait for the subscriber
+    // to pull all the data before calling onComplete.
+    // We use a CountDownLatch to figure out when it is safe to call close().
+    // This may cause the test to hang if data are buffered.
+    final CountDownLatch allBytesReceived = new CountDownLatch(1);
+
+    private final CompletableFuture<Void> completion;
+
+    public FlowTest() throws IOException {
+        executor = Executors.newCachedThreadPool();
+        srcPublisher = new SubmissionPublisher<>(executor, 20,
+                                                 this::handlePublisherException);
+        SSLContext ctx = (new SimpleSSLContext()).get();
+        SSLEngine engineClient = ctx.createSSLEngine();
+        SSLParameters params = ctx.getSupportedSSLParameters();
+        params.setApplicationProtocols(new String[]{"proto1", "proto2"}); // server will choose proto2
+        params.setProtocols(new String[]{"TLSv1.2"}); // TODO: This is essential. Needs to be protocol impl
+        engineClient.setSSLParameters(params);
+        engineClient.setUseClientMode(true);
+        completion = new CompletableFuture<>();
+        SSLLoopbackSubscriber looper = new SSLLoopbackSubscriber(ctx, executor, allBytesReceived);
+        looper.start();
+        EndSubscriber end = new EndSubscriber(TOTAL_LONGS, completion, allBytesReceived);
+        SSLFlowDelegate sslClient = new SSLFlowDelegate(engineClient, executor, end, looper);
+        // going to measure how long handshake takes
+        final long start = System.currentTimeMillis();
+        sslClient.alpn().whenComplete((String s, Throwable t) -> {
+            if (t != null)
+                t.printStackTrace();
+            long endTime = System.currentTimeMillis();
+            alpn = s;
+            System.out.println("ALPN: " + alpn);
+            long period = (endTime - start);
+            System.out.printf("Handshake took %d ms\n", period);
+        });
+        Subscriber<List<ByteBuffer>> reader = sslClient.upstreamReader();
+        Subscriber<List<ByteBuffer>> writer = sslClient.upstreamWriter();
+        looper.setReturnSubscriber(reader);
+        // now connect all the pieces
+        srcPublisher.subscribe(writer);
+        String aa = sslClient.alpn().join();
+        System.out.println("AAALPN = " + aa);
+    }
+
+    private void handlePublisherException(Object o, Throwable t) {
+        System.out.println("Src Publisher exception");
+        t.printStackTrace(System.out);
+    }
+
+    private static ByteBuffer getBuffer(long startingAt) {
+        ByteBuffer buf = ByteBuffer.allocate(LONGS_PER_BUF * 8);
+        for (int j = 0; j < LONGS_PER_BUF; j++) {
+            buf.putLong(startingAt++);
+        }
+        buf.flip();
+        return buf;
+    }
+
+    @Test
+    public void run() {
+        long count = 0;
+        System.out.printf("Submitting %d buffer arrays\n", COUNTER);
+        System.out.printf("LoopCount should be %d\n", TOTAL_LONGS);
+        for (long i = 0; i < COUNTER; i++) {
+            ByteBuffer b = getBuffer(count);
+            count += LONGS_PER_BUF;
+            srcPublisher.submit(List.of(b));
+        }
+        System.out.println("Finished submission. Waiting for loopback");
+        // make sure we don't wait for allBytesReceived in case of error.
+        completion.whenComplete((r,t) -> allBytesReceived.countDown());
+        try {
+            allBytesReceived.await();
+        } catch (InterruptedException e) {
+            throw new RuntimeException(e);
+        }
+        System.out.println("All bytes received: ");
+        srcPublisher.close();
+        try {
+            completion.join();
+            if (!alpn.equals("proto2")) {
+                throw new RuntimeException("wrong alpn received");
+            }
+            System.out.println("OK");
+        } finally {
+            executor.shutdownNow();
+        }
+    }
+
+/*
+    public static void main(String[]args) throws Exception {
+        FlowTest test = new FlowTest();
+        test.run();
+    }
+*/
+
+    /**
+     * This Subscriber simulates an SSL loopback network. The object itself
+     * accepts outgoing SSL encrypted data which is looped back via two sockets
+     * (one of which is an SSLSocket emulating a server). The method
+     * {@link #setReturnSubscriber(java.util.concurrent.Flow.Subscriber) }
+     * is used to provide the Subscriber which feeds the incoming side
+     * of SSLFlowDelegate. Three threads are used to implement this behavior
+     * and a SubmissionPublisher drives the incoming read side.
+     * <p>
+     * A thread reads from the buffer, writes
+     * to the client j.n.Socket which is connected to a SSLSocket operating
+     * in server mode. A second thread loops back data read from the SSLSocket back to the
+     * client again. A third thread reads the client socket and pushes the data to
+     * a SubmissionPublisher that drives the reader side of the SSLFlowDelegate
+     */
+    static class SSLLoopbackSubscriber implements Subscriber<List<ByteBuffer>> {
+        private final BlockingQueue<ByteBuffer> buffer;
+        private final Socket clientSock;
+        private final SSLSocket serverSock;
+        private final Thread thread1, thread2, thread3;
+        private volatile Flow.Subscription clientSubscription;
+        private final SubmissionPublisher<List<ByteBuffer>> publisher;
+        private final CountDownLatch allBytesReceived;
+
+        SSLLoopbackSubscriber(SSLContext ctx,
+                              ExecutorService exec,
+                              CountDownLatch allBytesReceived) throws IOException {
+            SSLServerSocketFactory fac = ctx.getServerSocketFactory();
+            SSLServerSocket serv = (SSLServerSocket) fac.createServerSocket(0);
+            SSLParameters params = serv.getSSLParameters();
+            params.setApplicationProtocols(new String[]{"proto2"});
+            serv.setSSLParameters(params);
+
+
+            int serverPort = serv.getLocalPort();
+            clientSock = new Socket("127.0.0.1", serverPort);
+            serverSock = (SSLSocket) serv.accept();
+            this.buffer = new LinkedBlockingQueue<>();
+            this.allBytesReceived = allBytesReceived;
+            thread1 = new Thread(this::clientWriter, "clientWriter");
+            thread2 = new Thread(this::serverLoopback, "serverLoopback");
+            thread3 = new Thread(this::clientReader, "clientReader");
+            publisher = new SubmissionPublisher<>(exec, Flow.defaultBufferSize(),
+                    this::handlePublisherException);
+            SSLFlowDelegate.Monitor.add(this::monitor);
+        }
+
+        public void start() {
+            thread1.start();
+            thread2.start();
+            thread3.start();
+        }
+
+        private void handlePublisherException(Object o, Throwable t) {
+            System.out.println("Loopback Publisher exception");
+            t.printStackTrace(System.out);
+        }
+
+        private final AtomicInteger readCount = new AtomicInteger();
+
+        // reads off the SSLSocket the data from the "server"
+        private void clientReader() {
+            try {
+                InputStream is = clientSock.getInputStream();
+                final int bufsize = FlowTest.randomRange(512, 16 * 1024);
+                System.out.println("clientReader: bufsize = " + bufsize);
+                while (true) {
+                    byte[] buf = new byte[bufsize];
+                    int n = is.read(buf);
+                    if (n == -1) {
+                        System.out.println("clientReader close: read "
+                                + readCount.get() + " bytes");
+                        System.out.println("clientReader: got EOF. "
+                                            + "Waiting signal to close publisher.");
+                        allBytesReceived.await();
+                        System.out.println("clientReader: closing publisher");
+                        publisher.close();
+                        sleep(2000);
+                        Utils.close(is, clientSock);
+                        return;
+                    }
+                    ByteBuffer bb = ByteBuffer.wrap(buf, 0, n);
+                    readCount.addAndGet(n);
+                    publisher.submit(List.of(bb));
+                }
+            } catch (Throwable e) {
+                e.printStackTrace();
+                Utils.close(clientSock);
+            }
+        }
+
+        // writes the encrypted data from SSLFLowDelegate to the j.n.Socket
+        // which is connected to the SSLSocket emulating a server.
+        private void clientWriter() {
+            long nbytes = 0;
+            try {
+                OutputStream os =
+                        new BufferedOutputStream(clientSock.getOutputStream());
+
+                while (true) {
+                    ByteBuffer buf = buffer.take();
+                    if (buf == FlowTest.SENTINEL) {
+                        // finished
+                        //Utils.sleep(2000);
+                        System.out.println("clientWriter close: " + nbytes + " written");
+                        clientSock.shutdownOutput();
+                        System.out.println("clientWriter close return");
+                        return;
+                    }
+                    int len = buf.remaining();
+                    int written = writeToStream(os, buf);
+                    assert len == written;
+                    nbytes += len;
+                    assert !buf.hasRemaining()
+                            : "buffer has " + buf.remaining() + " bytes left";
+                    clientSubscription.request(1);
+                }
+            } catch (Throwable e) {
+                e.printStackTrace();
+            }
+        }
+
+        private int writeToStream(OutputStream os, ByteBuffer buf) throws IOException {
+            byte[] b = buf.array();
+            int offset = buf.arrayOffset() + buf.position();
+            int n = buf.limit() - buf.position();
+            os.write(b, offset, n);
+            buf.position(buf.limit());
+            os.flush();
+            return n;
+        }
+
+        private final AtomicInteger loopCount = new AtomicInteger();
+
+        public String monitor() {
+            return "serverLoopback: loopcount = " + loopCount.toString()
+                    + " clientRead: count = " + readCount.toString();
+        }
+
+        // thread2
+        private void serverLoopback() {
+            try {
+                InputStream is = serverSock.getInputStream();
+                OutputStream os = serverSock.getOutputStream();
+                final int bufsize = FlowTest.randomRange(512, 16 * 1024);
+                System.out.println("serverLoopback: bufsize = " + bufsize);
+                byte[] bb = new byte[bufsize];
+                while (true) {
+                    int n = is.read(bb);
+                    if (n == -1) {
+                        sleep(2000);
+                        is.close();
+                        serverSock.close();
+                        return;
+                    }
+                    os.write(bb, 0, n);
+                    os.flush();
+                    loopCount.addAndGet(n);
+                }
+            } catch (Throwable e) {
+                e.printStackTrace();
+            }
+        }
+
+
+        /**
+         * This needs to be called before the chain is subscribed. It can't be
+         * supplied in the constructor.
+         */
+        public void setReturnSubscriber(Subscriber<List<ByteBuffer>> returnSubscriber) {
+            publisher.subscribe(returnSubscriber);
+        }
+
+        @Override
+        public void onSubscribe(Flow.Subscription subscription) {
+            clientSubscription = subscription;
+            clientSubscription.request(5);
+        }
+
+        @Override
+        public void onNext(List<ByteBuffer> item) {
+            try {
+                for (ByteBuffer b : item)
+                    buffer.put(b);
+            } catch (InterruptedException e) {
+                e.printStackTrace();
+                Utils.close(clientSock);
+            }
+        }
+
+        @Override
+        public void onError(Throwable throwable) {
+            throwable.printStackTrace();
+            Utils.close(clientSock);
+        }
+
+        @Override
+        public void onComplete() {
+            try {
+                buffer.put(FlowTest.SENTINEL);
+            } catch (InterruptedException e) {
+                e.printStackTrace();
+                Utils.close(clientSock);
+            }
+        }
+    }
+
+    /**
+     * The final subscriber which receives the decrypted looped-back data.
+     * Just needs to compare the data with what was sent. The given CF is
+     * either completed exceptionally with an error or normally on success.
+     */
+    static class EndSubscriber implements Subscriber<List<ByteBuffer>> {
+
+        private final long nbytes;
+
+        private final AtomicLong counter;
+        private volatile Flow.Subscription subscription;
+        private final CompletableFuture<Void> completion;
+        private final CountDownLatch allBytesReceived;
+
+        EndSubscriber(long nbytes,
+                      CompletableFuture<Void> completion,
+                      CountDownLatch allBytesReceived) {
+            counter = new AtomicLong(0);
+            this.nbytes = nbytes;
+            this.completion = completion;
+            this.allBytesReceived = allBytesReceived;
+        }
+
+        @Override
+        public void onSubscribe(Flow.Subscription subscription) {
+            this.subscription = subscription;
+            subscription.request(5);
+        }
+
+        public static String info(List<ByteBuffer> i) {
+            StringBuilder sb = new StringBuilder();
+            sb.append("size: ").append(Integer.toString(i.size()));
+            int x = 0;
+            for (ByteBuffer b : i)
+                x += b.remaining();
+            sb.append(" bytes: " + Integer.toString(x));
+            return sb.toString();
+        }
+
+        @Override
+        public void onNext(List<ByteBuffer> buffers) {
+            long currval = counter.get();
+            //if (currval % 500 == 0) {
+            //System.out.println("End: " + currval);
+            //}
+
+            for (ByteBuffer buf : buffers) {
+                while (buf.hasRemaining()) {
+                    long n = buf.getLong();
+                    //if (currval > (FlowTest.TOTAL_LONGS - 50)) {
+                    //System.out.println("End: " + currval);
+                    //}
+                    if (n != currval++) {
+                        System.out.println("ERROR at " + n + " != " + (currval - 1));
+                        completion.completeExceptionally(new RuntimeException("ERROR"));
+                        subscription.cancel();
+                        return;
+                    }
+                }
+            }
+
+            counter.set(currval);
+            subscription.request(1);
+            if (currval >= TOTAL_LONGS) {
+                allBytesReceived.countDown();
+            }
+        }
+
+        @Override
+        public void onError(Throwable throwable) {
+            allBytesReceived.countDown();
+            completion.completeExceptionally(throwable);
+        }
+
+        @Override
+        public void onComplete() {
+            long n = counter.get();
+            if (n != nbytes) {
+                System.out.printf("nbytes=%d n=%d\n", nbytes, n);
+                completion.completeExceptionally(new RuntimeException("ERROR AT END"));
+            } else {
+                System.out.println("DONE OK: counter = " + n);
+                allBytesReceived.countDown();
+                completion.complete(null);
+            }
+        }
+    }
+
+    /**
+     * Creates a simple usable SSLContext for SSLSocketFactory
+     * or a HttpsServer using either a given keystore or a default
+     * one in the test tree.
+     * <p>
+     * Using this class with a security manager requires the following
+     * permissions to be granted:
+     * <p>
+     * permission "java.util.PropertyPermission" "test.src.path", "read";
+     * permission java.io.FilePermission
+     * "${test.src}/../../../../lib/testlibrary/jdk/testlibrary/testkeys", "read";
+     * The exact path above depends on the location of the test.
+     */
+    static class SimpleSSLContext {
+
+        private final SSLContext ssl;
+
+        /**
+         * Loads default keystore from SimpleSSLContext source directory
+         */
+        public SimpleSSLContext() throws IOException {
+            String paths = System.getProperty("test.src.path");
+            StringTokenizer st = new StringTokenizer(paths, File.pathSeparator);
+            boolean securityExceptions = false;
+            SSLContext sslContext = null;
+            while (st.hasMoreTokens()) {
+                String path = st.nextToken();
+                try {
+                    File f = new File(path, "../../../../lib/testlibrary/jdk/testlibrary/testkeys");
+                    if (f.exists()) {
+                        try (FileInputStream fis = new FileInputStream(f)) {
+                            sslContext = init(fis);
+                            break;
+                        }
+                    }
+                } catch (SecurityException e) {
+                    // catch and ignore because permission only required
+                    // for one entry on path (at most)
+                    securityExceptions = true;
+                }
+            }
+            if (securityExceptions) {
+                System.out.println("SecurityExceptions thrown on loading testkeys");
+            }
+            ssl = sslContext;
+        }
+
+        private SSLContext init(InputStream i) throws IOException {
+            try {
+                char[] passphrase = "passphrase".toCharArray();
+                KeyStore ks = KeyStore.getInstance("JKS");
+                ks.load(i, passphrase);
+
+                KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
+                kmf.init(ks, passphrase);
+
+                TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
+                tmf.init(ks);
+
+                SSLContext ssl = SSLContext.getInstance("TLS");
+                ssl.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
+                return ssl;
+            } catch (KeyManagementException | KeyStoreException |
+                    UnrecoverableKeyException | CertificateException |
+                    NoSuchAlgorithmException e) {
+                throw new RuntimeException(e.getMessage());
+            }
+        }
+
+        public SSLContext get() {
+            return ssl;
+        }
+    }
+
+    private static void sleep(int millis) {
+        try {
+            Thread.sleep(millis);
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+}
diff --git a/test/jdk/java/net/httpclient/whitebox/jdk.incubator.httpclient/jdk/incubator/http/Http1HeaderParserTest.java b/test/jdk/java/net/httpclient/whitebox/jdk.incubator.httpclient/jdk/incubator/http/Http1HeaderParserTest.java
new file mode 100644
index 00000000000..69d63d245d9
--- /dev/null
+++ b/test/jdk/java/net/httpclient/whitebox/jdk.incubator.httpclient/jdk/incubator/http/Http1HeaderParserTest.java
@@ -0,0 +1,372 @@
+/*
+ * Copyright (c) 2017, 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 jdk.incubator.http;
+
+import java.io.ByteArrayInputStream;
+import java.net.ProtocolException;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.IntStream;
+import sun.net.www.MessageHeader;
+import org.testng.annotations.Test;
+import org.testng.annotations.DataProvider;
+import static java.lang.System.out;
+import static java.lang.String.format;
+import static java.nio.charset.StandardCharsets.US_ASCII;
+import static java.util.stream.Collectors.toList;
+import static org.testng.Assert.*;
+
+// Mostly verifies the "new" Http1HeaderParser returns the same results as the
+// tried and tested sun.net.www.MessageHeader.
+
+public class Http1HeaderParserTest {
+
+    @DataProvider(name = "responses")
+    public Object[][] responses() {
+        List<String> responses = new ArrayList<>();
+
+        String[] basic =
+            { "HTTP/1.1 200 OK\r\n\r\n",
+
+              "HTTP/1.1 200 OK\r\n" +
+              "Date: Mon, 15 Jan 2001 12:18:21 GMT\r\n" +
+              "Server: Apache/1.3.14 (Unix)\r\n" +
+              "Connection: close\r\n" +
+              "Content-Type: text/html; charset=iso-8859-1\r\n" +
+              "Content-Length: 10\r\n\r\n" +
+              "123456789",
+
+              "HTTP/1.1 200 OK\r\n" +
+              "Content-Length: 9\r\n" +
+              "Content-Type: text/html; charset=UTF-8\r\n\r\n" +
+              "XXXXX",
+
+              "HTTP/1.1 200 OK\r\n" +
+              "Content-Length:   9\r\n" +
+              "Content-Type:   text/html; charset=UTF-8\r\n\r\n" +   // more than one SP after ':'
+              "XXXXX",
+
+              "HTTP/1.1 200 OK\r\n" +
+              "Content-Length:\t10\r\n" +
+              "Content-Type:\ttext/html; charset=UTF-8\r\n\r\n" +   // HT separator
+              "XXXXX",
+
+              "HTTP/1.1 200 OK\r\n" +
+              "Content-Length:\t\t10\r\n" +
+              "Content-Type:\t\ttext/html; charset=UTF-8\r\n\r\n" +   // more than one HT after ':'
+              "XXXXX",
+
+              "HTTP/1.1 407 Proxy Authorization Required\r\n" +
+              "Proxy-Authenticate: Basic realm=\"a fake realm\"\r\n\r\n",
+
+              "HTTP/1.1 401 Unauthorized\r\n" +
+              "WWW-Authenticate: Digest realm=\"wally land\" domain=/ " +
+              "nonce=\"2B7F3A2B\" qop=\"auth\"\r\n\r\n",
+
+              "HTTP/1.1 200 OK\r\n" +
+              "X-Foo:\r\n\r\n",      // no value
+
+              "HTTP/1.1 200 OK\r\n" +
+              "X-Foo:\r\n\r\n" +     // no value, with response body
+              "Some Response Body",
+
+              "HTTP/1.1 200 OK\r\n" +
+              "X-Foo:\r\n" +    // no value, followed by another header
+              "Content-Length: 10\r\n\r\n" +
+              "Some Response Body",
+
+              "HTTP/1.1 200 OK\r\n" +
+              "X-Foo:\r\n" +    // no value, followed by another header, with response body
+              "Content-Length: 10\r\n\r\n",
+
+              "HTTP/1.1 200 OK\r\n" +
+              "X-Foo: chegar\r\n" +
+              "X-Foo: dfuchs\r\n" +  // same header appears multiple times
+              "Content-Length: 0\r\n" +
+              "X-Foo: michaelm\r\n" +
+              "X-Foo: prappo\r\n\r\n",
+
+              "HTTP/1.1 200 OK\r\n" +
+              "X-Foo:\r\n" +    // no value, same header appears multiple times
+              "X-Foo: dfuchs\r\n" +
+              "Content-Length: 0\r\n" +
+              "X-Foo: michaelm\r\n" +
+              "X-Foo: prappo\r\n\r\n",
+
+              "HTTP/1.1 200 OK\r\n" +
+              "Accept-Ranges: bytes\r\n" +
+              "Cache-control: max-age=0, no-cache=\"set-cookie\"\r\n" +
+              "Content-Length: 132868\r\n" +
+              "Content-Type: text/html; charset=UTF-8\r\n" +
+              "Date: Sun, 05 Nov 2017 22:24:03 GMT\r\n" +
+              "Server: Apache/2.4.6 (Red Hat Enterprise Linux) OpenSSL/1.0.1e-fips Communique/4.2.2\r\n" +
+              "Set-Cookie: AWSELB=AF7927F5100F4202119876ED2436B5005EE;PATH=/;MAX-AGE=900\r\n" +
+              "Vary: Host,Accept-Encoding,User-Agent\r\n" +
+              "X-Mod-Pagespeed: 1.12.34.2-0\r\n" +
+              "Connection: keep-alive\r\n\r\n"
+            };
+        Arrays.stream(basic).forEach(responses::add);
+
+        String[] foldingTemplate =
+           {  "HTTP/1.1 200 OK\r\n" +
+              "Content-Length: 9\r\n" +
+              "Content-Type: text/html;$NEWLINE" +  // folding field-value with '\n'|'\r'
+              " charset=UTF-8\r\n" +                // one preceding SP
+              "Connection: close\r\n\r\n" +
+              "XXYYZZAABBCCDDEE",
+
+              "HTTP/1.1 200 OK\r\n" +
+              "Content-Length: 19\r\n" +
+              "Content-Type: text/html;$NEWLINE" +  // folding field-value with '\n'|'\r
+              "   charset=UTF-8\r\n" +              // more than one preceding SP
+              "Connection: keep-alive\r\n\r\n" +
+              "XXYYZZAABBCCDDEEFFGG",
+
+              "HTTP/1.1 200 OK\r\n" +
+              "Content-Length: 999\r\n" +
+              "Content-Type: text/html;$NEWLINE" +  // folding field-value with '\n'|'\r
+              "\tcharset=UTF-8\r\n" +               // one preceding HT
+              "Connection: close\r\n\r\n" +
+              "XXYYZZAABBCCDDEE",
+
+              "HTTP/1.1 200 OK\r\n" +
+              "Content-Length: 54\r\n" +
+              "Content-Type: text/html;$NEWLINE" +  // folding field-value with '\n'|'\r
+              "\t\t\tcharset=UTF-8\r\n" +           // more than one preceding HT
+              "Connection: keep-alive\r\n\r\n" +
+              "XXYYZZAABBCCDDEEFFGG",
+
+              "HTTP/1.1 200 OK\r\n" +
+              "Content-Length: -1\r\n" +
+              "Content-Type: text/html;$NEWLINE" +  // folding field-value with '\n'|'\r
+              "\t \t \tcharset=UTF-8\r\n" +         // mix of preceding HT and SP
+              "Connection: keep-alive\r\n\r\n" +
+              "XXYYZZAABBCCDDEEFFGGHH",
+
+              "HTTP/1.1 200 OK\r\n" +
+              "Content-Length: 65\r\n" +
+              "Content-Type: text/html;$NEWLINE" +  // folding field-value with '\n'|'\r
+              " \t \t charset=UTF-8\r\n" +          // mix of preceding SP and HT
+              "Connection: keep-alive\r\n\r\n" +
+              "XXYYZZAABBCCDDEEFFGGHHII",
+           };
+        for (String newLineChar : new String[] { "\n", "\r" }) {
+            for (String template : foldingTemplate)
+                responses.add(template.replace("$NEWLINE", newLineChar));
+        }
+
+        String[] bad = // much of this is to retain parity with legacy MessageHeaders
+           { "HTTP/1.1 200 OK\r\n" +
+             "Connection:\r\n\r\n",   // empty value, no body
+
+             "HTTP/1.1 200 OK\r\n" +
+             "Connection:\r\n\r\n" +  // empty value, with body
+             "XXXXX",
+
+             "HTTP/1.1 200 OK\r\n" +
+             ": no header\r\n\r\n",  // no/empty header-name, no body, no following header
+
+             "HTTP/1.1 200 OK\r\n" +
+             ": no; header\r\n" +  // no/empty header-name, no body, following header
+             "Content-Length: 65\r\n\r\n",
+
+             "HTTP/1.1 200 OK\r\n" +
+             ": no header\r\n" +  // no/empty header-name
+             "Content-Length: 65\r\n\r\n" +
+             "XXXXX",
+
+             "HTTP/1.1 200 OK\r\n" +
+             ": no header\r\n\r\n" +  // no/empty header-name, followed by header
+             "XXXXX",
+
+             "HTTP/1.1 200 OK\r\n" +
+             "Conte\r" +
+             " nt-Length: 9\r\n" +    // fold/bad header name ???
+             "Content-Type: text/html; charset=UTF-8\r\n\r\n" +
+             "XXXXX",
+
+             "HTTP/1.1 200 OK\r\n" +
+             "Conte\r" +
+             "nt-Length: 9\r\n" +    // fold/bad header name ??? without preceding space
+             "Content-Type: text/html; charset=UTF-8\r\n\r\n" +
+             "XXXXXYYZZ",
+
+             "HTTP/1.0 404 Not Found\r\n" +
+             "header-without-colon\r\n\r\n",
+
+             "HTTP/1.0 404 Not Found\r\n" +
+             "header-without-colon\r\n\r\n" +
+             "SOMEBODY",
+
+           };
+        Arrays.stream(bad).forEach(responses::add);
+
+        return responses.stream().map(p -> new Object[] { p }).toArray(Object[][]::new);
+    }
+
+    @Test(dataProvider = "responses")
+    public void verifyHeaders(String respString) throws Exception {
+        byte[] bytes = respString.getBytes(US_ASCII);
+        ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
+        MessageHeader m = new MessageHeader(bais);
+        Map<String,List<String>> messageHeaderMap = m.getHeaders();
+        int available = bais.available();
+
+        Http1HeaderParser decoder = new Http1HeaderParser();
+        ByteBuffer b = ByteBuffer.wrap(bytes);
+        decoder.parse(b);
+        Map<String,List<String>> decoderMap1 = decoder.headers().map();
+        assertEquals(available, b.remaining(),
+                     "stream available not equal to remaining");
+
+        // assert status-line
+        String statusLine1 = messageHeaderMap.get(null).get(0);
+        String statusLine2 = decoder.statusLine();
+        if (statusLine1.startsWith("HTTP")) {// skip the case where MH's messes up the status-line
+            assertEquals(statusLine1, statusLine2, "Status-line not equal");
+        } else {
+            assertTrue(statusLine2.startsWith("HTTP/1."), "Status-line not HTTP/1.");
+        }
+
+        // remove the null'th entry with is the status-line
+        Map<String,List<String>> map = new HashMap<>();
+        for (Map.Entry<String,List<String>> e : messageHeaderMap.entrySet()) {
+            if (e.getKey() != null) {
+                map.put(e.getKey(), e.getValue());
+            }
+        }
+        messageHeaderMap = map;
+
+        assertHeadersEqual(messageHeaderMap, decoderMap1,
+                          "messageHeaderMap not equal to decoderMap1");
+
+        // byte at a time
+        decoder = new Http1HeaderParser();
+        List<ByteBuffer> buffers = IntStream.range(0, bytes.length)
+                .mapToObj(i -> ByteBuffer.wrap(bytes, i, 1))
+                .collect(toList());
+        while (decoder.parse(buffers.remove(0)) != true);
+        Map<String,List<String>> decoderMap2 = decoder.headers().map();
+        assertEquals(available, buffers.size(),
+                     "stream available not equals to remaining buffers");
+        assertEquals(decoderMap1, decoderMap2, "decoder maps not equal");
+    }
+
+    @DataProvider(name = "errors")
+    public Object[][] errors() {
+        List<String> responses = new ArrayList<>();
+
+        // These responses are parsed, somewhat, by MessageHeaders but give
+        // nonsensible results. They, correctly, fail with the Http1HeaderParser.
+        String[] bad =
+           {// "HTTP/1.1 402 Payment Required\r\n" +
+            // "Content-Length: 65\r\n\r",   // missing trailing LF   //TODO: incomplete
+
+             "HTTP/1.1 402 Payment Required\r\n" +
+             "Content-Length: 65\r\n\rT\r\n\r\nGGGGGG",
+
+             "HTTP/1.1 200OK\r\n\rT",
+
+             "HTTP/1.1 200OK\rT",
+           };
+        Arrays.stream(bad).forEach(responses::add);
+
+        return responses.stream().map(p -> new Object[] { p }).toArray(Object[][]::new);
+    }
+
+    @Test(dataProvider = "errors", expectedExceptions = ProtocolException.class)
+    public void errors(String respString) throws ProtocolException {
+        byte[] bytes = respString.getBytes(US_ASCII);
+        Http1HeaderParser decoder = new Http1HeaderParser();
+        ByteBuffer b = ByteBuffer.wrap(bytes);
+        decoder.parse(b);
+    }
+
+    void assertHeadersEqual(Map<String,List<String>> expected,
+                            Map<String,List<String>> actual,
+                            String msg) {
+
+        if (expected.equals(actual))
+            return;
+
+        assertEquals(expected.size(), actual.size(),
+                     format("%s. Expected size %d, actual size %s. %nexpected= %s,%n actual=%s.",
+                            msg, expected.size(), actual.size(), mapToString(expected), mapToString(actual)));
+
+        for (Map.Entry<String,List<String>> e : expected.entrySet()) {
+            String key = e.getKey();
+            List<String> values = e.getValue();
+
+            boolean found = false;
+            for (Map.Entry<String,List<String>> other: actual.entrySet()) {
+                if (key.equalsIgnoreCase(other.getKey())) {
+                    found = true;
+                    List<String> otherValues = other.getValue();
+                    assertEquals(values.size(), otherValues.size(),
+                                 format("%s. Expected list size %d, actual size %s",
+                                        msg, values.size(), otherValues.size()));
+                    if (!values.containsAll(otherValues) && otherValues.containsAll(values))
+                        assertTrue(false, format("Lists are unequal [%s] [%s]", values, otherValues));
+                    break;
+                }
+            }
+            assertTrue(found, format("header name, %s, not found in %s", key, actual));
+        }
+    }
+
+    static String mapToString(Map<String,List<String>> map) {
+        StringBuilder sb = new StringBuilder();
+        List<String> sortedKeys = new ArrayList(map.keySet());
+        Collections.sort(sortedKeys);
+        for (String key : sortedKeys) {
+            List<String> values = map.get(key);
+            sb.append("\n\t" + key + " | " + values);
+        }
+        return sb.toString();
+    }
+
+    // ---
+
+    /* Main entry point for standalone testing of the main functional test. */
+    public static void main(String... args) throws Exception  {
+        Http1HeaderParserTest test = new Http1HeaderParserTest();
+        int count = 0;
+        for (Object[] objs : test.responses()) {
+            out.println("Testing " + count++ + ", " + objs[0]);
+            test.verifyHeaders((String) objs[0]);
+        }
+        for (Object[] objs : test.errors()) {
+            out.println("Testing " + count++ + ", " + objs[0]);
+            try {
+                test.errors((String) objs[0]);
+                throw new RuntimeException("Expected ProtocolException for " + objs[0]);
+            } catch (ProtocolException expected) { /* Ok */ }
+        }
+    }
+}
diff --git a/test/jdk/java/net/httpclient/whitebox/jdk.incubator.httpclient/jdk/incubator/http/RawChannelTest.java b/test/jdk/java/net/httpclient/whitebox/jdk.incubator.httpclient/jdk/incubator/http/RawChannelTest.java
index 7e9ca255795..9cb59d59d11 100644
--- a/test/jdk/java/net/httpclient/whitebox/jdk.incubator.httpclient/jdk/incubator/http/RawChannelTest.java
+++ b/test/jdk/java/net/httpclient/whitebox/jdk.incubator.httpclient/jdk/incubator/http/RawChannelTest.java
@@ -24,6 +24,7 @@
 package jdk.incubator.http;
 
 import jdk.incubator.http.internal.websocket.RawChannel;
+import jdk.incubator.http.internal.websocket.WebSocketRequest;
 import org.testng.annotations.Test;
 
 import java.io.IOException;
@@ -83,6 +84,7 @@ public class RawChannelTest {
             new TestServer(server).start();
 
             final RawChannel chan = channelOf(port);
+            print("RawChannel is %s", String.valueOf(chan));
             initialWriteStall.await();
 
             // It's very important not to forget the initial bytes, possibly
@@ -185,9 +187,21 @@ public class RawChannelTest {
         URI uri = URI.create("http://127.0.0.1:" + port + "/");
         print("raw channel to %s", uri.toString());
         HttpRequest req = HttpRequest.newBuilder(uri).build();
-        HttpResponse<?> r = HttpClient.newHttpClient().send(req, discard(null));
-        r.body();
-        return ((HttpResponseImpl) r).rawChannel();
+        // Switch on isWebSocket flag to prevent the connection from
+        // being returned to the pool.
+        ((WebSocketRequest)req).isWebSocket(true);
+        HttpClient client = HttpClient.newHttpClient();
+        try {
+            HttpResponse<?> r = client.send(req, discard(null));
+            r.body();
+            return ((HttpResponseImpl) r).rawChannel();
+        } finally {
+           // Need to hold onto the client until the RawChannel is
+           // created. This would not be needed if we had created
+           // a WebSocket, but here we are fiddling directly
+           // with the internals of HttpResponseImpl!
+           java.lang.ref.Reference.reachabilityFence(client);
+        }
     }
 
     private class TestServer extends Thread { // Powered by Slowpokes
diff --git a/test/jdk/java/net/httpclient/whitebox/jdk.incubator.httpclient/jdk/incubator/http/ResponseHeadersTest.java b/test/jdk/java/net/httpclient/whitebox/jdk.incubator.httpclient/jdk/incubator/http/ResponseHeadersTest.java
deleted file mode 100644
index bf2c6d751e6..00000000000
--- a/test/jdk/java/net/httpclient/whitebox/jdk.incubator.httpclient/jdk/incubator/http/ResponseHeadersTest.java
+++ /dev/null
@@ -1,223 +0,0 @@
-/*
- * Copyright (c) 2017, 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 jdk.incubator.http;
-
-import java.io.IOException;
-import java.io.UnsupportedEncodingException;
-import java.nio.ByteBuffer;
-import java.nio.channels.SocketChannel;
-import java.util.Optional;
-import java.util.concurrent.CompletableFuture;
-import org.testng.annotations.Test;
-import jdk.incubator.http.internal.common.ByteBufferReference;
-
-@Test
-public class ResponseHeadersTest {
-
-    static final String BODY =
-          "This is the body dude,\r\n"
-        + "not a header!\r\n";
-
-    static final String MESSAGE_OK =
-          "HTTP/1.1 200 OK\r\n"
-        + "Content-Length: " + BODY.length() + "\r\n"
-        + "MY-Folding-Header: YES\r\n"
-        + " OR\r\n"
-        + " NO\r\n"
-        + "\r\n"
-        + BODY;
-
-    static final String MESSAGE_NOK =
-          "HTTP/1.1 101 Switching Protocols\r\n"
-        + "\r\n";
-
-    //public static void main(String[] args) throws IOException {
-    //    new  ResponseHeadersTest().test();
-    //}
-
-    @Test
-    public void test() throws IOException {
-        testResponseHeaders(MESSAGE_OK);
-        testResponseHeaders(MESSAGE_NOK);
-    }
-
-    /**
-     * Verifies that ResponseHeaders behave as we expect.
-     * @param msg The response string.
-     * @throws IOException should not happen.
-     */
-    static void testResponseHeaders(String msg) throws IOException {
-        byte[] bytes = msg.getBytes("US-ASCII");
-        ByteBuffer buffer = ByteBuffer.wrap(bytes);
-
-        // Read status line
-        String statusLine = readStatusLine(buffer);
-        System.out.println("StatusLine: " + statusLine);
-        if (!statusLine.startsWith("HTTP/1.1")) {
-            throw new AssertionError("bad status line: " + statusLine);
-        }
-
-        // We have two cases:
-        //    - MESSAGE_OK: there will be some headers to read,
-        //    - MESSAGE_NOK: there will be no headers to read.
-        HttpHeaders headers = createResponseHeaders(buffer);
-
-        // Now get the expected length of the body
-        Optional<String> contentLengthValue = headers.firstValue("Content-length");
-        int contentLength = contentLengthValue.map(Integer::parseInt).orElse(0);
-
-        // We again have two cases:
-        //    - MESSAGE_OK:  there should be a Content-length: header
-        //    - MESSAGE_NOK: there should be no Content-length: header
-        if (contentLengthValue.isPresent()) {
-            // MESSAGE_NOK has no headers and no body and therefore
-            // no Content-length: header.
-            if (MESSAGE_NOK.equals(msg)) {
-                throw new AssertionError("Content-length: header found in"
-                          + " error 101 message");
-            }
-        } else {
-            if (!MESSAGE_NOK.equals(msg)) {
-                throw new AssertionError("Content-length: header not found");
-            }
-        }
-
-        // Now read the remaining bytes. It should either be
-        // the empty string (MESSAGE_NOK) or BODY (MESSAGE_OK),
-        // and it should not contains any leading CR or LF"
-        String remaining = readRemainingBytes(buffer);
-        System.out.println("Body: <<<" + remaining + ">>>");
-        if (remaining.length() != contentLength) {
-            throw new AssertionError("Unexpected body length: " + remaining.length()
-                     + " expected " + contentLengthValue);
-        }
-        if (contentLengthValue.isPresent()) {
-            if (!BODY.equals(remaining)) {
-                throw new AssertionError("Body does not match!");
-            }
-        }
-    }
-
-    static String readRemainingBytes(ByteBuffer buffer) throws UnsupportedEncodingException {
-        byte[] res = new byte[buffer.limit() - buffer.position()];
-        System.arraycopy(buffer.array(), buffer.position(), res, 0, res.length);
-        buffer.position(buffer.limit());
-        return new String(res, "US-ASCII");
-    }
-
-    static String readStatusLine(ByteBuffer buffer) throws IOException {
-        buffer.mark();
-        int p = buffer.position();
-        while(buffer.hasRemaining()) {
-            char c = (char)buffer.get();
-            if (c == '\r') {
-                c = (char)buffer.get();
-                if (c == '\n') {
-                    byte[] res = new byte[buffer.position() - p -2];
-                    System.arraycopy(buffer.array(), p, res, 0, res.length);
-                    return new String(res, "US-ASCII");
-                }
-            }
-        }
-        throw new IOException("Status line not found");
-    }
-
-    private static final class HttpConnectionStub extends HttpConnection {
-        public HttpConnectionStub() {
-            super(null, null);
-        }
-        @Override
-        public void connect() throws IOException, InterruptedException {
-            throw new AssertionError("Bad test assumption: should not have reached here!");
-        }
-        @Override
-        public CompletableFuture<Void> connectAsync() {
-            throw new AssertionError("Bad test assumption: should not have reached here!");
-        }
-        @Override
-        boolean connected() {
-            throw new AssertionError("Bad test assumption: should not have reached here!");
-        }
-        @Override
-        boolean isSecure() {
-            throw new AssertionError("Bad test assumption: should not have reached here!");
-        }
-        @Override
-        boolean isProxied() {
-            throw new AssertionError("Bad test assumption: should not have reached here!");
-        }
-        @Override
-        CompletableFuture<Void> whenReceivingResponse() {
-            throw new AssertionError("Bad test assumption: should not have reached here!");
-        }
-        @Override
-        SocketChannel channel() {
-            throw new AssertionError("Bad test assumption: should not have reached here!");
-        }
-        @Override
-        ConnectionPool.CacheKey cacheKey() {
-            throw new AssertionError("Bad test assumption: should not have reached here!");
-        }
-        @Override
-        long write(ByteBuffer[] buffers, int start, int number) throws IOException {
-            throw new AssertionError("Bad test assumption: should not have reached here!");
-        }
-        @Override
-        long write(ByteBuffer buffer) throws IOException {
-            throw new AssertionError("Bad test assumption: should not have reached here!");
-        }
-        @Override
-        void writeAsync(ByteBufferReference[] buffers) throws IOException {
-            throw new AssertionError("Bad test assumption: should not have reached here!");
-        }
-        @Override
-        void writeAsyncUnordered(ByteBufferReference[] buffers) throws IOException {
-            throw new AssertionError("Bad test assumption: should not have reached here!");
-        }
-        @Override
-        void flushAsync() throws IOException {
-            throw new AssertionError("Bad test assumption: should not have reached here!");
-        }
-        @Override
-        public void close() {
-            throw new AssertionError("Bad test assumption: should not have reached here!");
-        }
-        @Override
-        void shutdownInput() throws IOException {
-            throw new AssertionError("Bad test assumption: should not have reached here!");
-        }
-        @Override
-        void shutdownOutput() throws IOException {
-            throw new AssertionError("Bad test assumption: should not have reached here!");
-        }
-        @Override
-        protected ByteBuffer readImpl() throws IOException {
-            throw new AssertionError("Bad test assumption: should not have reached here!");
-        }
-    }
-
-    public static HttpHeaders createResponseHeaders(ByteBuffer buffer)
-        throws IOException{
-        return new ResponseHeaders(new HttpConnectionStub(), buffer);
-    }
-}
diff --git a/test/jdk/java/net/httpclient/whitebox/jdk.incubator.httpclient/jdk/incubator/http/SSLEchoTubeTest.java b/test/jdk/java/net/httpclient/whitebox/jdk.incubator.httpclient/jdk/incubator/http/SSLEchoTubeTest.java
new file mode 100644
index 00000000000..b818bcc71be
--- /dev/null
+++ b/test/jdk/java/net/httpclient/whitebox/jdk.incubator.httpclient/jdk/incubator/http/SSLEchoTubeTest.java
@@ -0,0 +1,420 @@
+/*
+ * Copyright (c) 2017, 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 jdk.incubator.http;
+
+import jdk.incubator.http.internal.common.Demand;
+import jdk.incubator.http.internal.common.FlowTube;
+import jdk.incubator.http.internal.common.SSLTube;
+import jdk.incubator.http.internal.common.SequentialScheduler;
+import jdk.incubator.http.internal.common.Utils;
+import org.testng.annotations.Test;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.List;
+import java.util.Queue;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ConcurrentLinkedQueue;
+import java.util.concurrent.Executor;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Flow;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.Consumer;
+
+@Test
+public class SSLEchoTubeTest extends AbstractSSLTubeTest {
+
+    @Test
+    public void runWithEchoServer() throws IOException {
+        ExecutorService sslExecutor = Executors.newCachedThreadPool();
+
+        /* Start of wiring */
+        /* Emulates an echo server */
+        FlowTube server = crossOverEchoServer(sslExecutor);
+
+        run(server, sslExecutor, allBytesReceived);
+    }
+
+    /**
+     * Creates a cross-over FlowTube than can be plugged into a client-side
+     * SSLTube (in place of the SSLLoopbackSubscriber).
+     * Note that the only method that can be called on the return tube
+     * is connectFlows(). Calling any other method will trigger an
+     * InternalError.
+     * @param sslExecutor an executor
+     * @return a cross-over FlowTube connected to an EchoTube.
+     * @throws IOException
+     */
+    private FlowTube crossOverEchoServer(Executor sslExecutor) throws IOException {
+        LateBindingTube crossOver = new LateBindingTube();
+        FlowTube server = new SSLTube(createSSLEngine(false),
+                                      sslExecutor,
+                                      crossOver);
+        EchoTube echo = new EchoTube(6);
+        server.connectFlows(FlowTube.asTubePublisher(echo), FlowTube.asTubeSubscriber(echo));
+
+        return new CrossOverTube(crossOver);
+    }
+
+    /**
+     * A cross-over FlowTube that makes it possible to reverse the direction
+     * of flows. The typical usage is to connect an two opposite SSLTube,
+     * one encrypting, one decrypting, to e.g. an EchoTube, with the help
+     * of a LateBindingTube:
+     * {@code
+     * client app => SSLTube => CrossOverTube <= LateBindingTube <= SSLTube <= EchoTube
+     * }
+     * <p>
+     * Note that the only method that can be called on the CrossOverTube is
+     * connectFlows(). Calling any other method will cause an InternalError to
+     * be thrown.
+     * Also connectFlows() can be called only once.
+     */
+    private static final class CrossOverTube implements FlowTube {
+        final LateBindingTube tube;
+        CrossOverTube(LateBindingTube tube) {
+            this.tube = tube;
+        }
+
+        @Override
+        public void subscribe(Flow.Subscriber<? super List<ByteBuffer>> subscriber) {
+            throw newInternalError();
+        }
+
+        @Override
+        public void connectFlows(TubePublisher writePublisher, TubeSubscriber readSubscriber) {
+            tube.start(writePublisher, readSubscriber);
+        }
+
+        @Override
+        public boolean isFinished() {
+            return tube.isFinished();
+        }
+
+        Error newInternalError() {
+            InternalError error = new InternalError();
+            error.printStackTrace(System.out);
+            return error;
+        }
+
+        @Override
+        public void onSubscribe(Flow.Subscription subscription) {
+            throw newInternalError();
+        }
+
+        @Override
+        public void onError(Throwable throwable) {
+            throw newInternalError();
+        }
+
+        @Override
+        public void onComplete() {
+            throw newInternalError();
+        }
+
+        @Override
+        public void onNext(List<ByteBuffer> item) {
+            throw newInternalError();
+        }
+    }
+
+    /**
+     * A late binding tube that makes it possible to create an
+     * SSLTube before the right-hand-side tube has been created.
+     * The typical usage is to make it possible to connect two
+     * opposite SSLTube (one encrypting, one decrypting) through a
+     * CrossOverTube:
+     * {@code
+     * client app => SSLTube => CrossOverTube <= LateBindingTube <= SSLTube <= EchoTube
+     * }
+     * <p>
+     * Note that this class only supports a single call to start(): it cannot be
+     * subscribed more than once from its left-hand-side (the cross over tube side).
+     */
+    private static class LateBindingTube implements FlowTube {
+
+        final CompletableFuture<Flow.Publisher<List<ByteBuffer>>> futurePublisher
+                = new CompletableFuture<>();
+        final ConcurrentLinkedQueue<Consumer<Flow.Subscriber<? super List<ByteBuffer>>>> queue
+                = new ConcurrentLinkedQueue<>();
+        AtomicReference<Flow.Subscriber<? super List<ByteBuffer>>> subscriberRef = new AtomicReference<>();
+        SequentialScheduler scheduler = SequentialScheduler.synchronizedScheduler(this::loop);
+        AtomicReference<Throwable> errorRef = new AtomicReference<>();
+        private volatile boolean finished;
+        private volatile boolean completed;
+
+
+        public void start(Flow.Publisher<List<ByteBuffer>> publisher,
+                          Flow.Subscriber<? super List<ByteBuffer>> subscriber) {
+            subscriberRef.set(subscriber);
+            futurePublisher.complete(publisher);
+            scheduler.runOrSchedule();
+        }
+
+        @Override
+        public boolean isFinished() {
+            return finished;
+        }
+
+        @Override
+        public void subscribe(Flow.Subscriber<? super List<ByteBuffer>> subscriber) {
+            futurePublisher.thenAccept((p) -> p.subscribe(subscriber));
+            scheduler.runOrSchedule();
+        }
+
+        @Override
+        public void onSubscribe(Flow.Subscription subscription) {
+            queue.add((s) -> s.onSubscribe(subscription));
+            scheduler.runOrSchedule();
+        }
+
+        @Override
+        public void onNext(List<ByteBuffer> item) {
+            queue.add((s) -> s.onNext(item));
+            scheduler.runOrSchedule();
+        }
+
+        @Override
+        public void onError(Throwable throwable) {
+            System.out.println("LateBindingTube onError");
+            throwable.printStackTrace(System.out);
+            queue.add((s) -> {
+                errorRef.compareAndSet(null, throwable);
+                try {
+                    System.out.println("LateBindingTube subscriber onError: " + throwable);
+                    s.onError(errorRef.get());
+                } finally {
+                    finished = true;
+                    System.out.println("LateBindingTube finished");
+                }
+            });
+            scheduler.runOrSchedule();
+        }
+
+        @Override
+        public void onComplete() {
+            System.out.println("LateBindingTube completing");
+            queue.add((s) -> {
+                completed = true;
+                try {
+                    System.out.println("LateBindingTube complete subscriber");
+                    s.onComplete();
+                } finally {
+                    finished = true;
+                    System.out.println("LateBindingTube finished");
+                }
+            });
+            scheduler.runOrSchedule();
+        }
+
+        private void loop() {
+            if (finished) {
+                scheduler.stop();
+                return;
+            }
+            Flow.Subscriber<? super List<ByteBuffer>> subscriber = subscriberRef.get();
+            if (subscriber == null) return;
+            try {
+                Consumer<Flow.Subscriber<? super List<ByteBuffer>>> s;
+                while ((s = queue.poll()) != null) {
+                    s.accept(subscriber);
+                }
+            } catch (Throwable t) {
+                if (errorRef.compareAndSet(null, t)) {
+                    onError(t);
+                }
+            }
+        }
+    }
+
+    /**
+     * An echo tube that just echoes back whatever bytes it receives.
+     * This cannot be plugged to the right-hand-side of an SSLTube
+     * since handshake data cannot be simply echoed back, and
+     * application data most likely also need to be decrypted and
+     * re-encrypted.
+     */
+    private static final class EchoTube implements FlowTube {
+
+        private final static Object EOF = new Object();
+        private final Executor executor = Executors.newSingleThreadExecutor();
+
+        private final Queue<Object> queue = new ConcurrentLinkedQueue<>();
+        private final int maxQueueSize;
+        private final SequentialScheduler processingScheduler =
+                new SequentialScheduler(createProcessingTask());
+
+        /* Writing into this tube */
+        private volatile long requested;
+        private Flow.Subscription subscription;
+
+        /* Reading from this tube */
+        private final Demand demand = new Demand();
+        private final AtomicBoolean cancelled = new AtomicBoolean();
+        private Flow.Subscriber<? super List<ByteBuffer>> subscriber;
+
+        private EchoTube(int maxBufferSize) {
+            if (maxBufferSize < 1)
+                throw new IllegalArgumentException();
+            this.maxQueueSize = maxBufferSize;
+        }
+
+        @Override
+        public void subscribe(Flow.Subscriber<? super List<ByteBuffer>> subscriber) {
+            this.subscriber = subscriber;
+            System.out.println("EchoTube got subscriber: " + subscriber);
+            this.subscriber.onSubscribe(new InternalSubscription());
+        }
+
+        @Override
+        public void onSubscribe(Flow.Subscription subscription) {
+            System.out.println("EchoTube request: " + maxQueueSize);
+            (this.subscription = subscription).request(requested = maxQueueSize);
+        }
+
+        private void requestMore() {
+            Flow.Subscription s = subscription;
+            if (s == null || cancelled.get()) return;
+            long unfulfilled = queue.size() + --requested;
+            if (unfulfilled <= maxQueueSize/2) {
+                long req = maxQueueSize - unfulfilled;
+                requested += req;
+                s.request(req);
+                System.out.printf("EchoTube request: %s [requested:%s, queue:%s, unfulfilled:%s]%n",
+                        req, requested-req, queue.size(), unfulfilled );
+            }
+        }
+
+        @Override
+        public void onNext(List<ByteBuffer> item) {
+            System.out.printf("EchoTube add %s [requested:%s, queue:%s]%n",
+                    Utils.remaining(item), requested, queue.size());
+            queue.add(item);
+            processingScheduler.deferOrSchedule(executor);
+        }
+
+        @Override
+        public void onError(Throwable throwable) {
+            System.out.println("EchoTube add " + throwable);
+            queue.add(throwable);
+            processingScheduler.deferOrSchedule(executor);
+        }
+
+        @Override
+        public void onComplete() {
+            System.out.println("EchoTube add EOF");
+            queue.add(EOF);
+            processingScheduler.deferOrSchedule(executor);
+        }
+
+        @Override
+        public boolean isFinished() {
+            return cancelled.get();
+        }
+
+        private class InternalSubscription implements Flow.Subscription {
+
+            @Override
+            public void request(long n) {
+                System.out.println("EchoTube got request: " + n);
+                if (n <= 0) {
+                    throw new InternalError();
+                }
+                if (demand.increase(n)) {
+                    processingScheduler.deferOrSchedule(executor);
+                }
+            }
+
+            @Override
+            public void cancel() {
+                cancelled.set(true);
+            }
+        }
+
+        @Override
+        public String toString() {
+            return "EchoTube";
+        }
+
+        int transmitted = 0;
+        private SequentialScheduler.RestartableTask createProcessingTask() {
+            return new SequentialScheduler.CompleteRestartableTask() {
+
+                @Override
+                protected void run() {
+                    try {
+                        while (!cancelled.get()) {
+                            Object item = queue.peek();
+                            if (item == null) {
+                                System.out.printf("EchoTube: queue empty, requested=%s, demand=%s, transmitted=%s%n",
+                                        requested, demand.get(), transmitted);
+                                requestMore();
+                                return;
+                            }
+                            try {
+                                System.out.printf("EchoTube processing item, requested=%s, demand=%s, transmitted=%s%n",
+                                        requested, demand.get(), transmitted);
+                                if (item instanceof List) {
+                                    if (!demand.tryDecrement()) {
+                                        System.out.println("EchoTube no demand");
+                                        return;
+                                    }
+                                    @SuppressWarnings("unchecked")
+                                    List<ByteBuffer> bytes = (List<ByteBuffer>) item;
+                                    Object removed = queue.remove();
+                                    assert removed == item;
+                                    System.out.println("EchoTube processing "
+                                            + Utils.remaining(bytes));
+                                    transmitted++;
+                                    subscriber.onNext(bytes);
+                                    requestMore();
+                                } else if (item instanceof Throwable) {
+                                    cancelled.set(true);
+                                    Object removed = queue.remove();
+                                    assert removed == item;
+                                    System.out.println("EchoTube processing " + item);
+                                    subscriber.onError((Throwable) item);
+                                } else if (item == EOF) {
+                                    cancelled.set(true);
+                                    Object removed = queue.remove();
+                                    assert removed == item;
+                                    System.out.println("EchoTube processing EOF");
+                                    subscriber.onComplete();
+                                } else {
+                                    throw new InternalError(String.valueOf(item));
+                                }
+                            } finally {
+                            }
+                        }
+                    } catch(Throwable t) {
+                        t.printStackTrace();
+                        throw t;
+                    }
+                }
+            };
+        }
+    }
+ }
diff --git a/test/jdk/java/net/httpclient/whitebox/jdk.incubator.httpclient/jdk/incubator/http/SSLTubeTest.java b/test/jdk/java/net/httpclient/whitebox/jdk.incubator.httpclient/jdk/incubator/http/SSLTubeTest.java
new file mode 100644
index 00000000000..d9897a38ec0
--- /dev/null
+++ b/test/jdk/java/net/httpclient/whitebox/jdk.incubator.httpclient/jdk/incubator/http/SSLTubeTest.java
@@ -0,0 +1,275 @@
+/*
+ * Copyright (c) 2017, 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 jdk.incubator.http;
+
+import jdk.incubator.http.internal.common.FlowTube;
+import jdk.incubator.http.internal.common.SSLFlowDelegate;
+import jdk.incubator.http.internal.common.Utils;
+import org.testng.annotations.Test;
+
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLParameters;
+import javax.net.ssl.SSLServerSocket;
+import javax.net.ssl.SSLServerSocketFactory;
+import javax.net.ssl.SSLSocket;
+import java.io.BufferedOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.Socket;
+import java.nio.ByteBuffer;
+import java.util.List;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Flow;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.SubmissionPublisher;
+import java.util.concurrent.atomic.AtomicInteger;
+
+@Test
+public class SSLTubeTest extends AbstractSSLTubeTest {
+
+    @Test
+    public void runWithSSLLoopackServer() throws IOException {
+        ExecutorService sslExecutor = Executors.newCachedThreadPool();
+
+        /* Start of wiring */
+        /* Emulates an echo server */
+        SSLLoopbackSubscriber server =
+                new SSLLoopbackSubscriber((new SimpleSSLContext()).get(),
+                        sslExecutor,
+                        allBytesReceived);
+        server.start();
+
+        run(server, sslExecutor, allBytesReceived);
+    }
+
+    /**
+     * This is a copy of the SSLLoopbackSubscriber used in FlowTest
+     */
+    private static class SSLLoopbackSubscriber implements FlowTube {
+        private final BlockingQueue<ByteBuffer> buffer;
+        private final Socket clientSock;
+        private final SSLSocket serverSock;
+        private final Thread thread1, thread2, thread3;
+        private volatile Flow.Subscription clientSubscription;
+        private final SubmissionPublisher<List<ByteBuffer>> publisher;
+        private final CountDownLatch allBytesReceived;
+
+        SSLLoopbackSubscriber(SSLContext ctx,
+                              ExecutorService exec,
+                              CountDownLatch allBytesReceived) throws IOException {
+            SSLServerSocketFactory fac = ctx.getServerSocketFactory();
+            SSLServerSocket serv = (SSLServerSocket) fac.createServerSocket(0);
+            SSLParameters params = serv.getSSLParameters();
+            params.setApplicationProtocols(new String[]{"proto2"});
+            serv.setSSLParameters(params);
+
+
+            int serverPort = serv.getLocalPort();
+            clientSock = new Socket("127.0.0.1", serverPort);
+            serverSock = (SSLSocket) serv.accept();
+            this.buffer = new LinkedBlockingQueue<>();
+            this.allBytesReceived = allBytesReceived;
+            thread1 = new Thread(this::clientWriter, "clientWriter");
+            thread2 = new Thread(this::serverLoopback, "serverLoopback");
+            thread3 = new Thread(this::clientReader, "clientReader");
+            publisher = new SubmissionPublisher<>(exec, Flow.defaultBufferSize(),
+                    this::handlePublisherException);
+            SSLFlowDelegate.Monitor.add(this::monitor);
+        }
+
+        public void start() {
+            thread1.start();
+            thread2.start();
+            thread3.start();
+        }
+
+        private void handlePublisherException(Object o, Throwable t) {
+            System.out.println("Loopback Publisher exception");
+            t.printStackTrace(System.out);
+        }
+
+        private final AtomicInteger readCount = new AtomicInteger();
+
+        // reads off the SSLSocket the data from the "server"
+        private void clientReader() {
+            try {
+                InputStream is = clientSock.getInputStream();
+                final int bufsize = randomRange(512, 16 * 1024);
+                System.out.println("clientReader: bufsize = " + bufsize);
+                while (true) {
+                    byte[] buf = new byte[bufsize];
+                    int n = is.read(buf);
+                    if (n == -1) {
+                        System.out.println("clientReader close: read "
+                                + readCount.get() + " bytes");
+                        System.out.println("clientReader: waiting signal to close publisher");
+                        allBytesReceived.await();
+                        System.out.println("clientReader: closing publisher");
+                        publisher.close();
+                        sleep(2000);
+                        Utils.close(is, clientSock);
+                        return;
+                    }
+                    ByteBuffer bb = ByteBuffer.wrap(buf, 0, n);
+                    readCount.addAndGet(n);
+                    publisher.submit(List.of(bb));
+                }
+            } catch (Throwable e) {
+                e.printStackTrace();
+                Utils.close(clientSock);
+            }
+        }
+
+        // writes the encrypted data from SSLFLowDelegate to the j.n.Socket
+        // which is connected to the SSLSocket emulating a server.
+        private void clientWriter() {
+            long nbytes = 0;
+            try {
+                OutputStream os =
+                        new BufferedOutputStream(clientSock.getOutputStream());
+
+                while (true) {
+                    ByteBuffer buf = buffer.take();
+                    if (buf == SENTINEL) {
+                        // finished
+                        //Utils.sleep(2000);
+                        System.out.println("clientWriter close: " + nbytes + " written");
+                        clientSock.shutdownOutput();
+                        System.out.println("clientWriter close return");
+                        return;
+                    }
+                    int len = buf.remaining();
+                    int written = writeToStream(os, buf);
+                    assert len == written;
+                    nbytes += len;
+                    assert !buf.hasRemaining()
+                            : "buffer has " + buf.remaining() + " bytes left";
+                    clientSubscription.request(1);
+                }
+            } catch (Throwable e) {
+                e.printStackTrace();
+            }
+        }
+
+        private int writeToStream(OutputStream os, ByteBuffer buf) throws IOException {
+            byte[] b = buf.array();
+            int offset = buf.arrayOffset() + buf.position();
+            int n = buf.limit() - buf.position();
+            os.write(b, offset, n);
+            buf.position(buf.limit());
+            os.flush();
+            return n;
+        }
+
+        private final AtomicInteger loopCount = new AtomicInteger();
+
+        public String monitor() {
+            return "serverLoopback: loopcount = " + loopCount.toString()
+                    + " clientRead: count = " + readCount.toString();
+        }
+
+        // thread2
+        private void serverLoopback() {
+            try {
+                InputStream is = serverSock.getInputStream();
+                OutputStream os = serverSock.getOutputStream();
+                final int bufsize = randomRange(512, 16 * 1024);
+                System.out.println("serverLoopback: bufsize = " + bufsize);
+                byte[] bb = new byte[bufsize];
+                while (true) {
+                    int n = is.read(bb);
+                    if (n == -1) {
+                        sleep(2000);
+                        is.close();
+                        os.close();
+                        serverSock.close();
+                        return;
+                    }
+                    os.write(bb, 0, n);
+                    os.flush();
+                    loopCount.addAndGet(n);
+                }
+            } catch (Throwable e) {
+                e.printStackTrace();
+            }
+        }
+
+
+        /**
+         * This needs to be called before the chain is subscribed. It can't be
+         * supplied in the constructor.
+         */
+        public void setReturnSubscriber(Flow.Subscriber<List<ByteBuffer>> returnSubscriber) {
+            publisher.subscribe(returnSubscriber);
+        }
+
+        @Override
+        public void onSubscribe(Flow.Subscription subscription) {
+            clientSubscription = subscription;
+            clientSubscription.request(5);
+        }
+
+        @Override
+        public void onNext(List<ByteBuffer> item) {
+            try {
+                for (ByteBuffer b : item)
+                    buffer.put(b);
+            } catch (InterruptedException e) {
+                e.printStackTrace();
+                Utils.close(clientSock);
+            }
+        }
+
+        @Override
+        public void onError(Throwable throwable) {
+            throwable.printStackTrace();
+            Utils.close(clientSock);
+        }
+
+        @Override
+        public void onComplete() {
+            try {
+                buffer.put(SENTINEL);
+            } catch (InterruptedException e) {
+                e.printStackTrace();
+                Utils.close(clientSock);
+            }
+        }
+
+        @Override
+        public boolean isFinished() {
+            return false;
+        }
+
+        @Override
+        public void subscribe(Flow.Subscriber<? super List<ByteBuffer>> subscriber) {
+            publisher.subscribe(subscriber);
+        }
+    }
+
+}
diff --git a/test/jdk/java/net/httpclient/whitebox/jdk.incubator.httpclient/jdk/incubator/http/SelectorTest.java b/test/jdk/java/net/httpclient/whitebox/jdk.incubator.httpclient/jdk/incubator/http/SelectorTest.java
index 9b091c1ce51..21ffc4d18e8 100644
--- a/test/jdk/java/net/httpclient/whitebox/jdk.incubator.httpclient/jdk/incubator/http/SelectorTest.java
+++ b/test/jdk/java/net/httpclient/whitebox/jdk.incubator.httpclient/jdk/incubator/http/SelectorTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2017, 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
@@ -76,7 +76,7 @@ public class SelectorTest {
         }
     }
 
-    @Test(timeOut = 10000)
+    @Test
     public void test() throws Exception {
 
         try (ServerSocket server = new ServerSocket(0)) {
@@ -88,57 +88,58 @@ public class SelectorTest {
             t.start();
             out.println("Started server thread");
 
-            final RawChannel chan = getARawChannel(port);
+            try (RawChannel chan = getARawChannel(port)) {
 
-            chan.registerEvent(new RawChannel.RawEvent() {
-                @Override
-                public int interestOps() {
-                    return SelectionKey.OP_READ;
-                }
-
-                @Override
-                public void handle() {
-                    readSomeBytes(chan);
-                    out.printf("OP_READ\n");
-                    final int count = counter.get();
-                    if (count != 1) {
-                        out.printf("OP_READ error counter = %d\n", count);
-                        error = true;
+                chan.registerEvent(new RawChannel.RawEvent() {
+                    @Override
+                    public int interestOps() {
+                        return SelectionKey.OP_READ;
                     }
-                }
-            });
 
-            chan.registerEvent(new RawChannel.RawEvent() {
-                @Override
-                public int interestOps() {
-                    return SelectionKey.OP_WRITE;
-                }
-
-                @Override
-                public void handle() {
-                    out.printf("OP_WRITE\n");
-                    final int count = counter.get();
-                    if (count != 0) {
-                        out.printf("OP_WRITE error counter = %d\n", count);
-                        error = true;
-                    } else {
-                        ByteBuffer bb = ByteBuffer.wrap(TestServer.INPUT);
-                        counter.incrementAndGet();
-                        try {
-                            chan.write(new ByteBuffer[]{bb}, 0, 1);
-                        } catch (IOException e) {
-                            throw new UncheckedIOException(e);
+                    @Override
+                    public void handle() {
+                        readSomeBytes(chan);
+                        out.printf("OP_READ\n");
+                        final int count = counter.get();
+                        if (count != 1) {
+                            out.printf("OP_READ error counter = %d\n", count);
+                            error = true;
                         }
                     }
-                }
+                });
 
-            });
-            out.println("Events registered. Waiting");
-            finishingGate.await(30, SECONDS);
-            if (error)
-                throw new RuntimeException("Error");
-            else
-                out.println("No error");
+                chan.registerEvent(new RawChannel.RawEvent() {
+                    @Override
+                    public int interestOps() {
+                        return SelectionKey.OP_WRITE;
+                    }
+
+                    @Override
+                    public void handle() {
+                        out.printf("OP_WRITE\n");
+                        final int count = counter.get();
+                        if (count != 0) {
+                            out.printf("OP_WRITE error counter = %d\n", count);
+                            error = true;
+                        } else {
+                            ByteBuffer bb = ByteBuffer.wrap(TestServer.INPUT);
+                            counter.incrementAndGet();
+                            try {
+                                chan.write(new ByteBuffer[]{bb}, 0, 1);
+                            } catch (IOException e) {
+                                throw new UncheckedIOException(e);
+                            }
+                        }
+                    }
+
+                });
+                out.println("Events registered. Waiting");
+                finishingGate.await(30, SECONDS);
+                if (error)
+                    throw new RuntimeException("Error");
+                else
+                    out.println("No error");
+            }
         }
     }
 
@@ -146,6 +147,10 @@ public class SelectorTest {
         URI uri = URI.create("http://127.0.0.1:" + port + "/");
         out.println("client connecting to " + uri.toString());
         HttpRequest req = HttpRequest.newBuilder(uri).build();
+        // Otherwise HttpClient will think this is an ordinary connection and
+        // thus all ordinary procedures apply to it, e.g. it must be put into
+        // the cache
+        ((HttpRequestImpl) req).isWebSocket(true);
         HttpResponse<?> r = defaultClient().send(req, discard(null));
         r.body();
         return ((HttpResponseImpl) r).rawChannel();
diff --git a/test/jdk/java/net/httpclient/whitebox/jdk.incubator.httpclient/jdk/incubator/http/WrapperTest.java b/test/jdk/java/net/httpclient/whitebox/jdk.incubator.httpclient/jdk/incubator/http/WrapperTest.java
new file mode 100644
index 00000000000..6cfcdf3f53e
--- /dev/null
+++ b/test/jdk/java/net/httpclient/whitebox/jdk.incubator.httpclient/jdk/incubator/http/WrapperTest.java
@@ -0,0 +1,263 @@
+/*
+ * Copyright (c) 2017, 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 jdk.incubator.http;
+
+import java.nio.ByteBuffer;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.concurrent.*;
+import java.util.concurrent.atomic.*;
+import org.testng.annotations.Test;
+import jdk.incubator.http.internal.common.SubscriberWrapper;
+
+@Test
+public class WrapperTest {
+    static final int LO_PRI = 1;
+    static final int HI_PRI = 2;
+    static final int NUM_HI_PRI = 240;
+    static final int BUFSIZE = 1016;
+    static final int BUFSIZE_INT = BUFSIZE/4;
+    static final int HI_PRI_FREQ = 40;
+
+    static final int TOTAL = 10000;
+    //static final int TOTAL = 500;
+
+    final SubmissionPublisher<List<ByteBuffer>> publisher;
+    final SubscriberWrapper sub1, sub2, sub3;
+    final ExecutorService executor = Executors.newCachedThreadPool();
+    volatile int hipricount = 0;
+
+    void errorHandler(Flow.Subscriber<? super List<ByteBuffer>> sub, Throwable t) {
+        System.err.printf("Exception from %s : %s\n", sub.toString(), t.toString());
+    }
+
+    public WrapperTest() {
+        publisher = new SubmissionPublisher<>(executor, 600,
+                (a, b) -> {
+                    errorHandler(a, b);
+                });
+
+        CompletableFuture<Void> notif = new CompletableFuture<>();
+        LastSubscriber ls = new LastSubscriber(notif);
+        sub1 = new Filter1(ls);
+        sub2 = new Filter2(sub1);
+        sub3 = new Filter2(sub2);
+    }
+
+    public class Filter2 extends SubscriberWrapper {
+        Filter2(SubscriberWrapper wrapper) {
+            super(wrapper);
+        }
+
+        // reverse the order of the bytes in each buffer
+        public void incoming(List<ByteBuffer> list, boolean complete) {
+            List<ByteBuffer> out = new LinkedList<>();
+            for (ByteBuffer inbuf : list) {
+                int size = inbuf.remaining();
+                ByteBuffer outbuf = ByteBuffer.allocate(size);
+                for (int i=size; i>0; i--) {
+                    byte b = inbuf.get(i-1);
+                    outbuf.put(b);
+                }
+                outbuf.flip();
+                out.add(outbuf);
+            }
+            if (complete) System.out.println("Filter2.complete");
+            outgoing(out, complete);
+        }
+
+        protected long windowUpdate(long currval) {
+            return currval == 0 ? 1 : 0;
+        }
+    }
+
+    volatile int filter1Calls = 0; // every third call we insert hi pri data
+
+    ByteBuffer getHiPri(int val) {
+        ByteBuffer buf = ByteBuffer.allocate(8);
+        buf.putInt(HI_PRI);
+        buf.putInt(val);
+        buf.flip();
+        return buf;
+    }
+
+    volatile int hiPriAdded = 0;
+
+    public class Filter1 extends SubscriberWrapper {
+        Filter1(Flow.Subscriber<List<ByteBuffer>> downstreamSubscriber)
+        {
+            super();
+            subscribe(downstreamSubscriber);
+        }
+
+        // Inserts up to NUM_HI_PRI hi priority buffers into flow
+        protected void incoming(List<ByteBuffer> in, boolean complete) {
+            if ((++filter1Calls % HI_PRI_FREQ) == 0 && (hiPriAdded++ < NUM_HI_PRI)) {
+                sub1.outgoing(getHiPri(hipricount++), false);
+            }
+            // pass data thru
+            if (complete) System.out.println("Filter1.complete");
+            outgoing(in, complete);
+        }
+
+        protected long windowUpdate(long currval) {
+            return currval == 0 ? 1 : 0;
+        }
+    }
+
+    /**
+     * Final subscriber in the chain. Compares the data sent by the original
+     * publisher.
+     */
+    static public class LastSubscriber implements Flow.Subscriber<List<ByteBuffer>> {
+        volatile Flow.Subscription subscription;
+        volatile int hipriCounter=0;
+        volatile int lopriCounter=0;
+        final CompletableFuture<Void> cf;
+
+        LastSubscriber(CompletableFuture<Void> cf) {
+            this.cf = cf;
+        }
+
+        @Override
+        public void onSubscribe(Flow.Subscription subscription) {
+            this.subscription = subscription;
+            subscription.request(50); // say
+        }
+
+        private void error(String...args) {
+            StringBuilder sb = new StringBuilder();
+            for (String s : args) {
+                sb.append(s);
+                sb.append(' ');
+            }
+            String msg = sb.toString();
+            System.out.println("Error: " + msg);
+            RuntimeException e = new RuntimeException(msg);
+            cf.completeExceptionally(e);
+            subscription.cancel(); // This is where we need a variant that include exception
+        }
+
+        private void check(ByteBuffer buf) {
+            int type = buf.getInt();
+            if (type == HI_PRI) {
+                // check next int is hi pri counter
+                int c = buf.getInt();
+                if (c != hipriCounter)
+                    error("hi pri counter", Integer.toString(c), Integer.toString(hipriCounter));
+                hipriCounter++;
+            } else {
+                while (buf.hasRemaining()) {
+                    if (buf.getInt() != lopriCounter)
+                        error("lo pri counter", Integer.toString(lopriCounter));
+                    lopriCounter++;
+                }
+            }
+        }
+
+        @Override
+        public void onNext(List<ByteBuffer> items) {
+            for (ByteBuffer item : items)
+                check(item);
+            subscription.request(1);
+        }
+
+        @Override
+        public void onError(Throwable throwable) {
+            error(throwable.getMessage());
+        }
+
+        @Override
+        public void onComplete() {
+            if (hipriCounter != NUM_HI_PRI)
+                error("hi pri at end wrong", Integer.toString(hipriCounter), Integer.toString(NUM_HI_PRI));
+            else {
+                System.out.println("LastSubscriber.complete");
+                cf.complete(null); // success
+            }
+        }
+    }
+
+    List<ByteBuffer> getBuffer(int c) {
+        ByteBuffer buf = ByteBuffer.allocate(BUFSIZE+4);
+        buf.putInt(LO_PRI);
+        for (int i=0; i<BUFSIZE_INT; i++) {
+            buf.putInt(c++);
+        }
+        buf.flip();
+        return List.of(buf);
+    }
+
+    boolean errorTest = false;
+
+    @Test
+    public void run() throws InterruptedException {
+        try {
+            CompletableFuture<Void> completion = sub3.completion();
+            publisher.subscribe(sub3);
+            // now submit a load of data
+            int counter = 0;
+            for (int i = 0; i < TOTAL; i++) {
+                List<ByteBuffer> bufs = getBuffer(counter);
+                //if (i==2)
+                    //bufs.get(0).putInt(41, 1234); // error
+                counter += BUFSIZE_INT;
+                publisher.submit(bufs);
+                //if (i % 1000 == 0)
+                    //Thread.sleep(1000);
+                //if (i == 99) {
+                    //publisher.closeExceptionally(new RuntimeException("Test error"));
+                    //errorTest = true;
+                    //break;
+                //}
+            }
+            if (!errorTest) {
+                publisher.close();
+            }
+            System.out.println("Publisher completed");
+            completion.join();
+            System.out.println("Subscribers completed ok");
+        } finally {
+            executor.shutdownNow();
+        }
+    }
+
+    static void display(CompletableFuture<?> cf) {
+        System.out.print (cf);
+        if (!cf.isDone())
+            return;
+        try {
+            cf.join(); // wont block
+        } catch (Exception e) {
+            System.out.println(" " + e);
+        }
+    }
+
+/*
+    public static void main(String[] args) throws InterruptedException {
+        WrapperTest test = new WrapperTest();
+        test.run();
+    }
+*/
+}
diff --git a/test/jdk/java/net/httpclient/whitebox/jdk.incubator.httpclient/jdk/incubator/http/internal/common/DemandTest.java b/test/jdk/java/net/httpclient/whitebox/jdk.incubator.httpclient/jdk/incubator/http/internal/common/DemandTest.java
new file mode 100644
index 00000000000..ab291da4a65
--- /dev/null
+++ b/test/jdk/java/net/httpclient/whitebox/jdk.incubator.httpclient/jdk/incubator/http/internal/common/DemandTest.java
@@ -0,0 +1,202 @@
+/*
+ * Copyright (c) 2017, 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 jdk.incubator.http.internal.common;
+
+import org.testng.annotations.Test;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.CyclicBarrier;
+import java.util.concurrent.atomic.AtomicReference;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertTrue;
+
+public class DemandTest {
+
+    @Test
+    public void test01() {
+        assertTrue(new Demand().isFulfilled());
+    }
+
+    @Test
+    public void test011() {
+        Demand d = new Demand();
+        d.increase(3);
+        d.decreaseAndGet(3);
+        assertTrue(d.isFulfilled());
+    }
+
+    @Test
+    public void test02() {
+        Demand d = new Demand();
+        d.increase(1);
+        assertFalse(d.isFulfilled());
+    }
+
+    @Test
+    public void test03() {
+        Demand d = new Demand();
+        d.increase(3);
+        assertEquals(d.decreaseAndGet(3), 3);
+    }
+
+    @Test
+    public void test04() {
+        Demand d = new Demand();
+        d.increase(3);
+        assertEquals(d.decreaseAndGet(5), 3);
+    }
+
+    @Test
+    public void test05() {
+        Demand d = new Demand();
+        d.increase(7);
+        assertEquals(d.decreaseAndGet(4), 4);
+    }
+
+    @Test
+    public void test06() {
+        Demand d = new Demand();
+        assertEquals(d.decreaseAndGet(3), 0);
+    }
+
+    @Test(expectedExceptions = IllegalArgumentException.class)
+    public void test07() {
+        Demand d = new Demand();
+        d.increase(0);
+    }
+
+    @Test(expectedExceptions = IllegalArgumentException.class)
+    public void test08() {
+        Demand d = new Demand();
+        d.increase(-1);
+    }
+
+    @Test(expectedExceptions = IllegalArgumentException.class)
+    public void test09() {
+        Demand d = new Demand();
+        d.increase(10);
+        d.decreaseAndGet(0);
+    }
+
+    @Test(expectedExceptions = IllegalArgumentException.class)
+    public void test10() {
+        Demand d = new Demand();
+        d.increase(13);
+        d.decreaseAndGet(-3);
+    }
+
+    @Test
+    public void test11() {
+        Demand d = new Demand();
+        d.increase(1);
+        assertTrue(d.tryDecrement());
+    }
+
+    @Test
+    public void test12() {
+        Demand d = new Demand();
+        d.increase(2);
+        assertTrue(d.tryDecrement());
+    }
+
+    @Test
+    public void test14() {
+        Demand d = new Demand();
+        assertFalse(d.tryDecrement());
+    }
+
+    @Test
+    public void test141() {
+        Demand d = new Demand();
+        d.increase(Long.MAX_VALUE);
+        assertFalse(d.isFulfilled());
+    }
+
+    @Test
+    public void test142() {
+        Demand d = new Demand();
+        d.increase(Long.MAX_VALUE);
+        d.increase(1);
+        assertFalse(d.isFulfilled());
+    }
+
+    @Test
+    public void test143() {
+        Demand d = new Demand();
+        d.increase(Long.MAX_VALUE);
+        d.increase(1);
+        assertFalse(d.isFulfilled());
+    }
+
+    @Test
+    public void test144() {
+        Demand d = new Demand();
+        d.increase(Long.MAX_VALUE);
+        d.increase(Long.MAX_VALUE);
+        d.decreaseAndGet(3);
+        d.decreaseAndGet(5);
+        assertFalse(d.isFulfilled());
+    }
+
+    @Test
+    public void test145() {
+        Demand d = new Demand();
+        d.increase(Long.MAX_VALUE);
+        d.decreaseAndGet(Long.MAX_VALUE);
+        assertTrue(d.isFulfilled());
+    }
+
+    @Test(invocationCount = 32)
+    public void test15() throws InterruptedException {
+        int N = Math.max(2, Runtime.getRuntime().availableProcessors() + 1);
+        int M = ((N + 1) * N) / 2; // 1 + 2 + 3 + ... N
+        Demand d = new Demand();
+        d.increase(M);
+        CyclicBarrier start = new CyclicBarrier(N);
+        CountDownLatch stop = new CountDownLatch(N);
+        AtomicReference<Throwable> error = new AtomicReference<>();
+        for (int i = 0; i < N; i++) {
+            int j = i + 1;
+            new Thread(() -> {
+                try {
+                    start.await();
+                } catch (Exception e) {
+                    error.compareAndSet(null, e);
+                }
+                try {
+                    assertEquals(d.decreaseAndGet(j), j);
+                } catch (Throwable t) {
+                    error.compareAndSet(null, t);
+                } finally {
+                    stop.countDown();
+                }
+            }).start();
+        }
+        stop.await();
+        assertTrue(d.isFulfilled());
+        assertEquals(error.get(), null);
+    }
+}
diff --git a/test/jdk/java/nio/file/Files/SetLastModifiedTime.java b/test/jdk/java/nio/file/Files/SetLastModifiedTime.java
index ef8cfaa0cec..4377242f0a0 100644
--- a/test/jdk/java/nio/file/Files/SetLastModifiedTime.java
+++ b/test/jdk/java/nio/file/Files/SetLastModifiedTime.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2014, 2017 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
@@ -21,6 +21,7 @@
  * questions.
  */
 
+import java.io.File;
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.nio.file.Paths;
@@ -36,7 +37,7 @@ import static org.testng.Assert.assertFalse;
 
 /**
  * @test
- * @bug 4313887 8062949
+ * @bug 4313887 8062949 8191872
  * @library ..
  * @run testng SetLastModifiedTime
  * @summary Unit test for Files.setLastModifiedTime
@@ -114,5 +115,20 @@ public class SetLastModifiedTime {
             assertTrue(false);
         } catch (NullPointerException expected) { }
     }
+
+    @Test
+    public void testCompare() throws Exception {
+        Path path = Files.createFile(testDir.resolve("path"));
+        long timeMillis = 1512520600195L;
+        FileTime fileTime = FileTime.fromMillis(timeMillis);
+        Files.setLastModifiedTime(path, fileTime);
+        File file = path.toFile();
+        long ioTime = file.lastModified();
+        long nioTime = Files.getLastModifiedTime(path).toMillis();
+        assertTrue(ioTime == timeMillis || ioTime == 1000*(timeMillis/1000),
+            "File.lastModified() not in {time, 1000*(time/1000)}");
+        assertEquals(nioTime, ioTime,
+            "File.lastModified() != Files.getLastModifiedTime().toMillis()");
+    }
 }
 
diff --git a/test/jdk/java/text/Format/MessageFormat/MessageRegression.java b/test/jdk/java/text/Format/MessageFormat/MessageRegression.java
index 1d71cc09892..f013ba00607 100644
--- a/test/jdk/java/text/Format/MessageFormat/MessageRegression.java
+++ b/test/jdk/java/text/Format/MessageFormat/MessageRegression.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2017, 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
@@ -25,7 +25,7 @@
  * @test
  * @bug 4031438 4058973 4074764 4094906 4104976 4105380 4106659 4106660 4106661
  * 4111739 4112104 4113018 4114739 4114743 4116444 4118592 4118594 4120552
- * 4142938 4169959 4232154 4293229
+ * 4142938 4169959 4232154 4293229 8187551
  * @summary Regression tests for MessageFormat and associated classes
  * @library /java/text/testlib
  * @run main MessageRegression
@@ -642,4 +642,44 @@ public class MessageRegression extends IntlTest {
                     expected + "\", got \"" + result + "\"");
         }
     }
+
+    /**
+     * @bug 8187551
+     * test MessageFormat.setFormat() method to throw AIOOBE on invalid index.
+     */
+    public void test8187551() {
+        //invalid cases ("pattern", "invalid format element index")
+        String[][] invalidCases = {{"The disk \"{1}\" contains {0}.", "2"},
+                {"The disk \"{1}\" contains {0}.", "9"},
+                {"On {1}, there are {0} and {2} folders", "3"}};
+
+        //invalid cases (must throw exception)
+        Arrays.stream(invalidCases).forEach(entry -> messageSetFormat(entry[0],
+                Integer.valueOf(entry[1])));
+    }
+
+    // test MessageFormat.setFormat() method for the given pattern and
+    // format element index
+    private void messageSetFormat(String pattern, int elemIndex) {
+        MessageFormat form = new MessageFormat(pattern);
+
+        double[] fileLimits = {0, 1, 2};
+        String[] filePart = {"no files", "one file", "{0,number} files"};
+        ChoiceFormat fileForm = new ChoiceFormat(fileLimits, filePart);
+
+        boolean AIOOBEThrown = false;
+        try {
+            form.setFormat(elemIndex, fileForm);
+        } catch (ArrayIndexOutOfBoundsException ex) {
+            AIOOBEThrown = true;
+        }
+
+        if (!AIOOBEThrown) {
+            throw new RuntimeException("[FAILED: Must throw" +
+                    " ArrayIndexOutOfBoundsException for" +
+                    " invalid index " + elemIndex + " used in" +
+                    " MessageFormat.setFormat(index, format)]");
+        }
+    }
+
 }
diff --git a/test/jdk/java/time/tck/TEST.properties b/test/jdk/java/time/tck/TEST.properties
index 2374566eefb..a9a165cdb35 100644
--- a/test/jdk/java/time/tck/TEST.properties
+++ b/test/jdk/java/time/tck/TEST.properties
@@ -1,5 +1,5 @@
 # java.time tests use TestNG
 TestNG.dirs = ..
 othervm.dirs = java/time/chrono
-lib.dirs = ../../../lib/testlibrary
-lib.build = jdk.testlibrary.RandomFactory
+lib.dirs = /test/lib
+lib.build = jdk.test.lib.RandomFactory
diff --git a/test/jdk/java/time/test/TEST.properties b/test/jdk/java/time/test/TEST.properties
index 0a36c095408..98f1a0011ac 100644
--- a/test/jdk/java/time/test/TEST.properties
+++ b/test/jdk/java/time/test/TEST.properties
@@ -1,5 +1,5 @@
 # java.time tests use TestNG
 TestNG.dirs = ..
 othervm.dirs = java/time/chrono java/time/format
-lib.dirs = ../../../lib/testlibrary
-lib.build = jdk.testlibrary.RandomFactory
+lib.dirs = /test/lib
+lib.build = jdk.test.lib.RandomFactory
diff --git a/test/jdk/java/time/test/java/time/format/TestZoneTextPrinterParser.java b/test/jdk/java/time/test/java/time/format/TestZoneTextPrinterParser.java
index 905f77da58a..9739ad6bdbe 100644
--- a/test/jdk/java/time/test/java/time/format/TestZoneTextPrinterParser.java
+++ b/test/jdk/java/time/test/java/time/format/TestZoneTextPrinterParser.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2017, 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
@@ -42,7 +42,7 @@ import java.util.Locale;
 import java.util.Random;
 import java.util.Set;
 import java.util.TimeZone;
-import jdk.testlibrary.RandomFactory;
+import jdk.test.lib.RandomFactory;
 
 import org.testng.annotations.DataProvider;
 import org.testng.annotations.Test;
diff --git a/test/jdk/java/util/Collection/MOAT.java b/test/jdk/java/util/Collection/MOAT.java
index b250759939f..8693f7483ed 100644
--- a/test/jdk/java/util/Collection/MOAT.java
+++ b/test/jdk/java/util/Collection/MOAT.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2005, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2005, 2017, 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
@@ -57,6 +57,8 @@ import java.util.*;
 import java.util.concurrent.*;
 import static java.util.Collections.*;
 import java.lang.reflect.*;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
 
 public class MOAT {
     // Collections under test must not be initialized to contain this value,
@@ -230,6 +232,17 @@ public class MOAT {
             testListMutatorsAlwaysThrow(list);
         }
 
+        List<Integer> listCopy = List.copyOf(Arrays.asList(1, 2, 3));
+        testCollection(listCopy);
+        testImmutableList(listCopy);
+        testListMutatorsAlwaysThrow(listCopy);
+
+        List<Integer> listCollected = Stream.of(1, 2, 3).collect(Collectors.toUnmodifiableList());
+        equal(listCollected, List.of(1, 2, 3));
+        testCollection(listCollected);
+        testImmutableList(listCollected);
+        testListMutatorsAlwaysThrow(listCollected);
+
         // Immutable Set
         testEmptySet(Set.of());
         testCollMutatorsAlwaysThrow(Set.of());
@@ -252,6 +265,18 @@ public class MOAT {
             testCollMutatorsAlwaysThrow(set);
         }
 
+        Set<Integer> setCopy = Set.copyOf(Arrays.asList(1, 2, 3));
+        testCollection(setCopy);
+        testImmutableSet(setCopy);
+        testCollMutatorsAlwaysThrow(setCopy);
+
+        Set<Integer> setCollected = Stream.of(1, 1, 2, 3, 2, 3)
+                                          .collect(Collectors.toUnmodifiableSet());
+        equal(setCollected, Set.of(1, 2, 3));
+        testCollection(setCollected);
+        testImmutableSet(setCollected);
+        testCollMutatorsAlwaysThrow(setCollected);
+
         // Immutable Map
 
         @SuppressWarnings("unchecked")
@@ -280,6 +305,35 @@ public class MOAT {
             testImmutableMap(map);
             testMapMutatorsAlwaysThrow(map);
         }
+
+        Map<Integer,Integer> mapCopy = Map.copyOf(new HashMap<>(Map.of(1, 101, 2, 202, 3, 303)));
+        testMap(mapCopy);
+        testImmutableMap(mapCopy);
+        testMapMutatorsAlwaysThrow(mapCopy);
+
+        Map<Integer,Integer> mapCollected1 =
+            Stream.of(1, 2, 3)
+                  .collect(Collectors.toUnmodifiableMap(i -> i, i -> 101 * i));
+        equal(mapCollected1, Map.of(1, 101, 2, 202, 3, 303));
+        testMap(mapCollected1);
+        testImmutableMap(mapCollected1);
+        testMapMutatorsAlwaysThrow(mapCollected1);
+
+        try {
+            Stream.of(1, 1, 2, 3, 2, 3)
+                  .collect(Collectors.toUnmodifiableMap(i -> i, i -> 101 * i));
+            fail("duplicates should have thrown an exception");
+        } catch (IllegalStateException ise) {
+            pass();
+        }
+
+        Map<Integer,Integer> mapCollected2 =
+            Stream.of(1, 1, 2, 3, 2, 3)
+                  .collect(Collectors.toUnmodifiableMap(i -> i, i -> 101 * i, Integer::sum));
+        equal(mapCollected2, Map.of(1, 202, 2, 404, 3, 606));
+        testMap(mapCollected2);
+        testImmutableMap(mapCollected2);
+        testMapMutatorsAlwaysThrow(mapCollected2);
     }
 
     private static void checkContainsSelf(Collection<Integer> c) {
diff --git a/test/jdk/java/util/Collection/SetFactories.java b/test/jdk/java/util/Collection/SetFactories.java
index 25a884501e0..80a57bc6adf 100644
--- a/test/jdk/java/util/Collection/SetFactories.java
+++ b/test/jdk/java/util/Collection/SetFactories.java
@@ -40,6 +40,9 @@ import org.testng.annotations.DataProvider;
 import org.testng.annotations.Test;
 
 import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertNotEquals;
+import static org.testng.Assert.assertNotSame;
+import static org.testng.Assert.assertSame;
 import static org.testng.Assert.assertTrue;
 import static org.testng.Assert.fail;
 
@@ -275,9 +278,9 @@ public class SetFactories {
     static <T> T serialClone(T obj) {
         try {
             ByteArrayOutputStream baos = new ByteArrayOutputStream();
-            ObjectOutputStream oos = new ObjectOutputStream(baos);
-            oos.writeObject(obj);
-            oos.close();
+            try (ObjectOutputStream oos = new ObjectOutputStream(baos)) {
+                oos.writeObject(obj);
+            }
             ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
             ObjectInputStream ois = new ObjectInputStream(bais);
             return (T) ois.readObject();
@@ -285,4 +288,53 @@ public class SetFactories {
             throw new AssertionError(e);
         }
     }
+
+    Set<Integer> genSet() {
+        return new HashSet<>(Arrays.asList(1, 2, 3));
+    }
+
+    @Test
+    public void copyOfResultsEqual() {
+        Set<Integer> orig = genSet();
+        Set<Integer> copy = Set.copyOf(orig);
+
+        assertEquals(orig, copy);
+        assertEquals(copy, orig);
+    }
+
+    @Test
+    public void copyOfModifiedUnequal() {
+        Set<Integer> orig = genSet();
+        Set<Integer> copy = Set.copyOf(orig);
+        orig.add(4);
+
+        assertNotEquals(orig, copy);
+        assertNotEquals(copy, orig);
+    }
+
+    @Test
+    public void copyOfIdentity() {
+        Set<Integer> orig = genSet();
+        Set<Integer> copy1 = Set.copyOf(orig);
+        Set<Integer> copy2 = Set.copyOf(copy1);
+
+        assertNotSame(orig, copy1);
+        assertSame(copy1, copy2);
+    }
+
+    @Test(expectedExceptions=NullPointerException.class)
+    public void copyOfRejectsNullCollection() {
+        Set<Integer> set = Set.copyOf(null);
+    }
+
+    @Test(expectedExceptions=NullPointerException.class)
+    public void copyOfRejectsNullElements() {
+        Set<Integer> set = Set.copyOf(Arrays.asList(1, null, 3));
+    }
+
+    @Test
+    public void copyOfAcceptsDuplicates() {
+        Set<Integer> set = Set.copyOf(Arrays.asList(1, 1, 2, 3, 3, 3));
+        assertEquals(set, Set.of(1, 2, 3));
+    }
 }
diff --git a/test/jdk/java/util/EnumSet/BogusEnumSet.java b/test/jdk/java/util/EnumSet/BogusEnumSet.java
index 6ba2840b149..c24bbfbbbee 100644
--- a/test/jdk/java/util/EnumSet/BogusEnumSet.java
+++ b/test/jdk/java/util/EnumSet/BogusEnumSet.java
@@ -34,7 +34,7 @@ public class BogusEnumSet {
     public static void main(String[] args) throws Throwable {
         // This test depends on the current serialVersionUID of EnumSet,
         // which may change if the EnumSet class is modified.
-        // The current value is 4168005130090799668L = 0x39D7BA9531116234L
+        // The current value is -2409567991088730183L = 0xde8f7eadb5012fb9L
         // If the value changes, it will have to be patched into the
         // serialized byte stream below at the location noted.
         byte[] serializedForm  = {
@@ -47,7 +47,7 @@ public class BogusEnumSet {
             0x11, 0x6a,  0x61,  0x76,  0x61, 0x2e,  0x75,  0x74,  0x69,
             0x6c,  0x2e, 0x45, 0x6e, 0x75, 0x6d, 0x53, 0x65, 0x74,
             // EnumSet's serialVersionUID is the following eight bytes (big-endian)
-            0x39, (byte)0xd7, (byte)0xba, (byte)0x95, 0x31, 0x11, 0x62, 0x34,
+            (byte)0xde, (byte)0x8f, 0x7e, (byte)0xad, (byte)0xb5, (byte)0x01, 0x2f, (byte)0xb9,
             0x2, 0x0, 0x2, 0x4c, 0x0, 0xb, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74,
             0x54, 0x79, 0x70, 0x65, 0x74, 0x0, 0x11, 0x4c, 0x6a, 0x61, 0x76,
             0x61, 0x2f, 0x6c, 0x61, 0x6e, 0x67, 0x2f, 0x43, 0x6c, 0x61, 0x73,
diff --git a/test/jdk/java/util/List/ListFactories.java b/test/jdk/java/util/List/ListFactories.java
index e34ca660705..63e3ecd91ac 100644
--- a/test/jdk/java/util/List/ListFactories.java
+++ b/test/jdk/java/util/List/ListFactories.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2017, 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,6 +39,9 @@ import static java.util.Arrays.asList;
 
 import static org.testng.Assert.assertEquals;
 import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertNotEquals;
+import static org.testng.Assert.assertNotSame;
+import static org.testng.Assert.assertSame;
 import static org.testng.Assert.assertTrue;
 import static org.testng.Assert.fail;
 
@@ -221,9 +224,9 @@ public class ListFactories {
     static <T> T serialClone(T obj) {
         try {
             ByteArrayOutputStream baos = new ByteArrayOutputStream();
-            ObjectOutputStream oos = new ObjectOutputStream(baos);
-            oos.writeObject(obj);
-            oos.close();
+            try (ObjectOutputStream oos = new ObjectOutputStream(baos)) {
+                oos.writeObject(obj);
+            }
             ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
             ObjectInputStream ois = new ObjectInputStream(bais);
             return (T) ois.readObject();
@@ -231,4 +234,47 @@ public class ListFactories {
             throw new AssertionError(e);
         }
     }
+
+    List<Integer> genList() {
+        return new ArrayList<>(Arrays.asList(1, 2, 3));
+    }
+
+    @Test
+    public void copyOfResultsEqual() {
+        List<Integer> orig = genList();
+        List<Integer> copy = List.copyOf(orig);
+
+        assertEquals(orig, copy);
+        assertEquals(copy, orig);
+    }
+
+    @Test
+    public void copyOfModifiedUnequal() {
+        List<Integer> orig = genList();
+        List<Integer> copy = List.copyOf(orig);
+        orig.add(4);
+
+        assertNotEquals(orig, copy);
+        assertNotEquals(copy, orig);
+    }
+
+    @Test
+    public void copyOfIdentity() {
+        List<Integer> orig = genList();
+        List<Integer> copy1 = List.copyOf(orig);
+        List<Integer> copy2 = List.copyOf(copy1);
+
+        assertNotSame(orig, copy1);
+        assertSame(copy1, copy2);
+    }
+
+    @Test(expectedExceptions=NullPointerException.class)
+    public void copyOfRejectsNullCollection() {
+        List<Integer> list = List.copyOf(null);
+    }
+
+    @Test(expectedExceptions=NullPointerException.class)
+    public void copyOfRejectsNullElements() {
+        List<Integer> list = List.copyOf(Arrays.asList(1, null, 3));
+    }
 }
diff --git a/test/jdk/java/util/Map/MapFactories.java b/test/jdk/java/util/Map/MapFactories.java
index 7495aa386c3..008a9651250 100644
--- a/test/jdk/java/util/Map/MapFactories.java
+++ b/test/jdk/java/util/Map/MapFactories.java
@@ -42,6 +42,9 @@ import org.testng.annotations.Test;
 
 import static org.testng.Assert.assertEquals;
 import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertNotEquals;
+import static org.testng.Assert.assertNotSame;
+import static org.testng.Assert.assertSame;
 import static org.testng.Assert.assertTrue;
 import static org.testng.Assert.fail;
 
@@ -70,6 +73,12 @@ public class MapFactories {
     }
 
     // for varargs Map.Entry methods
+
+    @SuppressWarnings("unchecked")
+    Map.Entry<Integer,String>[] genEmptyEntryArray1() {
+        return (Map.Entry<Integer,String>[])new Map.Entry<?,?>[1];
+    }
+
     @SuppressWarnings("unchecked")
     Map.Entry<Integer,String>[] genEntries(int n) {
         return IntStream.range(0, n)
@@ -322,21 +331,41 @@ public class MapFactories {
     }
 
     @Test(expectedExceptions=NullPointerException.class)
-    public void nullKeyDisallowedN() {
-        Map.Entry<Integer,String>[] entries = genEntries(MAX_ENTRIES);
-        entries[0] = new AbstractMap.SimpleImmutableEntry(null, "a");
+    public void nullKeyDisallowedVar1() {
+        Map.Entry<Integer,String>[] entries = genEmptyEntryArray1();
+        entries[0] = new AbstractMap.SimpleImmutableEntry<>(null, "a");
         Map<Integer, String> map = Map.ofEntries(entries);
     }
 
     @Test(expectedExceptions=NullPointerException.class)
-    public void nullValueDisallowedN() {
-        Map.Entry<Integer,String>[] entries = genEntries(MAX_ENTRIES);
-        entries[0] = new AbstractMap.SimpleImmutableEntry(0, null);
+    public void nullValueDisallowedVar1() {
+        Map.Entry<Integer,String>[] entries = genEmptyEntryArray1();
+        entries[0] = new AbstractMap.SimpleImmutableEntry<>(0, null);
         Map<Integer, String> map = Map.ofEntries(entries);
     }
 
     @Test(expectedExceptions=NullPointerException.class)
-    public void nullEntryDisallowedN() {
+    public void nullEntryDisallowedVar1() {
+        Map.Entry<Integer,String>[] entries = genEmptyEntryArray1();
+        Map<Integer, String> map = Map.ofEntries(entries);
+    }
+
+    @Test(expectedExceptions=NullPointerException.class)
+    public void nullKeyDisallowedVarN() {
+        Map.Entry<Integer,String>[] entries = genEntries(MAX_ENTRIES);
+        entries[0] = new AbstractMap.SimpleImmutableEntry<>(null, "a");
+        Map<Integer, String> map = Map.ofEntries(entries);
+    }
+
+    @Test(expectedExceptions=NullPointerException.class)
+    public void nullValueDisallowedVarN() {
+        Map.Entry<Integer,String>[] entries = genEntries(MAX_ENTRIES);
+        entries[0] = new AbstractMap.SimpleImmutableEntry<>(0, null);
+        Map<Integer, String> map = Map.ofEntries(entries);
+    }
+
+    @Test(expectedExceptions=NullPointerException.class)
+    public void nullEntryDisallowedVarN() {
         Map.Entry<Integer,String>[] entries = genEntries(MAX_ENTRIES);
         entries[5] = null;
         Map<Integer, String> map = Map.ofEntries(entries);
@@ -344,7 +373,7 @@ public class MapFactories {
 
     @Test(expectedExceptions=NullPointerException.class)
     public void nullArrayDisallowed() {
-        Map.ofEntries(null);
+        Map.ofEntries((Map.Entry<?,?>[])null);
     }
 
     @Test(dataProvider="all")
@@ -359,9 +388,9 @@ public class MapFactories {
     static <T> T serialClone(T obj) {
         try {
             ByteArrayOutputStream baos = new ByteArrayOutputStream();
-            ObjectOutputStream oos = new ObjectOutputStream(baos);
-            oos.writeObject(obj);
-            oos.close();
+            try (ObjectOutputStream oos = new ObjectOutputStream(baos)) {
+                oos.writeObject(obj);
+            }
             ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
             ObjectInputStream ois = new ObjectInputStream(bais);
             return (T) ois.readObject();
@@ -370,6 +399,62 @@ public class MapFactories {
         }
     }
 
+    Map<Integer, String> genMap() {
+        Map<Integer, String> map = new HashMap<>();
+        map.put(1, "a");
+        map.put(2, "b");
+        map.put(3, "c");
+        return map;
+    }
+
+    @Test
+    public void copyOfResultsEqual() {
+        Map<Integer, String> orig = genMap();
+        Map<Integer, String> copy = Map.copyOf(orig);
+
+        assertEquals(orig, copy);
+        assertEquals(copy, orig);
+    }
+
+    @Test
+    public void copyOfModifiedUnequal() {
+        Map<Integer, String> orig = genMap();
+        Map<Integer, String> copy = Map.copyOf(orig);
+        orig.put(4, "d");
+
+        assertNotEquals(orig, copy);
+        assertNotEquals(copy, orig);
+    }
+
+    @Test
+    public void copyOfIdentity() {
+        Map<Integer, String> orig = genMap();
+        Map<Integer, String> copy1 = Map.copyOf(orig);
+        Map<Integer, String> copy2 = Map.copyOf(copy1);
+
+        assertNotSame(orig, copy1);
+        assertSame(copy1, copy2);
+    }
+
+    @Test(expectedExceptions=NullPointerException.class)
+    public void copyOfRejectsNullMap() {
+        Map<Integer, String> map = Map.copyOf(null);
+    }
+
+    @Test(expectedExceptions=NullPointerException.class)
+    public void copyOfRejectsNullKey() {
+        Map<Integer, String> map = genMap();
+        map.put(null, "x");
+        Map<Integer, String> copy = Map.copyOf(map);
+    }
+
+    @Test(expectedExceptions=NullPointerException.class)
+    public void copyOfRejectsNullValue() {
+        Map<Integer, String> map = genMap();
+        map.put(-1, null);
+        Map<Integer, String> copy = Map.copyOf(map);
+    }
+
     // Map.entry() tests
 
     @Test(expectedExceptions=NullPointerException.class)
@@ -386,7 +471,7 @@ public class MapFactories {
     public void entryBasicTests() {
         Map.Entry<String,String> kvh1 = Map.entry("xyzzy", "plugh");
         Map.Entry<String,String> kvh2 = Map.entry("foobar", "blurfl");
-        Map.Entry<String,String> sie = new AbstractMap.SimpleImmutableEntry("xyzzy", "plugh");
+        Map.Entry<String,String> sie = new AbstractMap.SimpleImmutableEntry<>("xyzzy", "plugh");
 
         assertTrue(kvh1.equals(sie));
         assertTrue(sie.equals(kvh1));
@@ -404,5 +489,4 @@ public class MapFactories {
         Map<Number,Number> map = Map.ofEntries(e1, e2);
         assertEquals(map.size(), 2);
     }
-
 }
diff --git a/test/jdk/java/util/concurrent/tck/ExecutorCompletionServiceTest.java b/test/jdk/java/util/concurrent/tck/ExecutorCompletionServiceTest.java
index 9b7251e6865..2a936940c96 100644
--- a/test/jdk/java/util/concurrent/tck/ExecutorCompletionServiceTest.java
+++ b/test/jdk/java/util/concurrent/tck/ExecutorCompletionServiceTest.java
@@ -105,8 +105,7 @@ public class ExecutorCompletionServiceTest extends JSR166TestCase {
     /**
      * A taken submitted task is completed
      */
-    public void testTake()
-        throws InterruptedException, ExecutionException {
+    public void testTake() throws Exception {
         CompletionService cs = new ExecutorCompletionService(cachedThreadPool);
         cs.submit(new StringTask());
         Future f = cs.take();
@@ -127,8 +126,7 @@ public class ExecutorCompletionServiceTest extends JSR166TestCase {
     /**
      * poll returns non-null when the returned task is completed
      */
-    public void testPoll1()
-        throws InterruptedException, ExecutionException {
+    public void testPoll1() throws Exception {
         CompletionService cs = new ExecutorCompletionService(cachedThreadPool);
         assertNull(cs.poll());
         cs.submit(new StringTask());
@@ -147,15 +145,15 @@ public class ExecutorCompletionServiceTest extends JSR166TestCase {
     /**
      * timed poll returns non-null when the returned task is completed
      */
-    public void testPoll2()
-        throws InterruptedException, ExecutionException {
+    public void testPoll2() throws Exception {
         CompletionService cs = new ExecutorCompletionService(cachedThreadPool);
         assertNull(cs.poll());
         cs.submit(new StringTask());
 
         long startTime = System.nanoTime();
         Future f;
-        while ((f = cs.poll(SHORT_DELAY_MS, MILLISECONDS)) == null) {
+        while ((f = cs.poll(timeoutMillis(), MILLISECONDS)) == null) {
+            assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
             if (millisElapsedSince(startTime) > LONG_DELAY_MS)
                 fail("timed out");
             Thread.yield();
@@ -167,8 +165,7 @@ public class ExecutorCompletionServiceTest extends JSR166TestCase {
     /**
      * poll returns null before the returned task is completed
      */
-    public void testPollReturnsNull()
-        throws InterruptedException, ExecutionException {
+    public void testPollReturnsNullBeforeCompletion() throws Exception {
         CompletionService cs = new ExecutorCompletionService(cachedThreadPool);
         final CountDownLatch proceed = new CountDownLatch(1);
         cs.submit(new Callable() { public String call() throws Exception {
@@ -188,29 +185,28 @@ public class ExecutorCompletionServiceTest extends JSR166TestCase {
     /**
      * successful and failed tasks are both returned
      */
-    public void testTaskAssortment()
-        throws InterruptedException, ExecutionException {
+    public void testTaskAssortment() throws Exception {
         CompletionService cs = new ExecutorCompletionService(cachedThreadPool);
         ArithmeticException ex = new ArithmeticException();
-        for (int i = 0; i < 2; i++) {
+        final int rounds = 2;
+        for (int i = rounds; i--> 0; ) {
             cs.submit(new StringTask());
             cs.submit(callableThrowing(ex));
             cs.submit(runnableThrowing(ex), null);
         }
         int normalCompletions = 0;
         int exceptionalCompletions = 0;
-        for (int i = 0; i < 3 * 2; i++) {
+        for (int i = 3 * rounds; i--> 0; ) {
             try {
-                if (cs.take().get() == TEST_STRING)
-                    normalCompletions++;
-            }
-            catch (ExecutionException expected) {
-                assertTrue(expected.getCause() instanceof ArithmeticException);
+                assertSame(TEST_STRING, cs.take().get());
+                normalCompletions++;
+            } catch (ExecutionException expected) {
+                assertSame(ex, expected.getCause());
                 exceptionalCompletions++;
             }
         }
-        assertEquals(2 * 1, normalCompletions);
-        assertEquals(2 * 2, exceptionalCompletions);
+        assertEquals(1 * rounds, normalCompletions);
+        assertEquals(2 * rounds, exceptionalCompletions);
         assertNull(cs.poll());
     }
 
diff --git a/test/jdk/java/util/logging/Level/CustomLevel.java b/test/jdk/java/util/logging/Level/CustomLevel.java
index 8045831d33c..0b74148a07d 100644
--- a/test/jdk/java/util/logging/Level/CustomLevel.java
+++ b/test/jdk/java/util/logging/Level/CustomLevel.java
@@ -22,6 +22,8 @@
  */
 
 import java.io.*;
+import java.lang.management.ManagementFactory;
+import java.lang.management.PlatformLoggingMXBean;
 import java.lang.ref.Reference;
 import java.lang.ref.ReferenceQueue;
 import java.lang.ref.WeakReference;
@@ -35,9 +37,11 @@ import java.util.logging.*;
 
 /*
  * @test
- * @bug 8026027 6543126
+ * @bug 8026027 6543126 8187073
+ * @modules java.logging
+  *         java.management
  * @summary Test Level.parse to look up custom levels by name and its
- *          localized name
+ *          localized name, as well as severity.
  *
  * @run main/othervm CustomLevel
  */
@@ -73,6 +77,8 @@ public class CustomLevel extends Level {
     public static void main(String[] args) throws Exception {
         setupCustomLevels();
         setUpCustomLevelsOtherLoader();
+        PlatformLoggingMXBean mxbean = ManagementFactory.getPlatformMXBean(PlatformLoggingMXBean.class);
+        Logger logger = Logger.getLogger("foo.bar");
 
         // Level.parse will return the custom Level instance
         for (Level level : levels) {
@@ -96,8 +102,27 @@ public class CustomLevel extends Level {
                     + l.getClass() + " for " + localizedName
                     + " in " + rb.getBaseBundleName());
             }
+            l = Level.parse(String.valueOf(level.intValue()));
+            System.out.println("Level.parse(" + level.intValue() + ") returns " + l);
+            if (l != level) {
+                if (l == null || l.intValue() != level.intValue()) {
+                    throw new RuntimeException("Unexpected level " + l
+                            + (l == null ? "" : (" " + l.getClass()))
+                            + " for " + level.intValue());
+                }
+            }
+            mxbean.setLoggerLevel(logger.getName(), String.valueOf(level.intValue()));
+            Level l2 = logger.getLevel();
+            if (l2 != level) {
+                if (l2 == null || l2.intValue() != level.intValue()) {
+                    throw new RuntimeException("Unexpected level " + l2
+                            + (l2 == null ? "" : (" " + l2.getClass()))
+                            + " for " + level.intValue());
+                }
+            }
         }
 
+
         final long otherLevelCount = levels.stream()
             .filter(CustomLevel::isCustomLoader)
             .count();
diff --git a/test/jdk/java/util/logging/LogManager/Configuration/rootLoggerHandlers/RootLoggerHandlers.java b/test/jdk/java/util/logging/LogManager/Configuration/rootLoggerHandlers/RootLoggerHandlers.java
new file mode 100644
index 00000000000..8b7b841f43d
--- /dev/null
+++ b/test/jdk/java/util/logging/LogManager/Configuration/rootLoggerHandlers/RootLoggerHandlers.java
@@ -0,0 +1,180 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.logging.Handler;
+import java.util.logging.Level;
+import java.util.logging.LogManager;
+import java.util.logging.Logger;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+/**
+ * @test
+ * @bug 8191033
+ * @build custom.DotHandler custom.Handler
+ * @run main/othervm RootLoggerHandlers
+ * @author danielfuchs
+ */
+public class RootLoggerHandlers {
+
+    public static final Path SRC_DIR =
+            Paths.get(System.getProperty("test.src", "src"));
+    public static final Path USER_DIR =
+            Paths.get(System.getProperty("user.dir", "."));
+    public static final Path CONFIG_FILE = Paths.get("logging.properties");
+
+    // Uncomment this to run the test on Java 8. Java 8 does not have
+    // List.of(...)
+    //    static final class List {
+    //        static <T> java.util.List<T> of(T... items) {
+    //            return Collections.unmodifiableList(Arrays.asList(items));
+    //        }
+    //    }
+
+    public static void main(String[] args) throws IOException {
+        Path initialProps = SRC_DIR.resolve(CONFIG_FILE);
+        Path loggingProps = USER_DIR.resolve(CONFIG_FILE);
+        System.setProperty("java.util.logging.config.file", loggingProps.toString());
+        Files.copy(initialProps, loggingProps);
+        System.out.println("Root level is: " + Logger.getLogger("").getLevel());
+        if (Logger.getLogger("").getLevel() != Level.INFO) {
+            throw new RuntimeException("Expected root level INFO, got: "
+                                        + Logger.getLogger("").getLevel());
+        }
+        // Verify that we have two handlers. One was configured with
+        // handlers=custom.Handler, the other with
+        // .handlers=custom.DotHandler
+        // Verify that exactly one of the two handlers is a custom.Handler
+        // Verify that exactly one of the two handlers is a custom.DotHandler
+        // Verify that the two handlers has an id of '1'
+        checkHandlers(Logger.getLogger("").getHandlers(),
+                1L,
+                custom.Handler.class,
+                custom.DotHandler.class);
+
+        // The log message "hi" should appear twice on the console.
+        // We don't check that. This is just for log analysis in case
+        // of test failure.
+        Logger.getAnonymousLogger().info("hi");
+
+        // Change the root logger level to FINE in the properties file
+        // and reload the configuration.
+        Files.write(loggingProps,
+                Files.lines(initialProps)
+                        .map((s) -> s.replace("INFO", "FINE"))
+                        .collect(Collectors.toList()));
+        LogManager.getLogManager().readConfiguration();
+
+        System.out.println("Root level is: " + Logger.getLogger("").getLevel());
+        if (Logger.getLogger("").getLevel() != Level.FINE) {
+            throw new RuntimeException("Expected root level FINE, got: "
+                    + Logger.getLogger("").getLevel());
+        }
+
+        // Verify that we have now only one handler, configured with
+        // handlers=custom.Handler, and that the other configured with
+        // .handlers=custom.DotHandler was ignored.
+        // Verify that the handler is a custom.Handler
+        // Verify that the handler has an id of '2'
+        checkHandlers(Logger.getLogger("").getHandlers(),
+                2L,
+                custom.Handler.class);
+
+        // The log message "there" should appear only once on the console.
+        // We don't check that. This is just for log analysis in case
+        // of test failure.
+        Logger.getAnonymousLogger().info("there!");
+
+        // Change the root logger level to FINER in the properties file
+        // and reload the configuration.
+        Files.write(loggingProps,
+                Files.lines(initialProps)
+                        .map((s) -> s.replace("INFO", "FINER"))
+                        .collect(Collectors.toList()));
+        LogManager.getLogManager().readConfiguration();
+
+        System.out.println("Root level is: " + Logger.getLogger("").getLevel());
+        if (Logger.getLogger("").getLevel() != Level.FINER) {
+            throw new RuntimeException("Expected root level FINER, got: "
+                    + Logger.getLogger("").getLevel());
+        }
+
+        // Verify that we have only one handler, configured with
+        // handlers=custom.Handler, and that the other configured with
+        // .handlers=custom.DotHandler was ignored.
+        // Verify that the handler is a custom.Handler
+        // Verify that the handler has an id of '3'
+        checkHandlers(Logger.getLogger("").getHandlers(),
+                3L,
+                custom.Handler.class);
+
+        // The log message "done" should appear only once on the console.
+        // We don't check that. This is just for log analysis in case
+        // of test failure.
+        Logger.getAnonymousLogger().info("done!");
+    }
+
+    static void checkHandlers(Handler[] handlers, Long expectedID, Class<?>... clz) {
+        // Verify that we have the expected number of handlers.
+        if (Stream.of(handlers).count() != clz.length) {
+            throw new RuntimeException("Expected " + clz.length + " handlers, got: "
+                    + List.of(Logger.getLogger("").getHandlers()));
+        }
+        for (Class<?> cl : clz) {
+            // Verify that the handlers are of the expected class.
+            // For each class, we should have exactly one handler
+            // of that class.
+            if (Stream.of(handlers)
+                    .map(Object::getClass)
+                    .filter(cl::equals)
+                    .count() != 1) {
+                throw new RuntimeException("Expected one " + cl +", got: "
+                        + List.of(Logger.getLogger("").getHandlers()));
+            }
+        }
+        // Verify that all handlers have the expected ID
+        if (Stream.of(Logger.getLogger("").getHandlers())
+                .map(RootLoggerHandlers::getId)
+                .filter(expectedID::equals)
+                .count() != clz.length) {
+            throw new RuntimeException("Expected ids to be " + expectedID + ", got: "
+                    + List.of(Logger.getLogger("").getHandlers()));
+        }
+    }
+
+    static long getId(Handler h) {
+        if (h instanceof custom.Handler) {
+            return ((custom.Handler)h).id;
+        }
+        if (h instanceof custom.DotHandler) {
+            return ((custom.DotHandler)h).id;
+        }
+        return -1;
+    }
+}
diff --git a/test/jdk/java/util/logging/LogManager/Configuration/rootLoggerHandlers/custom/DotHandler.java b/test/jdk/java/util/logging/LogManager/Configuration/rootLoggerHandlers/custom/DotHandler.java
new file mode 100644
index 00000000000..21734f190c4
--- /dev/null
+++ b/test/jdk/java/util/logging/LogManager/Configuration/rootLoggerHandlers/custom/DotHandler.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2017, 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 custom;
+
+import java.util.concurrent.atomic.AtomicLong;
+
+/**
+ *
+ * @author danielfuchs
+ */
+public class DotHandler extends java.util.logging.ConsoleHandler {
+
+    public static final AtomicLong IDS = new AtomicLong();
+    public final long id = IDS.incrementAndGet();
+    public DotHandler() {
+        System.out.println("DotHandler(" + id + ") created");
+        //new Exception("DotHandler").printStackTrace();
+    }
+
+    @Override
+    public void close() {
+        System.out.println("DotHandler(" + id + ") closed");
+        super.close();
+    }
+
+    @Override
+    public String toString() {
+        return this.getClass().getName() + '(' + id + ')';
+    }
+}
diff --git a/test/jdk/java/util/logging/LogManager/Configuration/rootLoggerHandlers/custom/Handler.java b/test/jdk/java/util/logging/LogManager/Configuration/rootLoggerHandlers/custom/Handler.java
new file mode 100644
index 00000000000..5f845cfb093
--- /dev/null
+++ b/test/jdk/java/util/logging/LogManager/Configuration/rootLoggerHandlers/custom/Handler.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2017, 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 custom;
+
+import java.util.concurrent.atomic.AtomicLong;
+
+/**
+ *
+ * @author danielfuchs
+ */
+public class Handler extends java.util.logging.ConsoleHandler {
+
+    static final AtomicLong IDS = new AtomicLong();
+    public final long id = IDS.incrementAndGet();
+    public Handler() {
+        System.out.println("Handler(" + id + ") created");
+    }
+
+    @Override
+    public void close() {
+        System.out.println("Handler(" + id + ") closed");
+        super.close();
+    }
+
+    @Override
+    public String toString() {
+        return this.getClass().getName() + '(' + id + ')';
+    }
+}
diff --git a/test/jdk/java/util/logging/LogManager/Configuration/rootLoggerHandlers/logging.properties b/test/jdk/java/util/logging/LogManager/Configuration/rootLoggerHandlers/logging.properties
new file mode 100644
index 00000000000..a432c19a810
--- /dev/null
+++ b/test/jdk/java/util/logging/LogManager/Configuration/rootLoggerHandlers/logging.properties
@@ -0,0 +1,18 @@
+############################################################
+#  	Global properties
+############################################################
+
+# "handlers" specifies a comma separated list of log Handler
+# classes.  These handlers will be installed during VM startup.
+#handlers= java.util.logging.ConsoleHandler
+handlers= custom.Handler
+.handlers= custom.DotHandler
+
+# Default global logging level.
+.level= INFO
+
+# Other configuration
+custom.Handler.level=ALL
+custom.DotHandler.level=ALL
+java.util.logging.SimpleFormatter.format=%4$s [%1$tc]: %2$s: %5$s%n
+
diff --git a/test/jdk/javax/management/security/HashedPasswordFileTest.java b/test/jdk/javax/management/security/HashedPasswordFileTest.java
new file mode 100644
index 00000000000..4ae4981c2e5
--- /dev/null
+++ b/test/jdk/javax/management/security/HashedPasswordFileTest.java
@@ -0,0 +1,511 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+ /* @test
+ * @bug 5016517
+ * @summary Test Hashed passwords
+ * @library /test/lib
+ * @modules java.management
+ * @build HashedPasswordFileTest
+ * @run testng/othervm  HashedPasswordFileTest
+ *
+ */
+
+import jdk.test.lib.Utils;
+import jdk.test.lib.process.ProcessTools;
+import org.testng.Assert;
+import org.testng.annotations.AfterClass;
+import org.testng.annotations.Test;
+
+import javax.management.MBeanServer;
+import javax.management.remote.*;
+import java.io.*;
+import java.lang.management.ManagementFactory;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.FileSystems;
+import java.nio.file.Files;
+import java.nio.file.attribute.PosixFilePermission;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.*;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.*;
+
+@Test
+public class HashedPasswordFileTest {
+
+    private final String[] randomWords = {"accost", "savoie", "bogart", "merest",
+            "azuela", "hoodie", "bursal", "lingua", "wincey", "trilby", "egesta",
+            "wester", "gilgai", "weinek", "ochone", "sanest", "gainst", "defang",
+            "ranket", "mayhem", "tagger", "timber", "eggcup", "mhren", "colloq",
+            "dreamy", "hattie", "rootle", "bloody", "helyne", "beater", "cosine",
+            "enmity", "outbox", "issuer", "lumina", "dekker", "vetoed", "dennis",
+            "strove", "gurnet", "talkie", "bennie", "behove", "coates", "shiloh",
+            "yemeni", "boleyn", "coaxal", "irne"};
+
+    private final String[] hashAlgs = {
+            "MD2",
+            "MD5",
+            "SHA-1",
+            "SHA-224",
+            "SHA-256",
+            "SHA-384",
+            "SHA-512/224",
+            "SHA-512/256",
+            "SHA3-224",
+            "SHA3-256",
+            "SHA3-384",
+            "SHA3-512"
+    };
+
+    private final Random random = Utils.getRandomInstance();
+
+    private JMXConnectorServer cs;
+
+    private String randomWord() {
+        int idx = random.nextInt(randomWords.length);
+        return randomWords[idx];
+    }
+
+    private String[] getHash(String algorithm, String password) {
+        try {
+            byte[] salt = new byte[64];
+            random.nextBytes(salt);
+
+            MessageDigest digest = MessageDigest.getInstance(algorithm);
+            digest.reset();
+            digest.update(salt);
+            byte[] hash = digest.digest(password.getBytes(StandardCharsets.UTF_8));
+
+            String saltStr = Base64.getEncoder().encodeToString(salt);
+            String hashStr = Base64.getEncoder().encodeToString(hash);
+
+            return new String[]{saltStr, hashStr};
+        } catch (NoSuchAlgorithmException ex) {
+            throw new RuntimeException(ex);
+        }
+    }
+
+    private String getPasswordFilePath() {
+        String testDir = System.getProperty("test.src");
+        String testFileName = "jmxremote.password";
+        return testDir + File.separator + testFileName;
+    }
+
+    private File createNewPasswordFile() throws IOException {
+        File file = new File(getPasswordFilePath());
+        if (file.exists()) {
+            file.delete();
+        }
+        file.createNewFile();
+        return file;
+    }
+
+    private Map<String, String> generateClearTextPasswordFile() throws IOException {
+        File file = createNewPasswordFile();
+        Map<String, String> props = new HashMap<>();
+        BufferedWriter br;
+        try (FileWriter fw = new FileWriter(file)) {
+            br = new BufferedWriter(fw);
+            int numentries = random.nextInt(5) + 3;
+            for (int i = 0; i < numentries; i++) {
+                String username;
+                do {
+                    username = randomWord();
+                } while (props.get(username) != null);
+                String password = randomWord();
+                props.put(username, password);
+                br.write(username + " " + password + "\n");
+            }
+            br.flush();
+        }
+        br.close();
+        return props;
+    }
+
+    private boolean isPasswordFileHashed() throws IOException {
+        BufferedReader br;
+        boolean result;
+        try (FileReader fr = new FileReader(getPasswordFilePath())) {
+            br = new BufferedReader(fr);
+            result = br.lines().anyMatch(line -> {
+                if (line.startsWith("#")) {
+                    return false;
+                }
+                String[] tokens = line.split("\\s+");
+                return tokens.length == 3 || tokens.length == 4;
+            });
+        }
+        br.close();
+        return result;
+    }
+
+    private Map<String, String> generateHashedPasswordFile() throws IOException {
+        File file = createNewPasswordFile();
+        Map<String, String> props = new HashMap<>();
+        BufferedWriter br;
+        try (FileWriter fw = new FileWriter(file)) {
+            br = new BufferedWriter(fw);
+            int numentries = random.nextInt(5) + 3;
+            for (int i = 0; i < numentries; i++) {
+                String username;
+                do {
+                    username = randomWord();
+                } while (props.get(username) != null);
+                String password = randomWord();
+                String alg = hashAlgs[random.nextInt(hashAlgs.length)];
+                String[] b64str = getHash(alg, password);
+                br.write(username + " " + b64str[0] + " " + b64str[1] + " " + alg + "\n");
+                props.put(username, password);
+            }
+            br.flush();
+        }
+        br.close();
+        return props;
+    }
+
+    private JMXServiceURL createServerSide(boolean useHash)
+            throws IOException {
+        MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
+        JMXServiceURL url = new JMXServiceURL("rmi", null, 0);
+
+        HashMap<String, Object> env = new HashMap<>();
+        env.put("jmx.remote.x.password.file", getPasswordFilePath());
+        env.put("jmx.remote.x.password.toHashes", useHash ? "true" : "false");
+        cs = JMXConnectorServerFactory.newJMXConnectorServer(url, env, mbs);
+        cs.start();
+        return cs.getAddress();
+    }
+
+    @Test
+    public void testClearTextPasswordFile() throws IOException {
+        Boolean[] bvals = new Boolean[]{true, false};
+        for (boolean bval : bvals) {
+            try {
+                Map<String, String> credentials = generateClearTextPasswordFile();
+                JMXServiceURL serverUrl = createServerSide(bval);
+                for (Map.Entry<String, String> entry : credentials.entrySet()) {
+                    HashMap<String, Object> env = new HashMap<>();
+                    env.put("jmx.remote.credentials",
+                            new String[]{entry.getKey(), entry.getValue()});
+                    try (JMXConnector cc = JMXConnectorFactory.connect(serverUrl, env)) {
+                        cc.getMBeanServerConnection();
+                    }
+                }
+                Assert.assertEquals(isPasswordFileHashed(), bval);
+            } finally {
+                cs.stop();
+            }
+        }
+    }
+
+    @Test
+    public void testReadOnlyPasswordFile() throws IOException {
+        Boolean[] bvals = new Boolean[]{true, false};
+        for (boolean bval : bvals) {
+            try {
+                Map<String, String> credentials = generateClearTextPasswordFile();
+                File file = new File(getPasswordFilePath());
+                file.setReadOnly();
+                JMXServiceURL serverUrl = createServerSide(bval);
+                for (Map.Entry<String, String> entry : credentials.entrySet()) {
+                    HashMap<String, Object> env = new HashMap<>();
+                    env.put("jmx.remote.credentials",
+                            new String[]{entry.getKey(), entry.getValue()});
+                    try (JMXConnector cc = JMXConnectorFactory.connect(serverUrl, env)) {
+                        cc.getMBeanServerConnection();
+                    }
+                }
+                Assert.assertEquals(isPasswordFileHashed(), false);
+            } finally {
+                cs.stop();
+            }
+        }
+    }
+
+    @Test
+    public void testHashedPasswordFile() throws IOException {
+        Boolean[] bvals = new Boolean[]{true, false};
+        for (boolean bval : bvals) {
+            try {
+                Map<String, String> credentials = generateHashedPasswordFile();
+                JMXServiceURL serverUrl = createServerSide(bval);
+                Assert.assertEquals(isPasswordFileHashed(), true);
+                for (Map.Entry<String, String> entry : credentials.entrySet()) {
+                    HashMap<String, Object> env = new HashMap<>();
+                    env.put("jmx.remote.credentials",
+                            new String[]{entry.getKey(), entry.getValue()});
+                    try (JMXConnector cc = JMXConnectorFactory.connect(serverUrl, env)) {
+                        cc.getMBeanServerConnection();
+                    }
+                }
+            } finally {
+                cs.stop();
+            }
+        }
+    }
+
+    private static class SimpleJMXClient implements Callable {
+        private final JMXServiceURL url;
+        private final Map<String, String> credentials;
+
+        public SimpleJMXClient(JMXServiceURL url, Map<String, String> credentials) {
+            this.url = url;
+            this.credentials = credentials;
+        }
+
+        @Override
+        public Object call() throws Exception {
+            for (Map.Entry<String, String> entry : credentials.entrySet()) {
+                HashMap<String, Object> env = new HashMap<>();
+                env.put("jmx.remote.credentials",
+                        new String[]{entry.getKey(), entry.getValue()});
+                try (JMXConnector cc = JMXConnectorFactory.connect(url, env)) {
+                    cc.getMBeanServerConnection();
+                }
+            }
+            return null;
+        }
+    }
+
+    @Test
+    public void testMultipleClients() throws Throwable {
+        Map<String, String> credentials = generateClearTextPasswordFile();
+        JMXServiceURL serverUrl = createServerSide(true);
+        Assert.assertEquals(isPasswordFileHashed(), false);
+        // create random number of clients
+        int numClients = random.nextInt(20) + 10;
+        List<Future> futures = new ArrayList<>();
+        ExecutorService executor = Executors.newFixedThreadPool(numClients);
+        for (int i = 0; i < numClients; i++) {
+            Future future = executor.submit(new SimpleJMXClient(serverUrl, credentials));
+            futures.add(future);
+        }
+        try {
+            for (Future future : futures) {
+                future.get();
+            }
+        } catch (InterruptedException ex) {
+            Thread.currentThread().interrupt();
+        } catch (ExecutionException ex) {
+            throw ex.getCause();
+        } finally {
+            executor.shutdown();
+        }
+
+        Assert.assertEquals(isPasswordFileHashed(), true);
+    }
+
+    @Test
+    public void testPasswordChange() throws IOException {
+        try {
+            Map<String, String> credentials = generateClearTextPasswordFile();
+            JMXServiceURL serverUrl = createServerSide(true);
+            Assert.assertEquals(isPasswordFileHashed(), false);
+
+            for (Map.Entry<String, String> entry : credentials.entrySet()) {
+                HashMap<String, Object> env = new HashMap<>();
+                env.put("jmx.remote.credentials",
+                        new String[]{entry.getKey(), entry.getValue()});
+                try (JMXConnector cc = JMXConnectorFactory.connect(serverUrl, env)) {
+                    cc.getMBeanServerConnection();
+                }
+            }
+            Assert.assertEquals(isPasswordFileHashed(), true);
+
+            // Read the file back. Add new entries. Change passwords for few
+            BufferedReader br = new BufferedReader(new FileReader(getPasswordFilePath()));
+            String line;
+            StringBuilder sbuild = new StringBuilder();
+            while ((line = br.readLine()) != null) {
+                if (line.trim().startsWith("#")) {
+                    sbuild.append(line).append("\n");
+                    continue;
+                }
+
+                // Change password for random entries
+                if (random.nextBoolean()) {
+                    String[] tokens = line.split("\\s+");
+                    if ((tokens.length == 4 || tokens.length == 3)) {
+                        String password = randomWord();
+                        credentials.put(tokens[0], password);
+                        sbuild.append(tokens[0]).append(" ").append(password).append("\n");
+                    }
+                } else {
+                    sbuild.append(line).append("\n");
+                }
+            }
+
+            // Add new entries in clear
+            int newentries = random.nextInt(2) + 3;
+            for (int i = 0; i < newentries; i++) {
+                String username;
+                do {
+                    username = randomWord();
+                } while (credentials.get(username) != null);
+                String password = randomWord();
+                credentials.put(username, password);
+                sbuild.append(username).append(" ").append(password).append("\n");
+            }
+
+            // Add new entries as a hash
+            int numentries = random.nextInt(2) + 3;
+            for (int i = 0; i < numentries; i++) {
+                String username;
+                do {
+                    username = randomWord();
+                } while (credentials.get(username) != null);
+                String password = randomWord();
+                String alg = hashAlgs[random.nextInt(hashAlgs.length)];
+                String[] b64str = getHash(alg, password);
+                credentials.put(username, password);
+                sbuild.append(username).append(" ").append(b64str[0])
+                        .append(" ").append(b64str[1]).append(" ")
+                        .append(alg).append("\n");
+            }
+
+            try (BufferedWriter bw = new BufferedWriter(new FileWriter(getPasswordFilePath()))) {
+                bw.write(sbuild.toString());
+            }
+
+            for (Map.Entry<String, String> entry : credentials.entrySet()) {
+                HashMap<String, Object> env = new HashMap<>();
+                env.put("jmx.remote.credentials",
+                        new String[]{entry.getKey(), entry.getValue()});
+                try (JMXConnector cc = JMXConnectorFactory.connect(serverUrl, env)) {
+                    cc.getMBeanServerConnection();
+                }
+            }
+        } finally {
+            cs.stop();
+        }
+    }
+
+    @Test
+    public void testDefaultAgent() throws Exception {
+        List<String> pbArgs = new ArrayList<>();
+        int port = Utils.getFreePort();
+        generateClearTextPasswordFile();
+
+        // This will run only on a POSIX compliant system
+        if (!FileSystems.getDefault().supportedFileAttributeViews().contains("posix")) {
+            return;
+        }
+
+        // Make sure only owner is able to read/write the file or else
+        // default agent will fail to start
+        File file = new File(getPasswordFilePath());
+        Set<PosixFilePermission> perms = new HashSet<>();
+        perms.add(PosixFilePermission.OWNER_READ);
+        perms.add(PosixFilePermission.OWNER_WRITE);
+        Files.setPosixFilePermissions(file.toPath(), perms);
+
+        pbArgs.add("-cp");
+        pbArgs.add(System.getProperty("test.class.path"));
+
+        pbArgs.add("-Dcom.sun.management.jmxremote.port=" + port);
+        pbArgs.add("-Dcom.sun.management.jmxremote.authenticate=true");
+        pbArgs.add("-Dcom.sun.management.jmxremote.password.file=" + file.getAbsolutePath());
+        pbArgs.add("-Dcom.sun.management.jmxremote.ssl=false");
+        pbArgs.add(TestApp.class.getSimpleName());
+
+        ProcessBuilder pb = ProcessTools.createJavaProcessBuilder(
+                pbArgs.toArray(new String[0]));
+        Process process = ProcessTools.startProcess(
+                TestApp.class.getSimpleName(),
+                pb);
+
+        if (process.waitFor() != 0) {
+            throw new RuntimeException("Test Failed : Error starting default agent");
+        }
+        Assert.assertEquals(isPasswordFileHashed(), true);
+    }
+
+    @Test
+    public void testDefaultAgentNoHash() throws Exception {
+        List<String> pbArgs = new ArrayList<>();
+        int port = Utils.getFreePort();
+        generateClearTextPasswordFile();
+
+        // This will run only on a POSIX compliant system
+        if (!FileSystems.getDefault().supportedFileAttributeViews().contains("posix")) {
+            return;
+        }
+
+        // Make sure only owner is able to read/write the file or else
+        // default agent will fail to start
+        File file = new File(getPasswordFilePath());
+        Set<PosixFilePermission> perms = new HashSet<>();
+        perms.add(PosixFilePermission.OWNER_READ);
+        perms.add(PosixFilePermission.OWNER_WRITE);
+        Files.setPosixFilePermissions(file.toPath(), perms);
+
+        pbArgs.add("-cp");
+        pbArgs.add(System.getProperty("test.class.path"));
+
+        pbArgs.add("-Dcom.sun.management.jmxremote.port=" + port);
+        pbArgs.add("-Dcom.sun.management.jmxremote.authenticate=true");
+        pbArgs.add("-Dcom.sun.management.jmxremote.password.file=" + file.getAbsolutePath());
+        pbArgs.add("-Dcom.sun.management.jmxremote.password.toHashes=false");
+        pbArgs.add("-Dcom.sun.management.jmxremote.ssl=false");
+        pbArgs.add(TestApp.class.getSimpleName());
+
+        ProcessBuilder pb = ProcessTools.createJavaProcessBuilder(
+                pbArgs.toArray(new String[0]));
+        Process process = ProcessTools.startProcess(
+                TestApp.class.getSimpleName(),
+                pb);
+
+        if (process.waitFor() != 0) {
+            throw new RuntimeException("Test Failed : Error starting default agent");
+        }
+        Assert.assertEquals(isPasswordFileHashed(), false);
+    }
+
+    @AfterClass
+    public void cleanUp() {
+        File file = new File(getPasswordFilePath());
+        if (file.exists()) {
+            file.delete();
+        }
+    }
+}
+
+class TestApp {
+
+    public static void main(String[] args) throws IOException {
+        try {
+            JMXServiceURL url = new JMXServiceURL("service:jmx:rmi:///jndi/rmi://localhost:"
+                    + System.getProperty("com.sun.management.jmxremote.port") + "/jmxrmi");
+            Map<String, Object> env = new HashMap<>(1);
+            // any dummy credentials will do. We just have to trigger password hashing
+            env.put("jmx.remote.credentials", new String[]{"a", "a"});
+            try (JMXConnector cc = JMXConnectorFactory.connect(url, env)) {
+                cc.getMBeanServerConnection();
+            }
+        } catch (SecurityException ex) {
+            // Catch authentication failure here
+        }
+    }
+}
diff --git a/test/jdk/javax/net/ssl/DTLS/NoMacInitialClientHello.java b/test/jdk/javax/net/ssl/DTLS/NoMacInitialClientHello.java
index 2a5c74fa684..0f4b6b992d6 100644
--- a/test/jdk/javax/net/ssl/DTLS/NoMacInitialClientHello.java
+++ b/test/jdk/javax/net/ssl/DTLS/NoMacInitialClientHello.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2017, 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,6 +44,7 @@ public class NoMacInitialClientHello extends DTLSOverDatagram {
     boolean needInvalidRecords = true;
 
     public static void main(String[] args) throws Exception {
+        System.setProperty("jdk.tls.useExtendedMasterSecret", "false");
         NoMacInitialClientHello testCase = new NoMacInitialClientHello();
         testCase.runTest(testCase);
     }
diff --git a/test/jdk/javax/swing/DefaultButtonModel/DefaultButtonModelCrashTest.java b/test/jdk/javax/swing/DefaultButtonModel/DefaultButtonModelCrashTest.java
index eb68e59650a..efcf335a54d 100644
--- a/test/jdk/javax/swing/DefaultButtonModel/DefaultButtonModelCrashTest.java
+++ b/test/jdk/javax/swing/DefaultButtonModel/DefaultButtonModelCrashTest.java
@@ -20,12 +20,15 @@
  * or visit www.oracle.com if you need additional information or have any
  * questions.
  */
-/*
+
+/**
  * @test
  * @bug 8182577
  * @summary  Verifies if moving focus via custom ButtonModel causes crash
+ * @key headful
  * @run main DefaultButtonModelCrashTest
  */
+
 import java.awt.BorderLayout;
 import java.awt.Container;
 import java.awt.Point;
@@ -61,7 +64,7 @@ public class DefaultButtonModelCrashTest {
             robot.keyPress(KeyEvent.VK_TAB);
             robot.keyRelease(KeyEvent.VK_TAB);
         } finally {
-            SwingUtilities.invokeAndWait(()->frame  .dispose());
+            if (frame != null) { SwingUtilities.invokeAndWait(()->frame.dispose()); }
         }
     }
 
diff --git a/test/jdk/javax/swing/GraphicsConfigNotifier/TestMultiScreenGConfigNotify.java b/test/jdk/javax/swing/GraphicsConfigNotifier/TestMultiScreenGConfigNotify.java
index adadd7201df..3d1742cfe6d 100644
--- a/test/jdk/javax/swing/GraphicsConfigNotifier/TestMultiScreenGConfigNotify.java
+++ b/test/jdk/javax/swing/GraphicsConfigNotifier/TestMultiScreenGConfigNotify.java
@@ -20,12 +20,14 @@
  * or visit www.oracle.com if you need additional information or have any
  * questions.
  */
-/*
+
+/**
  * @test
  * @bug 8178025
  * @summary  Verifies if graphicsConfiguration property notification is sent
  *           when frame is moved from one screen to another in multiscreen
  *           environment.
+ * @key headful
  * @run main TestMultiScreenGConfigNotify
  */
 
diff --git a/test/jdk/javax/swing/JButton/TestGlyphBreak.java b/test/jdk/javax/swing/JButton/TestGlyphBreak.java
index a3bf249611d..bf87b88e56b 100644
--- a/test/jdk/javax/swing/JButton/TestGlyphBreak.java
+++ b/test/jdk/javax/swing/JButton/TestGlyphBreak.java
@@ -20,10 +20,12 @@
  * or visit www.oracle.com if you need additional information or have any
  * questions.
  */
-/*
+
+/**
  * @test
  * @bug 8191428
  * @summary  Verifies if text view is not borken into multiple lines
+ * @key headful
  * @run main/othervm -Dsun.java2d.uiScale=1.2 TestGlyphBreak
  */
 
diff --git a/test/jdk/javax/swing/JComboBox/8182031/ComboPopupTest.java b/test/jdk/javax/swing/JComboBox/8182031/ComboPopupTest.java
index 3e1f9b80040..9321bd154be 100644
--- a/test/jdk/javax/swing/JComboBox/8182031/ComboPopupTest.java
+++ b/test/jdk/javax/swing/JComboBox/8182031/ComboPopupTest.java
@@ -20,12 +20,15 @@
  * or visit www.oracle.com if you need additional information or have any
  * questions.
  */
-/*
+
+/**
  * @test
  * @bug 8182031
  * @summary  Verifies if ComboBox Popup opens and closes immediately
+ * @key headful
  * @run main ComboPopupTest
  */
+
 import java.awt.Container;
 import java.awt.Dimension;
 import java.awt.FlowLayout;
@@ -80,7 +83,7 @@ public class ComboPopupTest {
                 throw new RuntimeException("combobox popup is not visible");
             }
         } finally {
-            SwingUtilities.invokeAndWait(()->frame.dispose());
+            if (frame != null) { SwingUtilities.invokeAndWait(()->frame.dispose()); }
         }
     }
 
diff --git a/test/jdk/javax/swing/JMenu/8178430/LabelDotTest.java b/test/jdk/javax/swing/JMenu/8178430/LabelDotTest.java
index 83477848a34..dc42877e96b 100644
--- a/test/jdk/javax/swing/JMenu/8178430/LabelDotTest.java
+++ b/test/jdk/javax/swing/JMenu/8178430/LabelDotTest.java
@@ -20,13 +20,16 @@
  * or visit www.oracle.com if you need additional information or have any
  * questions.
  */
-/*
+
+/**
  * @test
  * @bug 8178430
  * @summary JMenu in GridBagLayout flickers when label text shows "..." and
  * is updated
+ * @key headful
  * @run main LabelDotTest
  */
+
 import java.awt.Dimension;
 import java.awt.GridBagConstraints;
 import java.awt.GridBagLayout;
@@ -112,7 +115,9 @@ public class LabelDotTest
             SwingUtilities.invokeAndWait(() -> createUI());
             runTest(50);
         } finally {
-            SwingUtilities.invokeAndWait(() -> frame.dispose());
+            if (frame != null) {
+                SwingUtilities.invokeAndWait(() -> frame.dispose());
+            }
             if (isException)
                 throw new RuntimeException("Size of Menu bar is not correct.");
         }
diff --git a/test/jdk/javax/swing/JTextArea/TestTabSize.java b/test/jdk/javax/swing/JTextArea/TestTabSize.java
index e69fe318eb0..0893680e92e 100644
--- a/test/jdk/javax/swing/JTextArea/TestTabSize.java
+++ b/test/jdk/javax/swing/JTextArea/TestTabSize.java
@@ -20,10 +20,12 @@
  * or visit www.oracle.com if you need additional information or have any
  * questions.
  */
-/*
+
+/**
  * @test
  * @bug 8187957
  * @summary  Verifies Tab Size works correctly in JTextArea
+ * @key headful
  * @run main TestTabSize
  */
 
@@ -96,7 +98,9 @@ public class TestTabSize {
             } catch (BadLocationException ex) {
                 excpnthrown = true;
             } finally {
-                f.dispose();
+                if (f != null) {
+                    f.dispose();
+                }
             }
         });
         if (excpnthrown) {
diff --git a/test/jdk/javax/swing/dnd/8139050/NativeErrorsInTableDnD.java b/test/jdk/javax/swing/dnd/8139050/NativeErrorsInTableDnD.java
index fe84ed4c976..5e0230a29e6 100644
--- a/test/jdk/javax/swing/dnd/8139050/NativeErrorsInTableDnD.java
+++ b/test/jdk/javax/swing/dnd/8139050/NativeErrorsInTableDnD.java
@@ -40,10 +40,12 @@ import static javax.swing.UIManager.getInstalledLookAndFeels;
 /**
  * @test
  * @bug 8139050 8153871
+ * @key headful
  * @library ../../../../lib/testlibrary
  * @build ExtendedRobot
  * @run main/othervm/timeout=360 -Xcheck:jni NativeErrorsInTableDnD
  */
+
 public final class NativeErrorsInTableDnD {
 
     private static JFrame frame;
diff --git a/test/jdk/javax/swing/plaf/nimbus/TestNimbusOverride.java b/test/jdk/javax/swing/plaf/nimbus/TestNimbusOverride.java
index 17a9d10a9d2..2cf8f0cfa24 100644
--- a/test/jdk/javax/swing/plaf/nimbus/TestNimbusOverride.java
+++ b/test/jdk/javax/swing/plaf/nimbus/TestNimbusOverride.java
@@ -20,13 +20,16 @@
  * or visit www.oracle.com if you need additional information or have any
  * questions.
  */
-/*
+
+/**
  * @test
  * @bug 8043315
  * @summary  Verifies if setting Nimbus.Overrides property affects
  *           keymap installation
+ * @key headful
  * @run main TestNimbusOverride
  */
+
 import java.awt.BorderLayout;
 import java.awt.Component;
 import java.awt.Dimension;
diff --git a/test/jdk/javax/swing/text/DefaultCaret/HidingSelection/HidingSelectionTest.java b/test/jdk/javax/swing/text/DefaultCaret/HidingSelection/HidingSelectionTest.java
index a422eed798b..6b2331f390d 100644
--- a/test/jdk/javax/swing/text/DefaultCaret/HidingSelection/HidingSelectionTest.java
+++ b/test/jdk/javax/swing/text/DefaultCaret/HidingSelection/HidingSelectionTest.java
@@ -30,6 +30,7 @@ import java.awt.image.BufferedImage;
  * @test
  * @bug 8188081
  * @summary  Text selection does not clear after focus is lost
+ * @key headful
  * @run main HidingSelectionTest
  */
 
diff --git a/test/jdk/jdk/internal/misc/JavaLangAccess/NewUnsafeString.java b/test/jdk/jdk/internal/misc/JavaLangAccess/NewUnsafeString.java
deleted file mode 100644
index 302fb185809..00000000000
--- a/test/jdk/jdk/internal/misc/JavaLangAccess/NewUnsafeString.java
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
- * Copyright (c) 2012, 2017, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-
-import java.util.Objects;
-import java.util.Comparator;
-import jdk.internal.misc.JavaLangAccess;
-import jdk.internal.misc.SharedSecrets;
-
-/*
- * @test
- * @bug 8013528
- * @summary Test JavaLangAccess.newUnsafeString
- * @modules java.base/jdk.internal.misc
- * @compile -XDignore.symbol.file NewUnsafeString.java
- * @run main NewUnsafeString
- */
-public class NewUnsafeString {
-
-    static final JavaLangAccess jla = SharedSecrets.getJavaLangAccess();
-
-    public static void testNewUnsafeString() {
-        String benchmark = "exemplar";
-        String constructorCopy = new String(benchmark);
-        char[] jlaChars = benchmark.toCharArray();
-        String jlaCopy = jla.newStringUnsafe(jlaChars);
-
-        if (benchmark == constructorCopy) {
-            throw new Error("should be different instances");
-        }
-        if (!benchmark.equals(constructorCopy)) {
-            throw new Error("Copy not equal");
-        }
-        if (0 != Objects.compare(benchmark, constructorCopy, Comparator.naturalOrder())) {
-            throw new Error("Copy not equal");
-        }
-
-        if (benchmark == jlaCopy) {
-            throw new Error("should be different instances");
-        }
-        if (!benchmark.equals(jlaCopy)) {
-            throw new Error("Copy not equal");
-        }
-        if (0 != Objects.compare(benchmark, jlaCopy, Comparator.naturalOrder())) {
-            throw new Error("Copy not equal");
-        }
-
-        if (constructorCopy == jlaCopy) {
-            throw new Error("should be different instances");
-        }
-        if (!constructorCopy.equals(jlaCopy)) {
-            throw new Error("Copy not equal");
-        }
-        if (0 != Objects.compare(constructorCopy, jlaCopy, Comparator.naturalOrder())) {
-            throw new Error("Copy not equal");
-        }
-
-        // The following is extremely "evil". Never ever do this in non-test code.
-        jlaChars[0] = 'X';
-        if (!"Xxemplar".equals(jlaCopy)) {
-            throw new Error("jla.newStringUnsafe did not use provided string");
-        }
-
-    }
-
-    public static void main(String[] args) {
-        testNewUnsafeString();
-    }
-}
diff --git a/test/jdk/lib/testlibrary/jdk/testlibrary/RandomFactory.java b/test/jdk/lib/testlibrary/jdk/testlibrary/RandomFactory.java
deleted file mode 100644
index 4cac61c1a0c..00000000000
--- a/test/jdk/lib/testlibrary/jdk/testlibrary/RandomFactory.java
+++ /dev/null
@@ -1,108 +0,0 @@
-/*
- * Copyright (c) 2015, 2017, 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 jdk.testlibrary;
-
-import java.util.Random;
-import java.util.SplittableRandom;
-
-/**
- * Factory class which generates and prints to STDOUT a long-valued seed
- * for use in initializing a PRNG.  An instance of {@code Random} or
- * {@code SplittableRandom} may likewise be obtained.
- *
- * @deprecated  This class is deprecated. Use the one from
- *              {@code <root>/test/lib/jdk/test/lib}
- *
- */
-@Deprecated
-public class RandomFactory {
-    /**
-     * Attempt to obtain the seed from the value of the "seed" property.
-     * @return The seed or {@code null} if the "seed" property was not set or
-     *         could not be parsed.
-     */
-    private static Long getSystemSeed() {
-        Long seed = null;
-        try {
-            // note that Long.valueOf(null) also throws a
-            // NumberFormatException so if the property is undefined this
-            // will still work correctly
-            seed = Long.valueOf(System.getProperty("seed"));
-        } catch (NumberFormatException e) {
-            // do nothing: seed is still null
-        }
-
-        return seed;
-    }
-
-    /**
-     * Obtain a seed from an independent PRNG.
-     *
-     * @return A random seed.
-     */
-    private static long getRandomSeed() {
-        return new Random().nextLong();
-    }
-
-    /**
-     * Obtain and print to STDOUT a seed appropriate for initializing a PRNG.
-     * If the system property "seed" is set and has value which may be correctly
-     * parsed it is used, otherwise a seed is generated using an independent
-     * PRNG.
-     *
-     * @return The seed.
-     */
-    public static long getSeed() {
-        Long seed = getSystemSeed();
-        if (seed == null) {
-            seed = getRandomSeed();
-        }
-        System.out.println("Seed from RandomFactory = "+seed+"L");
-        return seed;
-    }
-
-    /**
-     * Obtain and print to STDOUT a seed and use it to initialize a new
-     * {@code Random} instance which is returned. If the system
-     * property "seed" is set and has value which may be correctly parsed it
-     * is used, otherwise a seed is generated using an independent PRNG.
-     *
-     * @return The {@code Random} instance.
-     */
-    public static Random getRandom() {
-        return new Random(getSeed());
-    }
-
-    /**
-     * Obtain and print to STDOUT a seed and use it to initialize a new
-     * {@code SplittableRandom} instance which is returned. If the system
-     * property "seed" is set and has value which may be correctly parsed it
-     * is used, otherwise a seed is generated using an independent PRNG.
-     *
-     * @return The {@code SplittableRandom} instance.
-     */
-    public static SplittableRandom getSplittableRandom() {
-        return new SplittableRandom(getSeed());
-    }
-}
diff --git a/test/jdk/sun/security/provider/FileInputStreamPool/FileInputStreamPoolTest.java b/test/jdk/sun/security/provider/FileInputStreamPool/FileInputStreamPoolTest.java
index e7fafc10fb5..860b2d99910 100644
--- a/test/jdk/sun/security/provider/FileInputStreamPool/FileInputStreamPoolTest.java
+++ b/test/jdk/sun/security/provider/FileInputStreamPool/FileInputStreamPoolTest.java
@@ -24,26 +24,34 @@
 /**
  * @test
  * @bug 8047769
- * @modules java.base/java.lang.ref:open
+ * @modules java.base/java.io:open
+ *          java.base/java.lang.ref:open
  *          java.base/sun.security.provider:open
  * @summary SecureRandom should be more frugal with file descriptors
  */
 
 import java.io.File;
+import java.io.FileDescriptor;
+import java.io.FileInputStream;
 import java.io.FileOutputStream;
+import java.io.FilterInputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.lang.ref.Reference;
+import java.lang.ref.ReferenceQueue;
+import java.lang.ref.WeakReference;
+import java.lang.reflect.Field;
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 import java.lang.reflect.UndeclaredThrowableException;
 import java.util.Arrays;
+import java.util.HashSet;
 
 public class FileInputStreamPoolTest {
 
     static final byte[] bytes = new byte[]{1, 2, 3, 4, 5, 6, 7, 8};
 
-    static void testCaching(File file) throws IOException {
+    static FilterInputStream testCaching(File file) throws IOException {
         InputStream in1 = TestProxy.FileInputStreamPool_getInputStream(file);
         InputStream in2 = TestProxy.FileInputStreamPool_getInputStream(file);
         assertTrue(in1 == in2,
@@ -58,6 +66,8 @@ public class FileInputStreamPoolTest {
         assertTrue(Arrays.equals(readBytes, bytes),
             "readBytes: " + Arrays.toString(readBytes) +
                 " not equal to expected: " + Arrays.toString(bytes));
+
+       return (FilterInputStream)in1;
     }
 
     static void assertTrue(boolean test, String message) {
@@ -66,13 +76,34 @@ public class FileInputStreamPoolTest {
         }
     }
 
-    static void processReferences() {
-        // make JVM process References
-        System.gc();
-        // help ReferenceHandler thread enqueue References
-        while (TestProxy.Reference_waitForReferenceProcessing()) { }
-        // help run Finalizers
-        System.runFinalization();
+    static void processReferences(FilterInputStream in1) throws InterruptedException, IOException {
+        FileInputStream fis = TestProxy.FilterInputStream_getInField(in1);
+        FileDescriptor fd = fis.getFD();
+        System.out.printf("fis: %s, fd: %s%n", fis, fd);
+        // Prepare to wait for FD to be reclaimed
+        ReferenceQueue<Object> queue = new ReferenceQueue<>();
+        HashSet<Reference<?>> pending = new HashSet<>();
+        pending.add(new WeakReference<>(in1, queue));
+        pending.add(new WeakReference<>(fis, queue));
+        pending.add(new WeakReference<>(fd, queue));
+
+        Reference<?> r;
+        while (((r = queue.remove(10L)) != null)
+                || !pending.isEmpty()) {
+            System.out.printf("r: %s, pending: %d%n", r, pending.size());
+            if (r != null) {
+                pending.remove(r);
+            } else {
+                fd = null;
+                fis = null;
+                in1 = null;
+                System.gc();  // attempt to reclaim the FD
+            }
+            Thread.sleep(10L);
+        }
+        Reference.reachabilityFence(fd);
+        Reference.reachabilityFence(fis);
+        Reference.reachabilityFence(in1);
     }
 
     public static void main(String[] args) throws Exception {
@@ -88,16 +119,14 @@ public class FileInputStreamPoolTest {
                 out.write(bytes);
             }
 
-            // test caching 1t time
-            testCaching(file);
+            // test caching 1st time
 
-            processReferences();
+            processReferences(testCaching(file));
 
             // test caching 2nd time - this should only succeed if the stream
             // is re-opened as a consequence of cleared WeakReference
-            testCaching(file);
 
-            processReferences();
+            processReferences(testCaching(file));
         }
     }
 
@@ -109,6 +138,7 @@ public class FileInputStreamPoolTest {
     static class TestProxy {
         private static final Method getInputStreamMethod;
         private static final Method waitForReferenceProcessingMethod;
+        private static final Field inField;
 
         static {
             try {
@@ -122,6 +152,9 @@ public class FileInputStreamPoolTest {
                 waitForReferenceProcessingMethod =
                     Reference.class.getDeclaredMethod("waitForReferenceProcessing");
                 waitForReferenceProcessingMethod.setAccessible(true);
+
+                inField = FilterInputStream.class.getDeclaredField("in");
+                inField.setAccessible(true);
             } catch (Exception e) {
                 throw new Error(e);
             }
@@ -165,5 +198,13 @@ public class FileInputStreamPoolTest {
                 throw new RuntimeException(e);
             }
         }
+
+        static FileInputStream FilterInputStream_getInField(FilterInputStream fis) {
+            try {
+                return (FileInputStream) inField.get(fis);
+            } catch (IllegalAccessException e) {
+                throw new RuntimeException(e);
+            }
+        }
     }
 }
diff --git a/test/jdk/sun/security/ssl/DHKeyExchange/DHEKeySizing.java b/test/jdk/sun/security/ssl/DHKeyExchange/DHEKeySizing.java
index c00337789f0..26e30608bdf 100644
--- a/test/jdk/sun/security/ssl/DHKeyExchange/DHEKeySizing.java
+++ b/test/jdk/sun/security/ssl/DHKeyExchange/DHEKeySizing.java
@@ -31,45 +31,45 @@
  * @bug 6956398
  * @summary make ephemeral DH key match the length of the certificate key
  * @run main/othervm
- *      DHEKeySizing TLS_DHE_RSA_WITH_AES_128_CBC_SHA  false 1639 267
+ *      DHEKeySizing TLS_DHE_RSA_WITH_AES_128_CBC_SHA  false 1643 267
  * @run main/othervm -Djsse.enableFFDHE=false
- *      DHEKeySizing SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA true 1255 75
+ *      DHEKeySizing SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA true 1259 75
  * @run main/othervm -Djsse.enableFFDHE=false
  *      -Djdk.tls.ephemeralDHKeySize=matched
- *      DHEKeySizing SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA true 1255 75
+ *      DHEKeySizing SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA true 1259 75
  * @run main/othervm -Djsse.enableFFDHE=false
  *      -Djdk.tls.ephemeralDHKeySize=legacy
- *      DHEKeySizing SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA true 1255 75
+ *      DHEKeySizing SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA true 1259 75
  * @run main/othervm -Djsse.enableFFDHE=false
  *      -Djdk.tls.ephemeralDHKeySize=1024
- *      DHEKeySizing SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA true 1255 75
+ *      DHEKeySizing SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA true 1259 75
  *
  * @run main/othervm -Djsse.enableFFDHE=false
- *      DHEKeySizing SSL_DH_anon_EXPORT_WITH_DES40_CBC_SHA true 229 75
+ *      DHEKeySizing SSL_DH_anon_EXPORT_WITH_DES40_CBC_SHA true 233 75
  *
  * @run main/othervm -Djsse.enableFFDHE=false
- *      DHEKeySizing TLS_DHE_RSA_WITH_AES_128_CBC_SHA  false 1383 139
+ *      DHEKeySizing TLS_DHE_RSA_WITH_AES_128_CBC_SHA  false 1387 139
  * @run main/othervm -Djsse.enableFFDHE=false
  *      -Djdk.tls.ephemeralDHKeySize=legacy
- *      DHEKeySizing TLS_DHE_RSA_WITH_AES_128_CBC_SHA  false 1319 107
+ *      DHEKeySizing TLS_DHE_RSA_WITH_AES_128_CBC_SHA  false 1323 107
  * @run main/othervm -Djsse.enableFFDHE=false
  *      -Djdk.tls.ephemeralDHKeySize=matched
- *      DHEKeySizing TLS_DHE_RSA_WITH_AES_128_CBC_SHA  false 1639 267
+ *      DHEKeySizing TLS_DHE_RSA_WITH_AES_128_CBC_SHA  false 1643 267
  * @run main/othervm -Djsse.enableFFDHE=false
  *      -Djdk.tls.ephemeralDHKeySize=1024
- *      DHEKeySizing TLS_DHE_RSA_WITH_AES_128_CBC_SHA  false 1383 139
+ *      DHEKeySizing TLS_DHE_RSA_WITH_AES_128_CBC_SHA  false 1387 139
  *
  * @run main/othervm -Djsse.enableFFDHE=false
- *      DHEKeySizing SSL_DH_anon_WITH_RC4_128_MD5  false 357 139
+ *      DHEKeySizing SSL_DH_anon_WITH_RC4_128_MD5  false 361 139
  * @run main/othervm -Djsse.enableFFDHE=false
  *      -Djdk.tls.ephemeralDHKeySize=legacy
- *      DHEKeySizing SSL_DH_anon_WITH_RC4_128_MD5  false 293 107
+ *      DHEKeySizing SSL_DH_anon_WITH_RC4_128_MD5  false 297 107
  * @run main/othervm -Djsse.enableFFDHE=false
  *      -Djdk.tls.ephemeralDHKeySize=matched
- *      DHEKeySizing SSL_DH_anon_WITH_RC4_128_MD5  false 357 139
+ *      DHEKeySizing SSL_DH_anon_WITH_RC4_128_MD5  false 361 139
  * @run main/othervm -Djsse.enableFFDHE=false
  *      -Djdk.tls.ephemeralDHKeySize=1024
- *      DHEKeySizing SSL_DH_anon_WITH_RC4_128_MD5  false 357 139
+ *      DHEKeySizing SSL_DH_anon_WITH_RC4_128_MD5  false 361 139
  */
 
 /*
@@ -101,10 +101,10 @@
  * Here is a summary of the record length in the test case.
  *
  *            |  ServerHello Series  |  ClientKeyExchange | ServerHello Anon
- *   512-bit  |          1255 bytes  |           75 bytes |        229 bytes
- *   768-bit  |          1319 bytes  |          107 bytes |        293 bytes
- *  1024-bit  |          1383 bytes  |          139 bytes |        357 bytes
- *  2048-bit  |          1639 bytes  |          267 bytes |        357 bytes
+ *   512-bit  |          1259 bytes  |           75 bytes |        233 bytes
+ *   768-bit  |          1323 bytes  |          107 bytes |        297 bytes
+ *  1024-bit  |          1387 bytes  |          139 bytes |        361 bytes
+ *  2048-bit  |          1643 bytes  |          267 bytes |        361 bytes
  */
 
 import javax.net.ssl.*;
diff --git a/test/jdk/sun/security/ssl/ExtensionType/OptimalListSize.java b/test/jdk/sun/security/ssl/ExtensionType/OptimalListSize.java
index 89c50b26027..375a060e39b 100644
--- a/test/jdk/sun/security/ssl/ExtensionType/OptimalListSize.java
+++ b/test/jdk/sun/security/ssl/ExtensionType/OptimalListSize.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2017, 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
@@ -38,6 +38,6 @@ public class OptimalListSize {
     public static void main(String[] args) throws Throwable {
         OptimalCapacity.ofArrayList(
                 Class.forName("sun.security.ssl.ExtensionType"),
-                "knownExtensions", 15);
+                "knownExtensions", 16);
     }
 }
diff --git a/test/jdk/sun/security/tools/jarsigner/TimestampCheck.java b/test/jdk/sun/security/tools/jarsigner/TimestampCheck.java
index f0774029606..86d72f53b71 100644
--- a/test/jdk/sun/security/tools/jarsigner/TimestampCheck.java
+++ b/test/jdk/sun/security/tools/jarsigner/TimestampCheck.java
@@ -694,11 +694,15 @@ public class TimestampCheck {
         gencert("ts", "-ext eku:critical=ts");
 
 
-        // Issue another cert for "ts" with a different EKU.
-        // Length should be the same. Try several times.
-        keytool("-gencert -alias ca -infile ts.req -outfile ts2.cert " +
-                "-ext eku:critical=1.3.6.1.5.5.7.3.9");
         for (int i = 0; i < 5; i++) {
+            // Issue another cert for "ts" with a different EKU.
+            // Length might be different because serial number is
+            // random. Try several times until a cert with the same
+            // length is generated so we can substitute ts.cert
+            // embedded in the PKCS7 block with ts2.cert.
+            // If cannot create one, related test will be ignored.
+            keytool("-gencert -alias ca -infile ts.req -outfile ts2.cert " +
+                    "-ext eku:critical=1.3.6.1.5.5.7.3.9");
             if (Files.size(Paths.get("ts.cert")) != Files.size(Paths.get("ts2.cert"))) {
                 Files.delete(Paths.get("ts2.cert"));
                 System.out.println("Warning: cannot create same length");
diff --git a/test/jdk/sun/security/tools/keytool/RealType.java b/test/jdk/sun/security/tools/keytool/RealType.java
new file mode 100644
index 00000000000..3ce12f5316b
--- /dev/null
+++ b/test/jdk/sun/security/tools/keytool/RealType.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug 8192987
+ * @summary keytool should remember real storetype if it is not provided
+ * @library /test/lib
+ * @build jdk.test.lib.SecurityTools
+ *        jdk.test.lib.Utils
+ *        jdk.test.lib.JDKToolFinder
+ *        jdk.test.lib.JDKToolLauncher
+ *        jdk.test.lib.Platform
+ *        jdk.test.lib.process.*
+ * @run main/othervm RealType
+ */
+
+import jdk.test.lib.SecurityTools;
+import jdk.test.lib.process.OutputAnalyzer;
+
+import java.nio.file.Files;
+import java.nio.file.Paths;
+
+public class RealType {
+
+    public static void main(String[] args) throws Throwable {
+
+        kt("-genkeypair -alias a -dname CN=A -keypass changeit -storetype jks")
+                .shouldHaveExitValue(0);
+
+        // -keypasswd command should be allowed on JKS
+        kt("-keypasswd -alias a -new t0ps3cr3t")
+                .shouldHaveExitValue(0);
+
+        Files.delete(Paths.get("ks"));
+
+        kt("-genkeypair -alias a -dname CN=A -keypass changeit -storetype pkcs12")
+                .shouldHaveExitValue(0);
+
+        // A pkcs12 keystore cannot be loaded as a JCEKS keystore
+        kt("-list -storetype jceks").shouldHaveExitValue(1);
+    }
+
+    static OutputAnalyzer kt(String arg) throws Exception {
+        return SecurityTools.keytool("-debug -keystore ks -storepass changeit " + arg);
+    }
+}
diff --git a/test/jdk/tools/jlink/ExplodedModuleNameTest.java b/test/jdk/tools/jlink/ExplodedModuleNameTest.java
new file mode 100644
index 00000000000..bcbc1ffd86c
--- /dev/null
+++ b/test/jdk/tools/jlink/ExplodedModuleNameTest.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.io.IOException;
+import java.nio.file.attribute.BasicFileAttributes;
+import java.nio.file.Files;
+import java.nio.file.FileVisitResult;
+import java.nio.file.Path;
+import java.nio.file.SimpleFileVisitor;
+import java.util.spi.ToolProvider;
+
+import tests.Helper;
+import tests.JImageGenerator;
+import tests.Result;
+
+/*
+ * @test
+ * @bug 8192986
+ * @summary Inconsistent handling of exploded modules in jlink
+ * @library ../lib
+ * @modules java.base/jdk.internal.jimage
+ *          jdk.jdeps/com.sun.tools.classfile
+ *          jdk.jlink/jdk.tools.jlink.internal
+ *          jdk.jlink/jdk.tools.jmod
+ *          jdk.jlink/jdk.tools.jimage
+ *          jdk.compiler
+ * @build tests.*
+ * @run main ExplodedModuleNameTest
+ */
+public class ExplodedModuleNameTest {
+    static final ToolProvider JLINK_TOOL = ToolProvider.findFirst("jlink")
+        .orElseThrow(() ->
+            new RuntimeException("jlink tool not found")
+        );
+
+    public static void main(String[] args) throws Exception {
+        Helper helper = Helper.newHelper();
+        if (helper == null) {
+            System.err.println("Test not run");
+            return;
+        }
+
+        // generate a new exploded module
+        String modName = "mod8192986";
+        Path modDir = helper.generateDefaultExplodedModule(modName).getFile();
+        // rename the module containing directory
+        Path renamedModDir = modDir.resolveSibling("modified_mod8192986");
+        // copy the content from original directory to modified name directory
+        copyDir(modDir, renamedModDir);
+
+        Path outputDir = helper.createNewImageDir("image8192986");
+        JImageGenerator.getJLinkTask()
+                .modulePath(renamedModDir.toAbsolutePath().toString())
+                .output(outputDir)
+                .addMods(modName)
+                .launcher(modName + "=" + modName + "/" + modName +".Main")
+                .call().assertSuccess();
+    }
+
+    private static void copyDir(Path srcDir, Path destDir) throws IOException {
+        Files.walkFileTree(srcDir, new SimpleFileVisitor<Path>() {
+            @Override
+            public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
+                Path target = destDir.resolve(srcDir.relativize(dir));
+                Files.createDirectory(target);
+                return FileVisitResult.CONTINUE;
+            }
+
+            @Override
+            public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
+                Files.copy(file, destDir.resolve(srcDir.relativize(file)));
+                return FileVisitResult.CONTINUE;
+            }
+        });
+    }
+}
diff --git a/test/jdk/tools/jlink/ModuleNamesOrderTest.java b/test/jdk/tools/jlink/ModuleNamesOrderTest.java
index 20b642d1ef3..e7c8af4ecdc 100644
--- a/test/jdk/tools/jlink/ModuleNamesOrderTest.java
+++ b/test/jdk/tools/jlink/ModuleNamesOrderTest.java
@@ -24,14 +24,17 @@
 import java.io.File;
 import java.io.FileReader;
 import java.io.IOException;
-import java.io.PrintWriter;
-import java.io.StringWriter;
 import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.List;
 import java.util.Properties;
 import java.util.spi.ToolProvider;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
 
 import tests.Helper;
 import tests.JImageGenerator;
+import tests.JImageGenerator.JLinkTask;
 
 /*
  * @test
@@ -44,6 +47,9 @@ import tests.JImageGenerator;
  *          jdk.jlink/jdk.tools.jmod
  *          jdk.jlink/jdk.tools.jimage
  *          jdk.compiler
+ *          jdk.scripting.nashorn
+ *          jdk.scripting.nashorn.shell
+ *
  * @build tests.*
  * @run main ModuleNamesOrderTest
  */
@@ -60,12 +66,18 @@ public class ModuleNamesOrderTest {
             return;
         }
 
-        Path outputDir = helper.createNewImageDir("image8168925");
-        JImageGenerator.getJLinkTask()
-                .modulePath(helper.defaultModulePath())
-                .output(outputDir)
-                .addMods("jdk.scripting.nashorn")
-                .call().assertSuccess();
+        testDependences(helper);
+        testModulesOrder(helper);
+    }
+
+    private static List<String> modulesProperty(Path outputDir, String modulePath, String... roots)
+        throws IOException
+    {
+        JLinkTask jlinkTask = JImageGenerator.getJLinkTask()
+                                             .modulePath(modulePath)
+                                             .output(outputDir);
+        Stream.of(roots).forEach(jlinkTask::addMods);
+        jlinkTask.call().assertSuccess();
 
         File release = new File(outputDir.toString(), "release");
         if (!release.exists()) {
@@ -81,9 +93,21 @@ public class ModuleNamesOrderTest {
         if (!modules.startsWith("\"java.base ")) {
             throw new AssertionError("MODULES should start with 'java.base'");
         }
+        if (modules.charAt(0) != '"' || modules.charAt(modules.length()-1) != '"') {
+            throw new AssertionError("MODULES value should be double quoted");
+        }
 
-        if (!modules.endsWith(" jdk.scripting.nashorn\"")) {
-            throw new AssertionError("MODULES end with 'jdk.scripting.nashorn'");
+        return Stream.of(modules.substring(1, modules.length()-1).split("\\s+"))
+                     .collect(Collectors.toList());
+    }
+
+    private static void testDependences(Helper helper) throws IOException {
+        Path outputDir = helper.createNewImageDir("test");
+        List<String> modules = modulesProperty(outputDir, helper.defaultModulePath(),
+            "jdk.scripting.nashorn");
+        String last = modules.get(modules.size()-1);
+        if (!last.equals("jdk.scripting.nashorn")) {
+            throw new AssertionError("Unexpected MODULES value: " + modules);
         }
 
         checkDependency(modules, "java.logging", "java.base");
@@ -94,7 +118,23 @@ public class ModuleNamesOrderTest {
         checkDependency(modules, "jdk.scripting.nashorn", "java.scripting");
     }
 
-    private static void checkDependency(String modules, String fromMod, String toMod) {
+    /*
+     * Verify the MODULES list must be the same for the same module graph
+     */
+    private static void testModulesOrder(Helper helper) throws IOException {
+        Path image1 = helper.createNewImageDir("test1");
+        List<String> modules1 = modulesProperty(image1, helper.defaultModulePath(),
+            "jdk.scripting.nashorn", "jdk.scripting.nashorn.shell");
+        Path image2 = helper.createNewImageDir("test2");
+        List<String> modules2 = modulesProperty(image2, helper.defaultModulePath(),
+            "jdk.scripting.nashorn.shell", "jdk.scripting.nashorn");
+        if (!modules1.equals(modules2)) {
+            throw new AssertionError("MODULES should be a stable order: " +
+                modules1 + " vs " + modules2);
+        }
+    }
+
+    private static void checkDependency(List<String> modules, String fromMod, String toMod) {
         int fromModIdx = modules.indexOf(fromMod);
         if (fromModIdx == -1) {
             throw new AssertionError(fromMod + " is missing in MODULES");
diff --git a/test/jdk/tools/jlink/multireleasejar/JLinkMRJavaBaseVersionTest.java b/test/jdk/tools/jlink/multireleasejar/JLinkMRJavaBaseVersionTest.java
index dd8d66391e3..46912a68033 100644
--- a/test/jdk/tools/jlink/multireleasejar/JLinkMRJavaBaseVersionTest.java
+++ b/test/jdk/tools/jlink/multireleasejar/JLinkMRJavaBaseVersionTest.java
@@ -25,6 +25,8 @@
  * @test
  * @bug 8177471
  * @summary  jlink should use the version from java.base.jmod to find modules
+ * @bug 8185130
+ * @summary jlink should throw error if target image and current JDK versions don't match
  * @modules java.base/jdk.internal.module
  * @library /test/lib
  * @build jdk.test.lib.process.* CheckRuntimeVersion
@@ -122,7 +124,9 @@ public class JLinkMRJavaBaseVersionTest {
         System.out.println("Testing jlink with " + getJmods() + " of target version " + version);
 
         // use jlink to build image from multi-release jar
-        jlink("m1.jar", "myimage");
+        if (jlink("m1.jar", "myimage")) {
+            return;
+        }
 
         // validate runtime image
         Path java = Paths.get("myimage", "bin", "java");
@@ -130,12 +134,7 @@ public class JLinkMRJavaBaseVersionTest {
 
         // validate the image linked with the proper MR version
 
-        if (version.equalsIgnoreOptional(Runtime.version())) {
-            ProcessTools.executeProcess(java.toString(), "-cp", System.getProperty("test.classes"),
-                                        "CheckRuntimeVersion", String.valueOf(version.major()),
-                                        "java.base", "java.logging", "m1")
-                .shouldHaveExitValue(0);
-        } else {
+        if (!version.equalsIgnoreOptional(Runtime.version())) {
             ProcessTools.executeProcess(java.toString(), "-cp", System.getProperty("test.classes"),
                                         "CheckRuntimeVersion", String.valueOf(version.major()),
                                         "java.base", "m1")
@@ -151,13 +150,19 @@ public class JLinkMRJavaBaseVersionTest {
         return Runtime.Version.parse(mref.descriptor().version().get().toString());
     }
 
-    private void jlink(String jar, String image) {
+    private boolean jlink(String jar, String image) {
         List<String> args = List.of("--output", image,
                                     "--add-modules", "m1",
                                     "--module-path",
                                     getJmods().toString() + File.pathSeparator + jar);
         System.out.println("jlink " + args.stream().collect(Collectors.joining(" ")));
         int exitCode = JLINK_TOOL.run(System.out, System.err, args.toArray(new String[0]));
-        Assert.assertEquals(exitCode, 0);
+        boolean isJDK9 = System.getProperty("java9.home") != null;
+        if (isJDK9) {
+            Assert.assertNotEquals(exitCode, 0);
+        } else {
+            Assert.assertEquals(exitCode, 0);
+        }
+        return isJDK9;
     }
 }
diff --git a/test/jdk/tools/lib/tests/Helper.java b/test/jdk/tools/lib/tests/Helper.java
index 022370440c6..006807b1c85 100644
--- a/test/jdk/tools/lib/tests/Helper.java
+++ b/test/jdk/tools/lib/tests/Helper.java
@@ -219,7 +219,7 @@ public class Helper {
         generateModuleCompiledClasses(explodedmodssrc, explodedmodsclasses,
                 moduleName, classNames, dependencies);
 
-        Path dir = explodedmods.resolve(moduleName);
+        Path dir = explodedmods.resolve("classes").resolve(moduleName);
         return new Result(0, "", dir);
     }
 
diff --git a/test/jdk/tools/lib/tests/JImageGenerator.java b/test/jdk/tools/lib/tests/JImageGenerator.java
index 59e7d37cbce..763cd355e3a 100644
--- a/test/jdk/tools/lib/tests/JImageGenerator.java
+++ b/test/jdk/tools/lib/tests/JImageGenerator.java
@@ -110,6 +110,7 @@ public class JImageGenerator {
     private static final String ADD_MODULES_OPTION = "--add-modules";
     private static final String LIMIT_MODULES_OPTION = "--limit-modules";
     private static final String PLUGIN_MODULE_PATH = "--plugin-module-path";
+    private static final String LAUNCHER = "--launcher";
 
     private static final String CMDS_OPTION = "--cmds";
     private static final String CONFIG_OPTION = "--config";
@@ -579,12 +580,18 @@ public class JImageGenerator {
         private String repeatedLimitMods;
         private Path output;
         private Path existing;
+        private String launcher; // optional
 
         public JLinkTask modulePath(String modulePath) {
             this.modulePath = modulePath;
             return this;
         }
 
+        public JLinkTask launcher(String cmd) {
+            launcher = Objects.requireNonNull(cmd);
+            return this;
+        }
+
         public JLinkTask repeatedModulePath(String modulePath) {
             this.repeatedModulePath = modulePath;
             return this;
@@ -682,6 +689,10 @@ public class JImageGenerator {
                 options.add(PLUGIN_MODULE_PATH);
                 options.add(toPath(pluginModulePath));
             }
+            if (launcher != null && !launcher.isEmpty()) {
+                options.add(LAUNCHER);
+                options.add(launcher);
+            }
             options.addAll(this.options);
             return options.toArray(new String[options.size()]);
         }
diff --git a/test/jtreg-ext/requires/VMProps.java b/test/jtreg-ext/requires/VMProps.java
index 13632fe7abe..215e143b610 100644
--- a/test/jtreg-ext/requires/VMProps.java
+++ b/test/jtreg-ext/requires/VMProps.java
@@ -74,6 +74,7 @@ public class VMProps implements Callable<Map<String, String>> {
         map.put("vm.aot", vmAOT());
         // vm.cds is true if the VM is compiled with cds support.
         map.put("vm.cds", vmCDS());
+        map.put("vm.cds.custom.loaders", vmCDSForCustomLoaders());
         // vm.graal.enabled is true if Graal is used as JIT
         map.put("vm.graal.enabled", isGraalEnabled());
         map.put("docker.support", dockerSupport());
@@ -296,6 +297,19 @@ public class VMProps implements Callable<Map<String, String>> {
         }
     }
 
+    /**
+     * Check for CDS support for custom loaders.
+     *
+     * @return true if CDS is supported for customer loader by the VM to be tested.
+     */
+    protected String vmCDSForCustomLoaders() {
+        if (vmCDS().equals("true") && Platform.areCustomLoadersSupportedForCDS()) {
+            return "true";
+        } else {
+            return "false";
+        }
+    }
+
     /**
      * Check if Graal is used as JIT compiler.
      *
diff --git a/test/langtools/jdk/internal/shellsupport/doc/JavadocHelperTest.java b/test/langtools/jdk/internal/shellsupport/doc/JavadocHelperTest.java
index 766857f94fb..48106face35 100644
--- a/test/langtools/jdk/internal/shellsupport/doc/JavadocHelperTest.java
+++ b/test/langtools/jdk/internal/shellsupport/doc/JavadocHelperTest.java
@@ -23,28 +23,37 @@
 
 /*
  * @test
- * @bug 8131019 8190552
+ * @bug 8131019 8189778 8190552
  * @summary Test JavadocHelper
  * @library /tools/lib
  * @modules jdk.compiler/com.sun.tools.javac.api
  *          jdk.compiler/com.sun.tools.javac.main
  *          jdk.compiler/jdk.internal.shellsupport.doc
  * @build toolbox.ToolBox toolbox.JarTask toolbox.JavacTask
- * @run testng JavadocHelperTest
+ * @run testng/timeout=900/othervm JavadocHelperTest
  */
 
 import java.io.IOException;
 import java.net.URI;
 import java.net.URISyntaxException;
+import java.nio.file.DirectoryStream;
+import java.nio.file.FileSystem;
+import java.nio.file.FileSystems;
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.nio.file.Paths;
+import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
 import java.util.function.Function;
 import java.util.jar.JarEntry;
 import java.util.jar.JarOutputStream;
 
 import javax.lang.model.element.Element;
+import javax.lang.model.element.ModuleElement;
+import javax.lang.model.element.ModuleElement.ExportsDirective;
+import javax.lang.model.element.TypeElement;
 import javax.lang.model.util.ElementFilter;
 import javax.tools.Diagnostic.Kind;
 import javax.tools.DiagnosticListener;
@@ -70,15 +79,15 @@ public class JavadocHelperTest {
                       "Top level. ");
         doTestJavadoc("",
                       t -> getFirstMethod(t, "test.Super"),
-                      " javadoc1A\n" +
+                      " javadoc1\n" +
                       "\n" +
-                      " @param p1 param1A\n" +
-                      " @param p2 param2A\n" +
-                      " @param p3 param3A\n" +
-                      " @throws IllegalStateException exc1A\n" +
-                      " @throws IllegalArgumentException exc2A\n" +
-                      " @throws IllegalAccessException exc3A\n" +
-                      " @return valueA\n");
+                      " @param p1 param1\n" +
+                      " @param p2 param2\n" +
+                      " @param p3 param3\n" +
+                      " @throws IllegalStateException exc1\n" +
+                      " @throws IllegalArgumentException exc2\n" +
+                      " @throws IllegalAccessException exc3\n" +
+                      " @return value\n");
     }
 
     private Element getFirstMethod(JavacTask task, String typeName) {
@@ -90,15 +99,15 @@ public class JavadocHelperTest {
     public void testInheritNoJavadoc() throws Exception {
         doTestJavadoc("",
                       getSubTest,
-                      " javadoc1A\n" +
+                      " javadoc1\n" +
                       "\n" +
-                      " @param p1 param1A\n" +
-                      " @param p2 param2A\n" +
-                      " @param p3 param3A\n" +
-                      " @throws IllegalStateException exc1A\n" +
-                      " @throws IllegalArgumentException exc2A\n" +
-                      " @throws IllegalAccessException exc3A\n" +
-                      " @return valueA\n");
+                      " @param p1 param1\n" +
+                      " @param p2 param2\n" +
+                      " @param p3 param3\n" +
+                      " @throws IllegalStateException exc1\n" +
+                      " @throws IllegalArgumentException exc2\n" +
+                      " @throws IllegalAccessException exc3\n" +
+                      " @return value\n");
     }
 
     public void testInheritFull() throws Exception {
@@ -140,7 +149,7 @@ public class JavadocHelperTest {
                       " Prefix javadoc1 suffix.\n" +
                       "\n" +
                       " @param p1 prefix param1 suffix\n" +
-                      "@param p2  param2\n" +
+                      "@param p2 param2\n" +
                       " @param p3 prefix param3 suffix\n" +
                       " @throws IllegalStateException prefix exc1 suffix\n" +
                       " @throws IllegalArgumentException prefix exc2 suffix\n" +
@@ -161,8 +170,8 @@ public class JavadocHelperTest {
                       "     */\n",
                       getSubTest,
                       " Prefix javadoc1 suffix.\n" +
-                      "@param p1  param1\n" +
                       "\n" +
+                      "@param p1 param1\n" +
                       " @param p2 prefix param2 suffix\n" +
                       " @param p3 prefix param3 suffix\n" +
                       " @throws IllegalStateException prefix exc1 suffix\n" +
@@ -189,7 +198,7 @@ public class JavadocHelperTest {
                       " @param p2 prefix param2 suffix\n" +
                       " @param p3 prefix param3 suffix\n" +
                       " @throws IllegalStateException prefix exc1 suffix\n" +
-                      "@throws java.lang.IllegalArgumentException  exc2\n" +
+                      "@throws java.lang.IllegalArgumentException exc2\n" +
                       " @throws IllegalAccessException prefix exc3 suffix\n" +
                       " @return prefix value suffix\n");
     }
@@ -214,11 +223,101 @@ public class JavadocHelperTest {
                       " @throws IllegalStateException prefix exc1 suffix\n" +
                       " @throws IllegalArgumentException prefix exc2 suffix\n" +
                       " @throws IllegalAccessException prefix exc3 suffix\n" +
-                      "@return  value\n");
+                      "@return value\n");
     }
 
+    public void testInheritAllButOne() throws Exception {
+        doTestJavadoc("    /**\n" +
+                      "     * @throws IllegalArgumentException {@inheritDoc}\n" +
+                      "     */\n",
+                      getSubTest,
+                      "javadoc1\n" +
+                      "@param p1 param1\n" +
+                      "@param p2 param2\n" +
+                      "@param p3 param3\n" +
+                      "@throws java.lang.IllegalStateException exc1\n" +
+                      " @throws IllegalArgumentException exc2\n" +
+                      "@throws java.lang.IllegalAccessException exc3\n" +
+                      "@return value\n");
+    }
+
+    public void testInheritEmpty() throws Exception {
+        doTestJavadoc("    /**\n" +
+                      "     */\n",
+                      "    /**@param p1\n" +
+                      "     * @param p2\n" +
+                      "     * @param p3\n" +
+                      "     * @throws IllegalStateException\n" +
+                      "     * @throws IllegalArgumentException\n" +
+                      "     * @throws IllegalAccessException\n" +
+                      "     * @return\n" +
+                      "     */\n",
+                      getSubTest,
+                      "\n" +
+                      "@param p1 \n" +
+                      "@param p2 \n" +
+                      "@param p3 \n" +
+                      "@throws java.lang.IllegalStateException \n" +
+                      "@throws java.lang.IllegalArgumentException \n" +
+                      "@throws java.lang.IllegalAccessException \n" +
+                      "@return \n");
+    }
+
+    public void testEmptyValue() throws Exception {
+        doTestJavadoc("    /**\n" +
+                      "     */\n",
+                      "    /**@param p1 {@value}\n" +
+                      "     * @param p2\n" +
+                      "     * @param p3\n" +
+                      "     * @throws IllegalStateException\n" +
+                      "     * @throws IllegalArgumentException\n" +
+                      "     * @throws IllegalAccessException\n" +
+                      "     * @return\n" +
+                      "     */\n",
+                      getSubTest,
+                      "\n" +
+                      "@param p1 {@value}\n" +
+                      "@param p2 \n" +
+                      "@param p3 \n" +
+                      "@throws java.lang.IllegalStateException \n" +
+                      "@throws java.lang.IllegalArgumentException \n" +
+                      "@throws java.lang.IllegalAccessException \n" +
+                      "@return \n");
+    }
+
+    public void testShortComment() throws Exception {
+        doTestJavadoc("    /**Test.*/\n",
+                      getSubTest,
+                      "Test." +
+                      "@param p1 param1\n" +
+                      "@param p2 param2\n" +
+                      "@param p3 param3\n" +
+                      "@throws java.lang.IllegalStateException exc1\n" +
+                      "@throws java.lang.IllegalArgumentException exc2\n" +
+                      "@throws java.lang.IllegalAccessException exc3\n" +
+                      "@return value\n");
+    }
 
     private void doTestJavadoc(String origJavadoc, Function<JavacTask, Element> getElement, String expectedJavadoc) throws Exception {
+        doTestJavadoc(origJavadoc,
+                      "    /**\n" +
+                      "     * javadoc1\n" +
+                      "     *\n" +
+                      "     * @param p1 param1\n" +
+                      "     * @param p2 param2\n" +
+                      "     * @param p3 param3\n" +
+                      "     * @throws IllegalStateException exc1\n" +
+                      "     * @throws IllegalArgumentException exc2\n" +
+                      "     * @throws IllegalAccessException exc3\n" +
+                      "     * @return value\n" +
+                      "     */\n",
+                      getElement, expectedJavadoc);
+    }
+
+    private void doTestJavadoc(String origJavadoc,
+                               String superJavadoc,
+                               Function<JavacTask, Element> getElement,
+                               String expectedJavadoc) throws Exception {
         JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
         String subClass =
                 "package test;\n" +
@@ -231,17 +330,7 @@ public class JavadocHelperTest {
                 "/**Top level." +
                 " */\n" +
                 "public class Super {\n" +
-                "    /**\n" +
-                "     * javadoc1A\n" +
-                "     *\n" +
-                "     * @param p1 param1A\n" +
-                "     * @param p2 param2A\n" +
-                "     * @param p3 param3A\n" +
-                "     * @throws IllegalStateException exc1A\n" +
-                "     * @throws IllegalArgumentException exc2A\n" +
-                "     * @throws IllegalAccessException exc3A\n" +
-                "     * @return valueA\n" +
-                "     */\n" +
+                superJavadoc +
                 "    public String test(int p1, int p2, int p3) throws IllegalStateException, IllegalArgumentException, IllegalAccessException { return null;} \n" +
                 "}\n";
 
@@ -293,4 +382,53 @@ public class JavadocHelperTest {
         }
 
     }
+
+    public void testAllDocs() throws IOException {
+        JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
+        DiagnosticListener<? super JavaFileObject> noErrors = d -> {
+            if (d.getKind() == Kind.ERROR) {
+                throw new AssertionError(d.getMessage(null));
+            }
+        };
+
+        List<Path> sources = new ArrayList<>();
+        Path home = Paths.get(System.getProperty("java.home"));
+        Path srcZip = home.resolve("lib").resolve("src.zip");
+        if (Files.isReadable(srcZip)) {
+            URI uri = URI.create("jar:" + srcZip.toUri());
+            try (FileSystem zipFO = FileSystems.newFileSystem(uri, Collections.emptyMap())) {
+                Path root = zipFO.getRootDirectories().iterator().next();
+
+                //modular format:
+                try (DirectoryStream<Path> ds = Files.newDirectoryStream(root)) {
+                    for (Path p : ds) {
+                        if (Files.isDirectory(p)) {
+                            sources.add(p);
+                        }
+                    }
+                }
+                try (StandardJavaFileManager fm =
+                        compiler.getStandardFileManager(null, null, null)) {
+                    JavacTask task =
+                            (JavacTask) compiler.getTask(null, fm, noErrors, null, null, null);
+                    task.getElements().getTypeElement("java.lang.Object");
+                    for (ModuleElement me : task.getElements().getAllModuleElements()) {
+                        List<ExportsDirective> exports =
+                                ElementFilter.exportsIn(me.getDirectives());
+                        for (ExportsDirective ed : exports) {
+                            try (JavadocHelper helper = JavadocHelper.create(task, sources)) {
+                                List<? extends Element> content =
+                                        ed.getPackage().getEnclosedElements();
+                                for (TypeElement clazz : ElementFilter.typesIn(content)) {
+                                    for (Element el : clazz.getEnclosedElements()) {
+                                        helper.getResolvedDocComment(el);
+                                    }
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
 }
diff --git a/test/langtools/jdk/javadoc/doclet/testBadPackageFileInJar/TestBadPackageFileInJar.java b/test/langtools/jdk/javadoc/doclet/testBadPackageFileInJar/TestBadPackageFileInJar.java
index 38dc0e7ab0c..ac9ad3b995e 100644
--- a/test/langtools/jdk/javadoc/doclet/testBadPackageFileInJar/TestBadPackageFileInJar.java
+++ b/test/langtools/jdk/javadoc/doclet/testBadPackageFileInJar/TestBadPackageFileInJar.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2002, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2002, 2017, 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
@@ -24,27 +24,52 @@
 /*
  * @test
  * @bug 4691095 6306394
- * @summary Test to make sure that Javadoc emits a useful warning
- * when a bad package.html file is in the JAR.
+ * @summary Make sure that Javadoc emits a useful warning
+ *          when a bad package.html exists in a JAR archive.
  * @author jamieh
- * @library ../lib
+ * @library /tools/lib ../lib
  * @modules jdk.javadoc/jdk.javadoc.internal.tool
- * @build JavadocTester
+ * @build JavadocTester toolbox.ToolBox toolbox.JarTask
  * @run main TestBadPackageFileInJar
  */
 
+import toolbox.JarTask;
+import toolbox.Task.Result;
+import toolbox.ToolBox;
+
+import java.io.IOException;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+
 public class TestBadPackageFileInJar extends JavadocTester {
 
+    final ToolBox tb = new ToolBox();
+
     public static void main(String... args) throws Exception {
         TestBadPackageFileInJar tester = new TestBadPackageFileInJar();
         tester.runTests();
     }
 
     @Test
-    void test() {
+    void test() throws IOException {
+        // create the file
+        Path pkgDir = Paths.get("pkg");
+        tb.createDirectories(pkgDir);
+        Path pkgfilePath = pkgDir.resolve("package.html");
+        tb.writeFile(pkgfilePath, "<html>\n\n</html>");
+
+        // create the jar file
+        Path jarFile = Paths.get("badPackageFileInJar.jar");
+        JarTask jar = new JarTask(tb, "badPackageFileInJar.jar");
+        jar.files(pkgDir.toString()).run();
+
+        // clean up to prevent accidental pick up
+        tb.cleanDirectory(pkgDir);
+        tb.deleteFiles(pkgDir.toString());
+
         javadoc("-d", "out",
                 "-sourcepath", testSrc,
-                "-classpath",  testSrc("badPackageFileInJar.jar"),
+                "-classpath",  jarFile.toString(),
                 "pkg");
         checkExit(Exit.OK);
 
diff --git a/test/langtools/jdk/javadoc/doclet/testBadPackageFileInJar/badPackageFileInJar.jar b/test/langtools/jdk/javadoc/doclet/testBadPackageFileInJar/badPackageFileInJar.jar
deleted file mode 100644
index 5e71b3b3370..00000000000
Binary files a/test/langtools/jdk/javadoc/doclet/testBadPackageFileInJar/badPackageFileInJar.jar and /dev/null differ
diff --git a/test/langtools/jdk/javadoc/doclet/testDeprecatedDocs/TestDeprecatedDocs.java b/test/langtools/jdk/javadoc/doclet/testDeprecatedDocs/TestDeprecatedDocs.java
index c8f7c7ecb9a..0d75410a240 100644
--- a/test/langtools/jdk/javadoc/doclet/testDeprecatedDocs/TestDeprecatedDocs.java
+++ b/test/langtools/jdk/javadoc/doclet/testDeprecatedDocs/TestDeprecatedDocs.java
@@ -23,7 +23,8 @@
 
 /*
  * @test
- * @bug      4927552 8026567 8071982 8162674 8175200 8175218 8183511 8186332 8169819 8074407
+ * @bug      4927552 8026567 8071982 8162674 8175200 8175218 8183511 8186332
+ *           8169819 8074407 8191030
  * @summary  test generated docs for deprecated items
  * @author   jamieh
  * @library  ../lib
@@ -257,24 +258,30 @@ public class TestDeprecatedDocs extends JavadocTester {
                 + "<td class=\"colLast\"></td>\n"
                 + "</tr>\n"
                 + "<tr class=\"rowColor\">\n"
+                + "<th class=\"colDeprecatedItemName\" scope=\"row\"><a href=\"pkg/TestAnnotationType.html#field\">pkg.TestAnnotationType.field</a></th>\n"
+                + "<td class=\"colLast\">\n"
+                + "<div class=\"deprecationComment\">annotation_test4 passes.</div>\n"
+                + "</td>\n"
+                + "</tr>\n"
+                + "<tr class=\"altColor\">\n"
                 + "<th class=\"colDeprecatedItemName\" scope=\"row\"><a href=\"pkg/TestClass.html#field\">pkg.TestClass.field</a></th>\n"
                 + "<td class=\"colLast\">\n"
                 + "<div class=\"deprecationComment\">class_test2 passes. This is the second sentence of deprecated description for a field.</div>\n"
                 + "</td>\n"
                 + "</tr>\n"
-                + "<tr class=\"altColor\">\n"
+                + "<tr class=\"rowColor\">\n"
                 + "<th class=\"colDeprecatedItemName\" scope=\"row\"><a href=\"pkg/TestError.html#field\">pkg.TestError.field</a></th>\n"
                 + "<td class=\"colLast\">\n"
                 + "<div class=\"deprecationComment\">error_test2 passes.</div>\n"
                 + "</td>\n"
                 + "</tr>\n"
-                + "<tr class=\"rowColor\">\n"
+                + "<tr class=\"altColor\">\n"
                 + "<th class=\"colDeprecatedItemName\" scope=\"row\"><a href=\"pkg/TestException.html#field\">pkg.TestException.field</a></th>\n"
                 + "<td class=\"colLast\">\n"
                 + "<div class=\"deprecationComment\">exception_test2 passes.</div>\n"
                 + "</td>\n"
                 + "</tr>\n"
-                + "<tr class=\"altColor\">\n"
+                + "<tr class=\"rowColor\">\n"
                 + "<th class=\"colDeprecatedItemName\" scope=\"row\"><a href=\"pkg/TestInterface.html#field\">pkg.TestInterface.field</a></th>\n"
                 + "<td class=\"colLast\">\n"
                 + "<div class=\"deprecationComment\">interface_test2 passes.</div>\n"
diff --git a/test/langtools/jdk/javadoc/doclet/testGroupOption/InUnnamedPackage.java b/test/langtools/jdk/javadoc/doclet/testGroupOption/InUnnamedPackage.java
new file mode 100644
index 00000000000..df1ca5b3085
--- /dev/null
+++ b/test/langtools/jdk/javadoc/doclet/testGroupOption/InUnnamedPackage.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright (c) 2016, 2017, 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.
+ */
+
+public class InUnnamedPackage {}
diff --git a/test/langtools/jdk/javadoc/doclet/testGroupOption/TestGroupOption.java b/test/langtools/jdk/javadoc/doclet/testGroupOption/TestGroupOption.java
index 54f11eb9881..246aa79be84 100644
--- a/test/langtools/jdk/javadoc/doclet/testGroupOption/TestGroupOption.java
+++ b/test/langtools/jdk/javadoc/doclet/testGroupOption/TestGroupOption.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2003, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2017, 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
@@ -23,9 +23,9 @@
 
 /*
  * @test
- * @bug      4924383
- * @summary  Test to make sure the -group option does not cause a bad warning
- *           to be printed. Test for the group defined using patterns.
+ * @bug      4924383 8149402
+ * @summary  Test to make sure the -group option works correctly
+ *           with the given pattern usages.
  * @author   jamieh
  * @library  ../lib
  * @modules jdk.javadoc/jdk.javadoc.internal.tool
@@ -55,8 +55,7 @@ public class TestGroupOption extends JavadocTester {
                 "-group");
     }
 
-    // @Test
-    // @ignore 8149402
+     @Test
     // Make sure the "Other packages" section is printed and the header for empty section is not.
     // Make sure that the headers of group that is defined using patterns are printed.
     void test2() {
@@ -66,7 +65,7 @@ public class TestGroupOption extends JavadocTester {
                 "-group", "Group abc*", "abc*",
                 "-group", "Empty group", "qwerty*",
                 "-group", "Group a*", "a*",
-                "pkg1", "pkg2", "pkg3", "abc1",  "abc2", "abc3", "other", testSrc("C.java"));
+                "pkg1", "pkg2", "pkg3", "abc1",  "abc2", "abc3", "other", testSrc("InUnnamedPackage.java"));
         checkExit(Exit.OK);
 
         checkOutput("overview-summary.html", true, "Group pkg*", "Group abc*", "Other Packages");
diff --git a/test/langtools/jdk/javadoc/doclet/testSerializedForm/TestSerializedForm.java b/test/langtools/jdk/javadoc/doclet/testSerializedForm/TestSerializedForm.java
index ba8bec7c0fd..496eec4ed38 100644
--- a/test/langtools/jdk/javadoc/doclet/testSerializedForm/TestSerializedForm.java
+++ b/test/langtools/jdk/javadoc/doclet/testSerializedForm/TestSerializedForm.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2001, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2001, 2017, 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
@@ -23,7 +23,7 @@
 
 /*
  * @test
- * @bug 4341304 4485668 4966728 8032066 8071982
+ * @bug 4341304 4485668 4966728 8032066 8071982 8192933
  * @summary Test that methods readResolve and writeReplace show
  * up in serialized-form.html the same way that readObject and writeObject do.
  * If the doclet includes readResolve and writeReplace in the serialized-form
@@ -114,4 +114,21 @@ public class TestSerializedForm extends JavadocTester {
                 + "title=\"class in pkg1\">pkg1.PublicExcludeInnerClass.PubInnerClass</a> "
                 + "extends java.lang.Object implements Serializable</h3>");
     }
+
+    @Test
+    void test2() {
+        javadoc("-private",
+                "-d", "out-2",
+                "-sourcepath", testSrc,
+                "pkg2");
+        checkExit(Exit.OK);
+
+        checkOrder("serialized-form.html",
+                "int[] a1",
+                "int[][] a2",
+                "<a href=\"pkg2/Fields.html\" title=\"class in pkg2\">Fields</a>[][] doubleArray",
+                "<a href=\"pkg2/Fields.html\" title=\"class in pkg2\">Fields</a>[] singleArray",
+                "java.lang.Class&lt;<a href=\"pkg2/Fields.html\" "
+                + "title=\"type parameter in Fields\">E</a>&gt; someClass");
+    }
 }
diff --git a/test/langtools/jdk/javadoc/doclet/testSerializedForm/pkg2/Fields.java b/test/langtools/jdk/javadoc/doclet/testSerializedForm/pkg2/Fields.java
new file mode 100644
index 00000000000..6d8fad00aee
--- /dev/null
+++ b/test/langtools/jdk/javadoc/doclet/testSerializedForm/pkg2/Fields.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2017, 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 pkg2;
+
+public class Fields<E> implements java.io.Serializable {
+    /** Some doc. */
+    public Class<E> someClass;
+
+    /** a primitive array */
+    private int[] a1;
+
+    /** a two dimensional primitive array */
+    private int[][] a2;
+
+    /** a single object array */
+    private Fields[] singleArray;
+
+    /** a double object array */
+    private Fields[][] doubleArray;
+
+}
diff --git a/test/langtools/jdk/javadoc/doclet/testValueTag/TestValueTag.java b/test/langtools/jdk/javadoc/doclet/testValueTag/TestValueTag.java
index da9c775b0e8..78dff7b72ec 100644
--- a/test/langtools/jdk/javadoc/doclet/testValueTag/TestValueTag.java
+++ b/test/langtools/jdk/javadoc/doclet/testValueTag/TestValueTag.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2003, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2017, 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
@@ -23,9 +23,9 @@
 
 /*
  * @test
- * @bug      4764045 8004825 8026567
+ * @bug      4764045 8004825 8026567 8191030
  * @summary  This test ensures that the value tag works in all
- * use cases. The explainations for each test case are written below.
+ *           use cases, the tests are explained below.
  * @author   jamieh
  * @library ../lib
  * @modules jdk.javadoc/jdk.javadoc.internal.tool
@@ -103,7 +103,7 @@ public class TestValueTag extends JavadocTester {
         );
 
         checkOutput("pkg1/Class1.html", false,
-                //Base case:  using @value on a constant.
+                // Base case:  using @value on a constant.
                 "Result:  <a href=\"../pkg1/Class1.html#TEST_12_ERROR\">\"Test 12 "
                 + "generates an error message\"</a>");
 
@@ -119,23 +119,36 @@ public class TestValueTag extends JavadocTester {
                 "pkg1", "pkg2");
         checkExit(Exit.OK);
         checkOutput(Output.OUT, true,
-                //Test @value warning printed when used with non-constant.
+                // Test @value warning printed when used with non-constant.
                 "warning - @value tag (which references nonConstant) "
                 + "can only be used in constants.",
                 "warning - @value tag (which references NULL) "
                 + "can only be used in constants.",
                 "warning - @value tag (which references TEST_12_ERROR) "
-                + "can only be used in constants."
-// TODO: re-examine these.
-//                //Test warning printed for bad reference.
-//                "warning - UnknownClass#unknownConstant (referenced by "
-//                + "@value tag) is an unknown reference.",
-//                //Test warning printed for invalid use of @value.
-//                "warning - @value tag cannot be used here."
+                + "can only be used in constants.",
+                // Test warning printed for bad reference.
+                "warning - {@value UnknownClass#unknownConstant}"
+                + " (referenced by @value tag) is an unknown reference."
         );
         checkForException();
     }
 
+    @Test()
+    void test3() {
+        javadoc("-d", "out3",
+                "-sourcepath", testSrc,
+                "pkg2", "pkg3");
+        checkExit(Exit.OK);
+
+        checkOrder("pkg3/RT.html",
+                "The value is <a href=\"../pkg3/RT.html#CONSTANT\">\"constant\"</a>.",
+                "The value1 is <a href=\"../pkg3/RT.html#CONSTANT\">\"constant\"</a>.",
+                "The value2 is <a href=\"../pkg3/RT.html#CONSTANT\">\"constant\"</a>.",
+                "The value3 is <a href=\"../pkg2/Class3.html#TEST_12_PASSES\">"
+                + "\"Test 12 passes\"</a>.");
+    }
+
+
     void checkForException() {
         checkOutput(Output.STDERR, false, "DocletAbortException");
     }
diff --git a/test/langtools/jdk/javadoc/doclet/testValueTag/pkg3/RT.java b/test/langtools/jdk/javadoc/doclet/testValueTag/pkg3/RT.java
new file mode 100644
index 00000000000..f6917390aa1
--- /dev/null
+++ b/test/langtools/jdk/javadoc/doclet/testValueTag/pkg3/RT.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2017, 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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 pkg3;
+
+public @interface RT {
+
+    /** The CONSTANT */
+    static String CONSTANT = "constant";
+    /**
+     *  The value is {@value CONSTANT}.
+     *  @return a value
+     */
+     int value();
+
+    /**
+     *  The value1 is {@value #CONSTANT}.
+     *  @return a value
+     */
+    int value1();
+
+    /**
+     *  The value2 is {@value pkg3.RT#CONSTANT}.
+     *  @return a value
+     */
+     int value2();
+
+    /**
+     *  The value3 is {@value pkg2.Class3#TEST_12_PASSES}.
+     *  @return a value
+     */
+    int value3();
+
+}
diff --git a/test/langtools/jdk/javadoc/tool/testPackages/TestPackages.java b/test/langtools/jdk/javadoc/tool/testPackages/TestPackages.java
new file mode 100644
index 00000000000..5f20a2d26c5
--- /dev/null
+++ b/test/langtools/jdk/javadoc/tool/testPackages/TestPackages.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug 8191078
+ * @summary ensure that javadoc considers packages correctly
+ * @modules jdk.javadoc/jdk.javadoc.internal.tool
+ *          jdk.compiler/com.sun.tools.javac.api
+ *          jdk.compiler/com.sun.tools.javac.main
+ *          jdk.javadoc/jdk.javadoc.internal.api
+ *          jdk.javadoc/jdk.javadoc.internal.tool
+ * @library /tools/lib
+ * @build toolbox.JavadocTask toolbox.TestRunner toolbox.ToolBox
+ * @run main TestPackages
+ */
+
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.List;
+
+import toolbox.*;
+import toolbox.Task.Expect;
+import toolbox.Task.OutputKind;
+import toolbox.Task.Result;
+
+public class TestPackages extends TestRunner {
+
+    final ToolBox tb = new ToolBox();
+
+    public TestPackages() {
+        super(System.err);
+    }
+
+    public static void main(String[] args) throws Exception {
+        TestPackages t = new TestPackages();
+        t.runTests(m -> new Object[] { Paths.get(m.getName()) });
+    }
+
+    @Test
+    public void testEmptyPackage(Path base) throws Exception {
+        Files.createDirectories(base);
+        tb.writeFile(base.resolve("p1/package-info.java"), "package p1;\n");
+        tb.writeFile(base.resolve("p2/A.java"), "package p2;\npublic class A {}\n");
+
+        Path outDir = base.resolve("out");
+        Files.createDirectory(outDir);
+        JavadocTask task = new JavadocTask(tb);
+        task = task.outdir(outDir).sourcepath(base).options("p1", "p2");
+        Result r = task.run(Expect.SUCCESS);
+        List<String> list = tb.grep(".*warning.*not found.*", r.getOutputLines(OutputKind.DIRECT));
+        if (!list.isEmpty()) {
+            throw new Exception("Found warning: " + list.get(0));
+        }
+    }
+}
diff --git a/test/langtools/jdk/jshell/ClassesTest.java b/test/langtools/jdk/jshell/ClassesTest.java
index bcdd597da62..121cafdc441 100644
--- a/test/langtools/jdk/jshell/ClassesTest.java
+++ b/test/langtools/jdk/jshell/ClassesTest.java
@@ -23,7 +23,7 @@
 
 /*
  * @test
- * @bug 8145239 8129559 8080354
+ * @bug 8145239 8129559 8080354 8189248
  * @summary Tests for EvaluationState.classes
  * @build KullaTesting TestingInputStream ExpectedDiagnostic
  * @run testng ClassesTest
@@ -41,6 +41,7 @@ import org.testng.annotations.DataProvider;
 import org.testng.annotations.Test;
 
 import jdk.jshell.Diag;
+import jdk.jshell.Snippet.Status;
 import static java.util.stream.Collectors.toList;
 import static jdk.jshell.Snippet.Status.VALID;
 import static jdk.jshell.Snippet.Status.RECOVERABLE_NOT_DEFINED;
@@ -327,4 +328,24 @@ public class ClassesTest extends KullaTesting {
         VarSnippet variableKey = varKey(assertEval("a.x;"));
         assertEquals(variableKey.typeName(), "A.I1");
     }
+
+    public void testCircular() {
+        assertEval("import java.util.function.Supplier;");
+        TypeDeclSnippet aClass =
+                classKey(assertEval("public class A<T> {\n" +
+                                    "  private class SomeClass {}\n" +
+                                    "  public Supplier<T> m() {\n" +
+                                    "    return new B<>(this);\n" +
+                                    "  }\n" +
+                                    "}",
+                                   added(RECOVERABLE_DEFINED)));
+        assertEval("public class B<T> implements Supplier<T> {\n" +
+                   "  public B(A<T> a) {}\n" +
+                   "  public T get() {return null;}\n" +
+                   "}",
+                   added(VALID),
+                   ste(aClass, Status.RECOVERABLE_DEFINED, Status.VALID, true, null));
+        assertEval("new A()");
+    }
+
 }
diff --git a/test/langtools/tools/javac/6330997/T6330997.java b/test/langtools/tools/javac/6330997/T6330997.java
index 3ffdd78638d..3e4d8e535ed 100644
--- a/test/langtools/tools/javac/6330997/T6330997.java
+++ b/test/langtools/tools/javac/6330997/T6330997.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2006, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2006, 2017, 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
@@ -23,7 +23,7 @@
 
 /**
  * @test
- * @bug     6330997 7025789 8000961
+ * @bug     6330997 7025789 8000961 8188870
  * @summary javac should accept class files with major version of the next release
  * @author  Wei Tao
  * @modules jdk.compiler/com.sun.tools.javac.api
@@ -32,8 +32,8 @@
  *          jdk.compiler/com.sun.tools.javac.main
  *          jdk.compiler/com.sun.tools.javac.util
  * @clean T1 T2
- * @compile -source 9 -target 9 T1.java
- * @compile -source 9 -target 9 T2.java
+ * @compile -source 10 -target 10 T1.java
+ * @compile -source 10 -target 10 T2.java
  * @run main/othervm T6330997
  */
 
diff --git a/test/langtools/tools/javac/T5090006/AssertionFailureTest.java b/test/langtools/tools/javac/T5090006/AssertionFailureTest.java
deleted file mode 100644
index 334754cdb01..00000000000
--- a/test/langtools/tools/javac/T5090006/AssertionFailureTest.java
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * Copyright (c) 2013, 2016, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-
-/*
- * @test
- * @bug 5090006
- * @summary javac fails with assertion error
- * @library /tools/lib
- * @modules jdk.compiler/com.sun.tools.javac.api
- *          jdk.compiler/com.sun.tools.javac.main
- *          jdk.jdeps/com.sun.tools.javap
- * @build toolbox.ToolBox toolbox.JavacTask
- * @run main AssertionFailureTest
- */
-
-import java.io.File;
-import java.nio.file.Paths;
-
-import toolbox.JavacTask;
-import toolbox.ToolBox;
-
-// Original test: test/tools/javac/T5090006/compiler.sh
-public class AssertionFailureTest {
-
-    private static final String testSrc =
-        "import stub_tie_gen.wsdl_hello_lit.client.*;\n" +
-        "import junit.framework.*;\n" +
-        "import testutil.ClientServerTestUtil;\n" +
-        "\n" +
-        "public class Test {\n" +
-        "\n" +
-        "    void getStub() throws Exception {\n" +
-        "        Hello_PortType_Stub x = null;\n" +
-        "        new ClientServerTestUtil().setTransport(x, null, null, null);\n" +
-        "    }\n" +
-        "\n" +
-        "    public static void main(String[] args) {\n" +
-        "        System.out.println(\"FISK\");\n" +
-        "    }\n" +
-        "}";
-
-    public static void main(String args[]) throws Exception {
-        ToolBox tb = new ToolBox();
-        String classpath = Paths.get(tb.testSrc, "broken.jar")
-                + File.pathSeparator
-                + ".";
-        new JavacTask(tb)
-                .classpath(classpath)
-                .sources(testSrc)
-                .run();
-    }
-
-}
diff --git a/test/langtools/tools/javac/T5090006/broken.jar b/test/langtools/tools/javac/T5090006/broken.jar
deleted file mode 100644
index 8127ec4b203..00000000000
Binary files a/test/langtools/tools/javac/T5090006/broken.jar and /dev/null differ
diff --git a/test/langtools/tools/javac/T8192885/AddGotoAfterForLoopToLNTTest.java b/test/langtools/tools/javac/T8192885/AddGotoAfterForLoopToLNTTest.java
new file mode 100644
index 00000000000..5d5df2c4ff4
--- /dev/null
+++ b/test/langtools/tools/javac/T8192885/AddGotoAfterForLoopToLNTTest.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug 8192885
+ * @summary Compiler in JDK 10-ea+33 misses to include entry in LineNumberTable for goto instruction of foreach loop
+ * @library /tools/lib
+ * @modules jdk.jdeps/com.sun.tools.classfile
+ *          jdk.compiler/com.sun.tools.javac.api
+ *          jdk.compiler/com.sun.tools.javac.main
+ *          jdk.compiler/com.sun.tools.javac.util
+ *          jdk.jdeps/com.sun.tools.javap
+ * @build toolbox.ToolBox toolbox.JavacTask
+ * @run main AddGotoAfterForLoopToLNTTest
+ */
+
+import java.io.File;
+import java.nio.file.Paths;
+
+import com.sun.tools.classfile.ClassFile;
+import com.sun.tools.classfile.Code_attribute;
+import com.sun.tools.classfile.LineNumberTable_attribute;
+import com.sun.tools.classfile.Method;
+import com.sun.tools.javac.util.Assert;
+
+import toolbox.JavacTask;
+import toolbox.ToolBox;
+
+public class AddGotoAfterForLoopToLNTTest {
+
+    static final String testSource =
+    /* 01 */        "class GotoAtEnhancedForTest {\n" +
+    /* 02 */        "    void lookForThisMethod() {\n" +
+    /* 03 */        "        for (Object o : java.util.Collections.emptyList()) {\n" +
+    /* 04 */        "        }\n" +
+    /* 05 */        "    }\n" +
+    /* 06 */        "}";
+
+    static final int[][] expectedLNT = {
+    //  {line-number, start-pc},
+        {3,           0},
+        {4,           25},
+        {3,           28},
+        {5,           30},
+    };
+
+    static final String methodToLookFor = "lookForThisMethod";
+
+    public static void main(String[] args) throws Exception {
+        new AddGotoAfterForLoopToLNTTest().run();
+    }
+
+    ToolBox tb = new ToolBox();
+
+    void run() throws Exception {
+        compileTestClass();
+        checkClassFile(new File(Paths.get(System.getProperty("user.dir"),
+                "GotoAtEnhancedForTest.class").toUri()), methodToLookFor);
+    }
+
+    void compileTestClass() throws Exception {
+        new JavacTask(tb)
+                .sources(testSource)
+                .run();
+    }
+
+    void checkClassFile(final File cfile, String methodToFind) throws Exception {
+        ClassFile classFile = ClassFile.read(cfile);
+        boolean methodFound = false;
+        for (Method method : classFile.methods) {
+            if (method.getName(classFile.constant_pool).equals(methodToFind)) {
+                methodFound = true;
+                Code_attribute code = (Code_attribute) method.attributes.get("Code");
+                LineNumberTable_attribute lnt =
+                        (LineNumberTable_attribute) code.attributes.get("LineNumberTable");
+                Assert.check(lnt.line_number_table_length == expectedLNT.length,
+                        "The LineNumberTable found has a length different to the expected one");
+                int i = 0;
+                for (LineNumberTable_attribute.Entry entry: lnt.line_number_table) {
+                    Assert.check(entry.line_number == expectedLNT[i][0] &&
+                            entry.start_pc == expectedLNT[i][1],
+                            "LNT entry at pos " + i + " differ from expected." +
+                            "Found " + entry.line_number + ":" + entry.start_pc +
+                            ". Expected " + expectedLNT[i][0] + ":" + expectedLNT[i][1]);
+                    i++;
+                }
+            }
+        }
+        Assert.check(methodFound, "The seek method was not found");
+    }
+
+    void error(String msg) {
+        throw new AssertionError(msg);
+    }
+}
diff --git a/test/langtools/tools/javac/classfiles/ClassVersionChecker.java b/test/langtools/tools/javac/classfiles/ClassVersionChecker.java
index 419ec1313da..4a6cf188c89 100644
--- a/test/langtools/tools/javac/classfiles/ClassVersionChecker.java
+++ b/test/langtools/tools/javac/classfiles/ClassVersionChecker.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2017, 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
@@ -23,7 +23,7 @@
 
 /*
  * @test
- * @bug 7157626 8001112
+ * @bug 7157626 8001112 8188870
  * @summary Test major version for all legal combinations for -source and -target
  * @author sgoel
  *
@@ -38,7 +38,7 @@ import java.util.regex.*;
 public class ClassVersionChecker {
 
     int errors;
-    String[] jdk = {"", "1.6", "1.7", "1.8", "1.9"};
+    String[] jdk = {"", "1.6", "1.7", "1.8", "1.9", "1.10"};
     File javaFile = null;
 
     public static void main(String[] args) throws Throwable {
@@ -58,10 +58,11 @@ public class ClassVersionChecker {
          * -1 => invalid combinations
          */
         int[][] ver =
-                {{53, -1, -1, -1, -1},
-                 {53, 50, 51, 52, 53},
-                 {53, -1, 51, 52, 53},
-                 {53, -1, -1, 52, 53}};
+                {{54, -1, -1, -1, -1, -1},
+                 {54, 50, 51, 52, 53, 54},
+                 {54, -1, 51, 52, 53, 54},
+                 {54, -1, -1, 52, 53, 54},
+                 {54, -1, -1, -1, 53, 54}};
 
         // Loop to run all possible combinations of source/target values
         for (int i = 0; i< ver.length; i++) {
diff --git a/test/langtools/tools/javac/flow/tests/TestCaseForEach.java b/test/langtools/tools/javac/flow/tests/TestCaseForEach.java
index e97070bc0fb..978ae3fbb7e 100644
--- a/test/langtools/tools/javac/flow/tests/TestCaseForEach.java
+++ b/test/langtools/tools/javac/flow/tests/TestCaseForEach.java
@@ -3,7 +3,7 @@
 public class TestCaseForEach {
 
     @AliveRange(varName="o", bytecodeStart=25, bytecodeLength=11)
-    @AliveRange(varName="o", bytecodeStart=44, bytecodeLength=1)
+    @AliveRange(varName="o", bytecodeStart=41, bytecodeLength=1)
     void m(String[] args) {
         Object o;
         for (String s : args) {
diff --git a/test/langtools/tools/javac/lambda/ImplicitEnclosingInstanceTest.java b/test/langtools/tools/javac/lambda/ImplicitEnclosingInstanceTest.java
new file mode 100644
index 00000000000..71d4fb824ce
--- /dev/null
+++ b/test/langtools/tools/javac/lambda/ImplicitEnclosingInstanceTest.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/**
+ * @test
+ * @bug 8182401
+ * @summary Verification error for enclosing instance capture inside super constructor invocation
+ * @run main ImplicitEnclosingInstanceTest
+ */
+
+import java.util.function.Function;
+
+public class ImplicitEnclosingInstanceTest {
+
+    static String cookie = "deadbeef";
+
+    static Object f(Function<String, Object> f) {
+        return f.apply("feed");
+    }
+
+    class S {
+        S(Object s) {
+            cookie += "face";
+        }
+    }
+
+    class A {
+        A(String s) {
+            cookie = s;
+        }
+    }
+
+    class B extends S {
+        B() {
+            super(f(s->new A(s)));
+        }
+    }
+
+    public static void main(String[] args) {
+        new ImplicitEnclosingInstanceTest().new B();
+        if (!cookie.equals("feedface"))
+            throw new AssertionError("Incorrect cookie!");
+    }
+}
\ No newline at end of file
diff --git a/test/langtools/tools/javac/lambda/InnerInstanceCreationTest.java b/test/langtools/tools/javac/lambda/InnerInstanceCreationTest.java
new file mode 100644
index 00000000000..6a56e460db8
--- /dev/null
+++ b/test/langtools/tools/javac/lambda/InnerInstanceCreationTest.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/**
+ * @test
+ * @bug 8182401 8178444
+ * @summary Verification error for enclosing instance capture inside super constructor invocation
+ * @run main InnerInstanceCreationTest
+ */
+
+import java.util.function.Supplier;
+
+public class InnerInstanceCreationTest {
+
+    static String cookie = "";
+
+    public static void main(String[] args) {
+        new InnerInstanceCreationTest().new Producer();
+        new InnerInstanceCreationTest().new Producer(0);
+        new InnerInstanceCreationTest().new Producer("");
+        if (!cookie.equals("BlahBlahBlah"))
+            throw new AssertionError("Unexpected cookie");
+    }
+
+    class Inner {
+        Inner() {
+            cookie += "Blah";
+        }
+    }
+
+    class Producer {
+        Producer() {
+            this(Inner::new);
+        }
+        Producer(int x) {
+            this(() -> new Inner());
+        }
+        Producer(String s) {
+            this(() -> InnerInstanceCreationTest.this.new Inner());
+        }
+        Producer(Supplier<Object> supplier) {
+            supplier.get();
+        }
+    }
+}
diff --git a/test/langtools/tools/javac/lambda/methodReference/ImplicitEnclosingInstanceTest.java b/test/langtools/tools/javac/lambda/methodReference/ImplicitEnclosingInstanceTest.java
new file mode 100644
index 00000000000..a2c2a0c5d40
--- /dev/null
+++ b/test/langtools/tools/javac/lambda/methodReference/ImplicitEnclosingInstanceTest.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/**
+ * @test
+ * @bug 8182401
+ * @summary Verification error for enclosing instance capture inside super constructor invocation
+ * @run main ImplicitEnclosingInstanceTest
+ */
+
+
+import java.util.function.Function;
+
+public class ImplicitEnclosingInstanceTest {
+    static String cookie = "deadbeef";
+    static Object f(Function<String, Object> f) {
+        return f.apply("feed");
+    }
+
+    class S {
+        S(Object s) {
+            cookie += "face";
+        }
+    }
+
+    class A {
+        A(String s) {
+            cookie = s;
+        }
+    }
+
+    class B extends S {
+        B() {
+            super(f(A::new));
+        }
+    }
+
+    public static void main(String[] args) {
+        new ImplicitEnclosingInstanceTest().new B();
+        if (!cookie.equals("feedface"))
+            throw new AssertionError("Incorrect cookie!");
+    }
+}
\ No newline at end of file
diff --git a/test/langtools/tools/javac/lib/JavacTestingAbstractProcessor.java b/test/langtools/tools/javac/lib/JavacTestingAbstractProcessor.java
index 6509542c1ba..72c758d20d6 100644
--- a/test/langtools/tools/javac/lib/JavacTestingAbstractProcessor.java
+++ b/test/langtools/tools/javac/lib/JavacTestingAbstractProcessor.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2017, 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
@@ -31,13 +31,7 @@ import static javax.lang.model.SourceVersion.*;
  * An abstract annotation processor tailored to {@code javac} regression testing.
  */
 public abstract class JavacTestingAbstractProcessor extends AbstractProcessor {
-    private static final Set<String> allAnnotations;
-
-    static {
-        Set<String> tmp = new HashSet<>();
-        tmp.add("*");
-        allAnnotations = Collections.unmodifiableSet(tmp);
-    }
+    private static final Set<String> allAnnotations = Set.of("*");
 
     protected Elements eltUtils;
     protected Elements elements;
@@ -116,7 +110,7 @@ public abstract class JavacTestingAbstractProcessor extends AbstractProcessor {
      * corresponding platform visitor type.
      */
 
-    @SupportedSourceVersion(RELEASE_9)
+    @SupportedSourceVersion(RELEASE_10)
     public static abstract class AbstractAnnotationValueVisitor<R, P> extends AbstractAnnotationValueVisitor9<R, P> {
 
         /**
@@ -127,7 +121,7 @@ public abstract class JavacTestingAbstractProcessor extends AbstractProcessor {
         }
     }
 
-    @SupportedSourceVersion(RELEASE_9)
+    @SupportedSourceVersion(RELEASE_10)
     public static abstract class AbstractElementVisitor<R, P> extends AbstractElementVisitor9<R, P> {
         /**
          * Constructor for concrete subclasses to call.
@@ -137,7 +131,7 @@ public abstract class JavacTestingAbstractProcessor extends AbstractProcessor {
         }
     }
 
-    @SupportedSourceVersion(RELEASE_9)
+    @SupportedSourceVersion(RELEASE_10)
     public static abstract class AbstractTypeVisitor<R, P> extends AbstractTypeVisitor9<R, P> {
         /**
          * Constructor for concrete subclasses to call.
@@ -147,7 +141,7 @@ public abstract class JavacTestingAbstractProcessor extends AbstractProcessor {
         }
     }
 
-    @SupportedSourceVersion(RELEASE_9)
+    @SupportedSourceVersion(RELEASE_10)
     public static class ElementKindVisitor<R, P> extends ElementKindVisitor9<R, P> {
         /**
          * Constructor for concrete subclasses; uses {@code null} for the
@@ -168,7 +162,7 @@ public abstract class JavacTestingAbstractProcessor extends AbstractProcessor {
         }
     }
 
-    @SupportedSourceVersion(RELEASE_9)
+    @SupportedSourceVersion(RELEASE_10)
     public static class ElementScanner<R, P> extends ElementScanner9<R, P> {
         /**
          * Constructor for concrete subclasses; uses {@code null} for the
@@ -187,7 +181,7 @@ public abstract class JavacTestingAbstractProcessor extends AbstractProcessor {
         }
     }
 
-    @SupportedSourceVersion(RELEASE_9)
+    @SupportedSourceVersion(RELEASE_10)
     public static class SimpleAnnotationValueVisitor<R, P> extends SimpleAnnotationValueVisitor9<R, P> {
         /**
          * Constructor for concrete subclasses; uses {@code null} for the
@@ -208,7 +202,7 @@ public abstract class JavacTestingAbstractProcessor extends AbstractProcessor {
         }
     }
 
-    @SupportedSourceVersion(RELEASE_9)
+    @SupportedSourceVersion(RELEASE_10)
     public static class SimpleElementVisitor<R, P> extends SimpleElementVisitor9<R, P> {
         /**
          * Constructor for concrete subclasses; uses {@code null} for the
@@ -229,7 +223,7 @@ public abstract class JavacTestingAbstractProcessor extends AbstractProcessor {
         }
     }
 
-    @SupportedSourceVersion(RELEASE_9)
+    @SupportedSourceVersion(RELEASE_10)
     public static class SimpleTypeVisitor<R, P> extends SimpleTypeVisitor9<R, P> {
         /**
          * Constructor for concrete subclasses; uses {@code null} for the
@@ -250,7 +244,7 @@ public abstract class JavacTestingAbstractProcessor extends AbstractProcessor {
         }
     }
 
-    @SupportedSourceVersion(RELEASE_9)
+    @SupportedSourceVersion(RELEASE_10)
     public static class TypeKindVisitor<R, P> extends TypeKindVisitor9<R, P> {
         /**
          * Constructor for concrete subclasses to call; uses {@code null}
diff --git a/test/langtools/tools/javac/options/IsSupportedOptionTest.java b/test/langtools/tools/javac/options/IsSupportedOptionTest.java
new file mode 100644
index 00000000000..1dbf52d52d5
--- /dev/null
+++ b/test/langtools/tools/javac/options/IsSupportedOptionTest.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug 8189782
+ * @summary Test for isSupportedOption
+ * @modules java.compiler
+ *          jdk.compiler
+ * @run main IsSupportedOptionTest
+ */
+
+import javax.tools.JavaCompiler;
+import javax.tools.ToolProvider;
+
+/**
+ * Tests for JavaCompiler.isSupportedOption method.
+ */
+public class IsSupportedOptionTest {
+    public static void main(String... args) throws Exception {
+        new IsSupportedOptionTest().run();
+    }
+
+    public void run() throws Exception {
+        JavaCompiler tool = ToolProvider.getSystemJavaCompiler();
+        check(tool, "-source", 1);
+        check(tool, "--add-modules", 1);
+        check(tool, "-verbose", 0);
+        check(tool, "-proc:none", 0);
+        check(tool, "-Xlint", 0);
+        check(tool, "-Xlint:unchecked", 0);
+        check(tool, "-Xdoclint", 0);
+        check(tool, "-Xdoclint:stats", 0);
+        check(tool, "-Xdoclint/package:foo", 0);
+        check(tool, "--debug:any", 0);
+        check(tool, "-g", 0);
+        check(tool, "-g:vars", 0);
+        check(tool, "-g:none", 0);
+        check(tool, "-ZZZ", -1);
+        check(tool, "-Afoobar", 0);
+
+        try {
+            check(tool, null, -1);
+            throw new AssertionError("null was accepted without exception");
+        } catch (NullPointerException e) {
+        }
+    }
+
+    private void check(JavaCompiler tool, String option, int numArgs) {
+        System.err.println("check " + option);
+        int n = tool.isSupportedOption(option);
+        if (n != numArgs) {
+            throw new AssertionError("unexpected result for option: " + option + ": " + n);
+        }
+    }
+}
+
diff --git a/test/langtools/tools/javac/versions/Versions.java b/test/langtools/tools/javac/versions/Versions.java
index 9d6440038a0..52dfb9c5bc2 100644
--- a/test/langtools/tools/javac/versions/Versions.java
+++ b/test/langtools/tools/javac/versions/Versions.java
@@ -23,7 +23,7 @@
 
 /*
  * @test
- * @bug 4981566 5028634 5094412 6304984 7025786 7025789 8001112 8028545 8000961 8030610 8028546
+ * @bug 4981566 5028634 5094412 6304984 7025786 7025789 8001112 8028545 8000961 8030610 8028546 8188870
  * @summary Check interpretation of -target and -source options
  * @modules java.compiler
  *          jdk.compiler
@@ -64,12 +64,12 @@ public class Versions {
         String TC = "";
         System.out.println("Version.java: Starting");
 
-        check("53.0");
-        check("53.0", "-source 1.6");
-        check("53.0", "-source 1.7");
-        check("53.0", "-source 1.8");
-        check("53.0", "-source 1.9");
-        check("53.0", "-source 1.10");
+        check("54.0");
+        check("54.0", "-source 1.6");
+        check("54.0", "-source 1.7");
+        check("54.0", "-source 1.8");
+        check("54.0", "-source 1.9");
+        check("54.0", "-source 1.10");
 
         check_source_target("50.0", "6", "6");
         check_source_target("51.0", "6", "7");
@@ -81,7 +81,11 @@ public class Versions {
         check_source_target("53.0", "7", "9");
         check_source_target("53.0", "8", "9");
         check_source_target("53.0", "9", "9");
-        check_source_target("53.0", "10", "10");
+        check_source_target("54.0", "6", "10");
+        check_source_target("54.0", "7", "10");
+        check_source_target("54.0", "8", "10");
+        check_source_target("54.0", "9", "10");
+        check_source_target("54.0", "10", "10");
 
         checksrc16("-source 1.6");
         checksrc16("-source 6");
@@ -99,7 +103,6 @@ public class Versions {
         checksrc19("-source 9");
         checksrc19("-source 1.9", "-target 1.9");
         checksrc19("-source 9", "-target 9");
-
         checksrc110();
         checksrc110("-source 1.10");
         checksrc110("-source 10");
diff --git a/test/lib/jdk/test/lib/Platform.java b/test/lib/jdk/test/lib/Platform.java
index 16cfc52ccbd..d38a137068e 100644
--- a/test/lib/jdk/test/lib/Platform.java
+++ b/test/lib/jdk/test/lib/Platform.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2017, 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
@@ -288,4 +288,16 @@ public class Platform {
             return "so";
         }
     }
+
+    /*
+     * This should match the #if condition in ClassListParser::load_class_from_source().
+     */
+    public static boolean areCustomLoadersSupportedForCDS() {
+        boolean isLinux = Platform.isLinux();
+        boolean is64 = Platform.is64bit();
+        boolean isSolaris = Platform.isSolaris();
+        boolean isAix = Platform.isAix();
+
+        return (is64 && (isLinux || isSolaris || isAix));
+    }
 }
diff --git a/test/lib/jdk/test/lib/cds/CDSTestUtils.java b/test/lib/jdk/test/lib/cds/CDSTestUtils.java
index 8d0f9ef4bcb..5b1dd1846f8 100644
--- a/test/lib/jdk/test/lib/cds/CDSTestUtils.java
+++ b/test/lib/jdk/test/lib/cds/CDSTestUtils.java
@@ -166,9 +166,9 @@ public class CDSTestUtils {
             outStr.contains("Unable to map ReadWrite shared space at required address") ||
             outStr.contains("Unable to map MiscData shared space at required address") ||
             outStr.contains("Unable to map MiscCode shared space at required address") ||
-            outStr.contains("Unable to map shared string space at required address") ||
+            outStr.contains("Unable to map OptionalData shared space at required address") ||
             outStr.contains("Could not allocate metaspace at a compatible address") ||
-            outStr.contains("Unable to allocate shared string space: range is not within java heap") ))
+            outStr.contains("UseSharedSpaces: Unable to allocate region, range is not within java heap") ))
         {
             return true;
         }
diff --git a/test/nashorn/script/nosecurity/JDK-8193137.js b/test/nashorn/script/nosecurity/JDK-8193137.js
new file mode 100644
index 00000000000..b1fef72ce77
--- /dev/null
+++ b/test/nashorn/script/nosecurity/JDK-8193137.js
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2017, 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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.
+ */
+
+/**
+ * JDK-8193137 : Nashorn crashes when given an empty script file.
+ *
+ * @test
+ * @option -scripting
+ * @run
+ */
+
+var System = java.lang.System;
+var File = java.io.File;
+var javahome = System.getProperty("java.home");
+var nashornJar = new File(System.getProperty("nashorn.jar"));
+if (! nashornJar.isAbsolute()) {
+    nashornJar = new File(".", nashornJar);
+}
+
+// we want to use nashorn.jar passed and not the one that comes with JRE
+var jjsCmd = javahome + "/../bin/jjs";
+jjsCmd = jjsCmd.toString().replace(/\//g, File.separator);
+if (! new File(jjsCmd).isFile()) {
+    jjsCmd = javahome + "/bin/jjs";
+    jjsCmd = jjsCmd.toString().replace(/\//g, File.separator);
+}
+jjsCmd += " -J--patch-module=jdk.scripting.nashorn=" + nashornJar;
+
+$ENV.PWD=System.getProperty("user.dir")
+
+var emptyFile = new File($ENV.PWD+File.separator+"empty.js");
+emptyFile.createNewFile();
+emptyFile.deleteOnExit();
+
+$EXEC(jjsCmd + " empty.js");
+if($ERR != "")
+    fail("jjs fails with empty script file");
diff --git a/test/nashorn/src/jdk/dynalink/beans/test/BeanLinkerTest.java b/test/nashorn/src/jdk/dynalink/beans/test/BeanLinkerTest.java
index 44c009fdc0d..00bf14ee92f 100644
--- a/test/nashorn/src/jdk/dynalink/beans/test/BeanLinkerTest.java
+++ b/test/nashorn/src/jdk/dynalink/beans/test/BeanLinkerTest.java
@@ -243,13 +243,56 @@ public class BeanLinkerTest {
         Assert.assertEquals((int) cs.getTarget().invoke(list, 1), (int) list.get(1));
         Assert.assertEquals((int) cs.getTarget().invoke(list, 2), (int) list.get(2));
         try {
-            final int x = (int) cs.getTarget().invoke(list, -1);
+            cs.getTarget().invoke(list, -1);
             throw new RuntimeException("expected IndexOutOfBoundsException");
         } catch (final IndexOutOfBoundsException ex) {
         }
 
         try {
-            final int x = (int) cs.getTarget().invoke(list, list.size());
+            cs.getTarget().invoke(list, list.size());
+            throw new RuntimeException("expected IndexOutOfBoundsException");
+        } catch (final IndexOutOfBoundsException ex) {
+        }
+    }
+
+    private Object invokeWithFixedKey(boolean publicLookup, Operation op, Object name, MethodType mt, Object... args) throws Throwable {
+        return createCallSite(publicLookup, op.named(name), mt).getTarget().invokeWithArguments(args);
+    }
+
+    @Test(dataProvider = "flags")
+    public void getElementWithFixedKeyTest(final boolean publicLookup) throws Throwable {
+        final MethodType mt = MethodType.methodType(int.class, Object.class);
+
+        final int[] arr = {23, 42};
+        Assert.assertEquals((int) invokeWithFixedKey(publicLookup, GET_ELEMENT, 0, mt, arr), 23);
+        Assert.assertEquals((int) invokeWithFixedKey(publicLookup, GET_ELEMENT, 1, mt, arr), 42);
+        try {
+            invokeWithFixedKey(publicLookup, GET_ELEMENT, -1, mt, arr);
+            throw new RuntimeException("expected ArrayIndexOutOfBoundsException");
+        } catch (final ArrayIndexOutOfBoundsException ex) {
+        }
+
+        try {
+            invokeWithFixedKey(publicLookup, GET_ELEMENT, arr.length, mt, arr);
+            throw new RuntimeException("expected ArrayIndexOutOfBoundsException");
+        } catch (final ArrayIndexOutOfBoundsException ex) {
+        }
+
+        final List<Integer> list = new ArrayList<>();
+        list.add(23);
+        list.add(430);
+        list.add(-4354);
+        for (int i = 0; i < 3; ++i) {
+            Assert.assertEquals((int) invokeWithFixedKey(publicLookup, GET_ELEMENT, i, mt, list), (int) list.get(i));
+        }
+        try {
+            invokeWithFixedKey(publicLookup, GET_ELEMENT, -1, mt, list);
+            throw new RuntimeException("expected IndexOutOfBoundsException");
+        } catch (final IndexOutOfBoundsException ex) {
+        }
+
+        try {
+            invokeWithFixedKey(publicLookup, GET_ELEMENT, list.size(), mt, list);
             throw new RuntimeException("expected IndexOutOfBoundsException");
         } catch (final IndexOutOfBoundsException ex) {
         }
@@ -286,7 +329,9 @@ public class BeanLinkerTest {
         cs.getTarget().invoke(list, 0, -list.get(0));
         Assert.assertEquals((int) list.get(0), -23);
         cs.getTarget().invoke(list, 1, -430);
+        Assert.assertEquals((int) list.get(1), -430);
         cs.getTarget().invoke(list, 2, 4354);
+        Assert.assertEquals((int) list.get(2), 4354);
         try {
             cs.getTarget().invoke(list, -1, 343);
             throw new RuntimeException("expected IndexOutOfBoundsException");
@@ -300,6 +345,52 @@ public class BeanLinkerTest {
         }
     }
 
+    @Test(dataProvider = "flags")
+    public void setElementWithFixedKeyTest(final boolean publicLookup) throws Throwable {
+        final MethodType mt = MethodType.methodType(void.class, Object.class, int.class);
+
+        final int[] arr = {23, 42};
+        invokeWithFixedKey(publicLookup, SET_ELEMENT, 0, mt, arr, 0);
+        Assert.assertEquals(arr[0], 0);
+        invokeWithFixedKey(publicLookup, SET_ELEMENT, 1, mt, arr, -5);
+        Assert.assertEquals(arr[1], -5);
+
+        try {
+            invokeWithFixedKey(publicLookup, SET_ELEMENT, -1, mt, arr, 12);
+            throw new RuntimeException("expected ArrayIndexOutOfBoundsException");
+        } catch (final ArrayIndexOutOfBoundsException ex) {
+        }
+
+        try {
+            invokeWithFixedKey(publicLookup, SET_ELEMENT, arr.length, mt, arr, 20);
+            throw new RuntimeException("expected ArrayIndexOutOfBoundsException");
+        } catch (final ArrayIndexOutOfBoundsException ex) {
+        }
+
+        final List<Integer> list = new ArrayList<>();
+        list.add(23);
+        list.add(430);
+        list.add(-4354);
+
+        invokeWithFixedKey(publicLookup, SET_ELEMENT, 0, mt, list, -list.get(0));
+        Assert.assertEquals((int) list.get(0), -23);
+        invokeWithFixedKey(publicLookup, SET_ELEMENT, 1, mt, list, -430);
+        Assert.assertEquals((int) list.get(1), -430);
+        invokeWithFixedKey(publicLookup, SET_ELEMENT, 2, mt, list, 4354);
+        Assert.assertEquals((int) list.get(2), 4354);
+        try {
+            invokeWithFixedKey(publicLookup, SET_ELEMENT, -1, mt, list, 343);
+            throw new RuntimeException("expected IndexOutOfBoundsException");
+        } catch (final IndexOutOfBoundsException ex) {
+        }
+
+        try {
+            invokeWithFixedKey(publicLookup, SET_ELEMENT, list.size(), mt, list, 43543);
+            throw new RuntimeException("expected IndexOutOfBoundsException");
+        } catch (final IndexOutOfBoundsException ex) {
+        }
+    }
+
     @Test(dataProvider = "flags")
     public void newObjectTest(final boolean publicLookup) {
         final MethodType mt = MethodType.methodType(Object.class, Object.class);